/*
 *  Copyright (c) 2013 Croatia Control Ltd. (www.crocontrol.hr)
 *
 *  This file is part of Asterix.
 *
 *  Asterix is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Asterix is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Asterix.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * AUTHORS: Damir Salantic, Croatia Control Ltd.
 *
 */

#ifdef _DEBUG
#include "asterix.h"
#endif

#include "InputParser.h"

InputParser::InputParser(AsterixDefinition *pDefinition)
        : m_pDefinition(pDefinition) {
}

/*
 * Parse data
 */
AsterixData *
InputParser::parsePacket(const unsigned char *m_pBuffer, unsigned int m_nBufferSize, unsigned long nTimestamp) {
    AsterixData *pAsterixData = new AsterixData();
    unsigned int m_nPos = 0;
    unsigned int m_nDataLength = 0;
    const unsigned char *m_pData = m_pBuffer; // internally used pointer to parsed data

    while (m_nPos < m_nBufferSize) {
        bool bOK = true;
        m_nDataLength = m_nBufferSize - m_nPos;

        if (m_nDataLength <= 3) {
            Tracer::Error("Not enough data for Asterix header (%d)", m_nDataLength);
            break;
        }

        while (bOK && m_nDataLength > 3) {
            unsigned char nCategory = *m_pData;
            m_pData++;
            m_nPos++;
            unsigned short dataLen = *m_pData; // length
#ifdef _DEBUG
            unsigned char nDataLen1 = *m_pData;
#endif
            m_pData++;
            m_nPos++;
#ifdef _DEBUG
            unsigned char nDataLen2 = *m_pData;
#endif
            dataLen <<= 8;
            dataLen |= *m_pData;
            m_pData++;
            m_nPos++;

            // parse Asterix data
            if (dataLen <= 3 || dataLen > m_nDataLength) {
                Tracer::Error("Wrong Asterix data length (%d)", dataLen);

                if (dataLen <= 3) {
                    // otherwise finish
                    return pAsterixData;
                }

                // there is not enough data, but parse what is there
                dataLen = (unsigned short) m_nDataLength;
            }

            m_nDataLength -= 3;
            dataLen -= 3;
#ifdef _DEBUG
            std::stringstream buffer;
            buffer << std::hex << std::setfill('0') << std::setw(2) <<
          std::uppercase << static_cast<unsigned>(nCategory) << " ";
            buffer << std::hex << std::setfill('0') << std::setw(2) <<
              std::uppercase << static_cast<unsigned>(nDataLen1) << " ";
            buffer << std::hex << std::setfill('0') << std::setw(2) <<
              std::uppercase << static_cast<unsigned>(nDataLen2) << " ";

            for (int i = 0; i < dataLen; i++) {
              buffer << std::hex << std::setfill('0') << std::setw(2) <<
                std::uppercase << static_cast<unsigned>(m_pData[i]) << " ";
            }
            std::string hexString = buffer.str();
            hexString.erase(hexString.size() - 1);
            LOGDEBUG(1, "[%s]\n", hexString.c_str());
#endif
            DataBlock *db = new DataBlock(m_pDefinition->getCategory(nCategory), dataLen, m_pData, nTimestamp);
            m_pData += dataLen;
            m_nPos += dataLen;
            pAsterixData->m_lDataBlocks.push_back(db);

            if (db->m_bFormatOK == false) {
                break;
            }
            m_nDataLength -= dataLen;
        }
    }
    return pAsterixData;
}

DataBlock *
InputParser::parse_next_data_block(const unsigned char *m_pData, unsigned int &m_nPos, unsigned int m_nBufferSize,
                                   unsigned long nTimestamp, unsigned int &m_nDataLength)
/*  parse next data block
 *  AUTHOR: Krzysztof Rutkowski, ICM UW, krutk@icm.edu.pl
 */
{
    unsigned char nCategory = *m_pData;
    m_pData++;
    m_nPos++;
    unsigned short dataLen = *m_pData; // length
#ifdef _DEBUG
    unsigned char nDataLen1 = *m_pData;
#endif
    m_pData++;
    m_nPos++;
#ifdef _DEBUG
    unsigned char nDataLen2 = *m_pData;
#endif
    dataLen <<= 8;
    dataLen |= *m_pData;
    m_pData++;
    m_nPos++;

    // parse Asterix data
    if (dataLen <= 3 || dataLen > m_nDataLength) {
        Tracer::Error("Wrong Asterix data length (%d)", dataLen);

        if (dataLen <= 3) {
            // otherwise finish
            return NULL;
        }

        // there is not enough data, but parse what is there
        dataLen = (unsigned short) m_nDataLength;
    }

    m_nDataLength -= 3;
    dataLen -= 3;
#ifdef _DEBUG
    std::stringstream buffer;
    buffer << std::hex << std::setfill('0') << std::setw(2) <<
  std::uppercase << static_cast<unsigned>(nCategory) << " ";
    buffer << std::hex << std::setfill('0') << std::setw(2) <<
      std::uppercase << static_cast<unsigned>(nDataLen1) << " ";
    buffer << std::hex << std::setfill('0') << std::setw(2) <<
      std::uppercase << static_cast<unsigned>(nDataLen2) << " ";

    for (int i = 0; i < dataLen; i++) {
      buffer << std::hex << std::setfill('0') << std::setw(2) <<
        std::uppercase << static_cast<unsigned>(m_pData[i]) << " ";
    }
    std::string hexString = buffer.str();
    hexString.erase(hexString.size() - 1);
    LOGDEBUG(1, "[%s]\n", hexString.c_str());
#endif
    DataBlock *db = new DataBlock(m_pDefinition->getCategory(nCategory), dataLen, m_pData, nTimestamp);
    m_pData += dataLen;
    m_nPos += dataLen;
    m_nDataLength -= dataLen;
    return db;
}

std::string InputParser::printDefinition() {
    return m_pDefinition->printDescriptors();
}

bool InputParser::filterOutItem(int cat, std::string item, const char *name) {
    return m_pDefinition->filterOutItem(cat, item, name);
}

bool InputParser::isFiltered(int cat, std::string item, const char *name) {
    return m_pDefinition->isFiltered(cat, item, name);
}
