// Code originally from Xbox Media Player released under the GPLv2
// converted from Winblows to UNIX by Anders Rune Jensen
// changed to use http_fetcher library instead of buggy and ugly http class

#include "imdb.hpp"

#include "print.hpp"
#include "common.hpp"
#include "gettext.hpp"

#include "wget_wrapper.hpp"

// saving
#include <fstream>
#include <iostream>

using std::string;

CIMDB::CIMDB()
{

}

CIMDB::~CIMDB()
{

}

bool CIMDB::Get(const string& url, string& content)
{
  if (!WgetWrapper::download(url, content)) {
    DebugPrint perror(gettext("Unable to retrieve web page ") + url,
		      Print::DEBUGGING, DebugPrint::CRITICAL, "IMDB");
    return false;
  } else 
    return true;
}

bool CIMDB::FindMovie(const string &strMovie,IMDB_MOVIELIST& movielist)
{
  char szTitle[1024];
  char szURL[1024];
  CIMDBUrl url;
  movielist.clear();

  string strURL,strHTML;
  GetURL(strMovie,strURL);

  if (!Get(strURL,strHTML))
    return false;
  
  if (strHTML.size()==0) return false;

  char *szBuffer= new char[strHTML.size()+1];
  if (!szBuffer) return false;
  strcpy(szBuffer,strHTML.c_str());
	
  char *pStartOfMovieList=strstr(szBuffer," Titles</b>");
  if (!pStartOfMovieList)
    {       // We don't have the "Titles</b>" tag, try "Matches)</b>
      pStartOfMovieList=strstr(szBuffer," Matches)</b>");
    }
  if (!pStartOfMovieList)
    {
      char* pMovieTitle	 = strstr(szBuffer,"\"title\" content=\"");
      char* pMovieDirector = strstr(szBuffer,"Director");
      char* pMovieGenre	 = strstr(szBuffer,"Genre:");
      char* pMoviePlot	 = strstr(szBuffer,"Plot");

      // oh i say, it appears we've been redirected to the actual movie details...
      if (pMovieTitle && pMovieDirector && pMovieGenre && pMoviePlot)
	{
	  pMovieTitle+=17;
	  char *pEnd = strstr(pMovieTitle,"\"");
	  if (pEnd) *pEnd=0;

	  ConvertHTMLToAnsi(pMovieTitle, url.m_strTitle);
	  url.m_strTitle = string_format::convert(url.m_strTitle);
	  url.m_strURL   = strURL;
	  movielist.push_back(url);

	  delete [] szBuffer;
	  return true;
	}

      //      printf("Unable to locate start of movie list.\n");
      delete [] szBuffer;
      return false;
    }

  pStartOfMovieList+=strlen("<table>");
  char *pEndOfMovieList=strstr(pStartOfMovieList,"</table>");
  if (!pEndOfMovieList)
    {
      pEndOfMovieList=pStartOfMovieList+strlen(pStartOfMovieList);
      //      printf("Unable to locate end of movie list.\n");
      //		delete [] szBuffer;
      //	return false;
    }

  *pEndOfMovieList=0;
  while(1)
    {
      char* pAHREF=strstr(pStartOfMovieList,"<a href=\"/title");
      if (pAHREF)
	{
	  //<a href="/title/tt0313443/">Out of Time (2003/I)</a>
	  char* pendAHREF=strstr(pAHREF, "</a>");
	  if (pendAHREF)
	    {
	      char* pYear=pendAHREF+4;
	      char* pendYear = strstr(pYear, "<");
	      *pendAHREF	=0;
	      pAHREF+=strlen("<a href=.");
	      // get url
	      char *pURL=strstr(pAHREF,">");
	      if (pURL)
		{
		  pURL--;
		  *pURL=0;
		  pURL++;
		  *pURL=0;
		  char* pURLEnd=strstr(pURL+1,"<");
		  char *pURLQuestion=strstr(pAHREF, "?fr");
		  if (pURLQuestion)
		    {
		      *pURLQuestion=0;
		      pURLQuestion++;
		      *pURLQuestion=0;
		    }
		  strcpy(szURL, pAHREF);
		  if (pURLEnd)  
		    {
		      *pURLEnd=0;
		      strcpy(szTitle, pURL+1);
		      *pURLEnd='<';
		    }
		  else
		    strcpy(szTitle, pURL+1);
		  if (pendYear)
		    {
		      *pendYear = 0;
		      strcat(szTitle, pYear);
		      pendAHREF = pendYear;
		    }
		  ConvertHTMLToAnsi(szTitle, url.m_strTitle);

		  url.m_strTitle = string_format::convert(url.m_strTitle);
		
		  snprintf(szURL, 1024, "http://us.imdb.com/%s", &pAHREF[1]);
		  url.m_strURL=szURL;
		  movielist.push_back(url);
		}
	      pStartOfMovieList = pendAHREF+1;
	      if (pStartOfMovieList >=pEndOfMovieList) break;
	    }
	  else
	    {
	      break;
	    }
	}
      else
	{
	  break;
	}
    }

  delete [] szBuffer;
  return true;
}


bool CIMDB::GetDetails(const CIMDBUrl& url, CIMDBMovie& movieDetails)
{
  string strHTML;
  string strURL = url.m_strURL;

  movieDetails.m_strTitle=url.m_strTitle;
  movieDetails.m_strDirector=" ";
  movieDetails.m_strWritingCredits=" ";
  movieDetails.m_strGenre=" ";
  movieDetails.m_strTagLine=" ";
  movieDetails.m_strPlotOutline=" ";
  movieDetails.m_strPlot=" ";
  movieDetails.m_strPictureURL="";
  movieDetails.m_strRuntime="";
  movieDetails.m_iYear=0;
  movieDetails.m_fRating=0.0;
  movieDetails.m_strVotes="";
  movieDetails.m_strCast="";
  movieDetails.m_iTop250=0;

  if (!Get(strURL,strHTML))
    return false;
  if (strHTML.size()==0) 
    return false;

  char *szBuffer= new char[strHTML.size()+1];
  strcpy(szBuffer,strHTML.c_str());
  //printf ("%s", szBuffer);

  int idxUrlParams = strURL.find("?");
  if (idxUrlParams != string::npos) {
    strURL = strURL.substr(0, idxUrlParams);
  }

  char *pDirectedBy=strstr(szBuffer,"<h5>Director");
  char *pCredits=strstr(szBuffer,"<h5>Writer");
  char* pGenre=strstr(szBuffer,"Genre:");
  char* pTagLine=strstr(szBuffer,"Tagline:</h5>");	
  char* pPlotOutline=strstr(szBuffer,"Plot Outline:</h5>");
  char* pPlotSummary=strstr(szBuffer,"Plot Summary:</b>");
  char* pPlot=strstr(szBuffer,"<a href=\"plotsummary");
  char* pImage=strstr(szBuffer,"<a name=\"poster\"");
  if (!pImage)
    pImage = strstr(szBuffer,"title=\"Poster Not Submitted\"");
  char* pRating=strstr(szBuffer,"User Rating:</b>");
  char* pRuntime=strstr(szBuffer,"Runtime:</h5>");
  char* pCast=strstr(szBuffer,"first billed only)</small>");
  char* pCred=strstr(szBuffer,"Credited cast)");
  char* pTop=strstr(szBuffer, "top 250:");

  char *pYear=strstr(szBuffer,"/Sections/Years/");
  if (pYear)
    {
      char szYear[5];
      pYear+=strlen("/Sections/Years/");
      strncpy(szYear,pYear,4);
      szYear[4]=0;
      movieDetails.m_iYear=atoi(szYear);
    }

  if (pDirectedBy) 
    ParseAHREF(pDirectedBy, strURL, movieDetails.m_strDirector);

  if (pCredits) 
    ParseAHREF(pCredits, strURL, movieDetails.m_strWritingCredits);

  if (pGenre)  
    ParseGenres(pGenre, strURL, movieDetails.m_strGenre);

  if (pRating) // and votes
    {
      char *pStart = strstr(pRating, "<b>"); 
      if(pStart) 
	{
	  char *pEnd = strstr(pStart, "/");
	  *pEnd = 0;
	  // set rating
	  movieDetails.m_fRating = static_cast<float>(atof(pStart+3));

	  if(movieDetails.m_fRating != 0.0) {
	    // now, votes
	    pStart = strstr(pEnd+2, "(<a href=\"ratings\">");
	    if(pStart)
	      pEnd = strstr(pStart, " votes</a>)");

	    if(pEnd) {
	      *pEnd = 0;
	      pStart+=19; // skip the parantese and href before votes
	      movieDetails.m_strVotes = pStart; // save
	    } else {
	      movieDetails.m_strVotes = "0";
	    }
	  }
	}
    }

  if(pTop) // top rated movie :)
    {
      pTop += strlen("top 250:") + 2; // jump space and #
      char *pEnd = strstr(pTop, "</a>");
      *pEnd = 0;
      movieDetails.m_iTop250 = atoi(pTop);
    }

  if(!pCast) 
    {
      pCast=pCred;
    }

  if(pCast) 
    {
      char *pStart = strstr(pCast, "<td class=\"nm\">");
      pStart += strlen("<td class=\"nm\">");
      char *pEnd = pCast;

      if(pStart && pEnd)
	{
	  movieDetails.m_strCast = "Cast overview:\n";

	  while(true) 
	    {
	      string url_string = "";
	      string actor = "";
	      string role = "";
		
	      // actor
					
	      pEnd = strstr(pStart, "</a>");

	      pEnd += 4;
	      *pEnd = 0;

	      pEnd += 1;

	      ParseAHREF(pStart, url_string, actor);

	      // role

	      pStart = strstr(pEnd, "<td class=\"char\">");

	      if (pStart == 0)
		break;

	      pStart += strlen("<td class=\"char\">");

	      pEnd = strstr(pStart, "</td>");
	      *pEnd = 0;
				
	      ConvertHTMLToAnsi(pStart, role);

	      pEnd += 1;

	      // add to cast
	      movieDetails.m_strCast += actor;
	      if(!role.empty()) // Role not always listed
		movieDetails.m_strCast += " as " + role;
				
	      movieDetails.m_strCast += "\n";

	      // next actor
	      pStart = strstr(pEnd, "<td class=\"nm\">");
	      if (pStart != 0)
	      	pStart += strlen("<td class=\"nm\">");
	      else
		break;
	    }
	}
			
    }

  if (pTagLine)
    {
      pTagLine += strlen("Tagline:</h5>");
      char *pEnd = strstr(pTagLine,"<");
      if (pEnd) *pEnd=0;
      ConvertHTMLToAnsi(pTagLine, movieDetails.m_strTagLine);
      remove_newlines(movieDetails.m_strTagLine);
    }

  if (pRuntime)
    {
      pRuntime += strlen("Runtime:</h5>");
      char *pEnd = strstr(pRuntime,"<");
      if (pEnd) *pEnd=0;
      ConvertHTMLToAnsi(pRuntime,movieDetails.m_strRuntime);
    }
  if (!pPlotOutline)
    {
      if (pPlotSummary)
	{
	  pPlotSummary += strlen("Plot Summary:</b>");
	  char *pEnd = strstr(pPlotSummary,"<");
	  if (pEnd) *pEnd=0;
	  ConvertHTMLToAnsi(pPlotSummary,movieDetails.m_strPlotOutline);			
	  remove_links(movieDetails.m_strPlotOutline);
	}
    }
  else
    {
      pPlotOutline += strlen("Plot Outline:</h5>");
      char *pEnd = strstr(pPlotOutline,"</div");
      if (pEnd) *pEnd=0;
      ConvertHTMLToAnsi(pPlotOutline,movieDetails.m_strPlotOutline);
      remove_links(movieDetails.m_strPlotOutline);
      movieDetails.m_strPlot=movieDetails.m_strPlotOutline;
    }

  if (pImage)
    {
      char *pEnd = strstr(pImage,"</a>");
      if (pEnd) *pEnd=0;
      
      pImage = strstr(pImage, "\" src=\"");
      pImage += strlen("\" src=\"");

      pEnd = strstr(pImage,"\"");
      if (pEnd) *pEnd=0;

      movieDetails.m_strPictureURL=pImage;
      string::size_type i = string::npos;
      while ((i = movieDetails.m_strPictureURL.find('r')) != string::npos)
	movieDetails.m_strPictureURL.replace(i, 1, static_cast<char>(0));
    }

  if (pPlot)
    {
      string strPlotURL = url.m_strURL;

      if (idxUrlParams != string::npos && (url.m_strURL.find("Tsearch") == string::npos))
	{
	  strPlotURL = strPlotURL.substr(0, idxUrlParams);
	}
      strPlotURL = strPlotURL + "plotsummary";
      string strPlotHTML;
      if (Get(strPlotURL,strPlotHTML))
	{
	  if (0!=strPlotHTML.size())
	    {
	      char* szPlotHTML = new char[1+strPlotHTML.size()];
	      strcpy(szPlotHTML ,strPlotHTML.c_str());
	      char *strPlotStart=strstr(szPlotHTML,"<p class=\"plotpar\">");
	      if (strPlotStart)
		{
		  strPlotStart += strlen("<p class=\"plotpar\">");
		  char *strPlotEnd=strstr(strPlotStart,"</p>");
		  if (strPlotEnd) *strPlotEnd=0;

		  // Remove any tags that may appear in the text
		  bool inTag = false;
		  int iPlot = 0;
		  char* strPlot = new char[strlen(strPlotStart)+1];
		  for (int i = 0; i < strlen(strPlotStart); i++)
		    {
		      if (strPlotStart[i] == '<')
			inTag = true;
		      else if (strPlotStart[i] == '>')
			inTag = false;
		      else if (!inTag)
			strPlot[iPlot++] = strPlotStart[i];
		    }
		  strPlot[iPlot] = '\0';

		  ConvertHTMLToAnsi(strPlot, movieDetails.m_strPlot);

		  delete [] strPlot;
		}
	      delete [] szPlotHTML;
	    }
	}
    }
  delete [] szBuffer;

  movieDetails.m_strDirector = string_format::convert(movieDetails.m_strDirector);
  movieDetails.m_strWritingCredits = string_format::convert(movieDetails.m_strWritingCredits);
  movieDetails.m_strGenre = string_format::convert(movieDetails.m_strGenre);
  movieDetails.m_strTagLine = string_format::convert(movieDetails.m_strTagLine);
  movieDetails.m_strPlotOutline = string_format::convert(movieDetails.m_strPlotOutline);
  movieDetails.m_strPlot = string_format::convert(movieDetails.m_strPlot);
  movieDetails.m_strCast = string_format::convert(movieDetails.m_strCast);

  return true;
}

void CIMDB::remove_newlines(string &url_text)
{
  string::size_type pos;
  
  while (url_text[0] == '\n')
    url_text = url_text.substr(1);

  while ((pos = url_text.rfind("\n")) != string::npos)
    url_text = url_text.substr(0, pos);

}

void CIMDB::remove_links(string &url_text)
{
  string::size_type pos;
  
  while ((pos = url_text.rfind("<a class")) != string::npos)
    url_text = url_text.substr(0, pos);
}

void CIMDB::ParseAHREF(const char *ahref, string &strURL, string &strTitle)
{
  char* szAHRef;
  szAHRef=new char[strlen(ahref)+1];
  strncpy(szAHRef,ahref,strlen(ahref));
  szAHRef[strlen(ahref)]=0;
  strURL="";
  strTitle="";
  char *pStart=strstr(szAHRef,"<a href=\"");
  if (!pStart) pStart=strstr(szAHRef,"<A HREF=\"");
  if (!pStart) 
    {
      delete [] szAHRef;
      return;
    }
  char* pEnd = strstr(szAHRef,"</a>"); 
  if (!pEnd) pEnd = strstr(szAHRef,"</A>"); 

  if (!pEnd)
    {
      delete [] szAHRef;
      return;
    }
  *pEnd=0;
  pEnd+=2;
  pStart += strlen("<a href=\"");
	
  char *pSep=strstr(pStart,">");
  if (pSep) *pSep=0;
  strURL=pStart;
  pSep++;

  //strTitle=pSep;
  ConvertHTMLToAnsi(pSep,strTitle);

  delete [] szAHRef;
}

void CIMDB::ParseGenres(const char *ahref, string &strURL, string &strTitle)
{
  char* szAHRef;
  szAHRef=new char[strlen(ahref)+1];
  strncpy(szAHRef,ahref,strlen(ahref));
  szAHRef[strlen(ahref)]=0;

  string strGenre = "";

  char *pStart;
  char *pEnd=szAHRef;
	
  char *pSlash=strstr(szAHRef," / ");

  strTitle = "";

  if(pSlash) 
    {
      char *pRealEnd = strstr(szAHRef, "more");
      if(!pRealEnd) pRealEnd=strstr(szAHRef, "<br><br>");
      if (!pRealEnd)
	{
	  //	  printf("1\n");
	}
      while(pSlash<pRealEnd)
	{
	  pStart = pEnd+2;
	  pEnd = pSlash;
	  *pEnd = 0; // terminate CStdString after current genre
	  int iLen=pEnd-pStart;
	  if (iLen < 0) break;
	  char *szTemp = new char[iLen+1];
	  strncpy(szTemp, pStart, iLen);
	  szTemp[iLen] = 0;
	
	  ParseAHREF(szTemp, strURL, strGenre);
	  delete [] szTemp;
	  strTitle = strTitle + strGenre + " / ";
	  pSlash=strstr(pEnd+2," / ");
	  if(!pSlash) pSlash = pRealEnd;
	}
    }
  // last genre
  pEnd+=2;
  ParseAHREF(pEnd, strURL, strGenre);
  strTitle = strTitle + strGenre;

  delete [] szAHRef;
}

bool CIMDB::Download(const string& strURL, const string& strFileName)
{
  WgetWrapper::download_to_file(strURL, strFileName);
  return true;
}

void CIMDB::GetURL(const string &strMovie, string& strURL)
{
  char szURL[1024];
  char szMovie[1024];
  CIMDBUrl url;
  bool bSkip=false;
  int ipos=0;

  // skip extension
  int imax=0;
  for (int i=0; i < strMovie.size();i++)
    {
      if (strMovie[i]=='.') imax=i;
      if (i+2 < strMovie.size())
	{
	  // skip -CDx. also
	  if (strMovie[i]=='-')
	    {
	      if (strMovie[i+1]=='C' || strMovie[i+1]=='c')
		{
		  if (strMovie[i+2]=='D' || strMovie[i+2]=='d')
		    {
		      imax=i;
		      break;
		    }
		}
	    }
	}
    }
  if (!imax) imax=strMovie.size();
  for (int i=0; i < imax;i++)
    {
      char kar=strMovie[i];
      if (kar =='.') kar = ' ';
      if (kar ==32) kar = '+';
      if (kar == '[' || kar=='(' ) bSkip=true;			//skip everthing between () and []
      else if (kar == ']' || kar==')' ) bSkip=false;
      else if (!bSkip)
	{
	  if (isalnum(kar) ||kar==' ' || kar=='+')
	    {
	      szMovie[ipos]=kar;
	      szMovie[ipos+1]=0;
	      ipos++;
	    }
	}
    }
  snprintf(szURL, 1024, "http://us.imdb.com/Tsearch?title=%s", szMovie);
  strURL = szURL;
}

void CIMDB::ConvertHTMLToAnsi(const char *strHTML, string &strAnsi)
{
  int i=0; 

  int iAnsiPos=0;
  char *szAnsi = new char[strlen(strHTML)*2];

  while (i < int(strlen(strHTML)))
    {
      char kar=strHTML[i];
      if (kar=='&')
	{
	  if (strHTML[i+1]=='#')
	    {
	      int ipos=0;
	      i+=2;
	      char szDigit[12];
	      while ( ipos < 12 && strHTML[i] && isdigit(strHTML[i])) 
		{
		  szDigit[ipos]=strHTML[i];
		  szDigit[ipos+1]=0;
		  ipos++;
		  i++;
		}
	      szAnsi[iAnsiPos++] = static_cast<char>(atoi(szDigit));
	      i++;
	    }
	  else
	    {
	      i++;
	      int ipos=0;
	      char szKey[12];
	      while (strHTML[i] && strHTML[i] != ';' && ipos < 12)
		{
		  szKey[ipos]=tolower(static_cast<unsigned char>(strHTML[i]));
		  szKey[ipos+1]=0;
		  ipos++;
		  i++;
		}
	      i++;
	      if (strcmp(szKey,"amp")==0) szAnsi[iAnsiPos++] ='&';
	      if (strcmp(szKey,"nbsp")==0) szAnsi[iAnsiPos++] =' ';
	    }
	}
      else
	{
	  szAnsi[iAnsiPos++] =kar;
	  i++;
	}
    }
  szAnsi[iAnsiPos++]=0;
  strAnsi=szAnsi;
  delete [] szAnsi;;
}
