#ifndef GERMANAIRLINESVA_FILE_LOGBOOK_LOGBOOK_H
#define GERMANAIRLINESVA_FILE_LOGBOOK_LOGBOOK_H

#include <cstdint>
#include <fstream>
#include <string>
#include <utility>
#include <vector>

#include "constants.h"
#include "helpers.hpp"
#include "logbookEntry.hpp"
#include "util.hpp"

namespace germanairlinesva
{
namespace file
{
  namespace logbook
  {
    /*
     * Logbook Header (6)
     * CHAR[5] | UINT8
     * --------+--------
     * VGAL    | VERSION
     *
     * Logbook Entries (n)
     * LOGBOOKENTRY[]
     */
    class Logbook
    {
      private:
        std::vector<LogbookEntry> entries;

        inline void readVersion1(std::ifstream &in)
        {
          std::string date = readString(in, 10);
          std::string flightType = readString(in, 1);
          std::string flightNumber = readString(in, 4);
          std::string aircraftType = readString(in, 4);
          std::string aircraftRegistration = readString(in, 6);
          std::string departureAirport = readString(in);
          std::string departureGate = readString(in);
          std::string departureRunway = readString(in);
          std::string arrivalAirport = readString(in);
          std::string arrivalGate = readString(in);
          std::string arrivalRunway = readString(in);
          std::string offBlockTime = readString(in, 5);
          std::string outTime = readString(in, 5);
          std::string inTime = readString(in, 5);
          std::string onBlockTime = readString(in, 5);
          float totalFlightTime = read<float>(in);
          float taxiOutFuel = read<float>(in);
          float inFlightFuel = read<float>(in);
          float taxiInFuel = read<float>(in);
          float totalFuel = read<float>(in);
          float taxiOutDistance = read<float>(in);
          float inFlightDistance = read<float>(in);
          float taxiInDistance = read<float>(in);
          float totalDistance = read<float>(in);
          float maxLandingRate = read<float>(in);
          std::uint8_t touchdowns = read<std::uint8_t>(in);
          float maxLandingGees = read<float>(in);
          std::string recordingFilename = readString(in);
          float points = read<float>(in);
          std::uint8_t flags = read<std::uint8_t>(in);

          this->addEntry(date,
                         flightType,
                         flightNumber,
                         aircraftType,
                         aircraftRegistration,
                         departureAirport,
                         departureGate,
                         departureRunway,
                         arrivalAirport,
                         arrivalGate,
                         arrivalRunway,
                         offBlockTime,
                         outTime,
                         inTime,
                         onBlockTime,
                         totalFlightTime,
                         taxiOutFuel,
                         inFlightFuel,
                         taxiInFuel,
                         totalFuel,
                         taxiOutDistance,
                         inFlightDistance,
                         taxiInDistance,
                         totalDistance,
                         maxLandingRate,
                         touchdowns,
                         maxLandingGees,
                         recordingFilename,
                         points,
                         flags);
        }

        inline void fromFile()
        {
          std::ifstream in(BASE_DIRECTORY LOGBOOK, std::ifstream::binary);

          std::string ident = readString(in, 4);
          if (ident.compare(LOGBOOK_HEADER) != 0) {
            throw std::invalid_argument("Wrong file");
          }
          std::uint8_t version = read<std::uint8_t>(in);

          if (version == 1) {
            while (in.peek() != EOF) {
              this->readVersion1(in);
            }
          }
        }

      public:
        inline Logbook()
        {
          if (util::fileExists(BASE_DIRECTORY LOGBOOK)) {
            this->fromFile();
          }
        }

        template <class... Args> inline void addEntry(Args &&...args)
        {
          this->entries.emplace_back(std::forward<Args>(args)...);
        }

        inline void toFile() const
        {
          std::ofstream out(BASE_DIRECTORY LOGBOOK, std::fstream::binary);
          out.write(LOGBOOK_HEADER, 4);
          out.write("\1", 1);
          for (const LogbookEntry &entry : this->entries) {
            entry.toFile(out);
          }
          out.close();
        }
    };
  } // namespace logbook
} // namespace file
} // namespace germanairlinesva
#endif