#include "adif.h"

#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QDebug>

/*
<CALL:4>W1XT<BAND:3>20m<FREQ:6>14.076<GRIDSQUARE:4>DM33<MODE:4>JT65<RST_RCVD:3>-21<RST_SENT:3>-14<QSO_DATE:8>20110422<TIME_ON:6>041712<TIME_OFF:6>042435<TX_PWR:1>4<COMMENT:34>1st JT65A QSO.   Him: mag loop 20W<STATION_CALLSIGN:6>VK3ACF<MY_GRIDSQUARE:6>qf22lb<eor>
<CALL:6>IK1SOW<BAND:3>20m<FREQ:6>14.076<GRIDSQUARE:4>JN35<MODE:4>JT65<RST_RCVD:3>-19<RST_SENT:3>-11<QSO_DATE:8>20110422<TIME_ON:6>052501<TIME_OFF:6>053359<TX_PWR:1>3<STATION_CALLSIGN:6>VK3ACF<MY_GRIDSQUARE:6>qf22lb<eor>
<CALL:6:S>W4ABC> ...
*/

void ADIF::init(QString const& filename)
{
    _filename = filename;
    _data.clear();
}


QString ADIF::extractField(QString const& record, QString const& fieldName) const
{
    int fieldNameIndex = record.indexOf ('<' + fieldName + ':', 0, Qt::CaseInsensitive);
    if (fieldNameIndex >=0)
    {
        int closingBracketIndex = record.indexOf('>',fieldNameIndex);
        int fieldLengthIndex = record.indexOf(':',fieldNameIndex);  // find the size delimiter
        int dataTypeIndex = -1;
        if (fieldLengthIndex >= 0)
        {
          dataTypeIndex = record.indexOf(':',fieldLengthIndex+1);  // check for a second : indicating there is a data type
          if (dataTypeIndex > closingBracketIndex)
            dataTypeIndex = -1; // second : was found but it was beyond the closing >
        }

        if ((closingBracketIndex > fieldNameIndex) && (fieldLengthIndex > fieldNameIndex) && (fieldLengthIndex< closingBracketIndex))
        {
            int fieldLengthCharCount = closingBracketIndex - fieldLengthIndex -1;
            if (dataTypeIndex >= 0)
              fieldLengthCharCount -= 2; // data type indicator is always a colon followed by a single character
            QString fieldLengthString = record.mid(fieldLengthIndex+1,fieldLengthCharCount);
            int fieldLength = fieldLengthString.toInt();
            if (fieldLength > 0)
            {
              QString field = record.mid(closingBracketIndex+1,fieldLength);
              return field;
            }
       }
    }
    return "";
}



void ADIF::load()
{
    _data.clear();
    QFile inputFile(_filename);
    if (inputFile.open(QIODevice::ReadOnly))
    {
      QTextStream in(&inputFile);
      QString buffer;
      bool pre_read {false};
      int end_position {-1};

      // skip optional header record
      do
        {
          buffer += in.readLine () + '\n';
          if (buffer.startsWith (QChar {'<'})) // denotes no header
            {
              pre_read = true;
            }
          else
            {
              end_position = buffer.indexOf ("<EOH>", 0, Qt::CaseInsensitive);
            }
        }
      while (!in.atEnd () && !pre_read && end_position < 0);
      if (!pre_read)            // found header
        {
          buffer.remove (0, end_position + 5);
        }
      while (buffer.size () || !in.atEnd ())
        {
          do
            {
              end_position = buffer.indexOf ("<EOR>", 0, Qt::CaseInsensitive);
              if (!in.atEnd () && end_position < 0)
                {
                  buffer += in.readLine () + '\n';
                }
            }
          while (!in.atEnd () && end_position < 0);
          int record_length {end_position >= 0 ? end_position + 5 : -1};
          auto record = buffer.left (record_length).trimmed ();
          auto next_record = buffer.indexOf (QChar {'<'}, record_length);
          buffer.remove (0, next_record >=0 ? next_record : buffer.size ());
          record = record.mid (record.indexOf (QChar {'<'}));
          add (extractField (record, "CALL")
               , extractField (record, "BAND")
               , extractField (record, "MODE")
               , extractField (record, "QSO_DATE"));
        }
        inputFile.close ();
    }
}


void ADIF::add(QString const& call, QString const& band, QString const& mode, QString const& date)
{
    QSO q;
    q.call = call;
    q.band = band;
    q.mode = mode;
    q.date = date;
    if (q.call.size ())
      {
        _data.insert(q.call,q);
        // qDebug() << "Added as worked:" << call << band << mode << date;
      }
}

// return true if in the log same band and mode (where JT65 == JT9)
bool ADIF::match(QString const& call, QString const& band, QString const& mode) const
{
    QList<QSO> qsos = _data.values(call);
    if (qsos.size()>0)
    {
        QSO q;
        foreach(q,qsos)
        {
            if (     (band.compare(q.band,Qt::CaseInsensitive) == 0)
                  || (band=="")
                  || (q.band==""))
            {
                if (
                     (
                       ((mode.compare("JT65",Qt::CaseInsensitive)==0) ||
                        (mode.compare("JT9",Qt::CaseInsensitive)==0)  ||
                        (mode.compare("FT8",Qt::CaseInsensitive)==0))
                       &&
                       ((q.mode.compare("JT65",Qt::CaseInsensitive)==0) ||
                        (q.mode.compare("JT9",Qt::CaseInsensitive)==0)  ||
                        (q.mode.compare("FT8",Qt::CaseInsensitive)==0))
                     )
                        || (mode.compare(q.mode,Qt::CaseInsensitive)==0)
                        || (mode=="")
                        || (q.mode=="")
                    )
                return true;
            }
        }
    }
    return false;
}    

QList<QString> ADIF::getCallList() const
{
    QList<QString> p;
    QMultiHash<QString,QSO>::const_iterator i = _data.constBegin();
     while (i != _data.constEnd())
     {
         p << i.key();
         ++i;
     }
    return p;
}


    
int ADIF::getCount() const
{
    return _data.size();
}   
    
QByteArray ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QString const& mode
                           , QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn
                           , QDateTime const& dateTimeOff, QString const& band, QString const& comments
                           , QString const& name, QString const& strDialFreq, QString const& m_myCall
                           , QString const& m_myGrid, QString const& m_txPower, QString const& operator_call)
{
  QString t;
  t = "<call:" + QString::number(hisCall.length()) + ">" + hisCall;
  t += " <gridsquare:" + QString::number(hisGrid.length()) + ">" + hisGrid;
  t += " <mode:" + QString::number(mode.length()) + ">" + mode;
  t += " <rst_sent:" + QString::number(rptSent.length()) + ">" + rptSent;
  t += " <rst_rcvd:" + QString::number(rptRcvd.length()) + ">" + rptRcvd;
  t += " <qso_date:8>" + dateTimeOn.date().toString("yyyyMMdd");
  t += " <time_on:6>" + dateTimeOn.time().toString("hhmmss");
  t += " <qso_date_off:8>" + dateTimeOff.date().toString("yyyyMMdd");
  t += " <time_off:6>" + dateTimeOff.time().toString("hhmmss");
  t += " <band:" + QString::number(band.length()) + ">" + band;
  t += " <freq:" + QString::number(strDialFreq.length()) + ">" + strDialFreq;
  t += " <station_callsign:" + QString::number(m_myCall.length()) + ">" +
      m_myCall;
  t += " <my_gridsquare:" + QString::number(m_myGrid.length()) + ">" +
      m_myGrid;
  if (m_txPower != "")
    t += " <tx_pwr:" + QString::number(m_txPower.length()) +
        ">" + m_txPower;
  if (comments != "")
    t += " <comment:" + QString::number(comments.length()) +
        ">" + comments;
  if (name != "")
    t += " <name:" + QString::number(name.length()) +
        ">" + name;
  if (operator_call!="")
      t+=" <operator:" + QString::number(operator_call.length()) +
              ">" + operator_call;
  return t.toLatin1 ();
}


// open ADIF file and append the QSO details. Return true on success
bool ADIF::addQSOToFile(QByteArray const& ADIF_record)
{
    QFile f2(_filename);
    if (!f2.open(QIODevice::Text | QIODevice::Append))
        return false;
    else
    {
        QTextStream out(&f2);
        if (f2.size()==0)
            out << "WSJT-X ADIF Export<eoh>" << endl;  // new file

        out << ADIF_record << " <eor>" << endl;
        f2.close();
    }
    return true;
}