//
// Created by magino on 9. 3. 2020.
//

#ifndef PRODUCTIONSTATE_H
#define PRODUCTIONSTATE_H

class ProductionState {
private:
    RfidMqttClient& _client;
    int _startSector;
public:
    EProcedureStepState state;
    uint16_t step;
    uint64_t reservedActionCellId; //0 for not reserved yet
    uint64_t actionReservationId;
    uint64_t reservedTransportCellId;
    uint64_t transportReservationId;


    ProductionState(std::vector<uint8_t> data, RfidMqttClient& client, int startSector): _client(client), _startSector(startSector) {
        uint16_t tmpState;
        FromBytes::ToUInt16(data[0], data[1], tmpState);
        state = static_cast<EProcedureStepState>(tmpState);
        FromBytes::ToUInt16(data[2], data[3], step);

        reservedActionCellId = data[11] ;
        reservedActionCellId = (reservedActionCellId << 8 ) + data[10] ;
        reservedActionCellId = (reservedActionCellId << 8 ) + data[9] ;
        reservedActionCellId = (reservedActionCellId << 8 ) + data[8] ;
        reservedActionCellId = (reservedActionCellId << 8 ) + data[7] ;
        reservedActionCellId = (reservedActionCellId << 8 ) + data[6] ;
        reservedActionCellId = (reservedActionCellId << 8 ) + data[5] ;
        reservedActionCellId = (reservedActionCellId << 8 ) + data[4] ;

        actionReservationId = data[19] ;
        actionReservationId = (actionReservationId << 8 ) + data[18] ;
        actionReservationId = (actionReservationId << 8 ) + data[17] ;
        actionReservationId = (actionReservationId << 8 ) + data[16] ;
        actionReservationId = (actionReservationId << 8 ) + data[15] ;
        actionReservationId = (actionReservationId << 8 ) + data[14] ;
        actionReservationId = (actionReservationId << 8 ) + data[13] ;
        actionReservationId = (actionReservationId << 8 ) + data[12] ;

        reservedTransportCellId = data[27] ;
        reservedTransportCellId = (reservedTransportCellId << 8 ) + data[26] ;
        reservedTransportCellId = (reservedTransportCellId << 8 ) + data[25] ;
        reservedTransportCellId = (reservedTransportCellId << 8 ) + data[24] ;
        reservedTransportCellId = (reservedTransportCellId << 8 ) + data[23] ;
        reservedTransportCellId = (reservedTransportCellId << 8 ) + data[22] ;
        reservedTransportCellId = (reservedTransportCellId << 8 ) + data[21] ;
        reservedTransportCellId = (reservedTransportCellId << 8 ) + data[20] ;

        transportReservationId = data[35] ;
        transportReservationId = (transportReservationId << 8 ) + data[34] ;
        transportReservationId = (transportReservationId << 8 ) + data[33] ;
        transportReservationId = (transportReservationId << 8 ) + data[32] ;
        transportReservationId = (transportReservationId << 8 ) + data[31] ;
        transportReservationId = (transportReservationId << 8 ) + data[30] ;
        transportReservationId = (transportReservationId << 8 ) + data[29] ;
        transportReservationId = (transportReservationId << 8 ) + data[28] ;
    }
    ~ProductionState() = default;

    bool SetState(EProcedureStepState newState) {
        // i can write whole sector at once so I need to include with status also _pieceListLength and _procedureLength = 4bytes
        std::vector<uint8_t> byteData;
        byteData.reserve(4);
        ToBytes::Split16bitsToBytes(byteData, (uint16_t)newState);
        ToBytes::Split16bitsToBytes(byteData, step);

        auto jsonObjects = nlohmann::json::array();
        WriteSectorDataModel sectorData;
        sectorData.data = byteData;
        sectorData.sector = _startSector;

        jsonObjects.emplace_back(sectorData);

        nlohmann::json output;
        output["write_multi"] = jsonObjects;
        if(_client.SendMessage(output.dump())) {
            std::cout << "Setting New State was Successful. Old: " << unsigned(state) << "  New: " << unsigned(newState) << std::endl;
            state = newState;
            return true;
        }
        std::cout << "Setting New State Failed." << std::endl;
        return false;
    }

    bool SetCurrentStep(uint8_t newStep) {
        // I can write whole sector at once so I need to include with status also _pieceListLength and _procedureLength = 4bytes
        std::vector<uint8_t> byteData;
        byteData.reserve(4);
        ToBytes::Split16bitsToBytes(byteData, (uint16_t)state);
        ToBytes::Split16bitsToBytes(byteData, newStep);

        auto jsonObjects = nlohmann::json::array();
        WriteSectorDataModel sectorData;
        sectorData.data = byteData;
        sectorData.sector = _startSector;

        jsonObjects.emplace_back(sectorData);

        nlohmann::json output;
        output["write_multi"] = jsonObjects;
        if(_client.SendMessage(output.dump())) {
            std::cout << "Setting New Current Step was Successful. Old: " << unsigned(step) << "  New: " << unsigned(newStep) << std::endl;
            step = newStep;
            return true;
        }
        std::cout << "Setting Current Step Failed." << std::endl;
        return false;
    }

    bool SetReservedActionCellId(uint64_t newReservedActionCellId) {
        std::vector<uint8_t> byteData;
        byteData.reserve(8);
        ToBytes::Split64BitsToBytes(byteData, newReservedActionCellId);

        auto jsonObjects = nlohmann::json::array();

        WriteSectorDataModel sectorData1;
        sectorData1.data.insert(sectorData1.data.begin(), byteData.begin(), byteData.begin()+4);
        sectorData1.sector = _startSector+1;
        jsonObjects.emplace_back(sectorData1);

        WriteSectorDataModel sectorData2;
        sectorData2.data.insert(sectorData2.data.begin(), byteData.begin()+4, byteData.begin()+8);
        sectorData2.sector = _startSector+2;
        jsonObjects.emplace_back(sectorData2);

        nlohmann::json output;
        output["write_multi"] = jsonObjects;
        if(_client.SendMessage(output.dump())) {
            std::cout << "Setting New Reserved Action Cell Id was Successful. Old: " << reservedActionCellId << "  New: " << newReservedActionCellId << std::endl;
            reservedActionCellId = newReservedActionCellId;
            return true;
        }
        std::cout << "Setting New Reserved Action Cell Failed." << std::endl;
        return false;
    }

    bool SetActionReservationId(uint64_t newActionReservationId) {
        std::vector<uint8_t> byteData;
        byteData.reserve(8);
        ToBytes::Split64BitsToBytes(byteData, newActionReservationId);

        auto jsonObjects = nlohmann::json::array();

        WriteSectorDataModel sectorData1;
        sectorData1.data.insert(sectorData1.data.begin(), byteData.begin(), byteData.begin()+4);
        sectorData1.sector = _startSector+3;
        jsonObjects.emplace_back(sectorData1);

        WriteSectorDataModel sectorData2;
        sectorData2.data.insert(sectorData2.data.begin(), byteData.begin()+4, byteData.begin()+8);
        sectorData2.sector = _startSector+4;
        jsonObjects.emplace_back(sectorData2);

        nlohmann::json output;
        output["write_multi"] = jsonObjects;
        if(_client.SendMessage(output.dump())) {
            std::cout << "Setting New Action Reservation Cell Id was Successful. Old: " << reservedActionCellId << "  New: " << newActionReservationId << std::endl;
            actionReservationId = newActionReservationId;
            return true;
        }
        std::cout << "Setting New Action Reservation Cell Id Failed." << std::endl;
        return false;
    }

    bool SetReservedTransportCellId(uint64_t newReservedTransportCellId) {
        std::vector<uint8_t> byteData;
        byteData.reserve(8);
        ToBytes::Split64BitsToBytes(byteData, newReservedTransportCellId);

        auto jsonObjects = nlohmann::json::array();

        WriteSectorDataModel sectorData1;
        sectorData1.data.insert(sectorData1.data.begin(), byteData.begin(), byteData.begin()+4);
        sectorData1.sector = _startSector+5;
        jsonObjects.emplace_back(sectorData1);

        WriteSectorDataModel sectorData2;
        sectorData2.data.insert(sectorData2.data.begin(), byteData.begin()+4, byteData.begin()+8);
        sectorData2.sector = _startSector+6;
        jsonObjects.emplace_back(sectorData2);

        nlohmann::json output;
        output["write_multi"] = jsonObjects;
        if(_client.SendMessage(output.dump())) {
            std::cout << "Setting New Reserved Transport Cell Id was Successful. Old: " << reservedActionCellId << "  New: " << newReservedTransportCellId << std::endl;
            reservedTransportCellId = newReservedTransportCellId;
            return true;
        }
        std::cout << "Setting New Reserved Transport Cell Id Failed." << std::endl;
        return false;
    }

    bool SetTransportReservationId(uint64_t newTransportReservationId) {
        std::vector<uint8_t> byteData;
        byteData.reserve(8);
        ToBytes::Split64BitsToBytes(byteData, newTransportReservationId);

        auto jsonObjects = nlohmann::json::array();

        WriteSectorDataModel sectorData1;
        sectorData1.data.insert(sectorData1.data.begin(), byteData.begin(), byteData.begin()+4);
        sectorData1.sector = _startSector+7;
        jsonObjects.emplace_back(sectorData1);

        WriteSectorDataModel sectorData2;
        sectorData2.data.insert(sectorData2.data.begin(), byteData.begin()+4, byteData.begin()+8);
        sectorData2.sector = _startSector+8;
        jsonObjects.emplace_back(sectorData2);

        nlohmann::json output;
        output["write_multi"] = jsonObjects;
        if(_client.SendMessage(output.dump())) {
            std::cout << "Setting New Transport Reservation Cell Id was Successful. Old: " << reservedActionCellId << "  New: " << newTransportReservationId << std::endl;
            transportReservationId = newTransportReservationId;
            return true;
        }
        std::cout << "Setting New Transport Reservation Cell Id Failed." << std::endl;
        return false;
    }
};

#endif //PRODUCTIONSTATE_H