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

 Copyright 2016 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 <fstream>
#include <iostream>
#include <sstream>
#include "fstream_mars_fix.h"

#include <Metview.h>
#include <MvScanFileType.h>
#include <MvNetwork.h>
#include <MvPath.hpp>


class Download : public MvService
{
public:
    Download(const char* name) :
        MvService(name) {}
    void serve(MvRequest&, MvRequest&);
    bool sanitiseUrl(std::string& url);
};


// --------------------------------------------------------
// filetoString
// utility function to read a text file into a std::string
// --------------------------------------------------------

std::string filetoString(std::string filename)
{
    // open the file
    std::ifstream ifStream;
    ifStream.open(filename.c_str());

    // get the text stream and convert into a string
    std::stringstream stream;
    stream << ifStream.rdbuf();
    std::string str = stream.str();
    return str;
}


// --------------------------------------------------------------------
// replace_all
// utility function to replace all occurences of a string with another.
// Code taken from ECMWF's ecFlow.
// --------------------------------------------------------------------

bool replace_all(std::string& subject, const std::string& search, const std::string& replace)
{
    bool replaced = false;
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != std::string::npos) {
        subject.replace(pos, search.length(), replace);
        pos += replace.length();
        replaced = true;
    }
    return replaced;
}


// --------------------------------------------------------------------
// Download::sanitiseUrl
// Utility function to remove/replace unwanted characters from the URL.
// Returns true if any strings were replaced.
// --------------------------------------------------------------------

bool Download::sanitiseUrl(std::string& url)
{
    bool replaced = replace_all(url, " ", "%20");
    return replaced;
}


// ------------------------------------
// Download::serve
// Main function, performs the download
// ------------------------------------

void Download::serve(MvRequest& in, MvRequest& out)
{
    std::cout << "Entry Download::serve..." << std::endl;
    in.print();


    // if the URL is not supplied, abort immediately
    const char* url = in("URL");
    if (!url) {
        setError(1, "Download-> URL has not been supplied");
        return;
    }


    // get the target path (optional parameter)
    std::string target;
    std::string errText;
    if (!in.getPath("TARGET", target, true)) {
        setError(1, "Download-> Target path has not been supplied");
        return;
    }


    std::string urlStr(url);
    bool urlChanged = sanitiseUrl(urlStr);

    if (urlChanged) {
        sendProgress("Download-> URL sanitised to this: %s", urlStr.c_str());
    }


    // perform the network transfer
    std::string outname(marstmp());

    auto* net = new MvNetwork();
    bool ok = net->get(urlStr, outname, "", errText);

    if (!ok) {
        setError(1, "Download-> Failed to download data: %s", errText.c_str());
        return;
    }


    std::string h = net->header();
    long responseCode = net->responseCode();
    // XXX comment out for now - need to trim to 1024 chars for mars logging
    // sendProgress("Download-> header %s response code: %ld", h.c_str(), responseCode);
    char responseCodeString[32];
    sprintf(responseCodeString, "%ld", responseCode);


    // decide whether we got the right thing back or not
    bool gotData = responseCode / 100 == 2;  // code is 2xx

    if (!gotData) {
        if (FileHasValidSize(outname.c_str())) {
            // in this case, we got an error, but also a file
            // - ASSUME that this file contains error information,
            // and therefore print it out
            std::string logtext = filetoString(outname);
            replace_all(logtext, "\n", " ");  // remove newlines for the error reporting
            setError(1, "Download-> Error code returned; message from server: %s", logtext.c_str());
            return;
        }
        else {
            setError(1, "Download-> Got error response code from server: %s", responseCodeString);
            return;
        }
    }


    // if we got to here, the download seems to have been ok
    // if the user specified a TARGET, we should copy the file there
    if (!target.empty()) {
        std::string copyCommand = "cp " + outname + " \"" + target + "\"";
        sendProgress("Download-> Copy: %s", copyCommand.c_str());
        int ret2 = system(copyCommand.c_str());

        if (ret2 != 0) {
            setError(1, "Download-> Failed to copy file to TARGET destination. Command was: %s", copyCommand.c_str());
            return;
        }
    }


    // Create output request
    std::string iconType = ScanFileType(outname.c_str());  // dynamically find the type
    out.setVerb(iconType.c_str());
    out("PATH") = outname.c_str();
    out("_RESPONSE_CODE") = responseCodeString;

    std::cout << "Exit Download::serve..." << std::endl;
    out.print();

    return;
}

int main(int argc, char** argv)
{
    MvApplication theApp(argc, argv);
    Download data("DOWNLOAD");

    theApp.run();
}
