#include "../../../inc/tagId/gs1/SGTIN96.h"

#include <QString>
#include <QrfeGlobal>

using namespace GS1;

#define SGTIN96_INFO_SIZE       44
#define SGTIN96_SERIAL_SIZE     38
#define SGTIN96_MAX_SERIAL_NO   0x3FFFFFFFFFULL

SGTIN96::SGTIN96()
    : QrfeTagId()
    , m_companyPrefix(0)
    , m_itemReference(0)
    , m_serialNumber(0)
    , m_filter(0)
    , m_partitionValue(0)
{
}

SGTIN96::SGTIN96(quint64 companyPrefix, quint64 itemReference, quint64 serialNumber, int filterValue, int partitionValue)
    : QrfeTagId()
    , m_companyPrefix(companyPrefix)
    , m_itemReference(itemReference)
    , m_serialNumber(serialNumber)
    , m_filter(filterValue)
    , m_partitionValue(partitionValue)
{
    m_valid = true;
    m_epc = toEPC();
}

SGTIN96::SGTIN96(const QByteArray& epc)
    : QrfeTagId(epc)
{
    m_valid = false;

    if(epc.count() != 12)
        return;

    quint64 companyPrefix;
    quint64 itemReference;
    quint64 serialNumber;
    int filterValue;
    int partitionValue;

    if(!parsePEC ( epc, companyPrefix, itemReference, serialNumber, filterValue, partitionValue ))
        return;

    m_companyPrefix = companyPrefix;
    m_itemReference = itemReference;
    m_filter = filterValue;
    m_partitionValue = partitionValue;
    m_serialNumber = serialNumber;

    m_valid = true;
}

QString SGTIN96::tagId() const
{
    return "urn:epc:tag:sgtin-96:" + QString::number(m_filter) + "." + QString::number(m_companyPrefix) + "." + QString::number(m_itemReference) + "." + QString::number(m_serialNumber);
}

quint8 SGTIN96::filter() const
{
    return m_filter;
}

quint8 SGTIN96::partitionValue() const
{
    return m_partitionValue;
}

quint64 SGTIN96::companyPrefix() const
{
    return m_companyPrefix;
}

quint32 SGTIN96::itemReference() const
{
    return m_itemReference;
}

quint64 SGTIN96::serialNumber() const
{
    return m_serialNumber;
}


QByteArray SGTIN96::toEPC()
{
    return generateEPC(m_companyPrefix, m_itemReference, m_serialNumber, m_filter, m_partitionValue);
}


SGTIN96 SGTIN96::fromEPC(const QByteArray& epc, bool* valid)
{
    quint64 companyPrefix;
    quint64 itemReference;
    quint64 serialNumber;
    int filterValue;
    int partitionValue;

    *valid = false;

    if(!parsePEC ( epc, companyPrefix, itemReference, serialNumber, filterValue, partitionValue ))
        return SGTIN96();

    *valid = true;

    return SGTIN96(companyPrefix, itemReference, serialNumber, filterValue, partitionValue);
}



int SGTIN96::getCompanyPrefixBitCount (int partitionValue)
{
    int companyPrefixBitCount = 40;
    switch(partitionValue)
    {
    case 0:
        companyPrefixBitCount = 40; break;
    case 1:
        companyPrefixBitCount = 37; break;
    case 2:
        companyPrefixBitCount = 34; break;
    case 3:
        companyPrefixBitCount = 30; break;
    case 4:
        companyPrefixBitCount = 27; break;
    case 5:
    default:
        companyPrefixBitCount = 24; break;
    case 6:
        companyPrefixBitCount = 20; break;
    }

    return companyPrefixBitCount;
}

int SGTIN96::getItemReferenceBitCount (int partitionValue)
{
    return SGTIN96_INFO_SIZE - getCompanyPrefixBitCount(partitionValue);
}

int SGTIN96::getCompanyPrefixDigitCount (int partitionValue)
{
    int companyPrefixDigitCount = 7;
    switch(partitionValue)
    {
    case 0:
        companyPrefixDigitCount = 12; break;
    case 1:
        companyPrefixDigitCount = 11; break;
    case 2:
        companyPrefixDigitCount = 10; break;
    case 3:
        companyPrefixDigitCount = 9; break;
    case 4:
        companyPrefixDigitCount = 8; break;
    case 5:
    default:
        companyPrefixDigitCount = 7; break;
    case 6:
        companyPrefixDigitCount = 6; break;
    }

    return companyPrefixDigitCount;
}

int SGTIN96::getItemReferenceDigitCount (int partitionValue)
{
    int itemReferenceDigitCount = 6;
    switch(partitionValue)
    {
    case 0:
        itemReferenceDigitCount = 1; break;
    case 1:
        itemReferenceDigitCount = 2; break;
    case 2:
        itemReferenceDigitCount = 3; break;
    case 3:
        itemReferenceDigitCount = 4; break;
    case 4:
        itemReferenceDigitCount = 5; break;
    case 5:
    default:
        itemReferenceDigitCount = 6; break;
    case 6:
        itemReferenceDigitCount = 7; break;
    }

    return itemReferenceDigitCount;
}

QByteArray SGTIN96::generateEPC ( quint64 companyPrefix, quint64 itemReference, quint64 serialNumber, int filterValue, int partitionValue )
{
    int companyPrefixBitCount = getCompanyPrefixBitCount(partitionValue);
    int itemReferenceBitCount = getItemReferenceBitCount(partitionValue);

    QByteArray epc;
    int bitPos = 0;
    epc = QrfeGlobal::appendBits(epc, bitPos, 0x30, 8);                               bitPos += 8;
    epc = QrfeGlobal::appendBits(epc, bitPos, filterValue, 3);                        bitPos += 3;
    epc = QrfeGlobal::appendBits(epc, bitPos, partitionValue, 3);                     bitPos += 3;
    epc = QrfeGlobal::appendBits(epc, bitPos, companyPrefix, companyPrefixBitCount);  bitPos += companyPrefixBitCount;
    epc = QrfeGlobal::appendBits(epc, bitPos, itemReference, itemReferenceBitCount);  bitPos += itemReferenceBitCount;
    epc = QrfeGlobal::appendBits(epc, bitPos, serialNumber, SGTIN96_SERIAL_SIZE);     bitPos += SGTIN96_SERIAL_SIZE;

    if(bitPos != 96)
        return QByteArray();

    return epc;
}

bool SGTIN96::parsePEC ( const QByteArray& epc, quint64& companyPrefix, quint64& itemReference, quint64& serialNumber, int& filterValue, int& partitionValue )
{
    if(epc.size() != 12)
        return false;

    int bitPos = 0;

    uchar header = QrfeGlobal::extractValue(epc, bitPos, 8);                            bitPos += 8;
    if(header != 0x30)
        return false;

    filterValue = QrfeGlobal::extractValue(epc, bitPos, 3);                             bitPos += 3;
    partitionValue = QrfeGlobal::extractValue(epc, bitPos, 3);                          bitPos += 3;

    int companyPrefixBitCount = getCompanyPrefixBitCount(partitionValue);
    int itemReferenceBitCount = getItemReferenceBitCount(partitionValue);

    companyPrefix = QrfeGlobal::extractValue(epc, bitPos, companyPrefixBitCount);       bitPos += companyPrefixBitCount;
    itemReference = QrfeGlobal::extractValue(epc, bitPos, itemReferenceBitCount);       bitPos += itemReferenceBitCount;
    serialNumber = QrfeGlobal::extractValue(epc, bitPos, SGTIN96_SERIAL_SIZE);          bitPos += SGTIN96_SERIAL_SIZE;

    return true;
}
