/*
	Movie Database - OMDb/IMDb

	(C) 2009-2016 NG-Team
	(C) 2016 NI-Team

	License: GPL

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.

*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <fstream>
#include <iostream>

#include <unistd.h>

#include <global.h>
#include <driver/screen_max.h>
#include <system/httptool.h>
#include <system/helpers.h>
#include <system/helpers-json.h>
#include <eitd/sectionsd.h>
#include <json/json.h>

#include "mdb-imdb.h"

CIMDB *CIMDB::getInstance()
{
	static CIMDB *imdb = NULL;
	if (!imdb)
		imdb = new CIMDB();
	return imdb;
}

CIMDB::CIMDB()
{
	key = g_settings.omdb_api_key;
	hintbox = NULL;
	search_url	= "http://www.google.de/search?q=";
	search_outfile	= "/tmp/google.out";
	search_error	= "IMDb: Google download failed";
	imdb_url	= "http://www.omdbapi.com/?plot=full&r=json&apikey=" + key + "&i=";
	imdb_outfile	= "/tmp/imdb.json";
	posterfile	= "/tmp/imdb.jpg";

	acc = 0;
}

CIMDB::~CIMDB()
{
	cleanup();
}

void CIMDB::setTitle(std::string epgtitle)
{
	hintbox = new CHintBox(LOCALE_MESSAGEBOX_INFO, LOCALE_IMDB_READ_DATA);
	hintbox->paint();

	getMovieDetails(epgtitle);

	if (hintbox)
	{
		hintbox->hide();
		delete hintbox;
		hintbox = NULL;
	}
}

std::string CIMDB::utf82url(std::string s)
{
	std::stringstream ss;
	for (size_t i = 0; i < s.length(); ++i)
	{
		if (unsigned(s[i]) <= ' ')
		{
			ss << '+';
		}
		else if (unsigned(s[i]) <= '\x27')
		{
			ss << "%" << std::hex << unsigned(s[i]);
		}
		else
		{
			ss << s[i];
		}
	}
	return ss.str();
}

std::string CIMDB::parseString(std::string search1, std::string search2, std::string str)
{
	std::string ret, search;
	size_t pos_wildcard, pos_search1, pos_search2;
	pos_wildcard = pos_search1 = pos_search2 = std::string::npos;

	if ((pos_wildcard = search1.find('*')) != std::string::npos)
	{
		search = search1.substr(0, pos_wildcard);
		//std::cout << "wildcard detected" << '\t' << "= " << search << "[*]" << search1.substr(pos_wildcard+1) << std::endl;
	}
	else
		search = search1;

	//std::cout << "search1" << "\t\t\t" << "= " << '"' << search << '"' << std::endl;
	if ((pos_search1 = str.find(search)) != std::string::npos)
	{
		//std::cout << "search1 found" << "\t\t" << "= " << '"' << search << '"' << " at pos "<< (int)(pos_search1) << " => " << str << std::endl;

		pos_search1 += search.length();

		if (pos_wildcard != std::string::npos)
		{
			size_t pos_wildcard_ext;
			std::string wildcard_ext = search1.substr(pos_wildcard + 1);

			//std::cout << "wildcard_ext" << "\t\t" << "= " << '"' << wildcard_ext << '"' << std::endl;
			if ((pos_wildcard_ext = str.find(wildcard_ext, pos_wildcard + 1)) != std::string::npos)
			{
				//std::cout << "wildcard_ext found" << "\t" << "= " << '"' << wildcard_ext << '"' << " at pos "<< (int)(pos_wildcard_ext) << " => " << str << std::endl;
				pos_search1 = pos_wildcard_ext + wildcard_ext.length();
			}
			else
			{
				//std::cout << "wildcard_ext not found in line " << acc << " - exit" << std::endl;
				return ("");
			}
		}
	}
	else
	{
		//std::cout << "search1 not found in line " << acc << " - exit" << std::endl;
		return ("");
	}

	if (pos_search1 != std::string::npos)
	{
		//std::cout << "search2 " << "\t\t" << "= " << '"' << search2 << '"' << std::endl;

		if (search2 == "\n")
		{
			ret = str.substr(pos_search1, str.length() - pos_search1);
			return (ret);
		}

		if ((pos_search2 = str.find(search2, pos_search1)) != std::string::npos)
		{
			if (search2.empty())
				pos_search2 = str.length();

			//std::cout << "search2" << "\t\t\t" << "= " << '"' << search2 << '"' << " found at "<< (int)(pos_search2) << " => " << str << std::endl;
			ret = str.substr(pos_search1, pos_search2 - pos_search1);
		}
		//else
			//std::cout << "search2 not found in line " << acc << " - exit" << std::endl;

	}

	return (ret);
}

std::string CIMDB::parseFile(std::string search1, std::string search2, const char *file, std::string firstline, int line_offset)
{

	acc = 0;
	std::ifstream fh;
	std::string str, ret;
	size_t pos_firstline;
	pos_firstline = std::string::npos;

	if (firstline.empty())
		pos_firstline = 0;

	fh.open(file, std::ios::in);
	if (fh.is_open())
	{
		int line = 0;
		while (!fh.eof())
		{
			getline(fh, str);
			acc++;

			if (pos_firstline == std::string::npos)
			{
				if ((pos_firstline = str.find(firstline)) != std::string::npos)
				{
					//std::cout << "firstline found " << str << std::endl;
				}
				continue;
			}

			if (line_offset /*&& pos_firstline != std::string::npos*/)
			{
				if (line + 1 != line_offset)
				{
					line++;
					continue;
				}
			}

			ret = parseString(search1, search2, str);

			if (!ret.empty())
				break;
		}
		fh.close();
	}

	return (ret);
}

std::string CIMDB::googleIMDb(std::string s)
{
	CHTTPTool httpTool;
	std::string ret = search_error;
	std::string search_string("title+");
	char *search_char = (char *) s.c_str();

	m.clear();
	unlink(search_outfile.c_str());
	unlink(imdb_outfile.c_str());
	unlink(posterfile.c_str());

	while (*search_char != 0)
	{
		if (*search_char == ' ')
		{
			search_string += '+';
		}
		else
		{
			search_string += *search_char;
		}
		search_char++;
	}

	std::string url = search_url + utf82url(search_string) + "%20www.imdb.com";

	if (httpTool.downloadFile(url, search_outfile.c_str()))
	{
		ret = parseFile("https://www.imdb.com/title/", ">", search_outfile.c_str());

		if (ret.empty())
			ret = parseFile("http://www.imdb.de/title/", ">", search_outfile.c_str());

		std::string delimiters = "/&;";
		size_t next = ret.find_first_of(delimiters, 0);
		ret = ret.substr(0, next);
	}

	return ret;
}

void CIMDB::initMap(std::map<std::string, std::string> &my)
{
	std::string errMsg = "";
	Json::Value root;

	std::ostringstream ss;
	std::ifstream fh(imdb_outfile.c_str(), std::ifstream::in);
	ss << fh.rdbuf();
	std::string filedata = ss.str();

	bool parsedSuccess = parseJsonFromString(filedata, &root, &errMsg);

	if (!parsedSuccess)
	{
		std::cout << "Failed to parse JSON\n";
		std::cout << errMsg << std::endl;

		my["Response"] = "False"; // we fake a false response
		return;
	}

	/*
	   we grab only what we need to avoid bad surprises
	   when api is changed
	*/

	my["Actors"]		= root.get("Actors", "").asString();
	my["Awards"]		= root.get("Awards", "").asString();
	my["BoxOffice"]		= root.get("BoxOffice", "").asString();
	my["Country"]		= root.get("Country", "").asString();
	my["Director"]		= root.get("Director", "").asString();
	my["Genre"]		= root.get("Genre", "").asString();
	my["imdbID"]		= root.get("imdbID", "").asString();
	my["imdbRating"]	= root.get("imdbRating", "N/A").asString();
	my["imdbVotes"]		= root.get("imdbVotes", "").asString();
	my["Metascore"]		= root.get("Metascore", "N/A").asString();
	my["Plot"]		= root.get("Plot", "").asString();
	my["Poster"]		= root.get("Poster", "N/A").asString();
	my["Production"]	= root.get("Production", "").asString();
	my["Released"]		= root.get("Released", "").asString();
	my["Response"]		= root.get("Response", "False").asString();
	my["Runtime"]		= root.get("Runtime", "").asString();
	my["Title"]		= root.get("Title", "").asString();
	my["Website"]		= root.get("Website", "").asString();
	my["Writer"]		= root.get("Writer", "").asString();
	my["Year"]		= root.get("Year", "").asString();

	// currently unused
	//my["Rated"]		= root.get("Rated", "").asString();
	//my["Type"]		= root.get("Type", "").asString();
}

int CIMDB::getMovieDetails(const std::string &epgTitle)
{
	CHTTPTool httpTool;
	int ret = 0;

	std::string imdb_id = googleIMDb(epgTitle);

	if (((imdb_id.find(search_error)) != std::string::npos))
		return ret;

	std::string url = imdb_url + imdb_id;

	if (httpTool.downloadFile(url, imdb_outfile.c_str()))
	{
		initMap(m);

		//std::cout << "m now contains " << m.size() << " elements.\n";

		if (m.empty() || m["Response"] != "True")
			return 0;

		//for (std::map<std::string,std::string>::iterator it=m.begin(); it!=m.end(); ++it)
		//	std::cout << it->first << " => " << it->second << '\n';

		//download Poster
		if (m["Poster"] != "N/A")
		{
			// if possible load bigger image
			std::string origURL("300");
			std::string replURL("600");

			if (m["Poster"].compare(m["Poster"].size() - 7, 3, origURL) == 0)
			{
				//std::cout << "########## " << m["Poster"] << " contains " << origURL << '\n';
				m["Poster"].replace(m["Poster"].size() - 7, 3, replURL);
				//std::cout << "########## New string: " << m["Poster"] << '\n';
			}

			if (httpTool.downloadFile(m["Poster"], posterfile.c_str()))
				return 2;
			else
			{
				if (access(posterfile.c_str(), F_OK) == 0)
					unlink(posterfile.c_str());
				return 1;
			}
		}
		ret = 2;
	}

	return ret;
}

bool CIMDB::checkElement(std::string element)
{
	if (m[element].empty() || m[element].compare("N/A") == 0)
		return false;
	else
		return true;
}

std::string CIMDB::getEPGText()
{
	if (m["imdbID"].empty() || m["Response"] != "True")
		return g_Locale->getText(LOCALE_IMDB_DATA_FAILED);

	std::string epgtext("");

	if (checkElement("imdbRating"))
	{
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_RATING) + ": " + m["imdbRating"] + "/10; ";
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_VOTES) + ": " + m["imdbVotes"] + "\n";
	}
	if (checkElement("Metascore"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_METASCORE) + ": " + m["Metascore"] + "/100\n";
	if (checkElement("Title"))
	{
		epgtext += "\n";
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_TITLE) + ": " + m["Title"] + "\n";
	}
	if (checkElement("Released"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_RELEASED) + ": " + m["Country"] + ", " + m["Released"] + "\n";
	if (checkElement("Runtime"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_RUNTIME) + ": " + m["Runtime"] + "\n";
	if (checkElement("Genre"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_GENRE) + ": " + m["Genre"] + "\n";
	if (checkElement("Awards"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_AWARDS) + ": " + m["Awards"] + "\n";
	if (checkElement("Director"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_DIRECTOR) + ": " + m["Director"] + "\n";
	if (checkElement("Writer"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_WRITER) + ": " + m["Writer"] + "\n";
	if (checkElement("Production"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_PRODUCTION) + ": " + m["Production"] + "\n";
	if (checkElement("Website"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_WEBSITE) + ": " + m["Website"] + "\n";
	if (checkElement("BoxOffice"))
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_BOXOFFICE) + ": " + m["BoxOffice"] + "\n";
	if (checkElement("Actors"))
	{
		epgtext += "\n";
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_ACTORS) + ": " + m["Actors"] + "\n";
	}
	if (checkElement("Plot"))
	{
		epgtext += "\n";
		epgtext += g_Locale->getString(LOCALE_IMDB_DATA_PLOT) + ": " + m["Plot"];
	}

	return epgtext;
}

std::string CIMDB::getMovieText()
{
	std::string movietext("");

	if (checkElement("Plot"))
	{
		movietext = m["Plot"] + "\n";
		if (checkElement("Title"))
		{
			movietext += "\n";
			movietext += g_Locale->getString(LOCALE_IMDB_DATA_TITLE) + ": ";
			movietext += m["Title"];
		}
		if (checkElement("Country"))
		{
			movietext += "\n";
			movietext += g_Locale->getString(LOCALE_IMDB_DATA_RELEASED) + ": ";
			movietext += m["Country"];
			if (checkElement("Released"))
				movietext += ", " + m["Released"];
		}
		if (checkElement("Actors"))
		{
			movietext += "\n";
			movietext += g_Locale->getString(LOCALE_IMDB_DATA_ACTORS) + ": ";
			movietext += m["Actors"];
		}
	}

	return movietext;
}

int CIMDB::getStars()
{
	std::string imdbRating = "";

	if (checkElement("imdbRating"))
		imdbRating = getIMDbElement("imdbRating");

	if (imdbRating.empty() || imdbRating == "N/A")
		return 0;

	return (int)(atof(imdbRating.c_str()) * 10);
}

void CIMDB::cleanup()
{
	if (access(search_outfile.c_str(), F_OK) == 0)
		unlink(search_outfile.c_str());
	if (access(posterfile.c_str(), F_OK) == 0)
		unlink(posterfile.c_str());
}
