import { Node, ServoNode, VariableNode, CurveNode, MathNode, MapNode, NoiseNode } from './Node.js';

export const NODE_TYPES = {
    Node: 0x01,
    Servo: 0x02,
    Curve: 0x03,
    Noise: 0x04,
    Variable: 0x05,
    Math: 0x06,
    Map: 0x07
};

function GetNodeType(node) {
    console.log(node.constructor.name);
    switch (node.constructor.name) {
        case "ServoNode":
            return NODE_TYPES.Servo;
        case "CurveNode":
            return NODE_TYPES.Curve;
        case "NoiseNode":
            return NODE_TYPES.Noise;
        case "VariableNode":
            return NODE_TYPES.Variable;
        case "MathNode":
            return NODE_TYPES.Math;
        case "MapNode":
            return NODE_TYPES.Map;
        default:
            return NODE_TYPES.Node;
    }
}

export function encodeNodeGraph(nodes, connections) {
    const bufferSize = 1024; // adjust based on expected graph size
    const buffer = new ArrayBuffer(bufferSize);
    const view = new DataView(buffer);
    let offset = 0;


    // Node count (1 byte)
    view.setUint8(offset++, nodes.length);
    //console.log(ndoes);
    // Encode nodes
    nodes.forEach((node, index) => {
        node.id = index;
        node.type = GetNodeType(node);

        view.setUint8(offset++, node.type); // Node type
        view.setUint8(offset++, node.id);  // Node ID
        view.setUint16(offset, node.x, true); offset += 2;
        view.setUint16(offset, node.y, true); offset += 2;


        switch (node.type) {
            case NODE_TYPES.Servo:
                view.setUint8(offset++, node.controls[0].getValue());
                break;

            case NODE_TYPES.Curve:
                view.setUint8(offset++, node.controls[0].getValue());
                break;

            case NODE_TYPES.Noise:
                view.setFloat32(offset, node.controls[0].getValue(), true); offset += 4;
                view.setUint16(offset, node.controls[1].getValue()); offset += 2;
                break;


            case NODE_TYPES.Variable:
                view.setUint8(offset++, node.controls[0].getValue());
                view.setUint8(offset++, node.controls[1].getValue());
                break;


            case NODE_TYPES.Math:
                view.setUint8(offset++, node.controls[0].getValue());
                view.setFloat32(offset, node.controls[1].getValue(), true); offset += 4;
                break;

            case NODE_TYPES.Map:
                view.setFloat32(offset, node.controls[0].getValue(), true); offset += 4;
                view.setFloat32(offset, node.controls[1].getValue(), true); offset += 4;
                view.setFloat32(offset, node.controls[2].getValue(), true); offset += 4;
                view.setFloat32(offset, node.controls[3].getValue(), true); offset += 4;
                //console.log(node.inMinInput.numericValue, node.inMaxInput.numericValue, node.outMinInput.numericValue, node.outMaxInput.numericValue);
                break;

            default:
                console.warn("Unknown node type:", node);
        }
    });

    // Connection count (1 byte)
    view.setUint8(offset++, connections.length);

    // Encode connections
    connections.forEach(conn => {
        view.setUint8(offset++, conn.from.id);
        view.setUint8(offset++, conn.to.id);
    });

    // Slice the buffer to actual used size
    return new Uint8Array(buffer.slice(0, offset));
}

export function loadFromBinary(editor, data) {
    let nodes = []
    let connections = []
    const view = new DataView(data.buffer);
    let offset = 0;

    const nodeCount = view.getUint8(offset++);
    const idMap = {}; // Map node IDs to actual node instances

    for (let i = 0; i < nodeCount; i++) {
        const type = view.getUint8(offset++);
        const id = view.getUint8(offset++);
        const x = view.getUint16(offset, true); offset += 2;
        const y = view.getUint16(offset, true); offset += 2;

        let node = null;

        switch (type) {
            case NODE_TYPES.Servo: {
                const motorID = view.getUint8(offset++);
                const newNode = new ServoNode(x, y, motorID);
                editor.nodes.push(newNode);
                node = newNode;
                break;
            }
            case NODE_TYPES.Curve: {
                const curveID = view.getUint8(offset++);
                const newNode = new CurveNode(x, y, curveID);
                editor.nodes.push(newNode);
                node = newNode;
                break;
            }
            case NODE_TYPES.Noise: {
                const frequency = view.getFloat32(offset, true); offset += 4;
                const seed = view.getUint16(offset, true); offset += 2;
                const newNode = new NoiseNode(x, y);
                editor.nodes.push(newNode);
                node = newNode;
                node.controls[0].text = String(frequency);
                node.controls[1].text = String(seed);
                break;
            }
            case NODE_TYPES.Variable: {
                const source = view.getUint8(offset++);
                const arg0 = view.getUint8(offset++);
                const newNode = new VariableNode(x, y);
                editor.nodes.push(newNode);
                node = newNode;
                node.controls[0].selectedIndex = source;
                node.controls[1].value = arg0;
                break;
            }
            case NODE_TYPES.Math: {
                const op = view.getUint8(offset++);
                const value = view.getFloat32(offset, true); offset += 4;
                const newNode = new MathNode(x, y);
                editor.nodes.push(newNode);
                node = newNode;
                node.controls[0].selectedIndex = op;
                node.controls[1].text = String(value);
                break;
            }
            case NODE_TYPES.Map: {
                const inMin = view.getFloat32(offset, true); offset += 4;
                const inMax = view.getFloat32(offset, true); offset += 4;
                const outMin = view.getFloat32(offset, true); offset += 4;
                const outMax = view.getFloat32(offset, true); offset += 4;
                const newNode = new MapNode(x, y);
                editor.nodes.push(newNode);
                node = newNode;
                node.controls[0].text = String(inMin);
                node.controls[1].text = String(inMax);
                node.controls[2].text = String(outMin);
                node.controls[3].text = String(outMax);
                break;
            }
            default: {
                console.error("UNKNOWN NODE");
                break;
            }
        }


        if (node) {
            node.id = id;
            idMap[id] = node;
        }
        //editor.nodes.push(node);
    }

    //console.log(this.getNodeByID(0));

    // 🔗 Load connections
    const connectionCount = view.getUint8(offset++);
    for (let i = 0; i < connectionCount; i++) {
        const fromID = view.getUint8(offset++);
        const toID = view.getUint8(offset++);
        const fromNode = idMap[fromID];
        const toNode = idMap[toID];
        if (fromNode && toNode) {
            editor.connections.push({ from: fromNode, to: toNode });
        }
    }


    // Reencode all positions to SIGNED ints
    editor.nodes.forEach(node => {
        node.x = (node.x << 16) >> 16;
        node.y = (node.y << 16) >> 16;
    });


    //editor.nodes = nodes;
    //editor.connections = connections;
    console.log(editor.nodes);
    console.log(editor.connections);
    editor.draw();
    //this._redraw();
}