#include "tpdu.h"
#include "cemi_frame.h"

TPDU::TPDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame)
{
}

TpduType TPDU::type() const
{
    if (control())
    {
        if (numbered())
        {
            if ((_data[0] & 1) == 0)
                return Ack;
            else
                return Nack;
        }
        else if ((_data[0] & 1) == 0)
            return Connect;
        else
            return Disconnect;
    }
    else
    {
        if (_frame.addressType() == GroupAddress)
        {
            if (_frame.destinationAddress() == 0)
                return DataBroadcast;
            else
                return DataGroup;
        }
        else if (numbered())
            return DataConnected;
        else
            return DataInduvidual;
    }
}

void TPDU::type(TpduType type)
{
    switch (type)
    {
        case DataBroadcast:
        case DataGroup:
        case DataInduvidual:
            _data[0] &= 0x3;
            break;

        case DataConnected:
            _data[0] &= 0xC3;
            _data[0] |= 0x40;
            break;

        case Connect:
            _data[0] = 0x80;
            break;

        case Disconnect:
            _data[0] = 0x81;
            break;

        case Ack:
            _data[0] &= ~0xC3;
            _data[0] |= 0xC2;
            break;

        case Nack:
            _data[0] |= 0xC3;
            break;
    }
}

bool TPDU::numbered() const
{
    return (_data[0] & 0x40) > 0;
}

void TPDU::numbered(bool value)
{
    if (value)
        _data[0] |= 0x40;
    else
        _data[0] &= ~0x40;
}

bool TPDU::control() const
{
    return (_data[0] & 0x80) > 0;
}

void TPDU::control(bool value)
{
    if (value)
        _data[0] |= 0x80;
    else
        _data[0] &= ~0x80;
}

uint8_t TPDU::sequenceNumber() const
{
    return ((_data[0] >> 2) & 0xF);
}

void TPDU::sequenceNumber(uint8_t value)
{
    _data[0] &= ~(0xF << 2);
    _data[0] |= ((value & 0xF) << 2);
}

APDU& TPDU::apdu()
{
    return _frame.apdu();
}

CemiFrame& TPDU::frame()
{
    return _frame;
}

void TPDU::printPDU()
{
    /*    print.print("TPDU: ");
        print.print(type(), HEX, 2);
        print.print("    ");
        for (uint8_t i = 0; i < apdu().length() + 1; ++i)
        {
            if (i) print.print(" ");
            print.print(_data[i], HEX, 2);
        }
        print.println()*/;
}