/**
 * Advanced Math Generators Definitions
 *
 * This file defines the generator functions for advanced math blocks.
 * Each generator corresponds to a block defined in 'advanced_math_blocks.js'.
 * The generators handle a single 'VALUES' input, which is a 'dynamic_value' containing values.
 * The output is a 'dynamic_value' containing the result operation object and any unused values.
 */

export function defineAdvancedMathGenerators() {

    // Ensure the base generator is initialized and 'createOperationObject' is available
    if (!Blockly.JSON || !Blockly.JSON.createOperationObject) {
        console.error("Blockly.JSON or createOperationObject not initialized. Please ensure 'json_base_generator.js' is loaded first.");
        return;
    }

    /**
     * Generator for 'power' Block
     */
    Blockly.JSON['power'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const base = valuesArray[0] !== undefined ? valuesArray[0] : 0;
        const exponent = valuesArray[1] !== undefined ? valuesArray[1] : 1;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('power', {
            base: base,
            exponent: exponent
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 2) {
            outputValues.push(...valuesArray.slice(2));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'modulo' Block
     */
    Blockly.JSON['modulo'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const dividend = valuesArray[0] !== undefined ? valuesArray[0] : 0;
        const divisor = valuesArray[1] !== undefined ? valuesArray[1] : 1;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('modulo', {
            dividend: dividend,
            divisor: divisor
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 2) {
            outputValues.push(...valuesArray.slice(2));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'sqrt' Block
     */
    Blockly.JSON['sqrt'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('sqrt', {
            number: number
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 1) {
            outputValues.push(...valuesArray.slice(1));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'abs' Block
     */
    Blockly.JSON['abs'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('abs', {
            number: number
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 1) {
            outputValues.push(...valuesArray.slice(1));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'max' Block
     */
    Blockly.JSON['max'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('max', {
            numbers: valuesArray
        });

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: [operationObject]
        };
    };

    /**
     * Generator for 'min' Block
     */
    Blockly.JSON['min'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('min', {
            numbers: valuesArray
        });

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: [operationObject]
        };
    };

    /**
     * Generator for 'factorial' Block
     */
    Blockly.JSON['factorial'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 1;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('factorial', {
            number: number
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 1) {
            outputValues.push(...valuesArray.slice(1));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'log' Block
     */
    Blockly.JSON['log'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 1;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('log', {
            number: number
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 1) {
            outputValues.push(...valuesArray.slice(1));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'ln' Block
     */
    Blockly.JSON['ln'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 1;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('ln', {
            number: number
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 1) {
            outputValues.push(...valuesArray.slice(1));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'trig' Block
     */
    Blockly.JSON['trig'] = function(block) {
        const operator = block.getFieldValue('operator'); // 'sin', 'cos', 'tan'
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const angle = valuesArray[0] !== undefined ? valuesArray[0] : 0;

        // Validate operator
        const validOperators = ['sin', 'cos', 'tan'];
        if (!validOperators.includes(operator)) {
            console.error(`Invalid operator '${operator}' in 'trig' block. Defaulting to 'sin'.`);
        }
        const validatedOperator = validOperators.includes(operator) ? operator : 'sin';

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('trig', {
            operator: validatedOperator,
            angle: angle
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 1) {
            outputValues.push(...valuesArray.slice(1));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'mean' Block
     */
    Blockly.JSON['mean'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('mean', {
            numbers: valuesArray
        });

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: [operationObject]
        };
    };

    /**
     * Generator for 'median' Block
     */
    Blockly.JSON['median'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('median', {
            numbers: valuesArray
        });

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: [operationObject]
        };
    };

    /**
     * Generator for 'std_dev' Block
     */
    Blockly.JSON['std_dev'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('std_dev', {
            numbers: valuesArray
        });

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: [operationObject]
        };
    };

    /**
     * Generator for 'round' Block
     */
    Blockly.JSON['round'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;
        const decimals = valuesArray[1] !== undefined ? valuesArray[1] : 0;

        // Validate decimals
        const validatedDecimals = Number.isInteger(decimals) && decimals >= 0 ? decimals : 0;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('round', {
            number: number,
            decimals: validatedDecimals
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 2) {
            outputValues.push(...valuesArray.slice(2));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'floor' Block
     */
    Blockly.JSON['floor'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('floor', {
            number: number
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 1) {
            outputValues.push(...valuesArray.slice(1));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'ceil' Block
     */
    Blockly.JSON['ceil'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('ceil', {
            number: number
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 1) {
            outputValues.push(...valuesArray.slice(1));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'random' Block
     */
    Blockly.JSON['random'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        let min, max;
        if (valuesArray.length >= 2) {
            min = valuesArray[0];
            max = valuesArray[1];
        } else if (valuesArray.length === 1) {
            min = 0;
            max = valuesArray[0];
        } else {
            min = 0;
            max = 1;
        }

        // Validate min and max
        const validatedMin = typeof min === 'number' ? min : 0;
        const validatedMax = typeof max === 'number' ? max : 1;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('random', {
            min: validatedMin,
            max: validatedMax
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 2) {
            outputValues.push(...valuesArray.slice(2));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'clamp' Block
     */
    Blockly.JSON['clamp'] = function(block) {
        const valuesArray = Blockly.JSON.processValuesInput(block);

        const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;
        const min = valuesArray[1] !== undefined ? valuesArray[1] : null;
        const max = valuesArray[2] !== undefined ? valuesArray[2] : null;

        // Validate min and max
        const validatedMin = typeof min === 'number' ? min : null;
        const validatedMax = typeof max === 'number' ? max : null;

        // Create operation object
        const operationObject = Blockly.JSON.createOperationObject('clamp', {
            number: number,
            min: validatedMin,
            max: validatedMax
        });

        // Include unused values if any
        const outputValues = [operationObject];
        if (valuesArray.length > 3) {
            outputValues.push(...valuesArray.slice(3));
        }

        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: outputValues
        };
    };

    /**
     * Generator for 'math_operation' Block
     */
    Blockly.JSON['math_operation'] = function(block) {
          /**
         * Helper Function: _extractValues
         * Extracts values from a given input block, handling 'dynamic_value' objects, arrays, or single values.
         *
         * @param {Blockly.Block} inputBlock - The block to extract values from.
         * @returns {Array} - An array of values extracted from the input block.
         */
        Blockly.JSON._extractValues = function(inputBlock) {
            if (!inputBlock) return [];
            const inputJson = Blockly.JSON.blockToCode(inputBlock);
            if (inputJson?.type === 'dynamic_value' && Array.isArray(inputJson.values)) {
                return inputJson.values; // Use values directly if it's a 'dynamic_value' array
            }
            return Array.isArray(inputJson) ? inputJson : [inputJson]; // Wrap in array if not already an array
        };

        const operator = block.getFieldValue('operator'); // 'ADD', 'SUBTRACT', 'MULTIPLY', 'DIVIDE'
        const leftValues = Blockly.JSON._extractValues(block.getInputTargetBlock('LEFT'));
        const rightValues = Blockly.JSON._extractValues(block.getInputTargetBlock('RIGHT'));

        // Use the first values or default to 0 if not available
        const leftOperand = leftValues[0] ?? 0;
        const rightOperand = rightValues[0] ?? 0;

        // Validate and format the operator
        const validOperators = ['ADD', 'SUBTRACT', 'MULTIPLY', 'DIVIDE'];
        const validatedOperator = validOperators.includes(operator) ? operator.toLowerCase() : 'add';
        if (!validOperators.includes(operator)) {
            console.error(`Invalid operator '${operator}' in 'math_operation' block. Defaulting to 'ADD'.`);
        }

        // Create the primary operation object
        const operationObject = Blockly.JSON.createOperationObject('math_operation', {
            operator: validatedOperator,
            left_operand: leftOperand,
            right_operand: rightOperand
        });


        // Output as dynamic_value
        return {
            type: 'dynamic_value',
            values: [operationObject]
        };
    };

}
