#pragma once
#include "IR_config.h"

class IR_Decoder;
namespace PacketTypes
{
    class BasePack
    {
        friend IR_Decoder;

    protected:
        bool isAvailable;
        bool isRawAvailable;
        bool isNeedAccept;

        uint8_t msgOffset;
        uint8_t addressFromOffset;
        uint8_t addressToOffset;
        uint8_t DataOffset;

        IR_FOX::PackInfo *packInfo;
        uint16_t id;

        virtual bool checkAddress();
        void set(IR_FOX::PackInfo *packInfo, uint16_t id);

        static uint16_t _getAddrFrom(BasePack *obj);
        static uint16_t _getAddrTo(BasePack *obj);
        static uint8_t _getDataSize(BasePack *obj);
        static uint8_t *_getDataPrt(BasePack *obj);
        static uint8_t _getDataRawSize(BasePack *obj);

    public:
        bool available();
        bool availableRaw();

        inline uint8_t getMsgInfo() { return packInfo->buffer[0] & IR_MASK_MSG_INFO; };
        inline uint8_t getMsgType() { return (packInfo->buffer[0] >> 5) & IR_MASK_MSG_TYPE; };
        inline uint8_t getMsgRAW() { return packInfo->buffer[0]; };
        inline uint16_t getErrorCount() { return packInfo->err.all(); };
        inline uint8_t getErrorLowSignal() { return packInfo->err.lowSignal; };
        inline uint8_t getErrorHighSignal() { return packInfo->err.highSignal; };
        inline uint8_t getErrorOther() { return packInfo->err.other; };
        inline uint16_t getTunerTime() { return packInfo->rTime; };
        inline uint8_t *getDataRawPtr() { return packInfo->buffer; };
        /** Полный размер кадра в байтах (как packInfo.packSize); доступен для gotRaw (BasePack). */
        inline uint8_t getDataRawSize() { return _getDataRawSize(this); };
    };

    class Data : public BasePack
    {
    public:
        Data()
        {
            msgOffset = 0;
            addressFromOffset = 1;
            addressToOffset = 3;
            DataOffset = 5;
        }

        inline uint16_t getAddrFrom() { return _getAddrFrom(this); };
        inline uint16_t getAddrTo() { return _getAddrTo(this); };

        inline uint8_t getDataSize() { return _getDataSize(this); };
        inline uint8_t *getDataPrt() { return _getDataPrt(this); };

    private:
        bool checkAddress() override;
    };

    class DataBack : public BasePack
    {
    public:
        DataBack()
        {
            msgOffset = 0;
            addressFromOffset = 1;
            addressToOffset = 3;
            DataOffset = 3;
        }

        inline uint16_t getAddrFrom() { return _getAddrFrom(this); };
        inline uint16_t getAddrTo() { return _getAddrTo(this); };

        inline uint8_t getDataSize() { return _getDataSize(this); };
        inline uint8_t *getDataPrt() { return _getDataPrt(this); };

    private:
        bool checkAddress() override;
    };

    class Accept : public BasePack
    {
    public:
        Accept()
        {
            msgOffset = 0;
            addressFromOffset = 1;
            DataOffset = 3;
        }

       inline uint16_t getAddrFrom() { return _getAddrFrom(this); };
       inline uint8_t getCustomByte() { return packInfo->buffer[DataOffset]; };

    private:
        bool checkAddress() override;
    };

    class Request : public BasePack
    {
    public:
        Request()
        {
            msgOffset = 0;
            addressFromOffset = 1;
            addressToOffset = 3;
            DataOffset = 3;
        }

        inline uint16_t getAddrFrom() { return _getAddrFrom(this); };
        inline uint16_t getAddrTo() { return _getAddrTo(this); };

    private:
        bool checkAddress() override;
    };

}
