/**********************************************************************************************
   Copyright (C) 2015 Ivo Kronenberg <>

   This program 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.

   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

**********************************************************************************************/

#include "gis/fit/decoder/CFitDecoder.h"

#include "gis/fit/decoder/CFitCrcState.h"
#include "gis/fit/decoder/CFitDevFieldDefinitionState.h"
#include "gis/fit/decoder/CFitFieldDataState.h"
#include "gis/fit/decoder/CFitFieldDefinitionState.h"
#include "gis/fit/decoder/CFitHeaderState.h"
#include "gis/fit/decoder/CFitRecordContentState.h"
#include "gis/fit/decoder/CFitRecordHeaderState.h"
#include "gis/fit/defs/fit_const.h"

CFitDecoder::CFitDecoder() {
  stateMap[eDecoderStateFileHeader] = new CFitHeaderState(data);
  stateMap[eDecoderStateRecord] = new CFitRecordHeaderState(data);
  stateMap[eDecoderStateRecordContent] = new CFitRecordContentState(data);
  stateMap[eDecoderStateFieldDef] = new CFitFieldDefinitionState(data);
  stateMap[eDecoderStateDevFieldDef] = new CFitDevFieldDefinitionState(data);
  stateMap[eDecoderStateFieldData] = new CFitFieldDataState(data);
  stateMap[eDecoderStateFileCrc] = new CFitCrcState(data);
}

CFitDecoder::~CFitDecoder() {
  qDeleteAll(stateMap);
  stateMap.clear();

  data.messages.clear();
}

void CFitDecoder::resetSharedData() {
  data.definitions = QMap<quint8, CFitDefinitionMessage>();
  data.definitionHistory = QList<CFitDefinitionMessage>();
  data.messages = QList<CFitMessage>();
  data.devFieldProfiles = QList<CFitFieldProfile>();
  data.lastDefinition = nullptr;
  data.lastMessage = nullptr;
  data.timestamp = 0;
  data.lastTimeOffset = 0;
  data.fileBytesRead = 0;
  data.fileLength = 0;
  data.crc = 0;
}

void printDefinitions(const QList<CFitDefinitionMessage>& defs) {
  for (int i = 0; i < defs.size(); i++) {
    for (QString& s : defs[i].messageInfo()) {
      qDebug() << s;
    }
  }
}

void printMessages(const QList<CFitMessage>& messages) {
  for (int i = 0; i < messages.size(); i++) {
    for (QString& s : messages[i].messageInfo()) {
      qDebug() << s;
    }
  }
}

void CFitDecoder::printDebugInfo(){FITDEBUG(1, printDefinitions(data.definitionHistory))
                                       FITDEBUG(1, printMessages(data.messages))}

QList<QString> decoderStateNames = {
    "File Header", "Record", "Record Content", "Field Definition", "Development Field Definition", "Field Data",
    "CRC",         "End"};

void printByte(QFile& file, decode_state_e state, quint8 dataByte) {
  FITDEBUG(3, qDebug() << QString("decoding byte %1 - %2 - %3")
                              .arg(file.pos(), 6, 10, QLatin1Char(' '))
                              .arg(dataByte, 8, 2, QLatin1Char('0'))
                              .arg(decoderStateNames.at(state)));
}

void CFitDecoder::decode(QFile& file) {
  resetSharedData();

  file.seek(0);

  quint8 dataByte;
  decode_state_e state = eDecoderStateFileHeader;
  while (!file.atEnd()) {
    file.getChar((char*)&dataByte);
    try {
      printByte(file, state, dataByte);
      state = stateMap[state]->processByte(dataByte);
      if (state == eDecoderStateEnd) {
        // end of file, everything ok
        printDebugInfo();
        return;
      }
    } catch (QString& errormsg) {
      printDebugInfo();
      throw errormsg;
    }
  }
  // unexpected end of file
  printDebugInfo();
  throw tr("FIT decoding error: unexpected end of file %1.").arg(file.fileName());
}

const QList<CFitMessage>& CFitDecoder::getMessages() const { return data.messages; }
