function loadSubCircuit(savedData, scope) {
    var v = new SubCircuit(savedData["x"], savedData["y"], scope, savedData["id"], savedData);
    // if(v.version == "1.0"){
    // 	v.version = "2.0";
    // 	v.x-=v.width/2;
    // 	v.y-=v.height/2;
    // }
}

//subCircuit class
function SubCircuit(x, y, scope = globalScope, id = undefined, savedData = undefined) {

    this.id = id || prompt("Enter Id: ");
    var subcircuitScope = scopeList[this.id]; // Scope of the subcircuit

    // Error handing
    if (subcircuitScope == undefined) {
        showError("SubCircuit : " + ((savedData && savedData.title) || this.id) + " Not found");
    } else if (!checkIfBackup(subcircuitScope)) {
        showError("SubCircuit : " + ((savedData && savedData.title) || subcircuitScope.name) + " is an empty circuit");
    } else if (subcircuitScope.checkDependency(scope.id)) {
        showError("Cyclic Circuit Error");
    }

    // Error handling, cleanup
    if (subcircuitScope == undefined || subcircuitScope.checkDependency(scope.id) || !checkIfBackup(subcircuitScope)) {
        if (savedData) {
            for (var i = 0; i < savedData["inputNodes"].length; i++) {
                scope.allNodes[savedData["inputNodes"][i]].deleted = true;

            }
            for (var i = 0; i < savedData["outputNodes"].length; i++) {
                scope.allNodes[savedData["outputNodes"][i]].deleted = true;
            }
        }
        return;

    }

    CircuitElement.call(this, x, y, scope, "RIGHT", 1);
    this.directionFixed = true;
    this.fixedBitWidth = true;

    this.savedData = savedData;
    this.inputNodes = [];
    this.outputNodes = [];
    this.localScope = new Scope();

    if (this.savedData != undefined) {
        updateSubcircuit = true;
        scheduleUpdate();

        this.version = this.savedData["version"] || "1.0";

        this.id = this.savedData["id"];
        for (var i = 0; i < this.savedData["inputNodes"].length; i++) {
            this.inputNodes.push(this.scope.allNodes[this.savedData["inputNodes"][i]]);
            this.inputNodes[i].parent = this;
            this.inputNodes[i].layout_id = subcircuitScope.Input[i].layoutProperties.id
        }
        for (var i = 0; i < this.savedData["outputNodes"].length; i++) {
            this.outputNodes.push(this.scope.allNodes[this.savedData["outputNodes"][i]]);
            this.outputNodes[i].parent = this;
            this.outputNodes[i].layout_id = subcircuitScope.Output[i].layoutProperties.id;
        }
        if (this.version == "1.0") { // For backward compatibility
            this.version = "2.0";
            this.x -= subcircuitScope.layout.width / 2;
            this.y -= subcircuitScope.layout.height / 2;
            for (var i = 0; i < this.inputNodes.length; i++) {
                this.inputNodes[i].x = subcircuitScope.Input[i].layoutProperties.x;
                this.inputNodes[i].y = subcircuitScope.Input[i].layoutProperties.y;
                this.inputNodes[i].leftx = this.inputNodes[i].x;
                this.inputNodes[i].lefty = this.inputNodes[i].y;
            }
            for (var i = 0; i < this.outputNodes.length; i++) {
                this.outputNodes[i].x = subcircuitScope.Output[i].layoutProperties.x;
                this.outputNodes[i].y = subcircuitScope.Output[i].layoutProperties.y;
                this.outputNodes[i].leftx = this.outputNodes[i].x;
                this.outputNodes[i].lefty = this.outputNodes[i].y;
            }

        }

        if (this.version == "2.0") {
            this.leftDimensionX = 0;
            this.upDimensionY = 0;
            this.rightDimensionX = subcircuitScope.layout.width;
            this.downDimensionY = subcircuitScope.layout.height;
        }

        this.nodeList.extend(this.inputNodes);
        this.nodeList.extend(this.outputNodes);

    } else {
        this.version = "2.0";
    }

    this.data = JSON.parse(scheduleBackup(subcircuitScope));
    this.buildCircuit();
    this.makeConnections();

}

SubCircuit.prototype = Object.create(CircuitElement.prototype);
SubCircuit.prototype.constructor = SubCircuit;

SubCircuit.prototype.makeConnections = function() {
    for (let i = 0; i < this.inputNodes.length; i++) {
        this.localScope.Input[i].output1.connectWireLess(this.inputNodes[i]);
        this.localScope.Input[i].output1.subcircuitOverride = true;
    }

    for (let i = 0; i < this.outputNodes.length; i++) {
        this.localScope.Output[i].inp1.connectWireLess(this.outputNodes[i]);
        this.outputNodes[i].subcircuitOverride = true;
    }
}

SubCircuit.prototype.removeConnections = function() {
    
    for (let i = 0; i < this.inputNodes.length; i++) {
        this.localScope.Input[i].output1.disconnectWireLess(this.inputNodes[i]);
    }

    for (let i = 0; i < this.outputNodes.length; i++)
        this.localScope.Output[i].inp1.disconnectWireLess(this.outputNodes[i]);
}


SubCircuit.prototype.buildCircuit = function() {

    var subcircuitScope = scopeList[this.id];
    loadScope(this.localScope, this.data);
    this.lastUpdated = this.localScope.timeStamp;
    updateSimulation = true;
    updateCanvas = true;

    if (this.savedData == undefined) {
        this.leftDimensionX = 0;
        this.upDimensionY = 0;
        this.rightDimensionX = subcircuitScope.layout.width;
        this.downDimensionY = subcircuitScope.layout.height;

        for (var i = 0; i < subcircuitScope.Output.length; i++) {
            var a = new Node(subcircuitScope.Output[i].layoutProperties.x, subcircuitScope.Output[i].layoutProperties.y, 1, this, subcircuitScope.Output[i].bitWidth);
            a.layout_id = subcircuitScope.Output[i].layoutProperties.id;
            this.outputNodes.push(a);
        }
        for (var i = 0; i < subcircuitScope.Input.length; i++) {
            var a = new Node(subcircuitScope.Input[i].layoutProperties.x, subcircuitScope.Input[i].layoutProperties.y, 0, this, subcircuitScope.Input[i].bitWidth);
            a.layout_id = subcircuitScope.Input[i].layoutProperties.id;
            this.inputNodes.push(a);
        }
    }

}

// Needs to be deprecated, removed
SubCircuit.prototype.reBuild = function() {
    return;
    new SubCircuit(x = this.x, y = this.y, scope = this.scope, this.id)
    this.scope.backups = []; // Because all previous states are invalid now
    this.delete();
    showMessage("Subcircuit: " + subcircuitScope.name + " has been reloaded.")
}

SubCircuit.prototype.reBuildCircuit = function() {
    this.data = JSON.parse(scheduleBackup(scopeList[this.id]));
    this.localScope = new Scope();
    loadScope(this.localScope, this.data);
    this.lastUpdated = this.localScope.timeStamp;
    this.scope.timeStamp = this.localScope.timeStamp;

}

SubCircuit.prototype.reset = function() {

    this.removeConnections();

    var subcircuitScope = scopeList[this.id];

    for (var i = 0; i < subcircuitScope.SubCircuit.length; i++) {
        subcircuitScope.SubCircuit[i].reset();
    }

    if (subcircuitScope.Input.length == 0 && subcircuitScope.Output.length == 0) {
        showError("SubCircuit : " + subcircuitScope.name + " is an empty circuit");
        this.delete();
        this.scope.backups = [];
        return;
    }

    subcircuitScope.layout.height = subcircuitScope.layout.height;
    subcircuitScope.layout.width = subcircuitScope.layout.width;
    this.leftDimensionX = 0;
    this.upDimensionY = 0;
    this.rightDimensionX = subcircuitScope.layout.width;
    this.downDimensionY = subcircuitScope.layout.height;


    var temp_map_inp = {}
    for (var i = 0; i < subcircuitScope.Input.length; i++) {
        temp_map_inp[subcircuitScope.Input[i].layoutProperties.id] = [subcircuitScope.Input[i], undefined];
    }
    for (var i = 0; i < this.inputNodes.length; i++) {
        if (temp_map_inp.hasOwnProperty(this.inputNodes[i].layout_id)) {
            temp_map_inp[this.inputNodes[i].layout_id][1] = this.inputNodes[i];
        } else {
            this.scope.backups = [];
            this.inputNodes[i].delete();
            this.nodeList.clean(this.inputNodes[i])
        }
    }

    for (id in temp_map_inp) {
        if (temp_map_inp[id][1]) {
            if (temp_map_inp[id][0].layoutProperties.x == temp_map_inp[id][1].x && temp_map_inp[id][0].layoutProperties.y == temp_map_inp[id][1].y)
                temp_map_inp[id][1].bitWidth = temp_map_inp[id][0].bitWidth;
            else {
                this.scope.backups = [];
                temp_map_inp[id][1].delete();
                this.nodeList.clean(temp_map_inp[id][1]);
                temp_map_inp[id][1] = new Node(temp_map_inp[id][0].layoutProperties.x, temp_map_inp[id][0].layoutProperties.y, 0, this, temp_map_inp[id][0].bitWidth)
                temp_map_inp[id][1].layout_id = id;
            }
        }
    }

    this.inputNodes = []
    for (var i = 0; i < subcircuitScope.Input.length; i++) {
        var input = temp_map_inp[subcircuitScope.Input[i].layoutProperties.id][0]
        if (temp_map_inp[input.layoutProperties.id][1])
            this.inputNodes.push(temp_map_inp[input.layoutProperties.id][1])
        else {
            var a = new Node(input.layoutProperties.x, input.layoutProperties.y, 0, this, input.bitWidth);
            a.layout_id = input.layoutProperties.id;
            this.inputNodes.push(a);
        }
    }

    var temp_map_out = {}
    for (var i = 0; i < subcircuitScope.Output.length; i++) {
        temp_map_out[subcircuitScope.Output[i].layoutProperties.id] = [subcircuitScope.Output[i], undefined];
    }
    for (var i = 0; i < this.outputNodes.length; i++) {
        if (temp_map_out.hasOwnProperty(this.outputNodes[i].layout_id)) {
            temp_map_out[this.outputNodes[i].layout_id][1] = this.outputNodes[i];
        } else {
            this.outputNodes[i].delete();
            this.nodeList.clean(this.outputNodes[i])
        }
    }

    for (id in temp_map_out) {
        if (temp_map_out[id][1]) {
            if (temp_map_out[id][0].layoutProperties.x == temp_map_out[id][1].x && temp_map_out[id][0].layoutProperties.y == temp_map_out[id][1].y)
                temp_map_out[id][1].bitWidth = temp_map_out[id][0].bitWidth;
            else {
                temp_map_out[id][1].delete();
                this.nodeList.clean(temp_map_out[id][1])
                temp_map_out[id][1] = new Node(temp_map_out[id][0].layoutProperties.x, temp_map_out[id][0].layoutProperties.y, 1, this, temp_map_out[id][0].bitWidth)
                temp_map_out[id][1].layout_id = id
            }
        }
    }

    this.outputNodes = []
    for (var i = 0; i < subcircuitScope.Output.length; i++) {
        var output = temp_map_out[subcircuitScope.Output[i].layoutProperties.id][0]
        if (temp_map_out[output.layoutProperties.id][1])
            this.outputNodes.push(temp_map_out[output.layoutProperties.id][1])
        else {
            var a = new Node(output.layoutProperties.x, output.layoutProperties.y, 1, this, output.bitWidth);
            a.layout_id = output.layoutProperties.id;
            this.outputNodes.push(a);
        }
    }

    if (subcircuitScope.timeStamp > this.lastUpdated) {
        this.reBuildCircuit();
    }

    this.localScope.reset();

    this.makeConnections();



}

SubCircuit.prototype.click = function() {
    // this.id=prompt();
}

SubCircuit.prototype.addInputs = function() {
    for (let i = 0; i < subCircuitInputList.length; i++)
        for (let j = 0; j < this.localScope[subCircuitInputList[i]].length; j++)
            simulationArea.simulationQueue.add(this.localScope[subCircuitInputList[i]][j], 0)
    for (let j = 0; j < this.localScope.SubCircuit.length; j++)
        this.localScope.SubCircuit[j].addInputs();
}
SubCircuit.prototype.isResolvable = function() {
    if (CircuitElement.prototype.isResolvable.call(this))
        return true;
    return false;
}
SubCircuit.prototype.dblclick = function() {
    switchCircuit(this.id)
}
SubCircuit.prototype.saveObject = function() {
    var data = {
        x: this.x,
        y: this.y,
        id: this.id,
        inputNodes: this.inputNodes.map(findNode),
        outputNodes: this.outputNodes.map(findNode),
        version: this.version,
    }
    return data;
}

SubCircuit.prototype.resolve = function() {


    // deprecated
    // var subcircuitScope = this.localScope;//scopeList[this.id];
    // // this.scope.pending.clean(this); // To remove any pending instances
    // // return;
    //
    // for (i = 0; i < subcircuitScope.Input.length; i++) {
    //     subcircuitScope.Input[i].state = this.inputNodes[i].value;
    // }
    //
    // for (i = 0; i < subcircuitScope.Input.length; i++) {
    //     simulationArea.simulationQueue.add(subcircuitScope.Input[i]);
    // }
    // play(subcircuitScope);
    //
    // for (i = 0; i < subcircuitScope.Output.length; i++) {
    //     this.outputNodes[i].value = subcircuitScope.Output[i].inp1.value;
    // }
    // for (i = 0; i < subcircuitScope.Output.length; i++) {
    //     this.scope.stack.push(this.outputNodes[i]);
    // }

}

SubCircuit.prototype.isResolvable = function() {
    return false
}

SubCircuit.prototype.verilogName = function() {
    return verilog.sanitizeLabel(scopeList[this.id].name);
}

SubCircuit.prototype.customDraw = function() {

    var subcircuitScope = scopeList[this.id];

    ctx = simulationArea.context;

    ctx.lineWidth = globalScope.scale * 3;
    ctx.strokeStyle = "black"; //("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    var xx = this.x;
    var yy = this.y;

    ctx.beginPath();

    ctx.textAlign = "center";
    ctx.fillStyle = "black";
    if (this.version == "1.0")
        fillText(ctx, subcircuitScope.name, xx, yy - subcircuitScope.layout.height / 2 + 13, 11);
    else if (this.version == "2.0"){
        if(subcircuitScope.layout.titleEnabled){
            fillText(ctx, subcircuitScope.name, subcircuitScope.layout.title_x + xx, yy + subcircuitScope.layout.title_y, 11);
        }
    }
    else
        console.log(this.version)

    for (var i = 0; i < subcircuitScope.Input.length; i++) {
        if (!subcircuitScope.Input[i].label) continue;
        var info = this.determine_label(this.inputNodes[i].x, this.inputNodes[i].y);
        ctx.textAlign = info[0];
        fillText(ctx, subcircuitScope.Input[i].label, this.inputNodes[i].x + info[1] + xx, yy + this.inputNodes[i].y + info[2], 12);
    }

    for (var i = 0; i < subcircuitScope.Output.length; i++) {
        if (!subcircuitScope.Output[i].label) continue;
        var info = this.determine_label(this.outputNodes[i].x, this.outputNodes[i].y);
        ctx.textAlign = info[0];
        fillText(ctx, subcircuitScope.Output[i].label, this.outputNodes[i].x + info[1] + xx, yy + this.outputNodes[i].y + info[2], 12);
    }
    ctx.fill();

    for (var i = 0; i < this.inputNodes.length; i++)
        this.inputNodes[i].draw();
    for (var i = 0; i < this.outputNodes.length; i++)
        this.outputNodes[i].draw();

}
SubCircuit.prototype.centerElement = true; // To center subcircuit when new

SubCircuit.prototype.determine_label = function(x, y) {
    if (x == 0) return ["left", 5, 5]
    if (x == scopeList[this.id].layout.width) return ["right", -5, 5]
    if (y == 0) return ["center", 0, 13]
    return ["center", 0, -6]
}
