/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <array>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <string>
#include <vector>

#include "fstream_mars_fix.h"

#include <cassert>
#include <cstdlib>

#include "MvMiscellaneous.h"

struct FxpCluster
{
    std::string lon;
    std::string lat;
    std::string z;
    std::string f;
    std::string rms;
};

struct FxpTraj
{
    std::vector<std::string> data;
    std::vector<FxpCluster> clVec;
};

struct FxpRelease : public std::map<std::string, std::string>
{
};

bool convert(const std::string& trFile, const std::string& outPattern);
bool write(const std::vector<FxpTraj>& tr, const std::string& id, const std::string& outPattern,
           const std::string&, const std::string&, const std::vector<FxpRelease>&);

bool convert(const std::string& trFile, const std::string& outPattern)
{
    std::ifstream in(trFile.c_str(), std::ios::in);
    if (!in.is_open()) {
        std::cerr << "Cannot open input file: " << trFile << std::endl;
        return false;
    }

    std::string line;

    // run date and time
    getline(in, line);
    std::string runDate, runTime;
    {
        std::stringstream sst(line);
        sst >> runDate >> runTime;
    }

    // options
    getline(in, line);

    // release
    int relNum = 0;
    getline(in, line);

    relNum = metview::fromString<int>(line);
    if (relNum <= 0 || relNum > 10000) {
        std::cerr << "Incorrect release number: " << relNum << std::endl;
        return false;
    }

    std::vector<FxpRelease> relVec;

    for (int i = 0; i < relNum; i++) {
        FxpRelease r;
        getline(in, line);
        {
            std::stringstream sst(line);
            sst >> r["start"] >> r["end"] >> r["lon1"] >>
                r["lat1"] >> r["lon2"] >> r["lat2"] >>
                r["z1"] >> r["z2"] >> r["zUnits"] >> r["particlesNum"];
        }
        getline(in, line);
        {
            // Todo
            // r["name"]=line;
        }
        relVec.push_back(r);
    }

    // Trajectory waypoints

    std::map<std::string, std::vector<FxpTraj> > trMap;
    std::array<int, 16> fieldWidthArr = {5, 8, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 6, 6};
    std::array<int, 5> clWidthArr = {8, 8, 7, 6, 8};

    while (getline(in, line)) {
        FxpTraj t;
        std::string str;
        int pos = 0;
        for (int i : fieldWidthArr) {
            t.data.push_back(metview::simplified(line.substr(pos, i)));
            pos += i;
        }

        std::vector<FxpCluster> clVec;
        while (pos < static_cast<int>(line.size())) {
            FxpCluster cl;
            cl.lon = metview::simplified(line.substr(pos, clWidthArr[0]));
            pos += clWidthArr[0];
            if (pos >= static_cast<int>(line.size()))
                return false;

            cl.lat = metview::simplified(line.substr(pos, clWidthArr[1]));
            pos += clWidthArr[1];
            if (pos >= static_cast<int>(line.size()))
                return false;

            cl.z = metview::simplified(line.substr(pos, clWidthArr[2]));
            pos += clWidthArr[2];
            if (pos >= static_cast<int>(line.size()))
                return false;

            cl.f = metview::simplified(line.substr(pos, clWidthArr[3]));
            pos += clWidthArr[3];
            if (pos >= static_cast<int>(line.size()))
                return false;

            cl.rms = metview::simplified(line.substr(pos, clWidthArr[4]));
            pos += clWidthArr[4];
            if (pos > static_cast<int>(line.size()))
                return false;

            clVec.push_back(cl);
        }

        t.clVec = clVec;

        std::string actId = t.data[0];
        trMap[actId].push_back(t);
    }

    for (auto& it : trMap) {
        if (!write(it.second, it.first, outPattern, runDate, runTime, relVec))
            return false;
    }

    return true;
}

bool write(const std::vector<FxpTraj>& tr, const std::string& id, const std::string& outPattern,
           const std::string& runDate, const std::string& runTime, const std::vector<FxpRelease>& relVec)
{
    std::ofstream out;
    int relId = metview::fromString<int>(id);
    assert(relId > 0);

    // Build output file name
    int digits = static_cast<int>(log10(relId)) + 1;
    if (digits <= 3)
        digits = 3;

    std::stringstream ss;
    ss << std::setw(digits) << std::setfill('0') << relId;
    std::string outFile = outPattern + ss.str() + ".csv";

    // Open output file
    out.open(outFile.c_str());
    if (!out.is_open()) {
        std::cerr << "Cannot open output file: " << outFile << std::endl;
        return false;
    }

    if (tr.empty()) {
        std::cerr << "No trajectory was found for id=" << id << std::endl;
        return false;
    }

    // metadata
    out << "runDate=" << runDate << " "
        << "runTime=" << runTime;

    const FxpTraj& tFirst = tr[0];
    int clNum = tFirst.clVec.size();
    out << " clNum=" << clNum;


    relId -= 1;
    if (relId < 0 || relId >= static_cast<int>(relVec.size())) {
        std::cerr << "Invalid release id=" << id << std::endl;
        return false;
    }

    for (const auto& it : relVec[relId]) {
        out << " " << it.first << "=" << it.second;
    }

    out << std::endl;

    // write header
    out << "time,meanLon,meanLat,meanZ,meanTopo,meanPBL,meanTropo,meanPv,rmsHBefore,"
        << "rmsHAfter,rmsVBefore,rmsVAfter,pblFract,pv2Fract,tropoFract";

    for (int j = 0; j < clNum; j++) {
        out << ",clLon_" << j + 1 << ",clLat_" << j + 1 << ",clZ_" << j + 1 << ",clFract_" << j + 1 << ",clRms_" << j + 1;
    }

    out << std::endl;

    // write data
    for (const auto& t : tr) {
        assert(t.data.size() == 16);

        for (size_t j = 1; j < t.data.size(); j++) {
            out << t.data[j] << ",";
        }

        for (size_t j = 0; j < t.clVec.size(); j++) {
            out << t.clVec[j].lon << "," << t.clVec[j].lat << "," << t.clVec[j].z << "," << t.clVec[j].f << "," << t.clVec[j].rms;

            if (j != t.clVec.size() - 1) {
                out << ",";
            }
        }

        out << std::endl;
    }

    return true;
}

//============================================
// Main
//============================================

int main(int argc, char** argv)
{
    if (argc != 3) {
        std::cerr << "Invalid number of arguments: " << argc << "(instead of 3)" << std::endl;
        exit(1);
    }

    std::string trFile(argv[1]);
    std::string outPattern(argv[2]);

    if (!convert(trFile, outPattern))
        exit(1);

    return 0;
}
