/***************************** 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 *************************************/

// CleanFile.cc, vk Dec-98
//
// Remove rubbish octets before the first GRIB or BUFR message
// (normally cursed by Fortran binary I/O) i.e. clean the file
// for Metview to recognise the correct file type.
//
// March 2002: added parameter SKIP_HIRLAM_CUSTOM_RECORD to be
// able to remove the custom Hirlam GRIB msg (1st msg),
// and removed MAX_RUBBISH_OCTETS parameter (now dynamic).
//

#include <fstream>
#include <iostream>

#include "fstream_mars_fix.h"

#include "Metview.h"

//__________________________________________________________ CleanFile

class CleanFile : public MvService
{
public:
    CleanFile();
    ~CleanFile();

    CleanFile(const CleanFile& anOther) = delete;
    void operator=(const CleanFile& anOther) = delete;

    void serve(MvRequest&, MvRequest&);

private:
    bool findNextMsg(std::ifstream& ifs, int& skipped, char& msgType);
};


//_________________________________________________________

CleanFile::CleanFile() :
    MvService("CLEANFILE")
{
}
//_________________________________________________________

CleanFile::~CleanFile() = default;
//_________________________________________________________

void CleanFile ::serve(MvRequest& in, MvRequest& out)
{
    std::cout << "\n--> CleanFile::serve() in" << std::endl;
    in.print();

    const char* data = in("DATA");
    const char* path = in("PATH");

    if (data && path)  //-- do not allow two data sources
    {
        setError(1, "CleanFile-> Only one input allowed: both Path and Data have values!");
        return;
    }
    if ((!data) && (!path))  //-- one data source required
    {
        setError(1, "CleanFile-> No input file: both Path and Data are empty!");
        return;
    }

    if (data) {
        MvRequest icon;
        in.getValue(icon, "DATA");
        path = icon("PATH");
    }

    //-- open file
    std::ifstream myStream(path, std::ios::in);
    if (!myStream) {
        setError(1, "CleanFile-> Cannot open file '%s'!", path);
        return;
    }

    //-- search for real data start
    char myMsgType = '?';
    int mySkipCount = 0;

    bool found = findNextMsg(myStream, mySkipCount, myMsgType);

    //-- did we find real data?
    if (!found) {
        setError(1, "CleanFile-> No messages found in %s, searched %d octets!", path, mySkipCount);
        return;
    }

    sendProgress("Removed %d bytes before the first %s msg", mySkipCount, (myMsgType == 'G' ? "GRIB" : "BUFR"));

    //-- skip 1st record too?

    const char* skipFirstMsg = in("SKIP_HIRLAM_CUSTOM_RECORD");

    if (skipFirstMsg && strcmp(skipFirstMsg, "YES") == 0) {
        int mySkipCount2 = 0;
        found = findNextMsg(myStream, mySkipCount2, myMsgType);

        //-- still data after 1st msg?
        if (!found) {
            setError(1, "CleanFile-> 'Custom Record' removed, but no more msgs found!");
            return;
        }

        mySkipCount2 += 4;  //-- +4: 1st "GRIB"/"BUFR" was already past

        sendProgress("Removed 'Custom Record' (first msg, %d octets more)", mySkipCount2);

        mySkipCount += mySkipCount2;
    }

    //-- create output file
    char* myFileOut = marstmp();
    std::ofstream myStreamOut(myFileOut, std::ios::out);

    //-- skip rubbish
    myStream.seekg(0);  // rewind
    int myCount = mySkipCount;
    while (myCount-- > 0) {
        myStream.get();
    }
    //-- create a clean file
    myCount = 0;
    int myOctet = myStream.get();
    while (!myStream.eof()) {
        ++myCount;
        myStreamOut.put(myOctet);
        myOctet = myStream.get();
    }
    //-- create return request
    MvRequest myOutReq((myMsgType == 'G' ? "GRIB" : "BUFR"));
    myOutReq("TEMPORARY") = 1;
    myOutReq("PATH") = myFileOut;
    out = myOutReq;

    sendProgress("Created %d clean octets", myCount);

    std::cout << "\n--> CleanFile::serve() out" << std::endl;
    out.print();
}
//_________________________________________________________

bool CleanFile::findNextMsg(std::ifstream& ifs, int& skipCount, char& msgType)
{
    const int BUFR[5] = {'B', 'U', 'F', 'R', '+'};
    const int GRIB[5] = {'G', 'R', 'I', 'B', '+'};

    int iB = 0;  //-- indices to BUFR[5] and GRIB[5]
    int iG = 0;

    int myOctet = ifs.get();
    while (!ifs.eof()) {
        ++skipCount;

        if (myOctet == BUFR[iB])
            ++iB;  //-- advance in finding "BUFR"
        else
            iB = 0;  //-- revert back to the 1st char

        if (myOctet == GRIB[iG])
            ++iG;  //-- advance in finding "GRIB"
        else
            iG = 0;  //-- revert back to the 1st char

        if (iB == 4) {
            msgType = 'B';  //-- we have found "BUFR"
            skipCount -= 4;
            return true;
        }
        else if (iG == 4) {
            msgType = 'G';  //-- we have found "GRIB"
            skipCount -= 4;
            return true;
        }

        myOctet = ifs.get();  //-- not yet => get next char
    }

    return false;
}
//_________________________________________________________

int main(int argc, char** argv)
{
    MvApplication myApp(argc, argv);
    CleanFile myProfessional;

    myApp.run();
}
