#include "ElectronicCircuitNode.h"

#include "TerminalNode.h"
#include "WireNode.h"
#include "LogicGateNode.h"

#include "LineNode.h"

#include "FileLogger.h"

ElectronicCircuitNode::ElectronicCircuitNode(const Transform& transform)
    : AbstractVisualNode(transform)
{

}

ElectronicCircuitNode::~ElectronicCircuitNode()
{

}

void ElectronicCircuitNode::setContent(ElectronicCircuit* content)
{
    mContent = content;
    mContentDirty = true;
}

void ElectronicCircuitNode::buildWireConnections()
{
    mWireInputConnections.clear();
    mWireOutputConnections.clear();

    for (auto terminal : mContent->getInputTerminals())
    {
        mWireOutputConnections[terminal->getConnection()] = terminal;
    }

    for (auto gate : mContent->getLogicGates())
    {
        for (std::size_t idx = 0; idx < gate->getNumInputs(); idx++)
        {
            mWireInputConnections[gate->getInput(idx)] = gate;
        }

        for (std::size_t idx = 0; idx < gate->getNumOutputs(); idx++)
        {
            mWireOutputConnections[gate->getOutput(idx)] = gate;
        }
    }

    for (auto terminal : mContent->getOutputTerminals())
    {
        mWireInputConnections[terminal->getConnection()] = terminal;
    }
}

void ElectronicCircuitNode::createOrUpdateGeometry(SceneInfo*)
{
    mInputTerminalNodes.clear();
    mWireNodes.clear();
    mLogicGateNodes.clear();

    buildWireConnections();

    // Layout terminals
    double terminal_vertical_spacing = 100;
    double terminal_left_margin = 10;

    double terminal_y = 10;
    for (auto terminal : mContent->getInputTerminals())
    {
        Point loc{ terminal_left_margin, terminal_y };
        auto terminal_node = std::make_unique<TerminalNode>(Transform(loc));

        terminal_node->setContent(terminal);

        addChild(terminal_node.get());

        mNodesForContent[terminal] = terminal_node.get();
        mInputTerminalNodes.push_back(std::move(terminal_node));

        terminal_y += terminal_vertical_spacing;
    }

    // Layout logic gates
    double logic_gate_vertical_spacing = 100;
    double logic_gate_horizontal_spacing = 100;

    double gate_x = logic_gate_vertical_spacing;
    double gate_y = 0;
    for (auto gate : mContent->getLogicGates())
    {
        Point loc{ gate_x, gate_y };
        auto gate_node = std::make_unique<LogicGateNode>(Transform(loc));

        gate_node->setContent(gate);

        addChild(gate_node.get());

        mNodesForContent[gate] = gate_node.get();
        mLogicGateNodes.push_back(std::move(gate_node));

        gate_x += logic_gate_vertical_spacing;
        gate_y += logic_gate_horizontal_spacing;
    }

    // Layout output terminals
    terminal_y = 10;
    double terminal_right_margin = 10;
    unsigned width = 500;
    for (auto terminal : mContent->getOutputTerminals())
    {
        Point loc{ width - terminal_right_margin, terminal_y };
        auto node = std::make_unique<TerminalNode>(Transform(loc));

        node->setContent(terminal);

        addChild(node.get());

        mNodesForContent[terminal] = node.get();

        mOutputTerminalNodes.push_back(std::move(node));

        terminal_y += terminal_vertical_spacing;
    }

    // Add wires
    for (auto wire : mContent->getWires())
    {
        auto start_node = mNodesForContent[mWireOutputConnections[wire]];
        auto end_node = mNodesForContent[mWireInputConnections[wire]];

        auto wire_node = std::make_unique<WireNode>(Transform());
        wire_node->setInputLocation(start_node->getConnectionLocation(wire));
        wire_node->setOutputLocation(end_node->getConnectionLocation(wire));

        addChild(wire_node.get());

        mWireNodes.push_back(std::move(wire_node));
    }
}

void ElectronicCircuitNode::update(SceneInfo* sceneInfo)
{
    if (mContentDirty)
    {
        createOrUpdateGeometry(sceneInfo);
        mContentDirty = false;
    }
}
