/* ****************************************************************************
  This file is part of KBabel

  Copyright (C) 1999-2001 by Matthias Kiefer
                            <matthias.kiefer@gmx.de>
		2002-2003 by Stanislav Visnovsky
			    <visnovsky@kde.org>

  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, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

**************************************************************************** */
#include "catalog.h"
#include "catalogsettings.h"
#include "editcmd.h"
#include "dictchooser.h"
#include "kbabeldictbox.h"
#include "roughtransdlg.h"
#include "tagextractor.h"

#include <qmemarray.h>
#include <qcheckbox.h>
#include <qhbuttongroup.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qradiobutton.h>
#include <qvgroupbox.h>
#include <qvbox.h>
#include <qwhatsthis.h>

#include <kapplication.h>
#include <kconfig.h>
#include <kglobal.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kprogress.h>


#include <kdebug.h>

RoughTransDlg::RoughTransDlg(KBabelDictBox *dict, Catalog *cat
                , QWidget *parent,const char *name)
        : KDialogBase(parent,name,true
                ,i18n("Caption of dialog","Rough Translation") 
                , User1|User2|User3|Close)
        ,dictBox(dict)
        ,catalog(cat)
        ,active(false)
        ,stop(false)
        ,cancel(false)
        ,exactTransCounter(0)
        ,partTransCounter(0)
        ,totalTried(0)
{
    setButtonBoxOrientation(Vertical);
    setButtonText(User1,i18n("&Start"));
    setButtonText(User2,i18n("S&top"));
    setButtonText(User3,i18n("C&ancel"));

    enableButton(User2,false);
    enableButton(User3,false);
    
    QWidget *mw = new QWidget(this);
    setMainWidget(mw);

    QVBoxLayout *mainLayout = new QVBoxLayout(mw);

    configWidget = new QVBox(mw);
    mainLayout->addWidget(configWidget);

    QVGroupBox *box = new QVGroupBox(i18n("What to Translate"),configWidget);

    QHButtonGroup *bBox = new QHButtonGroup(box);
    bBox->setMargin(0);
    bBox->setFrameStyle(QFrame::NoFrame);
    whatBox = bBox;
    untransButton = new QCheckBox(i18n("U&ntranslated entries"),bBox);
    fuzzyButton = new QCheckBox(i18n("&Fuzzy entries"),bBox);
    transButton = new QCheckBox(i18n("T&ranslated entries"),bBox);

    connect(bBox,SIGNAL(clicked(int)),this,SLOT(msgButtonClicked(int)));

    QWhatsThis::add(bBox,i18n("<qt><p><b>What entries to translate</b></p>"
                "<p>Choose here, for which entries of the file KBabel "
                "tries to find a translation. Changed entries are always "
                "marked as fuzzy, no matter which option you choose.</p></qt>"));
    
    bBox = new QHButtonGroup(box);
    bBox->setFrameStyle(QFrame::NoFrame);
    bBox->setMargin(0);

    fuzzyMatchButton = new QCheckBox(i18n("All&ow fuzzy translation (slow)")
            ,bBox);
    singleWordButton = new QCheckBox(i18n("A&llow single word translation")
            ,bBox);

    QWhatsThis::add(bBox,i18n("<qt><p><b>How messages get translated</b></p>"
                "<p>Here you can define if a message can only get translated "
                "completely, if similar messages are acceptable or if KBabel "
                "is supposed to try translating "
                "the single words of a message if no translation of the "
                "complete message or similar message was found.</p></qt>"));
 

    box = new QVGroupBox(i18n("Options"),configWidget);

    markFuzzyButton = new QCheckBox(i18n("&Mark changed entries as fuzzy"),box);
    markFuzzyButton->setChecked(true);
    QWhatsThis::add(markFuzzyButton,
            i18n("<qt><p><b>Mark changed entries as fuzzy</b></p>"
          "<p>When a translation for a message is found, the entry "
          "will be marked <b>fuzzy</b> by default. This is because the "
          "translation is just guessed by KBabel and you should always "
          "check the results carefully. Deactivate this option only if "
          "you know what you are doing.</p></qt>"));


    connect(markFuzzyButton, SIGNAL(toggled(bool))
            , this, SLOT(fuzzyButtonToggled(bool)));

    kdeButton = new QCheckBox(i18n("Initialize &KDE-specific entries"),box);
    kdeButton->setChecked(true);
    QWhatsThis::add(kdeButton,
            i18n("<qt><p><b>Initialize KDE-specific entries</b></p>"
          "<p>Initialize \"Comment=\" and \"Name=\" entries "
	  "if a translation is not found. Also, \"NAME OF TRANSLATORS\" "
	  "and \"EMAIL OF TRANSLATORS\" is filled with identity settings.</p></qt>"));

    QVGroupBox *dBox = new QVGroupBox(i18n("Dictionaries"),configWidget);
    configWidget->setStretchFactor(dBox,1);

    QPtrList<ModuleInfo> moduleList = dict->moduleInfos();

    KConfig *config = KGlobal::config();
    KConfigGroupSaver gs(config,"RoughTranslation");
    QStringList selectedList=config->readListEntry("Selected");
    if(selectedList.isEmpty())
    {
        int a = dict->activeModule();
        ModuleInfo *mi = moduleList.at(a);
        if(mi)
        {
            selectedList.append(mi->id);
        }
    }
    dictChooser = new DictChooser(moduleList,selectedList,dBox,"dictChooser");

    QWhatsThis::add(dictChooser,i18n("<qt><p><b>Dictionaries</b></p>"
                "<p>Choose here, which dictionaries have to be used for "
                "finding a translation. If you select more than one "
                "dictionary, they are used in the same order as they "
                "are displayed in the list.</p></qt>"));

    progressbar = new KProgress(mw,"progressbar");
    progressbar->setTextEnabled(true);
    progressbar->setFormat("%v/%m (%p%)");
    mainLayout->addWidget(progressbar);

    transButton->setChecked(config->readBoolEntry("Translated",false));
    untransButton->setChecked(config->readBoolEntry("Untranslated",true));
    fuzzyButton->setChecked(config->readBoolEntry("Fuzzies",false));

    bool flag = config->readBoolEntry("fuzzyMatch",true);
    fuzzyMatchButton->setChecked(flag);

    flag = config->readBoolEntry("singleWord",true);
    singleWordButton->setChecked(flag);

    flag = config->readBoolEntry("kdeSpecific",true);
    kdeButton->setChecked(flag);

    msgButtonClicked(0);
}

RoughTransDlg::~RoughTransDlg()
{
}

void RoughTransDlg::slotUser1()
{
    configWidget->setEnabled(false);
    enableButton(User1,false);
    enableButton(Close,false);
    enableButton(User2,true);
    enableButton(User3,true);

    active=true;
    stop=false;
    cancel=false;
    
    exactTransCounter=0;
    partTransCounter=0;
    totalTried=0;

    bool markFuzzy = markFuzzyButton->isChecked();
    bool translated = transButton->isChecked();
    bool untranslated = untransButton->isChecked();
    bool fuzzies = fuzzyButton->isChecked();
    bool kdeSpecific=kdeButton->isChecked();

    int total=catalog->numberOfEntries();
    progressbar->setTotalSteps(total);

    QStringList dictList = dictChooser->selectedDicts();

    EditCommand* tmp = new BeginCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(0);
    catalog->applyEditCommand(tmp,0);

    bool singleWords=singleWordButton->isChecked();
    bool fuzzyMatch=fuzzyMatchButton->isChecked();
    QRegExp contextReg=catalog->miscSettings().contextInfo;
    QChar accelMarker=catalog->miscSettings().accelMarker;
    QRegExp endPunctReg("[\\.?!: ]+$");
    

    for(int i = 0; i < total; i++)
    {
        progressbar->setProgress(i+1);
        kapp->processEvents(100);

        if(stop || cancel) break;

        // FIXME: should care about plural forms
	QString msg=catalog->msgid(i,true);
        QString translation;

        // this is KDE specific:
	if( kdeSpecific )
	{
//	    if( catalog->pluralForm(i) == NoPluralForm )
	    {
		QString origTrans = catalog->msgstr(i);
    		if(msg.find("_: NAME OF TRANSLATORS\\n")==0)
    		{
		    QString authorName;
		    if( !catalog->identitySettings().authorLocalizedName.isEmpty() )
			authorName = catalog->identitySettings().authorLocalizedName;
		    else // fallback to non-localized name
			if( !catalog->identitySettings().authorName.isEmpty() )
			    authorName = catalog->identitySettings().authorName;
			else continue; // there is no name to be inserted
		
		    if( !QStringList::split(',', origTrans).contains(authorName) )
		    {
        		if(origTrans.isEmpty() ) translation=authorName;
			else translation+=origTrans+","+authorName;
		    }
    		}
    		else if(msg.find("_: EMAIL OF TRANSLATORS\\n")==0)
    		{
		    // skip, if email is not specified in settings
		    if( catalog->identitySettings().authorEmail.isEmpty() ) continue;
		
		    if( !QStringList::split(',', origTrans).contains(catalog->identitySettings().authorEmail) )
		    {
			if(origTrans.isEmpty() ) translation=catalog->identitySettings().authorEmail;
			else translation=origTrans+","+catalog->identitySettings().authorEmail;
		    }
    		}
                else if (msg.find("ROLES_OF_TRANSLATORS") == 0)
                {
                  QString temp = "<othercredit role=\\\"translator\\\">\n<firstname></firstname>"
                    "<surname></surname>\n<affiliation><address><email>" + 
                    catalog->identitySettings( ).authorEmail+"</email></address>\n"
                    "</affiliation><contrib></contrib></othercredit>";
                  if (origTrans.isEmpty( ))
                    translation = temp;
                  else if (origTrans.find(catalog->identitySettings( ).authorEmail) < 0)
                    translation = origTrans + "\n" + temp;
                }
                else if (msg.find("CREDIT_FOR_TRANSLATORS") == 0)
                {
                  QString authorName;
                  if (!catalog->identitySettings( ).authorLocalizedName.isEmpty( ))
                    authorName = catalog->identitySettings( ).authorLocalizedName;
                  else if (!catalog->identitySettings( ).authorName.isEmpty( ))
                    authorName = catalog->identitySettings( ).authorName;
                  QString temp = "<para>" + authorName + "\n" + "<email>" + 
                    catalog->identitySettings( ).authorEmail + "</email></para>";
                  if (origTrans.isEmpty( ))
                    translation = temp;
                  else if (origTrans.find(authorName) < 0 && 
                           origTrans.find(catalog->identitySettings( ).authorEmail) < 0)
                    translation = origTrans + "\n" + temp;
                }
	    }
	} 
	else  // not kdeSpecific 
	{
	    // skip KDE specific texts
	    if( msg.find("_: EMAIL OF TRANSLATORS\\n")==0 || msg.find("_: NAME OF TRANSLATORS\\n")==0 || 
                msg.find("ROLES_OF_TRANSLATORS")==0 || msg.find("CREDIT_FOR_TRANSLATORS")==0)
		continue;
	}

	if( translation.isEmpty() ) // KDE-specific translation didn't work
	{
		if( !untranslated && catalog->isUntranslated(i) ) continue;
		if( !translated && !catalog->isUntranslated(i) && !catalog->isFuzzy(i) ) continue;
		if( !fuzzies && catalog->isFuzzy(i) ) continue;
	}

	totalTried++;

        if(msg.contains(contextReg))
        {
            msg.replace(contextReg,"");
        }

	// try exact translation
	QStringList::Iterator dit = dictList.begin();
	while(translation.isEmpty() && dit != dictList.end())
	  {
	    dictBox->setActiveModule(*dit);
	    translation = dictBox->translate(msg);

	    ++dit;
	  }

	if(!translation.isEmpty())
	  {
	    exactTransCounter++;
	  }

	// try fuzzy translation
	else if (fuzzyMatch) { 
	  QString tr;
	  int score, best_score = 0;
	  dit = dictList.begin();
	  while(dit != dictList.end())
	    {
	      dictBox->setActiveModule(*dit);
	      tr = dictBox->fuzzyTranslation(msg,score);

	      if (score > best_score) {
		translation = tr;
		best_score = score;
	      }

	      ++dit;
	    }

	  if(!translation.isEmpty())
	    {
	      partTransCounter++;
	    }
	}

	// try single word translation
        if(translation.isEmpty() && singleWords)
	  {
            QStringList wordList;
            QChar accel;
            QString endingPunctuation;
            int pos = msg.findRev(endPunctReg);
            if(pos >= 0)
	      {
                endingPunctuation = msg.right(msg.length()-pos);
	      }
	    
            msg=msg.simplifyWhiteSpace();
            msg=msg.stripWhiteSpace();


            TagExtractor te(msg);
            msg=te.tagsReplaced(" KBABELTAG ");
            QString word;
            int length = msg.length();
            QRegExp digitReg("^[0-9]*$");
            for(int index=0; index < length; index++)
            {
                QChar c=msg[index];
                
                if(c==accelMarker)
                {
                    index++;
                    if(index < length)
                    {
                        if(msg[index].isLetterOrNumber())
                        {
                            word+=msg[index];
                            accel=msg[index];
                        }
                        else if(!word.isEmpty() )
                        {
                            if(!word.contains(digitReg))
                                wordList.append(word);

                            word=QString::null;
                        }
                    }
                    else if(!word.isEmpty())
                    {
                        if(!word.contains(digitReg))
                            wordList.append(word);

                        word=QString::null;
                    }
                        
                }
                else if(c.isLetterOrNumber())
                {
                    word+=c;
                }
                else if(c == '\\')
                {
                    if(index < length-2)
                    {
                        if(msg[index+1]=='n' && msg[index+2].isSpace())
                        {
                            if(!word.isEmpty() && !word.contains(digitReg))
                                wordList.append(word);

                            word=QString::null;
                            
                            wordList.append("\\n\n");
                            index+=2;
                        }
                        else if(!word.isEmpty() )
                        {
                            if(!word.contains(digitReg))
                                wordList.append(word);

                            word=QString::null;
                        }
                    }
                    else if(!word.isEmpty())
                    {
                        if(!word.contains(digitReg))
                            wordList.append(word);

                        word=QString::null;
                    }
                }
                else if(!word.isEmpty())
                {
                    if(!word.contains(digitReg))
                        wordList.append(word);

                    word=QString::null;
                }
            }
            
            // handle the last word as well
            if( !word.isEmpty() ) wordList.append(word);

            dit = dictList.begin();
            int wordCounter=0;
            while(wordCounter==0 && dit != dictList.end())
            {
                dictBox->setActiveModule(*dit);

                for(QStringList::Iterator it=wordList.begin();
                        it!=wordList.end(); ++it)
                {
                    if( (*it)=="\\n\n" )
                    {
                        translation+="\\n\n";
                    }
                    else if( (*it)=="KBABELTAG" )
                    {
                        translation+=te.nextTag();
                    }
                    else
                    {
                        QString trans = dictBox->translate(*it);

                        if(!trans.isEmpty())
                        {
                            wordCounter++;
                            if(!translation.isEmpty())
                            {
                                translation += ' ';
                            }
                            translation += trans;
                        }
                    }
                }

                if(wordCounter==0)
                    translation=QString::null;

                ++dit;
            }

            if(!translation.isEmpty())
            {
                partTransCounter++;
                // try to set the correct keyboard accelerator
                if(!accel.isNull())
                {
                    int index = translation.find(accel,0,false);
                    if(index >= 0)
                    {
                        translation.insert(index,accelMarker);
                    }
                }

                translation+=endingPunctuation;
            }
        }

	// this is KDE specific:
	if(translation.isEmpty() && kdeSpecific)
	{
	    if( msg.startsWith("Name=") ) {
		translation="Name=";
		partTransCounter++;
	    }
	    if( msg.startsWith("Comment=") ) {
		translation="Comment=";
		partTransCounter++;
	    }
        }

        if(!translation.isEmpty())
        {
            if(!catalog->isUntranslated(i))
            {
                DelTextCmd* delCmd = new DelTextCmd(0
                        ,catalog->msgstr(i));
                delCmd->setPart(EditCommand::Msgstr);
                delCmd->setIndex(i);
                catalog->applyEditCommand(delCmd,0);
            }

            InsTextCmd* insCmd = new InsTextCmd(0,translation);
            insCmd->setPart(EditCommand::Msgstr);
            insCmd->setIndex(i);
            catalog->applyEditCommand(insCmd,0);

            if(markFuzzy)
            {
                catalog->setFuzzy(i,true);
            }
        }
    }

    tmp = new EndCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(0);
    catalog->applyEditCommand(tmp,0);

    int nothing=totalTried-partTransCounter-exactTransCounter;
    
    int divTotalTried = QMAX(1,totalTried);
    
    KLocale *locale = KGlobal::locale();
    QString statMsg = i18n("Result of the translation:\n"
            "Edited entries: %1\n"
            "Exact translations: %2 (%3%)\n"
            "Approximate translations: %4 (%5%)\n"
            "Nothing found: %6 (%7%)")
            .arg( locale->formatNumber(totalTried,0) )
            .arg( locale->formatNumber(exactTransCounter,0) )
            .arg( locale->formatNumber( ((double)(10000*exactTransCounter/divTotalTried))/100) )
            .arg( locale->formatNumber(partTransCounter,0) )
            .arg( locale->formatNumber(((double)(10000*partTransCounter/divTotalTried))/100) )
            .arg( locale->formatNumber(nothing,0) )
            .arg( locale->formatNumber(((double)(10000*nothing/divTotalTried))/100) );

    if(stop || cancel)
    {
        if(cancel)
        {
            catalog->undo();
        }
        else
        {
            KMessageBox::information(this, statMsg
                    , i18n("Rough Translation Statistics"));

            msgButtonClicked(0);
        }
        progressbar->setProgress(0);
        configWidget->setEnabled(true);
        active = false;
        stop=false;
        cancel=false;
        
        enableButton(User1,true);
        enableButton(Close,true);
        enableButton(User2,false);
        enableButton(User3,false);
        
        return;
    }
    
    KMessageBox::information(this, statMsg
            , i18n("Rough Translation Statistics"));
    
    KConfig *config=KGlobal::config();
    KConfigGroupSaver gs(config,"RoughTranslation");
    config->writeEntry("Selected",dictChooser->selectedDicts());

    bool flag=transButton->isChecked();
    config->writeEntry("Translated",flag);
    flag=untransButton->isChecked();
    config->writeEntry("Untranslated",flag);
    flag=fuzzyButton->isChecked();
    config->writeEntry("Fuzzies",flag);
    flag=singleWordButton->isChecked();
    config->writeEntry("singleWord",flag);
    flag=fuzzyMatchButton->isChecked();
    config->writeEntry("fuzzyMatch",flag);
    flag=kdeButton->isChecked();
    config->writeEntry("kdeSpecific",flag);

    accept();
}

void RoughTransDlg::slotClose()
{
    if(active)
    {
        cancel = true;
        return;
    }
    else
    {
       accept(); 
    }
}

void RoughTransDlg::slotUser2()
{
    stop=true;
}

void RoughTransDlg::slotUser3()
{
    cancel=true;
}

void RoughTransDlg::msgButtonClicked(int id)
{
    if(!transButton->isChecked() && !untransButton->isChecked() 
            && !fuzzyButton->isChecked())
    {
        QButton *button = whatBox->find(id);
        if(button == transButton)
        {
            transButton->setChecked(true);
        }
        else if(button == untransButton)
        {
            untransButton->setChecked(true);
        }
        else if(button == fuzzyButton)
        {
            fuzzyButton->setChecked(true);
        }
    }

    progressbar->setTotalSteps(catalog->numberOfEntries());

    enableButton(User1,catalog->numberOfEntries());
}

void RoughTransDlg::fuzzyButtonToggled(bool on)
{
    if(!on)
    {
        QString msg=i18n("<qt><p>"
          "When a translation for a message is found, the entry "
          "will be marked <b>fuzzy</b> by default. This is because the "
          "translation is just guessed by KBabel and you should always "
          "check the results carefully. Deactivate this option only if "
          "you know what you are doing.</p></qt>");

        KMessageBox::information(this, msg, QString::null,"MarkFuzzyWarningInRoughTransDlg");
    }
}

void RoughTransDlg::statistics(int &total, int& exact, int& part) const
{
    total = totalTried;
    exact = exactTransCounter;
    part = partTransCounter;
}

#include "roughtransdlg.moc"
