/*
 * (C) Copyright 1996-2012 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include <fstream>

#include "eckit/config/Resource.h"
#include "eckit/filesystem/PathName.h"
#include "eckit/log/Log.h"
#include "eckit/utils/StringTools.h"
#include "eckit/utils/Tokenizer.h"

#include "odc/FastODA2Request.h"
#include "odc/GribCodes.h"
#include "odc/LibOdc.h"
#include "odc/Select.h"
#include "odc/tools/ODA2RequestTool.h"

using namespace std;
using namespace eckit;

namespace odc {
namespace tool {

char * static_argv[] = { const_cast<char *>("oda2request") };

ODA2RequestTool::ODA2RequestTool(int argc, char **argv)
: Tool(argc, argv)
{
	registerOptionWithArgument("-c");
	readConfig();
}

ODA2RequestTool::ODA2RequestTool()
: Tool(1, static_argv)
{
	registerOptionWithArgument("-c");
	readConfig();
}

ODA2RequestTool::~ODA2RequestTool() {}

void ODA2RequestTool::help(std::ostream &o) { o << "Creates MARS ARCHIVE request for a given file"; }

void ODA2RequestTool::usage(const std::string& name, std::ostream &o)
{
	o << name << " [-c configFile] [-q] <input-file.odb> [<output-file>]";
}

void ODA2RequestTool::run()
{
	eckit::PathName inputFile;
	string outputFile;

	switch (parameters().size())
	{
		case 3:
			outputFile = parameters(2);
		case 2:
			inputFile = parameters(1);
			break;
		default:
			Log::error() << "Usage: ";
			usage(parameters(0), Log::error());
			Log::error() << std::endl;
			return;// 1;
			break;
	}

	readConfig();

	string request = generateMarsRequest(inputFile, optionIsSet("-q"));

	if (outputFile.size() == 0)
		std::cout << request << std::endl;
	else
	{
		ofstream out(outputFile.c_str());
		out << request << std::endl;
		out.close();
	}
	
	return;
}

PathName ODA2RequestTool::config()
{
    return optionArgument("-c", std::string("~odc/codes/ODA2RequestTool.cfg"));
}

void ODA2RequestTool::readConfig() { readConfig(config()); }

void ODA2RequestTool::readConfig(const PathName& fileName)
{
	LOG_DEBUG_LIB(LibOdc) << "ODA2RequestTool::readConfig: reading file '" << fileName << "'" << std::endl;
	columnName2requestKey_.clear();

	string s = readFile(fileName);
	
	LOG_DEBUG_LIB(LibOdc) << "ODA2RequestTool::readConfig: parsing '" << s << "'" << std::endl;

	parseConfig(s);
}

void ODA2RequestTool::parseConfig(const std::string& s)
{
	LOG_DEBUG_LIB(LibOdc) << "ODA2RequestTool::parseConfig: '" << s << "'" << std::endl;

    vector<std::string> lines;
    Tokenizer("\n")(s, lines);

    Tokenizer tokenizer(": \t");
    for (size_t i = 0; i < lines.size(); ++i)
	{
		vector<std::string> words;
		tokenizer(lines[i], words);

		if (words.size() == 0)
			continue;

		ASSERT("Each line of config file should be like: 'MARS_KEYWORD : oda_column_name'" && words.size() == 2);
		columnName2requestKey_[words[1]] = words[0];
	}
}

inline string int_as_double2string(double v)
{
	stringstream s;
	s.precision(0);
	s << fixed << v;
	return s.str();
}

string ODA2RequestTool::gatherStatsFast(const PathName& inputFile)
{
	FastODA2Request<ODA2RequestClientTraits> o;
	o.parseConfig(readFile(config()));
	o.scanFile(inputFile);
	return o.genRequest();
}

void ODA2RequestTool::gatherStats(const PathName& inputFile)
{
	size_t n = columnName2requestKey_.size();
	values_ = vector<Values>(n);

	string columnList;
	for (std::map<string, string>::iterator it = columnName2requestKey_.begin();
		 it != columnName2requestKey_.end();
		 ++it)
	{
		if (it != columnName2requestKey_.begin())
			columnList += ", ";
		columnList += it->first;
	}
	
	const string select = std::string("select ") + columnList + " from \"" + inputFile + "\";";
	Log::info() << "Executing '" << select << "'" << std::endl;

	Translator<double, string> double2string;
	odc::Select oda(select, inputFile);
	odc::Select::iterator end = oda.end();
	for (odc::Select::iterator row = oda.begin(); row != end; ++row) 
		for (size_t i = 0; i < n; ++i)
		{
            odc::api::ColumnType type = row->columns()[i]->type();
            Value v = type == odc::api::STRING ? (*row).string(i)
                    : type == odc::api::INTEGER ? int_as_double2string((*row)[i])
					: double2string((*row)[i]);
			values_[i].insert(v);
		}
}

string ODA2RequestTool::generateMarsRequest(const PathName& inputFile, bool fast)
{
	stringstream request;

	if (fast)
		request << gatherStatsFast(inputFile);
	else
	{
		gatherStats(inputFile);

		size_t i = 0;
		std::map<string, string>::iterator end = columnName2requestKey_.end();
		for (std::map<string, string>::iterator it = columnName2requestKey_.begin(); it != end; ++it)
		{
			if (request.str().size()) request << ",\n";

			const std::string& key = it->second;
			const string k = StringTools::upper(key);

			string valuesList;	
			Values& vs = values_[i++];
			for (Values::iterator vi = vs.begin(); vi != vs.end(); ++vi)
			{
				string v = *vi;
				LOG_DEBUG_LIB(LibOdc) << "ODA2RequestTool::genRequest: v = '" << v  << "', key = " << key << std::endl;
				if (k == "TIME")
					v = StringTool::patchTimeForMars(v);
				else
				if (k == "CLASS" || k == "TYPE" || k == "STREAM")
				{
					LOG_DEBUG_LIB(LibOdc) << "ODA2RequestTool::genRequest: checking if '" << v << "' is numeric" << std::endl;
					if (StringTool::check(v, isdigit))
					{
						v = StringTools::trim(v);
						LOG_DEBUG_LIB(LibOdc) << "ODA2RequestTool::genRequest: replacing " << v << " with ";
						v = GribCodes::alphanumeric(StringTools::lower(key), v);
						LOG_DEBUG_LIB(LibOdc) << v << std::endl;
					}
					v = StringTools::upper(v);
				}

				if (vi != vs.begin())
					valuesList += "/";

				valuesList += v;
			}
			request << key << " = " << valuesList;
		}
	}

	stringstream str;
	str << "ODB," << std::endl;
	str << request.str();
	return str.str();
}

} // namespace tool 
} // namespace odc 

