import { currMouseAction, backToEdit } from "../menutools.js"
import { gateIMG } from "../simulator.js";
import { gateType, MouseAction } from "./Enums.js"
import { Node } from "./Node.js";
import { colorMouseOver, fileManager } from "../simulator.js";
import { FileManager } from "../FileManager.js";
/**
* Logic Gate
* @class Logic Gate
*/
export class Gate {
/**
* Constructor
* @param {*} strType Gate Type
*/
constructor(strType) {
this.strType = strType;
this.type = this.convertToType(strType);
this.width = gateIMG[this.type].width;
this.height = gateIMG[this.type].height;
this.posX = mouseX - (this.width / 2);
this.posY = mouseY - (this.height / 2);
this.isSpawned = false;
this.offsetMouseX = 0;
this.offsetMouseY = 0;
this.isMoving = false;
this.isSaved = false;
this.input = [];
this.input.push(new Node(this.posX, this.posY + 15));
if (this.type != gateType.NOT) {
this.input.push(new Node(this.posX, this.posY + this.height - 15));
this.input[0].setBrother(this.input[1]);
this.input[1].setBrother(this.input[0]);
}
this.output = new Node(this.posX + this.width, this.posY + this.height / 2, true);
this.nodeStartID = this.input[0].id;
}
/**
* Destroy this gate
*/
destroy() {
for (let i = 0; i < this.input.length; i++) {
//Destroy and delete all input
this.input[i].destroy();
delete this.input[i];
}
//Destroy and delete the output
this.output.destroy();
delete this.output;
}
/**
* Draw this gate
*/
draw() {
if (!this.isSpawned) {
//If is not spawned, follow mouse
this.posX = mouseX - (this.width / 2);
this.posY = mouseY - (this.height / 2);
}else if(!this.isSaved)
{
//Else if is not saved, save it and set isSaved to true
fileManager.saveState();
this.isSaved = true;
}
if (this.isMoving) {
//If is moveing, follow mouse
this.posX = mouseX + this.offsetMouseX;
this.posY = mouseY + this.offsetMouseY;
}
if (this.type == gateType.NOT) {
//If this type is gateType.NOT update input[0] position
this.input[0].updatePosition(this.posX, this.posY + this.height / 2);
} else {
//Else update input[0] and input[1] postion
this.input[0].updatePosition(this.posX, this.posY + 15);
this.input[1].updatePosition(this.posX, this.posY + this.height - 15);
}
//Update gate output
this.output.updatePosition(this.posX + this.width, this.posY + this.height / 2);
if (this.isMouseOver()) {
//If mouse is over, highlights it
noFill();
strokeWeight(2);
stroke(colorMouseOver[0], colorMouseOver[1], colorMouseOver[2]);
rect(this.posX, this.posY, this.width, this.height);
}
//Draw gate image and inputs
image(gateIMG[this.type], this.posX, this.posY);
for (let i = 0; i < this.input.length; i++)
this.input[i].draw();
//Generate output and draw it
this.generateOutput();
this.output.draw();
}
/**
* Refresh nodes uniqueID
*/
refreshNodes()
{
let currentID = this.nodeStartID;
this.input[0].setID(currentID);
currentID++;
if (this.type != gateType.NOT)
{
this.input[1].setID(currentID);
currentID++;
}
this.output.setID(currentID);
}
/**
* Generate gate output
*/
generateOutput() {
this.output.setValue(this.calculateValue());
}
/**
* Calculate gate output by type value
*/
calculateValue() {
switch (this.type) {
case gateType.NOT:
return !this.input[0].getValue();
case gateType.AND:
return this.input[0].getValue() && this.input[1].getValue();
case gateType.NAND:
return !(this.input[0].getValue() && this.input[1].getValue());
case gateType.OR:
return this.input[0].getValue() || this.input[1].getValue();
case gateType.NOR:
return !(this.input[0].getValue() || this.input[1].getValue());
case gateType.XOR:
return this.input[0].getValue() ^ this.input[1].getValue();
case gateType.XNOR:
return !(this.input[0].getValue() ^ this.input[1].getValue());
}
}
/**
* Convert String to a gateType
* @param {*} str Gate tipe by string
* @returns {gateType} gateType Object
* @todo Error handler
*/
convertToType(str) {
switch (str) {
case "NOT":
return gateType.NOT;
case "AND":
return gateType.AND;
case "NAND":
return gateType.NAND;
case "OR":
return gateType.OR;
case "NOR":
return gateType.NOR;
case "XOR":
return gateType.XOR;
case "XNOR":
return gateType.XNOR;
//default here
}
}
/**
* Check if mouse is over
* @returns {Boolean} Boolean value
*/
isMouseOver() {
if (mouseX > this.posX && mouseX < (this.posX + this.width)
&& mouseY > this.posY && mouseY < (this.posY + this.height))
return true;
return false;
}
/**
* When mouse is pressed if gate is not spawned, spawn it, then return
*/
mousePressed() {
if (!this.isSpawned) {
this.posX = mouseX - (this.width / 2);
this.posY = mouseY - (this.height / 2);
this.isSpawned = true;
backToEdit();
return;
}
if (this.isMouseOver() || currMouseAction == MouseAction.MOVE) { //X
this.isMoving = true;
this.offsetMouseX = this.posX - mouseX;
this.offsetMouseY = this.posY - mouseY;
}
}
/**
* Called when mouse is released
*/
mouseReleased() {
this.isMoving = false;
}
/**
* Called when mouse is clicked
* @returns {Boolean} Boolean
*/
mouseClicked() {
let result = this.isMouseOver();
for (let i = 0; i < this.input.length; i++)
result |= this.input[i].mouseClicked();
result |= this.output.mouseClicked();
return result;
}
};