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

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

#include <qapplication.h>
#include <qregexp.h>
#include <QUrl>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QComboBox>
#include <QTextCodec>
#include <QGroupBox>
#include <QStyle>

#include "dcevent.h"
#include "dctranslator.h"

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

DCTranslator * g_pTranslator = 0;

DCTranslator::DCTranslator(QWidget * /*parent*/, const char * /*name*/ ) // : QWidget(parent,name) {
{
	pHttp = new QHttp();
	/* m_nGetRequestID = -1; */
	/* m_bReady = FALSE; */

	provider            = YAHOO;
	m_sLanguage         = "en_de";
	m_pComboBoxProvider = 0;
	m_pGroupBoxYahoo    = 0;
	m_pGroupBoxGoogle   = 0;
	
	/* 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");
	
	InitDocument();
	
	g_pTranslator = this;
}

DCTranslator::~DCTranslator()
{
	g_pTranslator = NULL;
	
	if ( pHttp )
	{
		pHttp->clearPendingRequests();
		delete pHttp;
		pHttp = 0;
	}
}

/** */
void DCTranslator::InitDocument()
{
	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
}

/** */
QString DCTranslator::GetUserAgent() const
{
	QString a;
	a.append("Valknut/");
	a.append(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( CString lang, QWidget * parent )
{
	QString oldlanguage = m_sLanguage;
	QString oldprovider = provider;
	
	if ( lang != "" )
	{
		m_sLanguage = lang.Data();
	}
	
	QDialog * dialog = new QDialog(parent);
	if ( parent != 0 )
	{
		dialog->setWindowModality(Qt::WindowModal); //fixme not working, still blocks whole application
	}
	dialog->setCaption(tr("Translator"));
	
	QVBoxLayout * vbl = new QVBoxLayout(dialog);
	QGroupBox * main = new QGroupBox(tr("Translator Settings"),dialog);
	QVBoxLayout * mainvl = new QVBoxLayout(main);
	vbl->addWidget(main);
	mainvl->addWidget(new QLabel(tr("Choose a provider"),main));
	
	m_pComboBoxProvider = new QComboBox(main);
	m_pComboBoxProvider->addItem(YAHOO);
	m_pComboBoxProvider->addItem(GOOGLE);
	m_pComboBoxProvider->setCurrentIndex(m_pComboBoxProvider->findText(provider));
	mainvl->addWidget(m_pComboBoxProvider);
	
	m_pGroupBoxYahoo = new QGroupBox(tr("Settings for Yahoo Babelfish"), dialog);
	vbl->addWidget(m_pGroupBoxYahoo); 
	
	QHBoxLayout * yahoo = new QHBoxLayout(m_pGroupBoxYahoo);
	yahoo->addWidget(new QLabel(tr("Choose the languages"),m_pGroupBoxYahoo));
	QComboBox * cbyahoo = new QComboBox(m_pGroupBoxYahoo);
	cbyahoo->addItems(langs_y);
	yahoo->addWidget(cbyahoo);
	
	m_pGroupBoxGoogle = new QGroupBox(tr("Settings for Google Translate"), dialog);
	vbl->addWidget(m_pGroupBoxGoogle);
	
	QHBoxLayout * google = new QHBoxLayout(m_pGroupBoxGoogle);
	google->addWidget(new QLabel(tr("Input language"),m_pGroupBoxGoogle));
	QComboBox * cbgooglefrom = new QComboBox(m_pGroupBoxGoogle);
	cbgooglefrom->addItems(langs_g_from);
	google->addWidget(cbgooglefrom);
	google->addWidget(new QLabel(tr("Output language"),m_pGroupBoxGoogle));
	QComboBox * cbgoogleto = new QComboBox(m_pGroupBoxGoogle);
	cbgoogleto->addItems(langs_g_to);
	google->addWidget(cbgoogleto);
	
	slotProviderChanged(-1);
	
	// 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.find('_');
	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");
	}
	
	cbyahoo->setCurrentIndex(i1);
	cbgooglefrom->setCurrentIndex(i2);
	cbgoogleto->setCurrentIndex(i3);
	
	QHBoxLayout * buttons = new QHBoxLayout(vbl);
	QPushButton * confirm = new QPushButton(tr("OK"),dialog);
	confirm->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogOkButton,0,confirm));
	confirm->setDefault(TRUE);
	buttons->addWidget(confirm);
	QPushButton * cancel = new QPushButton(tr("Cancel"),dialog);
	cancel->setIcon(qApp->style()->standardIcon(QStyle::SP_DialogCancelButton,0,cancel));
	buttons->addWidget(cancel);
	
	connect( m_pComboBoxProvider, SIGNAL(currentIndexChanged(int)), this, SLOT(slotProviderChanged(int)) );
	connect( confirm, SIGNAL(clicked()), dialog, SLOT(accept()) );
	connect( cancel, SIGNAL(clicked()), dialog, SLOT(reject()) );
	
	if ( dialog->exec() == QDialog::Accepted )
	{
		provider = m_pComboBoxProvider->currentText();
		if ( provider == YAHOO )
		{
			m_sLanguage = langs_ys[cbyahoo->currentIndex()];
		}
		else
		{
			m_sLanguage = langs_gs_from[cbgooglefrom->currentIndex()] + "_" + langs_gs_to[cbgoogleto->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;
	}
	
	disconnect( m_pComboBoxProvider, SIGNAL(currentIndexChanged(int)), this, SLOT(slotProviderChanged(int)) );
	delete dialog;
	m_pComboBoxProvider = 0;
	m_pGroupBoxYahoo    = 0;
	m_pGroupBoxGoogle   = 0;
	
	//printf("m_sLanguage = %s\n", m_sLanguage.toAscii().data());
	
	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.length() > 0 )
	{
		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=" + tr_text + "&lp=" + language + "&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=" + tr_text + "&sl=" + from + "&tl=" + to;
		ref = "/translate_t";
		path = ref + "?sl=" + from + "&tl=" + 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().data());
	//printf("data='%s'\n", sendline.toAscii().data());
	
	int id = pHttp->request( header, sendline.toUtf8() ); 
	
	if ( m_RequestList.Get( CString().setNum(id), (CObject *&)TranslationEvent ) == 0 )
	{
		printf("Error: request %d already in list\n",id);
		return FALSE;
	}

	TranslationEvent = new DC_TranslationEvent(EVENT_TRANSLATION);

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

	m_RequestList.Add( CString().setNum(id), (CObject*)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().data());
	}
	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_RequestList.Get( CString().setNum(id), (CObject *&)TranslationEvent ) != 0 )
	{
		//printf("TranslationEvent object not found: %d\n",id);
		return;
	}
	
	if ( error == FALSE )
	{
		QHttpResponseHeader header = pHttp->lastResponse();
		//printf("response header='%s'\n", header.toString().toAscii().data());
		QString encoding = header.value("content-type");
		encoding = encoding.mid(encoding.indexOf("charset=")+QString("charset=").length());
		QTextCodec * codec = QTextCodec::codecForName(encoding);
		QString html;
		if ( codec != 0 )
		{
			html = codec->toUnicode(pHttp->readAll());
		}
		else
		{
			printf("Warning no codec found for \'%s\', using UTF-8\n", encoding.toAscii().data());
			html = QString::fromUtf8(pHttp->readAll());
		}
		//printf("html='%s\n'", html.toAscii().data());
		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().data());
	}
	else
	{
		//printf( "Translation failed: %s\n", pHttp->errorString().toAscii().data() );
		TranslationEvent->m_sTranslation = pHttp->errorString();
		TranslationEvent->m_bTranslate = FALSE;
	}

	m_RequestList.Remove(CString().setNum(id));

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

/** */
void DCTranslator::slotProviderChanged( int /* index */ )
{
	if ( m_pComboBoxProvider->currentText() == YAHOO )
	{
		m_pGroupBoxYahoo->setEnabled(TRUE);
		m_pGroupBoxGoogle->setEnabled(FALSE);
	}
	else if ( m_pComboBoxProvider->currentText() == GOOGLE )
	{
		m_pGroupBoxYahoo->setEnabled(FALSE);
		m_pGroupBoxGoogle->setEnabled(TRUE);
	}
}
