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

  Copyright (C) 1999-2000 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 <kdebug.h>
#include <kfiledialog.h>
#include <klineeditdlg.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <knotifyclient.h>
#include <kurl.h>
#include <kio/netaccess.h>

#include <qcheckbox.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qmessagebox.h>
#include <qpopupmenu.h>
#include <qvbox.h>

#include "catalogsettings.h"
#include "editcmd.h"
#include "tagextractor.h"
#include "argextractor.h"
#include "kbabelview.h"
#include "kbabeldictbox.h"
#include "mymultilineedit.h"
#include "roughtransdlg.h"

#include "resources.h"

QPtrList<ModuleInfo> KBabelView::dictionaries()
{
   QPtrList<ModuleInfo> list = dictBox->moduleInfos();

   return list;
}

void KBabelView::configureDictionary(const QString id)
{
   dictBox->configure(id);
}

void KBabelView::editDictionary(const QString id)
{
   dictBox->edit(id);
}


void KBabelView::aboutDictionary(const QString id)
{
   dictBox->aboutModule(id);
}

void KBabelView::informDictionary()
{
    dictBox->setTextChanged(_catalog->msgid(_currentIndex,true),
            _catalog->msgstr(_currentIndex),_catalog->comment(_currentIndex));
}

void KBabelView::setNewLanguage()
{
    IdentitySettings s = _catalog->identitySettings();

    dictBox->setLanguage(s.languageCode, s.languageName);
}


void KBabelView::wheelEvent(QWheelEvent *e)
{
    if( (e->state() & ControlButton) && (e->state() & AltButton))
    {
        if(e->delta() > 0)
        {
            gotoPrevFuzzyOrUntrans();
        }
        else
        {
            gotoNextFuzzyOrUntrans();
        }
    }
    else if(e->state() & ControlButton)
    {
        if(e->delta() > 0)
        {
            gotoPrevFuzzy();
        }
        else
        {
            gotoNextFuzzy();
        }
    }
    else if(e->state() & AltButton)
    {
        if(e->delta() > 0)
        {
            gotoPrevUntranslated();
        }
        else
        {
            gotoNextUntranslated();
        }
    }
    else
    {
        if(e->delta() > 0)
        {
            gotoPrev();
        }
        else
        {
            gotoNext();
        }
    }
   
    e->accept();
}



void KBabelView::roughTranslation()
{
    RoughTransDlg *dlg = new RoughTransDlg(dictBox, _catalog, this
            , "roughtransDlg");

    dlg->exec();

    delete dlg;
}


void KBabelView::updateTags()
{
    bool hadTags = _tags.count() > 0;

    _tags = _catalog->tagList(_currentIndex);

    if(_tagsMenu)
    {
        _tagsMenu->clear();
     
        QStringList tList;
        QStringList::Iterator it;
        int counter=0;
        for(it=_tags.begin(); it!=_tags.end(); ++it)
        {
	    QString s = *it;
	    if( s.startsWith("&") ) s = "&"+s;
            if(!tList.contains(s))
            {
                _tagsMenu->insertItem(s,counter);
                tList.append(s);
            }
            counter++;
        }
    }

    bool haveTags = (_tags.count() > 0);
    
    if(isReadOnly())
        haveTags=false;

    if(haveTags != hadTags)
    {
        emit signalNextTagAvailable(haveTags);
        emit signalTagsAvailable(haveTags);
    }
}

void KBabelView::setTagsMenu(QPopupMenu *menu)
{
    _tagsMenu=menu;
    
    connect(_tagsMenu,SIGNAL(activated(int)),this,SLOT(insertTag(int)));
}


void KBabelView::insertTag(int n)
{
    EditCommand* tmp = new BeginCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);


    int line,col;
    msgstrEdit->getCursorPosition(&line,&col);
	int offset = msgstrEdit->pos2Offset(line,col);
    QString tag = _tagsMenu->text(n);
    if( tag.startsWith( "&&" ) ) tag = tag.remove(0,1); // replace && -> &. && is used for correct menu display
    InsTextCmd* insCmd = new InsTextCmd(offset,tag);
    insCmd->setPart(EditCommand::Msgstr);
    insCmd->setIndex(_currentIndex);
   
    msgstrEdit->processCommand(insCmd,false);
    forwardMsgstrEditCmd(insCmd);

    tmp = new EndCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);
    
    autoCheck( true ); // check it NOW - it should not be needed, but it is, I don't know why :-(
}

void KBabelView::insertNextTag()
{
    int line,col;
    msgstrEdit->getCursorPosition(&line,&col);
	int offset = msgstrEdit->pos2Offset(line,col);

    QString s = _catalog->msgstr(_currentIndex).left(offset);
    
    QString t;
    
    if( taglistView->isVisible() )
    {
	if(taglistView->currentItem() == -1)
	{
    	    KNotifyClient::beep();
    	    return;
	}
     
	t=taglistView->currentText();
    } 
    else
    {
	if( _catalog->msgid( _currentIndex ).startsWith("_n:") )
	{
	    int pos = msgstrEdit->currentIndex();
    	    int currentFormBegin=s.findRev("\\n",pos);
	    if( currentFormBegin == -1 ) currentFormBegin=0;
	    else currentFormBegin+=3; // skip the newline
	    int currentFormEnd=s.find("\\n",pos);
	    if( currentFormEnd == -1 ) currentFormEnd=s.length();
	
	    s=s.mid(currentFormBegin,currentFormEnd-currentFormBegin);
	}
		
	TagExtractor te(s);
	uint num=te.countTags();
	if(num >= _tags.count())
	{
	    KNotifyClient::beep();
	    return;
	}
	
	t=_tags[num];
    }

    if( taglistView->currentItem() != -1 )
    {
        QListBoxItem* item= taglistView->item(taglistView->currentItem())->next();
	if( item ) taglistView->setCurrentItem(item);
	else taglistView->clearSelection();
    }

    EditCommand* tmp = new BeginCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);

    InsTextCmd* insCmd = new InsTextCmd(offset,t);
    insCmd->setPart(EditCommand::Msgstr);
    insCmd->setIndex(_currentIndex);
   
    msgstrEdit->processCommand(insCmd,false);
    forwardMsgstrEditCmd(insCmd);
   
    tmp = new EndCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);
    
    autoCheck( true ); // check it NOW - it should not be needed, but it is, I don't know why :-(
}

void KBabelView::showTagsMenu()
{
    if(_tagsMenu && _tags.count() > 0)
    {
        int y=msgstrEdit->height()/2;
        int x=msgstrEdit->width()/2;
        _tagsMenu->exec(msgstrEdit->mapToGlobal( QPoint(x,y) ) );

        return;
    }
}


void KBabelView::updateArgs()
{
    bool hadArgs = _args.count() > 0;

    _args = _catalog->argList(_currentIndex);

    if(_argsMenu)
    {
        _argsMenu->clear();
     
        QStringList tList;
        QStringList::Iterator it;
        int counter=0;
        for(it=_args.begin(); it!=_args.end(); ++it)
        {
	    QString s = *it;
            if(!tList.contains(s))
            {
                _argsMenu->insertItem(s,counter);
                tList.append(s);
            }
            counter++;
        }
    }

    bool haveArgs = (_args.count() > 0);
    
    if(isReadOnly())
        haveArgs=false;

    if(haveArgs != hadArgs)
    {
        emit signalNextArgAvailable(haveArgs);
        emit signalArgsAvailable(haveArgs);
    }
}

void KBabelView::setArgsMenu(QPopupMenu *menu)
{
    _argsMenu=menu;
    
    connect(_argsMenu,SIGNAL(activated(int)),this,SLOT(insertArg(int)));
}


void KBabelView::insertArg(int n)
{
    EditCommand* tmp = new BeginCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);


    int line,col;
    msgstrEdit->getCursorPosition(&line,&col);
	int offset = msgstrEdit->pos2Offset(line,col);
    QString arg = _argsMenu->text(n);
    InsTextCmd* insCmd = new InsTextCmd(offset,arg);
    insCmd->setPart(EditCommand::Msgstr);
    insCmd->setIndex(_currentIndex);
   
    msgstrEdit->processCommand(insCmd,false);
    forwardMsgstrEditCmd(insCmd);

    tmp = new EndCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);
    
    autoCheck( true ); // check it NOW - it should not be needed, but it is, I don't know why :-(
}

void KBabelView::insertNextArg()
{
    int line,col;
    msgstrEdit->getCursorPosition(&line,&col);
	int offset = msgstrEdit->pos2Offset(line,col);

    QString s = _catalog->msgstr(_currentIndex).left(offset);
    
    if( _catalog->msgid( _currentIndex ).startsWith("_n:" ) )
    {
	int pos = msgstrEdit->currentIndex();
    	int currentFormBegin=s.findRev("\\n",pos);
	if( currentFormBegin == -1 ) currentFormBegin=0;
	else currentFormBegin+=3; // skip the newline
	int currentFormEnd=s.find("\\n",pos);
	if( currentFormEnd == -1 ) currentFormEnd=s.length();
	
	s=s.mid(currentFormBegin,currentFormEnd-currentFormBegin);
    }
    
    ArgExtractor te(s);
    uint num=te.countArgs();
    if(num >= _args.count())
    {
    	KNotifyClient::beep();
        return;
    }
     
    QString t=_args[num];

    EditCommand* tmp = new BeginCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);

    InsTextCmd* insCmd = new InsTextCmd(offset,t);
    insCmd->setPart(EditCommand::Msgstr);
    insCmd->setIndex(_currentIndex);
   
    msgstrEdit->processCommand(insCmd,false);
    forwardMsgstrEditCmd(insCmd);
   
    tmp = new EndCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);
    
    autoCheck( true ); // check it NOW - it should not be needed, but it is, I don't know why :-(
}

void KBabelView::showArgsMenu()
{
    if(_argsMenu && _args.count() > 0)
    {
        int y=msgstrEdit->height()/2;
        int x=msgstrEdit->width()/2;
        _argsMenu->exec(msgstrEdit->mapToGlobal( QPoint(x,y) ) );

        return;
    }
}


void KBabelView::diff()
{
    diffInternal(false);
}

void KBabelView::diffShowOrig()
{
    msgidLabel->setText(_catalog->msgid(_currentIndex));
    msgidLabel->forceUpdate();
}

void KBabelView::toggleAutoDiff(bool on)
{
    if(on != _diffEnabled)
    {
        _diffEnabled = on;

        if(on)
        {
            diff();
        }
        else
        {
            diffShowOrig();
        }
    }
}

void KBabelView::autoDiff()
{
    diffInternal(true);
}

void KBabelView::diffInternal(bool autoDf)
{    
    if(_diffing || _loadingDiffFile)
    {
        return;
    }

    _diffing = true;
    uint diffIndex = _currentIndex;
    
    QString diffString;
    
    Catalog::DiffResult r = _catalog->diff(_currentIndex, &diffString);

    if(r == Catalog::DiffNeedList)
    {
        if(_settings.useDBForDiff)
        { 
            _loadingDiffFile=true;
            bool wasEnabled=_diffEnabled;
            _diffEnabled=false;        

            QValueList<DiffEntry> diffList;
            QString error;
            QString package = _catalog->packageName()+".po";
            kdDebug(KBABEL) << "getting list for " << package << endl;    
            
            if(dictBox->messagesForPackage(package,diffList,error))
            {
                kdDebug(KBABEL) << "got " << diffList.count() 
                    << " messages" << endl;
                _catalog->setDiffList(diffList);
            }
            else
            {
                KMessageBox::sorry(this
                    ,i18n("An error occurred while trying to get the list "
                        "of messages for this file from the database:\n"
                        "%1").arg(error));
                
                _diffing=false;
                _diffEnabled=false;
                _loadingDiffFile=false;
                emit signalDiffEnabled(false);

                return;
            }
            
            _diffEnabled=wasEnabled;
            _loadingDiffFile=false;
        }
        else
        {
            _diffing=false;
            if(!openDiffFile(true))
            {
                _diffEnabled=false;
                 emit signalDiffEnabled(false);
            
                _diffing=false;
                return;
            }
        
            _diffing = true;
        }
        
        diffIndex = _currentIndex;        
        r = _catalog->diff(_currentIndex, &diffString);
    }

    // if the index changed in the meanwhile
    while(diffIndex != _currentIndex)
    {
        diffIndex=_currentIndex;
        r = _catalog->diff(_currentIndex,&diffString);
    }

    if(r == Catalog::DiffOk)
    {
	msgidLabel->setText(diffString);
	msgidLabel->forceUpdate();

        if(diffString == _catalog->msgid(_currentIndex) )
        {
            emit signalChangeStatusbar(i18n("No difference found"));
        }
        else
        {
            emit signalChangeStatusbar(i18n("Difference found"));
        }
    }
    else
    {
        if(!autoDf)
        {
            KMessageBox::information(this
                ,i18n("No corresponding message found"));
        }
        else
        {
            emit signalChangeStatusbar(
                    i18n("No corresponding message found"));
        }
    }

    _diffing = false;
}

bool KBabelView::openDiffFile()
{
    return openDiffFile(false);
}

bool KBabelView::openDiffFile(bool autoDiff)
{
    if(_diffing || _loadingDiffFile)
        return false;
    
    KURL url;

    if( autoDiff && !_settings.diffBaseDir.isEmpty() )
    {
        KURL fileURL = _catalog->currentURL();
        KURL poBaseURL(_catManSettings.poBaseDir);
        QString poBase = poBaseURL.path();
        int len = poBase.length();
        if(fileURL.path().left(len) == poBase)
        {
           QString fileRelPath = fileURL.path().mid(len);
           if(fileRelPath[0] == '/')
               fileRelPath=fileRelPath.mid(1);

           if(_settings.diffBaseDir.right(1) != "/")
               _settings.diffBaseDir += '/';

           QString diffFilePath = _settings.diffBaseDir + fileRelPath;

           
           KURL diffFileURL(diffFilePath);

           if(diffFileURL.isValid() && KIO::NetAccess::exists(diffFileURL))
           {
               url = diffFileURL;

               kdDebug(KBABEL) << "using file " << diffFileURL.prettyURL() 
                         << " as diff file" << endl;
           }
        }
    }
        
 
    if(url.isEmpty())
    {
        url = KFileDialog::getOpenURL(_settings.diffBaseDir, i18n("*.po *.pot|Gettext files")
               ,this, i18n("Select File to Diff With"));
    }

    if(url.isEmpty())
        return false;
 
    _loadingDiffFile=true;
    bool wasEnabled=_diffEnabled;
    _diffEnabled=false;
        

    Catalog cat;
    bool dummy;
    
    connect(&cat,SIGNAL(signalProgress(int)),this,SIGNAL(signalProgress(int)));
    emit signalResetProgressBar(i18n("loading file for diff"),100);
    
    Catalog::IOStatus stat = cat.openURL(url,dummy);
    
    emit signalClearProgressBar();

    
    if(stat != Catalog::OK && stat != Catalog::RECOVERED_PARSE_ERROR)
    {
        switch(stat)
        {
            case Catalog::PARSE_ERROR:
            {
                KMessageBox::sorry(this
                    ,i18n("Error while trying to read file:\n %1\n"
                   "Maybe it is not a valid PO file.").arg(url.prettyURL()));
                break;
            }
            case Catalog::NO_PERMISSIONS:
            {
                KMessageBox::sorry(this,i18n(
                "You don't have permissions to read file:\n %1")
                        .arg(url.prettyURL()));
                break;
            }
            case Catalog::NO_FILE:
            {
                 KMessageBox::sorry(this,i18n(
                 "You have not specified a valid file:\n %1")
                         .arg(url.prettyURL()));
                break;
            }
            default:
            {
                 KMessageBox::sorry(this,i18n(
                  "Error while trying to open file:\n %1")
                         .arg(url.prettyURL()));
                 break;
            }

        }
        
        _diffEnabled=wasEnabled;
        _loadingDiffFile=false;

        return false; 
    }

    _catalog->setDiffList( cat.asDiffList() );
    
    _diffEnabled=wasEnabled;
    _loadingDiffFile=false;

    return true;
}

void KBabelView::showTryLaterMessageBox()
{
    if( !_showTryLaterBox ) return;
	
    KDialogBase *dialog= new KDialogBase(
		i18n("Information"),
		KDialogBase::Yes,
		KDialogBase::Yes, KDialogBase::Yes,
		this, "information", true, true,
		KStdGuiItem::ok() );

    QVBox *topcontents = new QVBox (dialog);
    topcontents->setSpacing(KDialog::spacingHint()*2);
    topcontents->setMargin(KDialog::marginHint()*2);

    QWidget *contents = new QWidget(topcontents);
    QHBoxLayout * lay = new QHBoxLayout(contents);
    lay->setSpacing(KDialog::spacingHint()*2);

    lay->addStretch(1);
    QLabel *label1 = new QLabel( contents);
    label1->setPixmap(QMessageBox::standardIcon(QMessageBox::Information));
    lay->add( label1 );
    QLabel *label2 = new QLabel( i18n("The search string is not found yet.\n"
	         "However, the string can be found "
		 "in the files being searched at the moment.\n"
		 "Please try later."), contents);
    label2->setAlignment( Qt::AlignAuto | Qt::AlignVCenter | Qt::ExpandTabs | Qt::WordBreak );
    label2->setMinimumSize(label2->sizeHint());
    lay->add( label2 );
    lay->addStretch(1);

    QCheckBox *checkbox = new QCheckBox(i18n("Do not show in this find/replace session again"), topcontents);

    dialog->setMainWidget(topcontents);
    dialog->enableButtonSeparator(false);
    dialog->incInitialSize( QSize(50,0) );

    dialog->exec();

    _showTryLaterBox = !checkbox->isChecked();
    delete dialog;
}

void KBabelView::setFilePackage()
{
    bool result=false;
    QString p = KLineEditDlg::getText(i18n("Enter new package for the current file:"),_catalog->package(),&result,this);
    if( result )
    {
	_catalog->setPackage(p);
	emit signalChangeCaption(p);
    }
}

void KBabelView::insertTagFromTool( QListBoxItem* item )
{
    int line,col;
    msgstrEdit->getCursorPosition(&line,&col);
    int offset = msgstrEdit->pos2Offset(line,col);

    EditCommand* tmp = new BeginCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);

    InsTextCmd* insCmd = new InsTextCmd(offset,item->text());
    insCmd->setPart(EditCommand::Msgstr);
    insCmd->setIndex(_currentIndex);
   
    msgstrEdit->processCommand(insCmd,false);
    forwardMsgstrEditCmd(insCmd);
   
    tmp = new EndCommand();
    tmp->setPart(EditCommand::Msgstr);
    tmp->setIndex(_currentIndex);
    _catalog->applyEditCommand(tmp,this);
    
    msgstrEdit->setFocus();
    
    autoCheck( true ); // check it NOW - it should not be needed, but it is, I don't know why :-(
    
}
