#include "QuantumCircuitReader.h"

#include "QuantumCircuit.h"
#include "Qubit.h"
#include "QuantumTerminal.h"
#include "BasicQuantumGates.h"
#include "QuantumWire.h"

#include "File.h"
#include "StringUtils.h"
#include "FileLogger.h"

std::unique_ptr<QuantumCircuit> QuantumCircuitReader::read(const Path& path)
{
    File file(path);
    return read(file.readText());
}

std::unique_ptr<QuantumCircuit> QuantumCircuitReader::read(const std::string& content)
{
    auto circuit = std::make_unique<QuantumCircuit>();
    mWorkingCircuit = circuit.get();

    std::size_t cursor = 0;
    for (const auto& line : StringUtils::toLines(content))
    {
        onLine(line, cursor);
        cursor++;
    }
    circuit->buildWireConnections();
    return circuit;
}

void QuantumCircuitReader::onLine(const std::string& line, std::size_t jdx)
{
    mWorkingString.clear();
    std::size_t cursor = 0;

    while (cursor < line.size())
    {
        const auto c = line[cursor];
        MLOG_INFO("Working char: " << std::string(1, c));

        if (c == '|')
        {
            if (cursor + 1 < line.size())
            {
                if (line[cursor + 1] == '-')
                {
                    onVertialQuantumWire({ cursor, jdx });
                }
                else if (auto ket = checkForKet(line.substr(cursor + 1, line.size() - cursor)); !ket.empty())
                {
                    onKet({ cursor, jdx }, ket);
                    cursor += ket.size() + 1;
                }
            }
            else
            {
                mWorkingString += c;
            }
        }
        else if (c == '-')
        {
            if (cursor + 1 < line.size())
            {
                const auto end_id = getWireEnd(line.substr(cursor, line.size() - cursor));
                MLOG_INFO("Wire: " << cursor << " with length " << end_id);
                cursor += end_id - 1;
            }
            else
            {
                mWorkingString += c;
            }
        }
        else
        {
            if (cursor + 1 < line.size())
            {
                auto gate = checkForGate(line.substr(cursor, line.size() - cursor));
                onGate({ cursor, jdx }, gate);
                cursor += gate.size() - 1;
            }
            else
            {
                mWorkingString += c;
            }
        }
        cursor++;
    }
    onLineEnd();
}

void QuantumCircuitReader::onLineEnd()
{
    auto output_terminal = std::make_unique<QuantumTerminal>(QuantumTerminal::TerminalType::OUTPUT);

    auto wire = std::make_unique<QuantumWire>(mWorkingElement, output_terminal.get());
    mWorkingCircuit->addQuantumWire(std::move(wire));

    mWorkingCircuit->addOutputTerminal(std::move(output_terminal));

    mWorkingElement = nullptr;
}

void QuantumCircuitReader::onGate(Location, const std::string value)
{
    MLOG_INFO("Got gate: " << value);

    QuantumGatePtr gate;

    if (value == "X")
    {
        gate = std::make_unique<XQuantumGate>();
    }
    else if (value == "Z")
    {
        gate = std::make_unique<ZQuantumGate>();
    }

    if (gate)
    {
        auto wire = std::make_unique<QuantumWire>(mWorkingElement, gate.get());
        mWorkingCircuit->addQuantumWire(std::move(wire));

        mWorkingElement = gate.get();
        mWorkingCircuit->addLogicGate(std::move(gate));
    }
}

std::string QuantumCircuitReader::checkForGate(const std::string& segment)
{
    std::string working_string;
    for (const auto c : segment)
    {
        if (c == '-')
        {
            break;
        }
        working_string += c;
    }
    return working_string;
}

void QuantumCircuitReader::onKet(Location, const std::string value)
{
    MLOG_INFO("Got input state: " << value);
    Qubit qubit;
    if (value == "1")
    {
        qubit = Qubit({ 0.0, 0.0 }, { 1.0, 0.0 });
    }

    auto input_terminal = std::make_unique<QuantumTerminal>(QuantumTerminal::TerminalType::INPUT);

    mWorkingElement = input_terminal.get();
    mWorkingCircuit->addInputTerminal(std::move(input_terminal));
}

std::size_t QuantumCircuitReader::getWireEnd(const std::string& segment)
{
    std::size_t idx = 0;
    for (const auto c : segment)
    {
        if (c != '-')
        {
            break;
        }
        idx++;
    }
    return idx;
}

std::string QuantumCircuitReader::checkForKet(const std::string& segment)
{
    std::string working_string;
    bool found{ false };

    for (const auto c : segment)
    {
        if (c == '>')
        {
            found = true;
            break;
        }
        else
        {
            working_string += c;
        }
    }

    if (found)
    {
        return working_string;
    }
    else
    {
        return {};
    }
}

void QuantumCircuitReader::onVertialQuantumWire(Location)
{

}
