import { Observable } from '@zephyr3d/base';

/** @internal */ function getNodeTypeComponents(type) {
    switch(type){
        case 'float':
        case 'bool':
            return 1;
        case 'vec2':
        case 'bvec2':
            return 2;
        case 'vec3':
        case 'bvec3':
            return 3;
        case 'vec4':
        case 'bvec4':
            return 4;
        default:
            return 0;
    }
}
/**
 * Abstract base class for graph nodes
 *
 * @remarks
 * Provides common functionality for all graph node types:
 * - Input/output slot storage
 * - Error state management
 * - Basic validation logic
 * - Change event emission
 *
 * Subclasses must implement:
 * - `getType()`: Define output type logic
 * - Optionally override `validate()` for custom validation
 * - Optionally override `toString()` for code generation
 *
 * @example
 * ```typescript
 * class MultiplyNode extends BaseGraphNode {
 *   constructor() {
 *     super();
 *     this._inputs = [
 *       { id: 0, name: 'a', type: ['float', 'vec2', 'vec3'], required: true },
 *       { id: 1, name: 'b', type: ['float', 'vec2', 'vec3'], required: true }
 *     ];
 *     this._outputs = [
 *       { id: 0, name: 'result' }
 *     ];
 *   }
 *
 *   protected getType(id?: number): string {
 *     // Return type based on input types
 *     return this._inputs[0].inputNode?.getOutputType(0) ?? 'float';
 *   }
 *
 *   toString(): string {
 *     return `${this.getType()} result = a * b;`;
 *   }
 * }
 * ```
 *
 * @public
 */ class BaseGraphNode extends Observable {
    /** Internal storage for input slot definitions */ _inputs;
    /** Internal storage for output slot definitions */ _outputs;
    /** Internal storage for error message */ _error;
    /**
   * Creates a new BaseGraphNode instance
   *
   * @remarks
   * Initializes empty input/output arrays and error state.
   * Subclasses should populate `_inputs` and `_outputs` in their constructor.
   */ constructor(){
        super();
        this._inputs = [];
        this._outputs = [];
        this._error = '';
    }
    /**
   * Whether this node contains uniform value/texture
   */ get isUniform() {
        return false;
    }
    /**
   * Uniform parameter name
   */ get paramName() {
        return '';
    }
    /**
   * Gets the output type for a specific output slot
   *
   * @param id - The output slot ID
   * @returns The type name of the output
   *
   * @remarks
   * Default implementation delegates to the abstract `getType()` method.
   * Can be overridden for more complex type logic.
   */ getOutputType(id) {
        return this.getType(id);
    }
    /** Gets the input slot definitions array */ get inputs() {
        return this._inputs;
    }
    /** Gets the output slot definitions array */ get outputs() {
        return this._outputs;
    }
    /** Gets the current error message */ get error() {
        return this._error;
    }
    set error(str) {
        this._error = str;
    }
    /**
   * Generates a string representation of the node
   *
   * @returns Empty string by default
   *
   * @remarks
   * Subclasses should override this to return valid shader code
   * or a meaningful description of the node's operation.
   */ toString() {
        return '';
    }
    /**
   * Validates the node and updates error state
   *
   * @remarks
   * Calls the `validate()` method and stores the result in `_error`.
   * Emits a 'changed' event if the error state changes.
   */ check() {
        this._error = this.validate();
    }
    /**
   * Clears the error state
   *
   * @remarks
   * Sets `_error` to an empty string.
   */ reset() {
        this._error = '';
    }
    /**
   * Connects an input slot to another node's output
   *
   * @param id - The input slot ID to connect
   * @param node - The source node to connect from
   * @param inputId - The output slot ID of the source node
   *
   * @throws Error if the input slot with the given ID doesn't exist
   *
   * @example
   * ```typescript
   * const addNode = new AddNode();
   * const constantNode = new ConstantNode(5.0);
   *
   * // Connect constantNode's output 0 to addNode's input 0
   * addNode.setInput(0, constantNode, 0);
   * ```
   */ setInput(id, node, inputId) {
        const input = this._inputs.find((input)=>input.id === id);
        if (!input) {
            throw new Error(`Input with id ${id} not found`);
        }
        input.inputNode = node;
        input.inputId = inputId;
    }
    /**
   * Validates the node's current state
   *
   * @returns Error message string, or empty string if valid
   *
   * @remarks
   * Default implementation checks if all required inputs are connected.
   * Subclasses can override to add custom validation logic like:
   * - Type compatibility checking
   * - Value range validation
   * - Configuration validation
   *
   * @example
   * ```typescript
   * protected validate(): string {
   *   const baseError = super.validate();
   *   if (baseError) return baseError;
   *
   *   // Custom validation
   *   const type1 = this._inputs[0].inputNode?.getOutputType(0);
   *   const type2 = this._inputs[1].inputNode?.getOutputType(0);
   *   if (type1 !== type2) {
   *     return 'Input types must match';
   *   }
   *
   *   return '';
   * }
   * ```
   */ validate() {
        for(let i = 0; i < this._inputs.length; i++){
            const input = this._inputs[i];
            if (input.required && !input.inputNode) {
                return `Missing required argument: input[${i}]`;
            }
            if (input.inputNode) {
                const type = this._inputs[i].inputNode.getOutputType(this._inputs[i].inputId);
                if (!type) {
                    return `Cannot determine type of argument ${i}`;
                }
                if (!this._inputs[i].type.includes(type)) {
                    return `Invalid input type ${type} for argument ${i}`;
                }
            }
        }
        return '';
    }
}

export { BaseGraphNode, getNodeTypeComponents };
//# sourceMappingURL=node.js.map
