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

  Copyright (C) 1999-2001 by Matthias Kiefer
                            <matthias.kiefer@gmx.de>
		2002-2003 by StanislavVsinovsky
			    <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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

  In addition, as a special exception, the copyright holders give
  permission to link the code of this program with any edition of
  the TQt library by Trolltech AS, Norway (or with modified versions
  of TQt that use the same license as TQt), and distribute linked
  combinations including the two.  You must obey the GNU General
  Public License in all respects for all of the code used other than
  TQt. If you modify this file, you may extend this exception to
  your version of the file, but you are not obligated to do so.  If
  you do not wish to do so, delete this exception statement from
  your version.

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

#include <tqmemarray.h>
#include <tqcheckbox.h>
#include <tqhbuttongroup.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqradiobutton.h>
#include <tqtimer.h>
#include <tqvgroupbox.h>
#include <tqvbox.h>
#include <tqwhatsthis.h>

#include <tdeapplication.h>
#include <tdeconfig.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kprogress.h>


#include <kdebug.h>

using namespace KBabel;

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

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

    TQVBoxLayout *mainLayout = new TQVBoxLayout(mw);

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

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

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

    connect(bBox,TQT_SIGNAL(clicked(int)),this,TQT_SLOT(msgButtonClicked(int)));

    TQWhatsThis::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>"));
    
    box = new TQVGroupBox(i18n("How to Translate"),configWidget);
    bBox = new TQHButtonGroup(box);
    bBox->setFrameStyle(TQFrame::NoFrame);
    bBox->setMargin(0);
    
    searchMatchButton = new TQCheckBox(i18n("&Use dictionary settings")
	    ,bBox);

    fuzzyMatchButton = new TQCheckBox(i18n("Fu&zzy translation (slow)")
            ,bBox);
    singleWordButton = new TQCheckBox(i18n("&Single word translation")
            ,bBox);

    TQWhatsThis::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 TQVGroupBox(i18n("Options"),configWidget);

    markFuzzyButton = new TQCheckBox(i18n("&Mark changed entries as fuzzy"),box);
    markFuzzyButton->setChecked(true);
    TQWhatsThis::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, TQT_SIGNAL(toggled(bool))
            , this, TQT_SLOT(fuzzyButtonToggled(bool)));

    kdeButton = new TQCheckBox(i18n("Initialize &TDE-specific entries"),box);
    kdeButton->setChecked(true);
    TQWhatsThis::add(kdeButton,
            i18n("<qt><p><b>Initialize TDE-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>"));

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

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

    TDEConfig *config = TDEGlobal::config();
    TDEConfigGroupSaver gs(config,"RoughTranslation");
    TQStringList 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(dict,selectedList,dBox,"dictChooser");

    TQWhatsThis::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>"
		"<p>The <b>Configure</b> button allows you to temporarily "
		"configure selected dictionary. The original settings "
		"will be restored after closing the dialog.</p></qt>"));

    TQLabel* label = new TQLabel( i18n("Messages:"), mw );
    progressbar = new KProgress(mw,"progressbar");
    progressbar->setTextEnabled(true);
    progressbar->setFormat("%v/%m (%p%)");
    TQHBoxLayout* pblayout= new TQHBoxLayout(mainLayout);
    pblayout->add(label);
    pblayout->add(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("searchMatch",true);
    searchMatchButton->setChecked(flag);

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

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

    msgButtonClicked(0);
}

RoughTransDlg::~RoughTransDlg()
{
    TDEConfig *config=TDEGlobal::config();
    TDEConfigGroupSaver 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=searchMatchButton->isChecked();
    config->writeEntry("searchMatch",flag);
    flag=kdeButton->isChecked();
    config->writeEntry("kdeSpecific",flag);

}

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;

    TQTimer::singleShot(0,this, TQT_SLOT(translate()));
}

void RoughTransDlg::translate()
{
    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);

    TQStringList dictList = dictChooser->selectedDicts();

    catalog->applyBeginCommand(0,Msgstr,0);

    bool singleWords=singleWordButton->isChecked();
    bool fuzzyMatch=fuzzyMatchButton->isChecked();
    bool searchMatch=searchMatchButton->isChecked();
    TQRegExp contextReg=catalog->miscSettings().contextInfo;
    TQChar accelMarker=catalog->miscSettings().accelMarker;
    TQRegExp 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
	TQString msg=catalog->msgid(i,true).first();
        TQString translation;

        // this is KDE specific:
	if( kdeSpecific )
	{
	    if( catalog->pluralForm(i) == NoPluralForm )
	    {
		TQString origTrans = catalog->msgstr(i).first();
    		if(msg.find("_: NAME OF TRANSLATORS\\n")==0)
    		{
		    TQString 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( !TQStringList::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( !TQStringList::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)
                {
                  TQString 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)
                {
                  TQString authorName;
                  if (!catalog->identitySettings( ).authorLocalizedName.isEmpty( ))
                    authorName = catalog->identitySettings( ).authorLocalizedName;
                  else if (!catalog->identitySettings( ).authorName.isEmpty( ))
                    authorName = catalog->identitySettings( ).authorName;
                  TQString 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
	TQStringList::Iterator dit = dictList.begin();
	while(translation.isEmpty() && dit != dictList.end())
	  {
	    dictBox->setActiveModule(*dit);
	    translation = dictBox->translate(msg);

	    ++dit;
	  }

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

	// try search settings translation
	else if (searchMatch) { 
	  TQString tr;
	  int score, best_score = 0;
	  dit = dictList.begin();
	  while(dit != dictList.end())
	    {
	      dictBox->setActiveModule(*dit);
	      tr = dictBox->searchTranslation(msg,score);
	      kdDebug() << "Found: " << tr << ", score " << score << endl;

	      if (score > best_score) {
	        kdDebug() << "Best score" << endl;
		translation = tr;
		best_score = score;
	      }

	      ++dit;
	    }

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

	// try fuzzy translation
	else if (fuzzyMatch) { 
	  TQString 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++;
	    }
	}
	
	kdDebug() << "Best translation so far: " << translation << endl;

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


            RegExpExtractor te(catalog->tagSettings().tagExpressions);
	    te.setString(msg);
            msg=te.matchesReplaced(" KBABELTAG ");
	    
            TQString word;
            int length = msg.length();
            TQRegExp digitReg("^[0-9]*$");
            for(int index=0; index < length; index++)
            {
                TQChar 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=TQString();
                        }
                    }
                    else if(!word.isEmpty())
                    {
                        if(!word.contains(digitReg))
                            wordList.append(word);

                        word=TQString();
                    }
                        
                }
                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=TQString();
                            
                            wordList.append("\\n\n");
                            index+=2;
                        }
                        else if(!word.isEmpty() )
                        {
                            if(!word.contains(digitReg))
                                wordList.append(word);

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

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

                    word=TQString();
                }
            }
	    
	    // 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(TQStringList::Iterator it=wordList.begin();
                        it!=wordList.end(); ++it)
                {
                    if( (*it)=="\\n\n" )
                    {
                        translation+="\\n\n";
                    }
                    else if( (*it)=="KBABELTAG" )
                    {
                        translation+=te.nextMatch();
                    }
                    else
                    {
                        TQString trans = dictBox->translate(*it);

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

                if(wordCounter==0)
                    translation=TQString();

                ++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(kdeSpecific && translation.isEmpty())
	{
	    if( msg.startsWith("Name=") ) {
		translation="Name=";
		partTransCounter++;
	    }
	    if( msg.startsWith("Comment=") ) {
		translation="Comment=";
		partTransCounter++;
	    }
        }

        if(!translation.isEmpty())
        {
            if(!catalog->isUntranslated(i))
            {
		TQStringList msgs = catalog->msgstr(i);
		uint counter = 0;
		for( TQStringList::Iterator it = msgs.begin() ; it != msgs.end() ; ++it)
		{
            	    DelTextCmd* delCmd = new DelTextCmd(0
                        ,(*it),counter++);
            	    delCmd->setPart(Msgstr);
            	    delCmd->setIndex(i);
            	    catalog->applyEditCommand(delCmd,0);
		}
            }

	    for( int count=0; count < catalog->numberOfPluralForms(i) ; count++ )
	    {
		InsTextCmd* insCmd = new InsTextCmd(0,translation,count);
        	insCmd->setPart(Msgstr);
        	insCmd->setIndex(i);
        	catalog->applyEditCommand(insCmd,0);
	    }

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

    catalog->applyEndCommand(0,Msgstr,0);

    if(stop || cancel)
    {
        if(cancel)
        {
            catalog->undo();
        }
        else
        {
            msgButtonClicked(0);
        }
        progressbar->setProgress(0);
        configWidget->setEnabled(true);
        active = false;
        
        enableButton(User1,true);
        enableButton(Close,true);
        enableButton(User2,false);
        enableButton(User3,false);
        
        return;
    }
    
    showStatistics();
}

void RoughTransDlg::showStatistics()
{
    int nothing=totalTried-partTransCounter-exactTransCounter;
    TDELocale *locale = TDEGlobal::locale();
    TQString 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/TQMAX(totalTried,1)))/100) )
            .arg( locale->formatNumber(partTransCounter,0) )
            .arg( locale->formatNumber(((double)(10000*partTransCounter/TQMAX(totalTried,1)))/100) )
            .arg( locale->formatNumber(nothing,0) )
            .arg( locale->formatNumber(((double)(10000*nothing/TQMAX(totalTried,1)))/100) );

    KMessageBox::information(this, statMsg
            , i18n("Rough Translation Statistics"));
    
    dictChooser->restoreConfig();
    accept();
}

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

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

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

void RoughTransDlg::msgButtonClicked(int id)
{
    if(!transButton->isChecked() && !untransButton->isChecked() 
            && !fuzzyButton->isChecked())
    {
        TQButton *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)
    {
        TQString 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, TQString(),"MarkFuzzyWarningInRoughTransDlg");
    }
}

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

#include "roughtransdlg.moc"
