// /blocks/generators/json_base_generator.js

export function defineJsonBaseGenerator() {
    if (!Blockly.JSON) {
        Blockly.JSON = new Blockly.Generator('JSON');

        // Define operator precedence constants
        Blockly.JSON.ORDER_ATOMIC = 0;        // 0 "" ...
        Blockly.JSON.ORDER_NONE = 99;         // (no precedence)
        Blockly.JSON.ORDER_COMMA = 1;         // ,
        Blockly.JSON.ORDER_ASSIGNMENT = 2;    // =

        /**
         * Override the default blockToCode method to generate JSON from an individual block.
         */
        Blockly.JSON.blockToCode = function(block) {
            if (Blockly.JSON && Blockly.JSON[block.type]) {
                const jsonObject = Blockly.JSON[block.type](block);
                if (jsonObject && typeof jsonObject === 'object') {
                    return jsonObject;
                } else {
                    console.error(`Generator for block type '${block.type}' did not return a valid JSON object.`);
                    return null;
                }
            } else {
                console.error(`No generator function found for block type '${block.type}'.`);
                return null;
            }
        };

        /**
         * safeParse Function
         *
         * Description:
         * Safely parses a string into a JavaScript value. Attempts to parse as JSON,
         * number, boolean, or defaults to the original string if parsing fails.
         *
         * @param {string} code - The string to parse.
         * @returns {*} The parsed value, or the original string if parsing fails.
         */
        Blockly.JSON.safeParse = function(code) {
            // Attempt to parse as JSON
            try {
                return JSON.parse(code);
            } catch (jsonError) {
                // If JSON parsing fails, proceed to other types
            }

            // Trim the code to handle leading/trailing whitespace
            const trimmedCode = code.trim();

            // Attempt to parse as a number
            const number = parseFloat(trimmedCode);
            if (!isNaN(number)) {
                return number;
            }

            // Attempt to parse as a boolean
            if (trimmedCode.toLowerCase() === 'true') {
                return true;
            }
            if (trimmedCode.toLowerCase() === 'false') {
                return false;
            }

            // If all parsing attempts fail, return the original string
            return trimmedCode;
        };

        /**
         * Helper Function: createOperationObject
         * Creates an operation object with the specified type and inputs.
         * This function standardizes the creation of operation objects across generators.
         *
         * @param {string} type - The type of the operation (e.g., 'power', 'sqrt').
         * @param {object} inputs - An object containing the inputs for the operation.
         * @returns {object} - The operation object.
         */
        Blockly.JSON.createOperationObject = function(type, inputs) {
            return {
                type: type,
                inputs: inputs
            };
        };

        /**
         * Converts a Blockly block into a JSON representation.
         * Handles both custom generators and default JSON object creation.
         *
         * @param {Block} block - The block to convert.
         * @param {number} currentDepth - The current depth of the block in the workspace (for handling nesting).
         * @returns {object|null} - The JSON representation of the block, or null if conversion fails.
         */
        Blockly.JSON._blockToJson = function(block, currentDepth = 0) {
            // If the block is null, return null
            if (!block) {
                return null;
            }

            // Handle situations where recursion exceeds the maximum allowed depth.
            try {
                Blockly.JSON._calculateNestingDepth(block, currentDepth);
            } catch (error) {
                console.error(error.message);
                alert(error.message);
                throw error;
            }

            let json;

            if (Blockly.JSON && Blockly.JSON[block.type]) {
                // Custom generator exists
                json = Blockly.JSON[block.type](block);

                if (!json || typeof json !== 'object') {
                    console.error(`Generator for block type '${block.type}' did not return a valid JSON object.`);
                    return null;
                }

                // Skip additional parsing if specified
                if (json.skipAdditionalParsing) {
                    delete json.skipAdditionalParsing;
                    return json;
                }

            } else {
                // No custom generator, create a default JSON object
                json = {
                    type: block.type
                };

                // Capture fields and process inputs
                Blockly.JSON._captureFields(block, json);
                Blockly.JSON._processInputs(block, json, currentDepth);

            }
            // Handle 'next' connections recursively
            if (block.getNextBlock()) {
                json.next = Blockly.JSON._blockToJson(block.getNextBlock(), currentDepth);
            }

            return json;
        };

        /**
         * Processes the inputs of a block and adds them to the JSON representation.
         * Handles both value and statement inputs.
         *
         * @param {Blockly.Block} block - The block whose inputs are to be processed.
         * @param {object} json - The JSON object to populate with input data.
         * @param {number} currentDepth - The current depth of the block in the workspace.
         */
        Blockly.JSON._processInputs = function(block, json, currentDepth) {
            // Iterate over each input in the block's input list
            block.inputList.forEach(input => {
                // If the input does not have a connection to another block, skip to the next input
                if (!input.connection || !input.connection.targetBlock()) return;

                // Get and convert the connected block to JSON, increasing the depth
                const targetBlock = input.connection.targetBlock();
                const targetJson = Blockly.JSON._blockToJson(targetBlock, currentDepth + 1);

                // If the input is a value input, add it to the 'inputs' section of the json
                if (input.type === Blockly.INPUT_VALUE) {
                    if (!json.inputs) json.inputs = {};
                    json.inputs[input.name] = targetJson;
                }
                // If the input is a statement input, add it to the 'statements' section of the json
                else if (input.type === Blockly.NEXT_STATEMENT) {
                    if (!json.statements) json.statements = {};
                    if (!json.statements[input.name]) json.statements[input.name] = [];
                    json.statements[input.name].push(targetJson);
                }
            });
        };


        /**
         * Captures the fields of a block and adds them to the JSON representation.
         *
         * @param {Blockly.Block} block - The block whose fields are to be captured.
         * @param {object} json - The JSON object to populate with field data.
         */
        Blockly.JSON._captureFields = function(block, json) {
            // Iterate over each input in the block's input list
            block.inputList.forEach(input => {
                // If the input doesn't contain a fieldRow, skip to the next input.
                if (!input.fieldRow) return;

                // Iterate over each field in the fieldRow
                input.fieldRow.forEach(field => {
                    // If the field has no name or method to get its value, skip to the next field.
                    if (!field.name || !field.getValue) return;

                    // Initialize the fields object in JSON if it doesn't exist
                    if (!json.fields) json.fields = {};
                    // Add the field name and its value to the JSON fields object
                    json.fields[field.name] = field.getValue();
                });
            });
        };


        /**
         * Helper Function: _calculateNestingDepth
         * Calculates the nesting depth of a block and throws an error if it exceeds the maximum allowed depth.
         *
         * @param {Blockly.Block} block - The block to calculate depth for.
         * @param {number} currentDepth - The current recursion depth.
         */
        Blockly.JSON._calculateNestingDepth = function(block, currentDepth) {
            if (currentDepth > Blockly.JSON.maxDepth) {
                throw new Error(`Maximum block nesting depth of ${Blockly.JSON.maxDepth} exceeded.`);
            }
            // Add any additional depth calculations or checks if necessary
        };

        /**
         * Helper Function: extractValues
         * Extracts values from a block's input, handling dynamic_value arrays or single values.
         *
         * @param {Blockly.Block} block - The block containing the input.
         * @param {string} inputName - The name of the input field.
         * @param {number | null} defaultValue - Default value for single extractions (null for array extraction).
         * @returns {Array | number} - An array of values if defaultValue is null; otherwise, the first value or the default.
         */
        Blockly.JSON.extractValues = function(block, inputName, defaultValue = null) {
            const inputBlock = block.getInputTargetBlock(inputName);
            if (!inputBlock) {
                if (defaultValue !== null) {
                    console.warn(`No block connected to '${inputName}' input of '${block.type}' block. Using default value ${defaultValue}.`);
                    return defaultValue;
                }
                return []; // Return empty array for multi-value cases
            }

            const inputJson = Blockly.JSON.blockToCode(inputBlock);

            if (inputJson?.type === 'dynamic_value' && Array.isArray(inputJson.values)) {
                return defaultValue !== null ? inputJson.values[0] : inputJson.values;
            }

            if (defaultValue !== null) {
                console.error(`Invalid JSON structure for '${inputName}' in '${block.type}' block. Expected a 'dynamic_value' with at least one value.`);
                return defaultValue;
            }

            return Array.isArray(inputJson) ? inputJson : [inputJson];
        };

        // Usage in processValuesInput (array case)
        Blockly.JSON.processValuesInput = function(block) {
            return Blockly.JSON.extractValues(block, 'VALUES');
        };

        // Usage in processNumericInput (single numeric case)
        Blockly.JSON.processNumericInput = function(block, inputName, defaultValue) {
            return Blockly.JSON.extractValues(block, inputName, defaultValue);
        };


        console.log('Blockly.JSON generator initialized with helper functions: createOperationObject, _blockToJson, collectTradeOptions.');
    }
}
