/* eslint-disable import/no-cycle */
import Scope, { scopeList, switchCircuit, newCircuit } from './circuit';

import { loadScope } from './data/load';
import {
    scheduleUpdate, updateSimulationSet, updateSubcircuitSet, forceResetNodesSet,
} from './engine';
import { backUp } from './data/backupCircuit';
import { getNextPosition } from './modules';
import { generateId } from './utils';
import simulationArea from './simulationArea';

/**
 * Helper function to paste
 * @param {JSON} copyData - the data to be pasted
 * @category events
 */
export function paste(copyData) {
    if (copyData === undefined) return;
    var data = JSON.parse(copyData);
    if (!data.logixClipBoardData) return;

    var currentScopeId = globalScope.id;
    for (let i = 0; i < data.scopes.length; i++) {
        if (scopeList[data.scopes[i].id] === undefined) {
            var isVerilogCircuit = false;
            var isMainCircuit = false;
            if(data.scopes[i].verilogMetadata) {
                isVerilogCircuit = data.scopes[i].verilogMetadata.isVerilogCircuit;
                isMainCircuit = data.scopes[i].verilogMetadata.isMainCircuit;
            }
            var scope = newCircuit(data.scopes[i].name, data.scopes[i].id, isVerilogCircuit, isMainCircuit);
            loadScope(scope, data.scopes[i]);
            scopeList[data.scopes[i].id] = scope;
        }
    }

    switchCircuit(currentScopeId);
    var tempScope = new Scope(globalScope.name, globalScope.id);
    var oldOx = globalScope.ox;
    var oldOy = globalScope.oy;
    var oldScale = globalScope.scale;
    loadScope(tempScope, data);

    var prevLength = tempScope.allNodes.length;
    for (let i = 0; i < tempScope.allNodes.length; i++) {
        tempScope.allNodes[i].checkDeleted();
        if (tempScope.allNodes.length != prevLength) {
            prevLength--;
            i--;
        }
    }

    var approxX = 0;
    var approxY = 0;
    var count = 0;

    for (let i = 0; i < updateOrder.length; i++) {
        for (let j = 0; j < tempScope[updateOrder[i]].length; j++) {
            const obj = tempScope[updateOrder[i]][j];
            obj.updateScope(globalScope);
            if (obj.objectType != 'Wire') {
                approxX += obj.x;
                approxY += obj.y;
                count++;
            }
        }
    }

    for (let j = 0; j < tempScope.CircuitElement.length; j++) {
        const obj = tempScope.CircuitElement[j];
        obj.updateScope(globalScope);
    }

    approxX /= count;
    approxY /= count;

    approxX = Math.round(approxX / 10) * 10;
    approxY = Math.round(approxY / 10) * 10;


    for (let i = 0; i < updateOrder.length; i++) {
        for (let j = 0; j < tempScope[updateOrder[i]].length; j++) {
            const obj = tempScope[updateOrder[i]][j];
            if (obj.objectType !== 'Wire') {
                obj.x += simulationArea.mouseX - approxX;
                obj.y += simulationArea.mouseY - approxY;
            }
        }
    }

    Object.keys(tempScope).forEach((l) => {
        if (tempScope[l] instanceof Array && l !== 'objects' && l !== 'CircuitElement') {
            globalScope[l].extend(tempScope[l]);
        }
    });
    for (let i = 0; i < tempScope.Input.length; i++) {
        tempScope.Input[i].layoutProperties.y = getNextPosition(0, globalScope);
        tempScope.Input[i].layoutProperties.id = generateId();
    }
    for (let i = 0; i < tempScope.Output.length; i++) {
        tempScope.Output[i].layoutProperties.x = globalScope.layout.width;
        tempScope.Output[i].layoutProperties.id = generateId();
        tempScope.Output[i].layoutProperties.y = getNextPosition(globalScope.layout.width, globalScope);
    }
    var canvasUpdate = true;
    updateSimulationSet(true);
    updateSubcircuitSet(true);
    scheduleUpdate();
    globalScope.ox = oldOx;
    globalScope.oy = oldOy;
    globalScope.scale = oldScale;


    forceResetNodesSet(true);
}
/**
 * Helper function for cut
 * @param {JSON} copyList - The selected elements
 * @category events
 */
export function cut(copyList) {
    if (copyList.length === 0) return;
    var tempScope = new Scope(globalScope.name, globalScope.id);
    var oldOx = globalScope.ox;
    var oldOy = globalScope.oy;
    var oldScale = globalScope.scale;
    d = backUp(globalScope);
    loadScope(tempScope, d);
    scopeList[tempScope.id] = tempScope;

    for (let i = 0; i < copyList.length; i++) {
        const obj = copyList[i];
        if (obj.objectType === 'Node') obj.objectType = 'allNodes';
        for (let j = 0; j < tempScope[obj.objectType].length; j++) {
            if (tempScope[obj.objectType][j].x === obj.x && tempScope[obj.objectType][j].y === obj.y && (obj.objectType != 'Node' || obj.type === 2)) {
                tempScope[obj.objectType][j].delete();
                break;
            }
        }
    }
    tempScope.backups = globalScope.backups;
    for (let i = 0; i < updateOrder.length; i++) {
        let prevLength = globalScope[updateOrder[i]].length; // LOL length of list will reduce automatically when deletion starts
        for (let j = 0; j < globalScope[updateOrder[i]].length; j++) {
            const obj = globalScope[updateOrder[i]][j];
            if (obj.objectType != 'Wire') { // }&&obj.objectType!='CircuitElement'){//}&&(obj.objectType!='Node'||obj.type==2)){
                if (!copyList.contains(globalScope[updateOrder[i]][j])) {
                    globalScope[updateOrder[i]][j].cleanDelete();
                }
            }

            if (globalScope[updateOrder[i]].length != prevLength) {
                prevLength--;
                j--;
            }
        }
    }

    var prevLength = globalScope.wires.length;
    for (let i = 0; i < globalScope.wires.length; i++) {
        globalScope.wires[i].checkConnections();
        if (globalScope.wires.length != prevLength) {
            prevLength--;
            i--;
        }
    }

    updateSimulationSet(true);

    var data = backUp(globalScope);
    data.logixClipBoardData = true;
    var dependencyList = globalScope.getDependencies();
    data.dependencies = {};
    Object.keys(dependencyList).forEach((dependency) => {
        data.dependencies[dependency] = backUp(scopeList[dependency]);
    });
    data.logixClipBoardData = true;
    data = JSON.stringify(data);

    simulationArea.multipleObjectSelections = []; // copyList.slice();
    simulationArea.copyList = []; // copyList.slice();
    var canvasUpdate = true;
    updateSimulationSet(true);
    globalScope = tempScope;
    scheduleUpdate();
    globalScope.ox = oldOx;
    globalScope.oy = oldOy;
    globalScope.scale = oldScale;
    forceResetNodesSet(true);
    // eslint-disable-next-line consistent-return
    return data;
}
/**
 * Helper function for copy
 * @param {JSON} copyList - The data to copied
 * @param {boolean} cutflag - flase if we want to copy
 * @category events
 */
export function copy(copyList, cutflag = false) {
    if (copyList.length === 0) return;
    var tempScope = new Scope(globalScope.name, globalScope.id);
    var oldOx = globalScope.ox;
    var oldOy = globalScope.oy;
    var oldScale = globalScope.scale;
    var d = backUp(globalScope);

    loadScope(tempScope, d);
    scopeList[tempScope.id] = tempScope;

    if (cutflag) {
        for (let i = 0; i < copyList.length; i++) {
            const obj = copyList[i];
            if (obj.objectType === 'Node') obj.objectType = 'allNodes';
            for (let j = 0; j < tempScope[obj.objectType].length; j++) {
                if (tempScope[obj.objectType][j].x === obj.x && tempScope[obj.objectType][j].y === obj.y && (obj.objectType != 'Node' || obj.type === 2)) {
                    tempScope[obj.objectType][j].delete();
                    break;
                }
            }
        }
    }
    tempScope.backups = globalScope.backups;
    for (let i = 0; i < updateOrder.length; i++) {
        let prevLength = globalScope[updateOrder[i]].length; // LOL length of list will reduce automatically when deletion starts
        for (let j = 0; j < globalScope[updateOrder[i]].length; j++) {
            const obj = globalScope[updateOrder[i]][j];
            if (obj.objectType != 'Wire') { // }&&obj.objectType!='CircuitElement'){//}&&(obj.objectType!='Node'||obj.type==2)){
                if (!copyList.contains(globalScope[updateOrder[i]][j])) {
                    globalScope[updateOrder[i]][j].cleanDelete();
                }
            }

            if (globalScope[updateOrder[i]].length != prevLength) {
                prevLength--;
                j--;
            }
        }
    }

    var prevLength = globalScope.wires.length;
    for (let i = 0; i < globalScope.wires.length; i++) {
        globalScope.wires[i].checkConnections();
        if (globalScope.wires.length != prevLength) {
            prevLength--;
            i--;
        }
    }

    updateSimulationSet(true);

    var data = backUp(globalScope);
    data.scopes = [];
    var dependencyList = {};
    var requiredDependencies = globalScope.getDependencies();
    var completed = {};
    Object.keys(scopeList).forEach((id) => {
        dependencyList[id] = scopeList[id].getDependencies();
    });
    function saveScope(id) {
        if (completed[id]) return;
        for (let i = 0; i < dependencyList[id].length; i++) { saveScope(dependencyList[id][i]); }
        completed[id] = true;
        data.scopes.push(backUp(scopeList[id]));
    }
    for (let i = 0; i < requiredDependencies.length; i++) { saveScope(requiredDependencies[i]); }
    data.logixClipBoardData = true;
    data = JSON.stringify(data);
    simulationArea.multipleObjectSelections = []; // copyList.slice();
    simulationArea.copyList = []; // copyList.slice();
    var canvasUpdate = true;
    updateSimulationSet(true);
    globalScope = tempScope;
    scheduleUpdate();
    globalScope.ox = oldOx;
    globalScope.oy = oldOy;
    globalScope.scale = oldScale;
    forceResetNodesSet(true);
    // needs to be fixed
    // eslint-disable-next-line consistent-return
    return data;
}

/**
 * Function selects all the elements from the scope
 * @category events
 */
export function selectAll(scope = globalScope) {
    moduleList.forEach((val, _, __) => {
        if (scope.hasOwnProperty(val)) {
            simulationArea.multipleObjectSelections.push(...scope[val]);
        }
    });

    if (scope.nodes) {
        simulationArea.multipleObjectSelections.push(...scope.nodes);
    }
}
