#include "QuantumCircuit.h"

#include "QuantumGate.h"

#include "FileLogger.h"

void QuantumCircuit::addInputTerminal(QuantumTerminalPtr terminal)
{
	mInputTerminals.push_back(terminal.get());
	mElements.push_back(std::move(terminal));
}

void QuantumCircuit::addOutputTerminal(QuantumTerminalPtr terminal)
{
	mOutputTerminals.push_back(terminal.get());
	mElements.push_back(std::move(terminal));
}

void QuantumCircuit::addQuantumWire(QuantumWirePtr wire)
{
	mWires.push_back(wire.get());
	mElements.push_back(std::move(wire));
}

void QuantumCircuit::addLogicGate(QuantumGatePtr gate)
{
	mGates.push_back(gate.get());
	mElements.push_back(std::move(gate));
}

const std::vector<QuantumTerminal*>& QuantumCircuit::getInputTerminals() const
{
	return mInputTerminals;
}

const std::vector<QuantumTerminal*>& QuantumCircuit::getOutputTerminals() const
{
	return mOutputTerminals;
}

const std::vector<QuantumGate*>& QuantumCircuit::getLogicGates() const
{
	return mGates;
}

const std::vector<QuantumWire*>& QuantumCircuit::getQuantumWires() const
{
	return mWires;
}

QuantumState QuantumCircuit::getInputState() const
{
	QuantumState state;
	for (const auto input : mInputTerminals)
	{
		state.addValue(input->getValue());
	}
	return state;
}

void QuantumCircuit::buildWireConnections()
{
	for (const auto& wire : mWires)
	{
		bool input_set{ false };
		bool output_set{ false };

		for (auto input : mInputTerminals)
		{
			if (wire->getInput() == input)
			{
				input->setConnection(wire);
				input_set = true;
				break;
			}
		}

		for (auto output : mOutputTerminals)
		{
			if (wire->getOutput() == output)
			{
				output->setConnection(wire);
				output_set = true;
				break;
			}
		}

		if (input_set && output_set)
		{
			break;
		}

		for (auto gate : mGates)
		{
			if (wire->getOutput() == gate)
			{
				for (std::size_t idx = 0; idx < gate->getNumInputs(); idx++)
				{
					if (gate->getInput(idx) == nullptr)
					{
						gate->setAtInput(idx, wire);
						output_set = true;
						break;
					}
				}
			}

			if (wire->getInput() == gate)
			{
				for (std::size_t idx = 0; idx < gate->getNumOutputs(); idx++)
				{
					if (gate->getOutput(idx) == nullptr)
					{
						gate->setAtOutput(idx, wire);
						input_set = true;
						break;
					}
				}
			}

			if (input_set && output_set)
			{
				break;
			}
		}
	}

	if (!connectivityIsValid())
	{
		MLOG_ERROR("Input circuit does not have complete connectivity");
	}
}

bool QuantumCircuit::connectivityIsValid() const
{
	for (const auto& element : mElements)
	{
		if (!element->isFullyConnected())
		{
			return false;
		}
	}
	return true;
}