#include "EditorManager.h"
#include "FNU.h"
#include <assert.h>
#include <iostream>
using namespace std ;

#include <kfiledialog.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kiconloader.h>

#include <qiconset.h>
#include <qpixmap.h>
#include <qstrlist.h>
#include <qdragobject.h>
#include <qptrlist.h>

EditorManager::EditorManager ( QWidget *parent, KActionCollection *ac) :
        QTabWidget(parent), actions(ac), currenteditor(0)
{
    act("cut")->setEnabled(false) ;
    act("copy")->setEnabled(false) ;
    act("undo")->setEnabled(false) ;
    act("redo")->setEnabled(false) ;
    act("save")->setEnabled(false)  ;
    act("save_as")->setEnabled(true)  ;

    connect(act("new"),SIGNAL(activated()), this , SLOT(createEditor()) ) ;
    connect(act("open"), SIGNAL(activated()), this, SLOT(openFile()) ) ;
    connect(act("close"), SIGNAL(activated()), this, SLOT(removeCurrentEditor() )) ;
    connect(act("close_all"), SIGNAL(activated()), this, SLOT(closeAllFiles() )) ;
    connect(act("save_all"), SIGNAL(activated()), this, SLOT(saveAllFiles() )) ;
    connect(this, SIGNAL(currentChanged(QWidget *)), this , SLOT( changeToEditor(QWidget *)) ) ;

    //  IMHO, I'd rather not have this Editor window created if filenames were given in the command line
    //  createEditor() ;
}


/** No descriptions */
void EditorManager::slotOpenFile(const KURL &url)
{
    if( url.isValid())
    {
        // document already loaded ?
        QString filename = url.directory(false) +  url.fileName();

        KBEditor *e = findEditor(filename) ;
        if( e != 0)
        {
            if(  KMessageBox::questionYesNo (this , i18n("File \"%1\" already loaded.\nReload?").arg(filename ) ) ==   KMessageBox::Yes)
            {
                e->slotOpen(url) ;
                return ;
            }
        }
        else
        {
            e = currentEditor() ;
            // try to use current editor if it is not used
            if( e == 0 ||
              !(e->fileName().isEmpty()) ||
              e->textIsModified()
              ) createEditor() ;
            currentEditor()->slotOpen(url) ;
        }
    }
}

void EditorManager::openFile()
{
    KURL url ;

    url = KFileDialog::getOpenURL ( "." , i18n("*.asm *.src *.inc *.slb|Asm Sources (*.asm *.src *.inc *.slb)"
                                    "\n*.hex|Intel Hex (*.hex)\n*.c *.h|C files (*.c *.h)\n*|All (*)"),  this ) ;
    slotOpenFile(url) ;
}

void EditorManager::connectEditor(KBEditor *editor)
{
    disconnectEditor(currentEditor()) ;
    // actions->editeur
    if( editor == 0) return ;
    connect( act("split_view"), SIGNAL(activated()), editor, SLOT(slotAddView()) ) ;
    connect( act("remove_view"), SIGNAL(activated()), editor, SLOT(slotRemoveCurrentView()) ) ;
    connect( act("undo"), SIGNAL(activated()), editor, SLOT(slotUndo()) ) ;
    connect( act("redo"), SIGNAL(activated()), editor, SLOT(slotRedo()) ) ;
    connect( act("cut"), SIGNAL(activated()), editor, SLOT(slotCut()) ) ;
    connect( act("copy"), SIGNAL(activated()), editor, SLOT(slotCopy()) ) ;
    connect( act("paste"), SIGNAL(activated()), editor, SLOT(slotPaste()) ) ;
    connect( act("find"), SIGNAL(activated()), editor, SLOT(slotSearch()) ) ;
    connect( act("find_next"), SIGNAL(activated()), editor, SLOT(slotFindNext()) ) ;
    connect( act("find_previous"), SIGNAL(activated()), editor, SLOT(slotFindPrevious()) ) ;
    connect( act("replace"), SIGNAL(activated()), editor, SLOT(slotReplace()) ) ;
    connect( act("goto_line"), SIGNAL(activated()), editor, SLOT(slotGotoLine()) ) ;
    connect( act("save"), SIGNAL(activated()), editor, SLOT(slotSave()) ) ;
    connect( act("save_as"), SIGNAL(activated()), editor, SLOT(slotSaveAs()) ) ;
    connect( act("print_dialog"), SIGNAL(activated()), editor, SLOT(slotPrintDialog()) ) ;
    connect( act("line_numbers"), SIGNAL(activated()), editor, SLOT(slotToggleLineNumbersOn()) ) ;
    connect( act("icon_borders"), SIGNAL(activated()), editor, SLOT(slotToggleBorderOn()) ) ;
    connect( act("auto_wrap"), SIGNAL(activated()), editor, SLOT(slotToggleWrapOn()) ) ;
    connect( act("dos_eol"), SIGNAL(activated()), editor, SLOT(slotToggleDosEOLOn()) ) ;
    connect( act("comment"), SIGNAL(activated()), editor, SLOT(slotComment()) ) ;
    connect( act("uncomment"), SIGNAL(activated()), editor, SLOT(slotUncomment()) ) ;
    connect( act("indent"), SIGNAL(activated()), editor, SLOT(slotIndent()) ) ;

    // editors->UI
    connect(editor,SIGNAL(viewsStateChanged()), this, SLOT(viewsManagement())) ;
    connect(editor,SIGNAL(undoRedoStateChanged()), this, SLOT(undoRedoManagement())) ;
    connect(editor,SIGNAL(selectionStateChanged()), this, SLOT(selectionManagement())) ;
    connect(editor,SIGNAL(textChanged()), this, SLOT(saveManagement())) ;
    connect(editor,SIGNAL(fileNameChanged()), this, SLOT(titleManagement())) ;
    connect(editor,SIGNAL(gotFocus()), this, SLOT(focusManagement())) ;
    connect(editor,SIGNAL(eolChanged()), this, SLOT(eolManagement())) ;

    connect(editor,SIGNAL(dropEventPass(QDropEvent *) ) , this, SLOT(slotDropEvent( QDropEvent *) ) );

}


void EditorManager::disconnectEditor(KBEditor *editor)
{
    if( editor == 0) return ;
    disconnect( act("split_view"), SIGNAL(activated()), editor, SLOT(slotAddView()) ) ;
    disconnect( act("remove_view"), SIGNAL(activated()), editor, SLOT(slotRemoveCurrentView()) ) ;
    disconnect( act("undo"), SIGNAL(activated()), editor, SLOT(slotUndo()) ) ;
    disconnect( act("redo"), SIGNAL(activated()), editor, SLOT(slotRedo()) ) ;
    disconnect( act("cut"), SIGNAL(activated()), editor, SLOT(slotCut()) ) ;
    disconnect( act("copy"), SIGNAL(activated()), editor, SLOT(slotCopy()) ) ;
    disconnect( act("paste"), SIGNAL(activated()), editor, SLOT(slotPaste()) ) ;
    disconnect( act("find"), SIGNAL(activated()), editor, SLOT(slotSearch()) ) ;
    disconnect( act("find_next"), SIGNAL(activated()), editor, SLOT(slotFindNext()) ) ;
    disconnect( act("find_previous"), SIGNAL(activated()), editor, SLOT(slotFindPrevious()) ) ;
    disconnect( act("replace"), SIGNAL(activated()), editor, SLOT(slotReplace()) ) ;
    disconnect( act("goto_line"), SIGNAL(activated()), editor, SLOT(slotGotoLine()) ) ;
    disconnect( act("save"), SIGNAL(activated()), editor, SLOT(slotSave()) ) ;
    disconnect( act("save_as"), SIGNAL(activated()), editor, SLOT(slotSaveAs()) ) ;
    disconnect( act("print_dialog"), SIGNAL(activated()), editor, SLOT(slotPrintDialog()) ) ;
    disconnect( act("line_numbers"), SIGNAL(activated()), editor, SLOT(slotToggleLineNumbersOn()) ) ;
    disconnect( act("icon_borders"), SIGNAL(activated()), editor, SLOT(slotToggleBorderOn()) ) ;
    disconnect( act("auto_wrap"), SIGNAL(activated()), editor, SLOT(slotToggleWrapOn()) ) ;
    disconnect( act("dos_eol"), SIGNAL(activated()), editor, SLOT(slotToggleDosEOLOn()) ) ;
    disconnect( act("comment"), SIGNAL(activated()), editor, SLOT(slotComment()) ) ;
    disconnect( act("uncomment"), SIGNAL(activated()), editor, SLOT(slotUncomment()) ) ;
    disconnect( act("indent"), SIGNAL(activated()), editor, SLOT(slotIndent()) ) ;

    disconnect(editor,SIGNAL(viewsStateChanged()), this, SLOT(viewsManagement())) ;
    disconnect(editor,SIGNAL(undoRedoStateChanged()), this, SLOT(undoRedoManagement())) ;
    disconnect(editor,SIGNAL(selectionStateChanged()), this, SLOT(selectionManagement())) ;
    disconnect(editor,SIGNAL(textChanged()), this, SLOT(saveManagement())) ;
    disconnect(editor,SIGNAL(fileNameChanged()), this, SLOT(titleManagement())) ;
    disconnect(editor,SIGNAL(gotFocus()), this, SLOT(focusManagement())) ;
    disconnect(editor,SIGNAL(eolChanged()), this, SLOT(eolManagement())) ;

    disconnect(editor,SIGNAL(dropEventPass(QDropEvent *) ) , this, SLOT(slotDropEvent( QDropEvent *) ) );
}

void EditorManager::eolManagement()
{
    KBEditor *kbe = currentEditor() ;
    if( kbe != 0)
    {
			  KToggleAction* t =  dynamic_cast<KToggleAction*>(act("dos_eol")) ;
        if(t) t->setChecked(kbe->dosEOLOn()) ;
    }
}
void EditorManager::undoRedoManagement()
{
    KBEditor *kbe = currentEditor() ;
    if( kbe != 0)
    {
        act("undo")->setEnabled(kbe->undoCount() > 0) ;
        act("redo")->setEnabled(kbe->redoCount() > 0) ;
    }
}

void EditorManager::viewsManagement()
{
    KBEditor *kbe = currentEditor() ;
    if( kbe != 0)
    {
        act("remove_view")->setEnabled(kbe->viewCount() > 1) ;
    }
}

void EditorManager::selectionManagement()
{
    KBEditor *kbe = currentEditor() ;
    if( kbe != 0)
    {
        act("cut")->setEnabled(kbe->selectionCount() >  0 ) ;
        act("copy")->setEnabled(kbe->selectionCount() >  0 ) ;
    }
}
void EditorManager::saveManagement()
{
    KBEditor *kbe = currentEditor() ;
    if( kbe != 0)
    {
        act("save")->setEnabled(kbe->textIsModified()) ;
        titleManagement() ;
    }
}

void  EditorManager::titleManagement()
{
    // find icon stuff
    static QIconSet iset , nulliset ;
    if( iset.isNull())
    {
        KIconLoader loader ;
        iset = loader.loadIconSet ("filesave", KIcon::Small, 0) ;
    }

    KBEditor *kbe = currentEditor() ;
    if( kbe != 0)
    {
        QString name = shortName(kbe->fileURL().prettyURL(), 20) ;
        if( name.isEmpty() ) name = i18n("<noname>") ;
        changeTab(kbe , (kbe->textIsModified()) ? iset :  nulliset , name  ) ;
    }
}

void  EditorManager::focusManagement()
{
    undoRedoManagement();
    viewsManagement();
    selectionManagement();
    saveManagement();
    eolManagement();
}

EditorManager::~EditorManager()
{
    closeAllFiles() ;
}

void EditorManager::closeAllFiles()
{
    while(currentEditor() != 0)
        removeCurrentEditor() ;
}


void EditorManager::removeCurrentEditor()
{
    KBEditor *k = currentEditor() ;    ;

     if( k != 0)
    {
        if(k ->textIsModified())
        {
            if(   KMessageBox::questionYesNo (this , i18n("File \"%1\" not saved.\nSave?").arg(k->fileName() ) )
                    ==   KMessageBox::Yes)
                k->slotSave() ;
        }
        removePage(k); 			// remove the tab 
        editors.remove(k);  // remove editor pointer from the editor list
        KBEditor *g =  dynamic_cast<KBEditor *>( currentPage() ) ;    // might be 0
        // DO NOT delete k prior changeToEditor()
        // because this method disconnect k
        changeToEditor(g) ;
        delete k ;   // and its editor child
    }
}

void EditorManager::changeToEditor(QWidget *qw)
{
    KBEditor *ge = dynamic_cast<KBEditor *>(qw) ;

    // connect to editor 0 is not an error,
    // it just disconnect the current editor from th UI
    connectEditor(ge) ;
    currenteditor = ge ;
    if(currenteditor != 0)
    {
        showPage(currenteditor) ;
        currenteditor->clearFocus() ;   // force a got focus signal
        currenteditor->setFocus() ;
    }
}


KBEditor *EditorManager::currentEditor()
{
    return currenteditor ;
}

//
// search an editor by Filename
//
KBEditor *EditorManager::findEditor(const QString&  name)
{
    KBEditor *g ;

    for( g = editors.first()  ; g != 0  ; g = editors.next() )
    {
        if( name == g->filePathName() ) return g ;
    }
    return 0 ;
}
//
// Find a KBEditor or create it if it does not exist
//
KBEditor *EditorManager::findOrCreate(const QString&  name)
{
    KBEditor *g;
    
        g = findEditor(name) ;
        if( g == 0)
        {
            createEditor() ;    g = currentEditor() ;
        }
        showPage(  g ) ;
        return   g ;   
}

void EditorManager::createEditor()
{
  // seach for a free slot (ie : noname and clean)
    KBEditor *g ;

    for( g = editors.first()  ; g != 0  ; g = editors.next() )
    {
        if( g->filePathName() == "" && !g->textIsModified() )
        {
           changeToEditor(g) ; return ;
        }
    }
    // no free slot:  create it
    KBEditor *newed = new KBEditor(0);
    editors.append(newed) ;
    addTab(newed,QString::null);
    setTabEnabled(newed,true) ;
    changeToEditor(newed) ;
}

KAction *EditorManager::act(const char *name)
{
    KAction *a = actions->action(name) ;
    if(a == 0 )
    {
        cerr << "No such action : " << name << " (internal error, please report to author)" << endl << flush ;
    }
    return a    ;
}

QString EditorManager::shortName(const QString& s,int max)
{
    if( s.length() <= (unsigned int)max ) return s ;
    return QString("...") + s.right(max-3) ;
}

void EditorManager::slotDropEvent( QDropEvent *e)
{
    QStrList urls ;

    if( QUriDrag::decode(e,urls))
    {
        QPtrListIterator<char> it(urls) ;
        for( ; it.current() ; ++it)
        {
            KBEditor *e = currentEditor() ;
            // try to reuse current  editor if not used
            if(	 e == 0 ||
                    !(e->fileURL().isEmpty()) ||
                    e->textIsModified() ) createEditor() ;
            currentEditor()->slotOpen(*it) ;
        }
    }
}
/** Switch to editor buffer specified by filename
Il filename file is not currently loaded,
a new tab is created for it, and it is loaded.
\return
true if switch successfull, false otherwise.
 */
KBEditor *EditorManager::switchToEditor(const QString &filename)
{
    KBEditor *needed = findEditor(filename) ;
    if( needed == 0)
    {
        if( ! QFile::exists(filename)) return 0 ;
        needed = findOrCreate(filename) ;   // create tab
        needed->slotOpen(KURL(filename)) ;  //open file
    }
    changeToEditor(needed) ;
    return needed ;
}

/** returns the name of the currently displayed  file.
Empty string if no opened file */
QString EditorManager::currentFile()
{
	KBEditor *e = currentEditor() ;
	return e ? e->filePathName() : QString("") ;
}

/** No descriptions */
void EditorManager::saveAllFiles()
{
  KBEditor *e,*cur = currentEditor()  ;
  
  for( e = editors.first() ; e ; e = editors.next())
  {
    if( FNU::isAsmSource(e->fileName())  || FNU::isIncFile(e->fileName()) )
    {
      e->slotSave()  ;
      changeToEditor(e) ;
    }
  }
  changeToEditor(cur) ;
}
/** Returns the names of all currently opened files. */
QStringList EditorManager::getAllFileNames()
{
  QStringList names ;
  KBEditor *e ;
  for(e = editors.first() ; e ; e = editors.next())
    names.push_back(e->filePathName()) ;
  return names ;
}
/** No descriptions */
int EditorManager::editorCount()
{
  return (int)editors.count() ;
}

/** No descriptions */
QPtrList<KBEditor>& EditorManager::getEditors()
{
  return editors ;
}
