/***************************************************************************
                          dctranslator.cpp  -  description
                             -------------------
    begin                : Die Nov 19 2002
    copyright            : (C) 2002 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

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

#include "dctranslator.h"

#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#define PACKAGE_VERSION "Unknown"
#endif

#include <qapplication.h>
#include <qregexp.h>
#include <QUrl>
#include <QDialog>
#include <QComboBox>
#include <QRadioButton>
#include <QTextCodec>
#include <QHttp>
#include <QProcess>
#include <cstdio>

#include "dcevent.h"

#include "ui/DCDialogTranslatorSettings.h"

#define YAHOO "babelfish.yahoo.com"
#define GOOGLE "www.google.com"

DCTranslator * g_pTranslator = 0;

DCTranslator::DCTranslator( QObject * parent ) : QObject( parent )
{
	pHttp = new QHttp( this );
	
	/* set HTTP proxy */
	/* http_proxy=http:proxy@localhost:8080  */
	QStringList envvars = QProcess::systemEnvironment();
	for ( QStringList::const_iterator it = envvars.constBegin(); it != envvars.constEnd(); ++it )
	{
		if ( (*it).startsWith("http_proxy=") )
		{
			QString hp = (*it).mid( QString::fromUtf8("http_proxy=").length() );
			
			if ( hp.startsWith("http://") )
			{
				hp = hp.mid(7);
			}
			
			QString host, user, pass;
			int port = 8080;
			
			int pos = hp.indexOf('@');
			if ( pos != -1 )
			{
				user = hp.left(pos);
				hp = hp.mid(pos+1);
				
				pos = user.indexOf(':');
				if ( pos != -1 )
				{
					pass = user.mid(pos+1);
					user = user.left(pos);
				}
			}
				
			pos = hp.indexOf(':');
			if ( pos == -1 )
			{
				host = hp;
			}
			else
			{
				host = hp.left(pos);
				port = hp.mid(pos+1).toInt();
			}
			
			if ( !host.isEmpty() )
			{
				pHttp->setProxy( host, port, user, pass );
			}
			
			break;
		}
	}
	
	/* m_nGetRequestID = -1; */
	/* m_bReady = false; */

	provider            = YAHOO;
	m_sLanguage         = "en_de";
	
	/* it would be better to parse these from the html */
	langs_ys << "zh_en"; langs_y << tr("Chinese Simplified") + " " + tr("to") + +" " + tr("English");
	langs_ys << "zh_zt"; langs_y << tr("Chinese Simplified") + " " + tr("to") + +" " + tr("Chinese Traditional");
	langs_ys << "zt_en"; langs_y << tr("Chinese Traditional") + " " + tr("to") + +" " + tr("English");
	langs_ys << "zt_zh"; langs_y << tr("Chinese Traditional") + " " + tr("to") + +" " + tr("Chinese Simplified");
	langs_ys << "en_zh"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Chinese Simplified");
	langs_ys << "en_zt"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Chinese Traditional");
	langs_ys << "en_nl"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Dutch");
	langs_ys << "en_fr"; langs_y << tr("English") + " " + tr("to") + +" " + tr("French");
	langs_ys << "en_de"; langs_y << tr("English") + " " + tr("to") + +" " + tr("German");
	langs_ys << "en_el"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Greek");
	langs_ys << "en_it"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Italian");
	langs_ys << "en_ja"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Japanese");
	langs_ys << "en_ko"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Korean");
	langs_ys << "en_pt"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Portuguese");
	langs_ys << "en_ru"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Russian");
	langs_ys << "en_es"; langs_y << tr("English") + " " + tr("to") + +" " + tr("Spanish");
	langs_ys << "nl_en"; langs_y << tr("Dutch") + " " + tr("to") + +" " + tr("English");
	langs_ys << "nl_fr"; langs_y << tr("Dutch") + " " + tr("to") + +" " + tr("French");
	langs_ys << "fr_nl"; langs_y << tr("French") + " " + tr("to") + +" " + tr("Dutch");
	langs_ys << "fr_en"; langs_y << tr("French") + " " + tr("to") + +" " + tr("English");
	langs_ys << "fr_de"; langs_y << tr("French") + " " + tr("to") + +" " + tr("German");
	langs_ys << "fr_el"; langs_y << tr("French") + " " + tr("to") + +" " + tr("Greek");
	langs_ys << "fr_it"; langs_y << tr("French") + " " + tr("to") + +" " + tr("Italian");
	langs_ys << "fr_pt"; langs_y << tr("French") + " " + tr("to") + +" " + tr("Portuguese");
	langs_ys << "fr_es"; langs_y << tr("French") + " " + tr("to") + +" " + tr("Spanish");
	langs_ys << "de_en"; langs_y << tr("German") + " " + tr("to") + +" " + tr("English");
	langs_ys << "de_fr"; langs_y << tr("German") + " " + tr("to") + +" " + tr("French");
	langs_ys << "el_en"; langs_y << tr("Greek") + " " + tr("to") + +" " + tr("English");
	langs_ys << "el_fr"; langs_y << tr("Greek") + " " + tr("to") + +" " + tr("French");
	langs_ys << "it_en"; langs_y << tr("Italian") + " " + tr("to") + +" " + tr("English");
	langs_ys << "it_fr"; langs_y << tr("Italian") + " " + tr("to") + +" " + tr("French");
	langs_ys << "ja_en"; langs_y << tr("Japanese") + " " + tr("to") + +" " + tr("English");
	langs_ys << "ko_en"; langs_y << tr("Korean") + " " + tr("to") + +" " + tr("English");
	langs_ys << "pt_en"; langs_y << tr("Portuguese") + " " + tr("to") + +" " + tr("English");
	langs_ys << "pt_fr"; langs_y << tr("Portuguese") + " " + tr("to") + +" " + tr("French");
	langs_ys << "ru_en"; langs_y << tr("Russian") + " " + tr("to") + +" " + tr("English");
	langs_ys << "es_en"; langs_y << tr("Spanish") + " " + tr("to") + +" " + tr("English");
	langs_ys << "es_fr"; langs_y << tr("Spanish") + " " + tr("to") + +" " + tr("French");
	
	langs_gs_from << "auto";  langs_g_from << tr("Detect language");
	langs_gs_from << "ar";    langs_g_from << tr("Arabic");
	langs_gs_from << "bg";    langs_g_from << tr("Bulgarian");
	langs_gs_from << "zh-CN"; langs_g_from << tr("Chinese");
	langs_gs_from << "hr";    langs_g_from << tr("Croatian");
	langs_gs_from << "cs";    langs_g_from << tr("Czech");
	langs_gs_from << "da";    langs_g_from << tr("Danish");
	langs_gs_from << "nl";    langs_g_from << tr("Dutch");
	langs_gs_from << "en";    langs_g_from << tr("English");
	langs_gs_from << "fi";    langs_g_from << tr("Finnish");
	langs_gs_from << "fr";    langs_g_from << tr("French");
	langs_gs_from << "de";    langs_g_from << tr("German");
	langs_gs_from << "el";    langs_g_from << tr("Greek");
	langs_gs_from << "hi";    langs_g_from << tr("Hindi");
	langs_gs_from << "it";    langs_g_from << tr("Italian");
	langs_gs_from << "ja";    langs_g_from << tr("Japanese");
	langs_gs_from << "ko";    langs_g_from << tr("Korean");
	langs_gs_from << "no";    langs_g_from << tr("Norwegian");
	langs_gs_from << "pl";    langs_g_from << tr("Polish");
	langs_gs_from << "pt";    langs_g_from << tr("Portuguese");
	langs_gs_from << "ro";    langs_g_from << tr("Romanian");
	langs_gs_from << "ru";    langs_g_from << tr("Russian");
	langs_gs_from << "es";    langs_g_from << tr("Spanish");
	langs_gs_from << "sv";    langs_g_from << tr("Swedish");
	
	langs_gs_to << "ar";    langs_g_to << tr("Arabic");
	langs_gs_to << "bg";    langs_g_to << tr("Bulgarian");
	langs_gs_to << "zh-CN"; langs_g_to << tr("Chinese Simplified");
	langs_gs_to << "zh-TW"; langs_g_to << tr("Chinese Traditional");
	langs_gs_to << "hr";    langs_g_to << tr("Croatian");
	langs_gs_to << "cs";    langs_g_to << tr("Czech");
	langs_gs_to << "da";    langs_g_to << tr("Danish");
	langs_gs_to << "nl";    langs_g_to << tr("Dutch");
	langs_gs_to << "en";    langs_g_to << tr("English");
	langs_gs_to << "fi";    langs_g_to << tr("Finnish");
	langs_gs_to << "fr";    langs_g_to << tr("French");
	langs_gs_to << "de";    langs_g_to << tr("German");
	langs_gs_to << "el";    langs_g_to << tr("Greek");
	langs_gs_to << "hi";    langs_g_to << tr("Hindi");
	langs_gs_to << "it";    langs_g_to << tr("Italian");
	langs_gs_to << "ja";    langs_g_to << tr("Japanese");
	langs_gs_to << "ko";    langs_g_to << tr("Korean");
	langs_gs_to << "no";    langs_g_to << tr("Norwegian");
	langs_gs_to << "pl";    langs_g_to << tr("Polish");
	langs_gs_to << "pt";    langs_g_to << tr("Portuguese");
	langs_gs_to << "ro";    langs_g_to << tr("Romanian");
	langs_gs_to << "ru";    langs_g_to << tr("Russian");
	langs_gs_to << "es";    langs_g_to << tr("Spanish");
	langs_gs_to << "sv";    langs_g_to << tr("Swedish");
	
	connect( pHttp, SIGNAL(requestFinished(int,bool)), this, SLOT(slotPostRequestFinished(int,bool)) );
	pHttp->setHost(provider); // generates a request which we ignore because the id isn't in the list
	
	g_pTranslator = this;
}

DCTranslator::~DCTranslator()
{
	g_pTranslator = NULL;
	
	if ( pHttp )
	{
		pHttp->clearPendingRequests();
		delete pHttp;
		pHttp = 0;
	}
	
	qDeleteAll( m_RequestHash.values() );
	m_RequestHash.clear();
}

/** */
QString DCTranslator::GetUserAgent()
{
	QString a;
	a.append("Valknut/");
	a.append(PACKAGE_VERSION);
	a.append(" (QT/");
	a.append(qVersion());
	a.append(")");
	return a;
	// return "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.13) Gecko/20080417 Fedora/1.1.9-2.fc8 SeaMonkey/1.1.9";
}

/** */
QString DCTranslator::SelectLanguage( QString lang, QWidget * parent )
{
	QString oldlanguage = m_sLanguage;
	QString oldprovider = provider;
	
	if ( !lang.isEmpty() )
	{
		m_sLanguage = lang;
	}
	
	QDialog * dialog = new QDialog(parent);
	if ( parent != 0 )
	{
		dialog->setWindowModality(Qt::WindowModal); //fixme not working, still blocks whole application
	}
	Ui::DCDialogTranslatorSettings ui;
	ui.setupUi(dialog);
	
	if ( provider == YAHOO )
	{
		ui.RadioButton_YAHOO->setChecked(true);
	}
	else
	{
		ui.RadioButton_GOOGLE->setChecked(true);
	}
	
	ui.ComboBox_YAHOO_LANGS->addItems(langs_y);
	ui.ComboBox_GOOGLE_INPUT->addItems(langs_g_from);
	ui.ComboBox_GOOGLE_OUTPUT->addItems(langs_g_to);

	// set combo boxes to the correct language
	int i1 = 0, i2 = 0, i3 = 0;
	
	i1 = langs_ys.indexOf(m_sLanguage);
	if ( i1 < 0 )
	{
		i1 = langs_ys.indexOf("en_de");
	}
	
	int pos = m_sLanguage.indexOf('_');
	i2 = langs_gs_from.indexOf(m_sLanguage.mid(0,pos));
	i3 = langs_gs_to.indexOf(m_sLanguage.mid(pos+1));
	
	if ( i2 < 0 )
	{
		i2 = langs_gs_from.indexOf("en");
	}
	
	if ( i3 < 0 )
	{
		i3 = langs_gs_from.indexOf("de");
	}
	
	ui.ComboBox_YAHOO_LANGS->setCurrentIndex(i1);
	ui.ComboBox_GOOGLE_INPUT->setCurrentIndex(i2);
	ui.ComboBox_GOOGLE_OUTPUT->setCurrentIndex(i3);
	
	if ( dialog->exec() == QDialog::Accepted )
	{
		if ( ui.RadioButton_YAHOO->isChecked() )
		{
			provider = YAHOO;
			m_sLanguage = langs_ys[ui.ComboBox_YAHOO_LANGS->currentIndex()];
		}
		else
		{
			provider = GOOGLE;
			m_sLanguage = langs_gs_from[ui.ComboBox_GOOGLE_INPUT->currentIndex()] + "_" + langs_gs_to[ui.ComboBox_GOOGLE_OUTPUT->currentIndex()];
		}
		
		if ( provider != oldprovider )
		{
			pHttp->setHost(provider);  // generates a request which we ignore because the id isn't in the list
		}
	}
	else
	{
		m_sLanguage = oldlanguage;
		provider = oldprovider;
	}
	
	delete dialog;
	
	//printf("m_sLanguage = %s\n", m_sLanguage.toAscii().constData());
	
	return m_sLanguage;
}

/** */
bool DCTranslator::Translate( QObject * sender, QString lang, QString text )
{
	/* if ( m_bReady == false )
	{
		QHttpRequestHeader firstHeader( "GET", "/" );
		firstHeader.setValue( "Host", provider );
		firstHeader.setValue( "User-Agent", GetUserAgent() );
		connect( pHttp, SIGNAL(requestFinished(int,bool)), this, SLOT(slotGetRequestFinished(int,bool)) );
		m_nGetRequestID = pHttp->request( firstHeader );
		
		// wait up to 10 seconds for request to finish
		int i = 0;
		while ( (m_bReady == false) && (i < 1000) && (pHttp->currentId() == m_nGetRequestID) )
		{
			qApp->processEvents();
#ifdef WIN32
			Sleep(10); //milliseconds
#else
			usleep(10*1000); //microseconds
#endif
			i++;
		}
		
		if ( i >= 1000 )
		{
			printf("Warning translator timed out during initialisation\n");
		}
	}
	
	if ( m_bReady == false )
	{
		printf("Translator not ready\n");
		return false;
	} */
	
	// convert text to correct html ... 
	QString tr_text = text;
	tr_text.replace(' ', '+');
	tr_text = QUrl::toPercentEncoding(tr_text,"{+}");
	
	DC_TranslationEvent * TranslationEvent=0;
	QString language = m_sLanguage;
	if ( !lang.isEmpty() )
	{
		language = lang;
	}
	
	QString sendline;
	QString path;
	QString ref;
	
	if ( provider == YAHOO )
	{
		sendline  = "ei=UTF-8&doit=done&fr=bf-home&intl=1&tt=urltext&trtext=";
		sendline += tr_text;
		sendline += "&lp=";
		sendline += language;
		sendline += "&btnTrTxt=Translate";
		path = "/translate_txt";
		ref = path;
	}
	else if ( provider == GOOGLE )
	{
		QString from = language.split('_')[0];
		QString to = language.split('_')[1];
		sendline  = "hl=en&ie=UTF8&text=";
		sendline += tr_text;
		sendline += "&sl=";
		sendline += from;
		sendline += "&tl=";
		sendline += to;
		ref = "/translate_t";
		path  = ref;
		path += "?sl=";
		path += from;
		path += "&tl=";
		path += to;
		
	}
	
	QHttpRequestHeader header( "POST", path );
	header.setValue( "User-Agent", GetUserAgent() );
	header.setValue( "Referer", provider + ref );
	header.setValue( "Host", provider );
	header.setValue( "Content-Type", "application/x-www-form-urlencoded" );
	header.setValue( "Accept-Charset", "utf-8" );
	
	//printf("header='%s'\n", header.toString().toAscii().constData());
	//printf("data='%s'\n", sendline.toAscii().constData());
	
	int id = pHttp->request( header, sendline.toUtf8() ); 
	
	if ( m_RequestHash.contains( id ) )
	{
		printf("Error: request %d already in list\n",id);
		return false;
	}

	TranslationEvent = new DC_TranslationEvent();

	TranslationEvent->m_bTranslate   = false;
	TranslationEvent->m_pReceiver    = sender;
	TranslationEvent->m_sOriginal    = text;
	TranslationEvent->m_sTranslation = "";

	m_RequestHash.insert( id, TranslationEvent );
	
	return true;
}

/** */
/* void DCTranslator::slotDataReadProgress( int bytesDone, int bytesTotal )
{
	printf("%d/%d\n",bytesDone,bytesTotal);
//	QString s;
//	s.sprintf("%d/%d",bytesDone,bytesTotal);
//	TextLabel_Status->setText(s);
} */

/** */
/* void DCTranslator::slotGetRequestFinished( int id, bool error )
{
	if ( id != m_nGetRequestID )
	{
		printf("Unexpected request %d\n", id);
	}
	else if ( error == true )
	{
		printf("Error initialising translator %s\n", pHttp->errorString().toAscii().constData());
	}
	else
	{
		disconnect( pHttp, SIGNAL(requestFinished(int,bool)), this, SLOT(slotGetRequestFinished(int,bool)) );
		connect( pHttp, SIGNAL(requestFinished(int,bool)), this, SLOT(slotPostRequestFinished(int,bool)) );
		pHttp->readAll(); // clear data
		m_bReady = true;  // now we're ready!
	}
} */

/** */
void DCTranslator::slotPostRequestFinished( int id, bool error )
{
	DC_TranslationEvent * TranslationEvent=0;

	if ( m_RequestHash.contains(id) )
	{
		TranslationEvent = m_RequestHash.take(id);
	}
	else
	{
		// not an error because setHost generates an event
		//printf("TranslationEvent object not found: %d\n",id);
		return;
	}
	
	if ( error == false )
	{
		QHttpResponseHeader header = pHttp->lastResponse();
		//printf("response header='%s'\n", header.toString().toAscii().constData());
		QString encoding = header.value("content-type");
		encoding = encoding.mid(encoding.indexOf("charset=")+QString("charset=").length());
		QTextCodec * codec = QTextCodec::codecForName(encoding.toAscii().constData());
		QString html;
		if ( codec != 0 )
		{
			html = codec->toUnicode(pHttp->readAll());
		}
		else
		{
			printf("Warning no codec found for \'%s\', using UTF-8\n", encoding.toAscii().constData());
			html = QString::fromUtf8(pHttp->readAll());
		}
		//printf("html='%s\n'", html.toAscii().constData());
		QRegExp regex;
		regex.setMinimal( true );
		regex.setCaseSensitivity( Qt::CaseInsensitive );
		if ( provider == YAHOO )
		{
			regex.setPattern( "<div id=\"result\"><div style=\".*\">(.*)</div></div>" );
		}
		else if ( provider == GOOGLE )
		{
			regex.setPattern( "<div id=result_box dir=\".*\">(.*)</div>" );
		}
		else
		{
			TranslationEvent->m_sTranslation = tr("Unknown translation provider") + " " + provider;
			TranslationEvent->m_bTranslate = false;
		}
		
		if ( html.contains(regex) )
		{
			TranslationEvent->m_sTranslation = regex.cap(1);
			TranslationEvent->m_bTranslate = true;
		}
		else
		{
			TranslationEvent->m_sTranslation = tr("The translation could not be found in the HTML received.")
			+ "\n" + tr("This likely means that") + " " + provider + " " +
			tr("changed their system and this feature no longer works.");
			TranslationEvent->m_bTranslate = false;
		}
		
		//printf("translate: '%s'\n",TranslationEvent->m_sTranslation.toAscii().constData());
	}
	else
	{
		//printf( "Translation failed: %s\n", pHttp->errorString().toAscii().constData() );
		TranslationEvent->m_sTranslation = pHttp->errorString();
		TranslationEvent->m_bTranslate = false;
	}

	QApplication::postEvent( TranslationEvent->m_pReceiver, TranslationEvent );
}
