//
// Created by magino on 28. 8. 2019.
//

#ifndef RECEPTURE_H
#define RECEPTURE_H

#include <utility>
#include <vector>
#include "recepture/ProcedureStep.h"
#include "recepture/PieceItem.h"
#include "recepture/Header.h"
#include "recepture/ProductionState.h"
#include "helpers/VectorHelp.h"
#include "mqttcomm/RfidMqttClient.h"

class Recepture {
private:
    const int HEADER_BYTES = 40;
    const int PIECE_BYTES = 6;
    const int PROCEDURESTEP_BYTES = 10;
    const int STATE_BYTES = 36;
public:
    RfidMqttClient& client;
    Header header;
    std::vector<PieceItem> pieceItemList;
    std::vector<ProcedureStep> procedureStepList;
    ProductionState productionState;

    Recepture() = default;
    ~Recepture() = default;

    Recepture(std::vector<uint8_t>& rawData, RfidMqttClient& client):
            client(client),
            header(VectorHelp::CreateSubVector(rawData, 0, HEADER_BYTES), client),
            productionState(VectorHelp::CreateSubVector(rawData,
                                                        header.GetPieceListLength() * PIECE_BYTES + HEADER_BYTES + header.GetProcedureLength() * PROCEDURESTEP_BYTES,
                                                        header.GetPieceListLength() * PIECE_BYTES + HEADER_BYTES + header.GetProcedureLength() * PROCEDURESTEP_BYTES + STATE_BYTES),
                    client,
                            ((header.GetPieceListLength() * PIECE_BYTES + HEADER_BYTES + header.GetProcedureLength() * PROCEDURESTEP_BYTES) / 4) + 4)
    {
        for(int i=0; i < header.GetPieceListLength(); ++i) {
            pieceItemList.emplace_back(VectorHelp::CreateSubVector(rawData, HEADER_BYTES + i * PIECE_BYTES, HEADER_BYTES + (i + 1) * PIECE_BYTES));
        }

        int procedureStepsOffset = header.GetPieceListLength() * PIECE_BYTES + HEADER_BYTES;

        for(int i=0; i < header.GetProcedureLength(); ++i) {
            procedureStepList.emplace_back(VectorHelp::CreateSubVector(rawData, procedureStepsOffset + i * PROCEDURESTEP_BYTES, procedureStepsOffset + (i + 1) * PROCEDURESTEP_BYTES));
        }
    }

    ProcedureStep GetActualProcedureStep() {
        if(procedureStepList.size() != 0 && productionState.step <= procedureStepList.size())
            return procedureStepList.at(productionState.step);
        else {
            ProcedureStep end;
            return end;
        }
    }

    friend std::ostream& operator<<(std::ostream& stream, Recepture const& recepture) {
        stream << "[Header:]" << std::endl;
        stream << "RecipeId: " << recepture.header.recipeId << std::endl;
        stream << "Recipe Version: " << recepture.header.recipeVersion << std::endl;
        stream << "Release Date Time: " << recepture.header.releaseDateTime << std::endl;
        stream << "Serial Number: " << recepture.header.serialNumber << std::endl;
        stream << "Order Date Time: " << recepture.header.orderDateTime << std::endl;
        stream << "Dispense Date Time: " << recepture.header.dispenseDateTime << std::endl;
        stream << "Status: " << recepture.header.status << std::endl;
        stream << "Piece List Length: " << unsigned(recepture.header.pieceListLength) << std::endl;
        stream << "Procedure Length: " << unsigned(recepture.header.procedureLength) << std::endl;

        stream << "[Piece Item List:]" << std::endl;
        int i=0;
        for(auto& item: recepture.pieceItemList) {
            stream << "[Item:]"<< "[" << i++ << "]" << std::endl;
            stream << "Class: " << unsigned(item.classa) << std::endl;
            stream << "Definition: " << unsigned(item.definition) << std::endl;
            stream << "Amount: " << item.amount << std::endl;
        }

        stream << "[Procedure Step List:]" << std::endl;
        i=0;
        for(auto& item: recepture.procedureStepList) {
            stream << "[Step:] "<< "[" << i++ << "]" << std::endl;
            stream << "Order: " << unsigned(item.order) << std::endl;
            stream << "Operation Id: " << unsigned(item.operationId) << std::endl;
            stream << "Parameter A: " << item.parameterA << std::endl;
            stream << "Parameter B: " << item.parameterB << std::endl;
        }

        stream << "[States:]" << std::endl;
        stream << "State: " << recepture.productionState.state;// << std::endl;
        stream << "Step: " << recepture.productionState.step << std::endl;
        stream << "Reserved Action Cell Id: " << recepture.productionState.reservedActionCellId << std::endl;
        stream << "Action Reservation Id: " << recepture.productionState.actionReservationId << std::endl;
        stream << "Reserved Transport Cell Id: " << recepture.productionState.reservedTransportCellId << std::endl;
        stream << "Transport Reservation Id: " << recepture.productionState.transportReservationId << std::endl;

        return stream;
    }
};

#endif //RECEPTURE_H