/***************************************************************************
                          ProjectWid.cpp  -  description
                             -------------------
    begin                : sam jui 5 2003
    copyright            : (C) 2003 by Alain Gibaud
    email                : alain.gibaud@univ-valenciennes.fr
 ***************************************************************************/

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

#include "ProjectWid.h"
#include <qstringlist.h>
#include <qvbox.h>
#include <qhbox.h>
#include <qpushbutton.h>
#include <qpopupmenu.h>
#include <qlabel.h>
#include <qcolor.h>
#include <qdir.h>
#include <qmessagebox.h>
#include <qurl.h>
#include <qapplication.h>
#include <qdragobject.h>
#include <qglobal.h>

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

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

#include "NewProject.h"
#include "ProjectEditorWid.h"
#include "FNU.h"
#include "PikMain.h"

#include "project.xpm.h"
#include "sourcefile.xpm.h"
#include "includefile.xpm.h"
#include "csourcefile.xpm.h"
#include "objectfile.xpm.h"

ProjectWid::ProjectWid(PikMain *mainwin, QWidget *parent, const char *name  )
        : QVBox(parent,name),
        newproject(0),
        currentListViewItem(0),
        project(0),
        main_window(mainwin),
        projecteditor(0)
{

    QListView *projView =    new  QListView(this) ;
    int firstcol = projView->addColumn(i18n("Files"),170) ;
    projView->setColumnWidthMode(firstcol,QListView::Manual) ;
    // projView->setColumnWidthMode (0, QListView::Maximum  );
    
    projView->addColumn(i18n("Type")) ;
    projView->setRootIsDecorated(true) ;

    projectTree  =   new QListViewItem(projView, i18n("  <Empty>  "),"","p") ;
    projectTree->setPixmap(0, QPixmap(  (const char**)project_xpm )) ;

    connect(   projView    , SIGNAL(rightButtonPressed ( QListViewItem *, const QPoint &, int )),
               this, SLOT(slotProjectRButton (QListViewItem *, const QPoint &, int ) )) ;

    connect(    projView    , SIGNAL(clicked(QListViewItem *)), this,
                SLOT(slotProjectElementClicked(QListViewItem *)) ) ;

    slotProjectIsOpen(false) ;
    setAcceptDrops(true) ;


}


ProjectWid::~ProjectWid()
{
}

void  ProjectWid::slotProjectRButton (QListViewItem *li , const QPoint &p , int )
{

    KIconLoader loader ;
    QIconSet iconopenproject = loader.loadIconSet("pikdev_openproject", KIcon::Small) ;
    QIconSet iconnewproject = loader.loadIconSet("pikdev_createproject", KIcon::Small) ;
    QIconSet iconeditproject = loader.loadIconSet("pikdev_editproject", KIcon::Small) ;
    QIconSet iconcloseproject = loader.loadIconSet("pikdev_closeproject", KIcon::Small) ;
    QIconSet iconaddfile = loader.loadIconSet("pikdev_addfile", KIcon::Small) ;
    QIconSet iconaddcurrentfile = loader.loadIconSet("pikdev_addcurrentfile", KIcon::Small) ;
    QString menu_title ;

    currentListViewItem = li ;
    if(li != 0)
    {
        if(  li->text(2)  == "P")
        { // "open" project entry
            QPopupMenu *pop = makePopup(i18n("Project"), this) ;
            pop->insertItem(iconaddfile, i18n("Insert file"),1) ;
            pop->insertItem(iconaddcurrentfile,  i18n("Insert current file"),2) ;
            pop->insertItem(iconeditproject, i18n("Edit project"),3) ;
            pop->insertItem(iconcloseproject, i18n("Close project"),4) ;

            switch( pop->exec( p ) )
            {
            case 1 :
                slotInsertFile() ;  break ;
            case 2 :
                slotInsertCurrentFile() ;  break ;
            case 3 :
                slotEditProject() ; break ;
            case 4:
                slotCloseProject() ;  break ;
            }
        }
        else if(  li->text(2)  == "p")
        { // empty project entry

            QPopupMenu *pop = makePopup( i18n("Project"), this) ;
            pop->insertItem(iconnewproject, i18n("Create project"),1) ;
            pop->insertItem(iconopenproject, i18n("Open project"),2) ;

            int item = pop->exec( p ) ;
            if( item == 1 && newproject == 0)
            {
                slotNewProject() ;
            }
            else if( item == 2 && newproject == 0)
            {
                slotOpenProject() ;
            }
        }
        else
        {
            QString code =  li->text(2)  ;
            if(  code  == "A" ||  code  == "C")  menu_title =  i18n("Source file") ;
            else if(  code  == "O" )  menu_title = i18n("Object file") ;
            else if(  code  == "L" )  menu_title = i18n("Library file") ;
            else if(  code  == "I" )  menu_title =  i18n("Include file");
            else if(  code  == "S" )  menu_title =  i18n("Linker script file");
            else  menu_title =  i18n("Other file")  ;
            //
            QPopupMenu *pop = makePopup(menu_title , this) ;
            pop->insertItem( i18n("Remove from project"),1) ;

            int item = pop->exec( p ) ;
            if(item == 1)
            {
                slotRemoveFile() ;
            }
        }
    }
}

/** No descriptions */
void ProjectWid::slotCreateProject(const QString& projectname,
                                   const QString& projectdir,
                                   const QString& projecttype)

{
    QDir dir ;
    // multiple project files in one directory are now allowed

    QString projfile ( projectdir + "/" + projectname + ".pikprj" ) ;
    QFile f( projfile ) ;
    // reject request if project file already exists
    if(f.exists())
    {
        QMessageBox::critical(this,i18n("Sorry"),
                              i18n("The project file <b>%1</b> already exists" ).arg(projfile) ,
                              QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton) ;
        return ;
    }

    // create dir if it does not exist
    if( !dir.exists(projectdir) && !dir.mkdir(projectdir) )
    {
        QMessageBox::critical(this,i18n("Sorry"),
                              i18n("Cannot create directory <b>%1</b>" ).arg(projectdir) ,
                              QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton) ;
        return ;
    }

    if( project ) delete project ;

    project = new Project(projfile) ;
    // set minimal project data
    project->setDescription("") ;
    project->setOutput(projectname+".hex") ;
    project->setVersion("0.1") ;
    project->setType(projecttype) ;
    project->commit() ;

    projectTree->setText(2,"P") ; // change status to "open project"
    if(  projecttype == "asm" )
      projectTree->setText(1, i18n("Asm project")  ) ;
    else if(    projecttype == "C" )
      projectTree->setText(1, i18n("C project")  ) ;

    cout << "projecttype = " <<  projecttype << endl ;
    
    projectTree->setText(0, projfile  ) ; // change label to file name
    slotProjectIsOpen(true) ;

}
/** No descriptions */
void ProjectWid::slotNewProjectClosed()
{
    newproject = 0 ;
}
/** No descriptions */
void ProjectWid::slotCloseProject()
{
    if(project)
    {
        if( projecteditor != 0)
        {
            projecteditor->close(true) ; projecteditor = 0 ;
        }
        projectTree->setText(2,"p") ; // change status to "closed project"
        projectTree->setText(1,"") ;
        projectTree->setText(0,i18n("  <Empty>  ") ) ;

        for(QListViewItem * it = projectTree->firstChild();  it ; )
        {
            // close editor for this file (if open)
            QString absfilename = project->absFilename(it->text(0)) ;
            if( main_window->editorManager()  && main_window->editorManager()->switchToEditor(absfilename) != 0)
            { // editor found and set current : close it
                main_window->editorManager()->removeCurrentEditor() ;
            }
            QListViewItem *it1 = it ;
            it = it->nextSibling();
            delete it1 ;
        }
        slotProjectIsOpen(false) ;
        delete project ;
        project = 0 ;
    }
}
/** Display file selector and insert a new File to project */
void ProjectWid::slotInsertFile()
{
    if( project == 0) return ; // security

    
    QString filename(KFileDialog::getOpenFileName( project->projectPath() ,
                     i18n("*.src *.asm *.slb *.SLB|Asm source (*.src, *.asm)\n"
                     "*.c *.C|C source (*.c)\n"
                     "*.h *.H|C header (*.h)\n"
                     "*.o *.O|Object file (*.o)\n"
                     "*.a *.lib *.slb|Library (*.a, *.lib, *.slb)\n"
                     "*.inc *.INC|Include file (*.inc, *.INC)\n"
                     "*.lkr|Linker script (*.lkr)")
                     ,0,
                     i18n("Select file") )) ;
    if(filename != "")  insertAbsFile(filename) ;
}

bool ProjectWid::insertAbsFile(const QString &fname)
{
    if( project->absFileExists(fname) ) return false ; // already exists
    QString f = project->projectRelative(fname) ; // make filename project relative
    project->addFile(f) ;
    addFile2Tree(f) ;
    return true ;
}

/** No descriptions */
void ProjectWid::slotOpenProject()
{
    if( newproject != 0) newproject->close(true) ;
    if( project != 0) slotCloseProject() ; // security

     QString projectfile(KFileDialog::getOpenFileName(QString::null,
             i18n("*.pikprj|Project File (*.pikprj)"),0,
             i18n("Select Project file") )) ;

    slotOpenProjectFile(projectfile) ;
}
/** Add a file name to the project tree
filename may be :
a) absolute
b) relative to the project file location
 */
void ProjectWid::addFile2Tree(const QString& f)
{
    // QString ex = FNU::ext(f) ;
    char **icon = (char **) sourcefile_xpm;
    QString type, code ;

    if(FNU::isAsmSource(f))
    {
        icon = (char **) sourcefile_xpm ;
        type =  i18n("ASM file")  ;
        code =  "A"    ;
    }
    if(FNU::isIncFile(f))
    {
        icon = (char **) includefile_xpm ;
        type =  i18n("Include file" ) ;
        code =  "I"    ;
    }
    else if(FNU::isCSource(f))
    {
        icon = (char **) csourcefile_xpm ;
        type =  i18n("C file")  ;
        code =  "C"    ;
    }
    else if(FNU::isCHeader(f))
    {
        icon = (char **) includefile_xpm ;
        type =  i18n("C header")  ;
        code =  "H"    ;
    }
    else if(FNU::isSrcLib(f))
    {
        icon = (char **) objectfile_xpm ;
        type =  i18n("Source Lib")  ;
        code =  "R"    ;
    }
    else if(FNU::isObjFile(f))
    {
        icon = (char **) objectfile_xpm ;
        type =  i18n("Object file")  ;
        code =  "O"    ;
    }
    else if(FNU::isLibFile(f))
    {
        icon = (char **) objectfile_xpm ;
        type =  i18n("Library file")  ;
        code =  "L"    ;
    }
    else if(FNU::isLnkScrFile(f))
    {
        icon = (char **) sourcefile_xpm ;
        type =  i18n("Linker script file")  ;
        code =  "S"    ;
    }

    QListViewItem *entry = new QListViewItem(projectTree,f, type,code) ;
    entry->setPixmap(0, QPixmap(  (const char**)icon )) ;
    // open tree
    projectTree->setOpen(true) ;

}
/** Remove the file corresponding to the selected item */
void ProjectWid::slotRemoveFile()
{
    if(   currentListViewItem == 0) return ;

    project->removeFile(currentListViewItem->text(0)) ;
    delete   currentListViewItem   ;
    currentListViewItem  = 0 ;
}
/** Return the current project object (may be NULL) */
Project *ProjectWid::getProject()
{
    return project  ;
}
/** Make an empty popup menu with "title" header */
QPopupMenu *ProjectWid::makePopup(const QString& txt, QWidget *parent)
{
    QPopupMenu *pop = new   QPopupMenu (parent) ;
    QLabel *title =   new QLabel(txt,  pop )   ;
#if QT_VERSION >= 0x030101
    title->setFrameStyle(    QFrame::GroupBoxPanel +  QFrame::Raised  ) ;
#else
    // was not defined in Qt 3.0.x
    title->setFrameStyle(   /* QFrame::GroupBoxPanel +  */ QFrame::Raised  ) ;
#endif

    title->setLineWidth(1 ) ;
    title->setMidLineWidth(0 ) ;
    title->setBackgroundColor(QColor( 0xBF,0xBF,0xBF));
    pop->insertItem( title ) ;

    return pop ;

}
/** No descriptions */
void ProjectWid::slotProjectElementClicked(QListViewItem *item )
{
    // click on the widget background calls this slot with a NULL pointer !
    if(item == 0) return ;

    QString type(  item->text(2)) ;
    // we only switch to source files
    if( type != "A" && type != "C" && type != "I" && type != "H" && type != "R" && type != "S" )
    {
        QApplication::beep() ;
        return ;
    }
    QString file(project->absFilename(item->text(0))) ;
    if( main_window->editorManager()  && main_window->editorManager()->switchToEditor(file) == 0)
    {
        QMessageBox::warning(0,i18n("Sorry"),
        i18n("Cannot find file %1").arg(file) ,
        QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton) ;
    }
}
/** No descriptions */
void ProjectWid::slotInsertCurrentFile()
{
    QString cur(main_window->editorManager()->currentFile()) ;
    if( FNU::isCSource(cur) || FNU::isAsmSource(cur) ||
      FNU::isIncFile(cur) || FNU::isCHeader(cur) ||
      FNU::isSrcLib(cur))                     insertAbsFile(cur) ;
    else   QApplication::beep() ;
}
/** Invokes the Project Editor Widget */
void ProjectWid::slotEditProject()
{
    if(project  && projecteditor == 0 )
    {
        projecteditor = new ProjectEditorWid(project,0,"project editor widget") ;
        connect(  projecteditor  , SIGNAL(destroyed()), this , SLOT(slotProjectEditorClosed()) ) ;
    }
}
/** No descriptions */
void ProjectWid::slotNewProject()
{

    if(project != 0 || newproject != 0) return ;

    newproject = new NewProject;  // "new project"   widget
    connect(newproject, SIGNAL(projectData(const QString&,const QString&, const QString& )),
            this, SLOT(slotCreateProject(const QString&,const QString&, const QString& )) ) ;
    connect(  newproject  , SIGNAL(destroyed()), this , SLOT(slotNewProjectClosed()) ) ;

}
/** Updates the UI when a project is opened/closed */
void ProjectWid::slotProjectIsOpen(bool open)
{
    // set up action validity
    main_window->act("new_project")->setEnabled(!open) ;
    main_window->act("open_project")->setEnabled(!open) ;
    if( open )
    {
        KRecentFilesAction *a = dynamic_cast< KRecentFilesAction *>(main_window->act("open_recent_project"))  ;
        a->addURL(KURL( project->projectPathFileName() )) ;
    }
    main_window->act("open_recent_project")->setEnabled(!open) ;

    main_window->act("close_project")->setEnabled(open) ;
    main_window->act("edit_project")->setEnabled(open) ;
    main_window->act("add_to_project")->setEnabled(open) ;
    main_window->act("addcurrent_to_project")->setEnabled(open) ;
}

/** No descriptions */
void ProjectWid::slotProjectEditorClosed()
{
    projecteditor = 0 ;
}
/** No descriptions */
void ProjectWid::slotOpenProjectFile(const QString& projectfile)
{
    if( projectfile == "" ) return ;

    if( project ) slotCloseProject() ;

    project = new Project(projectfile) ;
    projectTree->setText(2,"P") ; // change status to "open project"
    projectTree->setText(0,projectfile ) ;
    
    if(  project->getType() == "asm")
      projectTree->setText(1,i18n("Asm project") ) ;
    else if(  project->getType() == "C")
      projectTree->setText(1,i18n("C project") ) ;
    else
      projectTree->setText(1,"" ) ;
    
    QStringList files = project->getFiles() ;
    for( QStringList::iterator i = files.begin() ; i != files.end() ; ++i)
    {
        addFile2Tree(*i) ;
    }
    projectTree->setOpen(true) ;
    slotProjectIsOpen(true) ;
}
/** Drag and drop management
 */
void ProjectWid::dragEnterEvent(QDragEnterEvent *ev)
{
    // accept if dragged object can be decoded as an URI
    ev->accept( QUriDrag::canDecode(ev) ) ;


}
/** Drop on project widget */
void ProjectWid::dropEvent(QDropEvent *ev)
{
    QStringList files ;
    if ( QUriDrag::decodeLocalFiles(ev, files) )
    {
        // only one file name, and it is a project file and no project already open
        if( project == 0 && files.count() == 1 && FNU::isProjectFile(*(files.begin())))
        {  // yes, open the project
            slotOpenProjectFile(*(files.begin())) ;
        }
        else if(project != 0 && files.count() == 1 && FNU::isProjectFile(*(files.begin())) )
        {
            // close current project, and open a new one
            slotCloseProject() ;
            slotOpenProjectFile(*(files.begin())) ;
        }
        else if( project != 0 )
        {
            // try to include files in the current project
            for( QStringList::Iterator it = files.begin(); it != files.end(); ++it)
            {
                if( FNU::isAsmSource(*it) || FNU::isCSource(*it) || FNU::isLibFile(*it)
                        ||  FNU::isObjFile(*it) || FNU::isIncFile(*it) ||
                        FNU::isSrcLib(*it) || FNU::isCHeader(*it) )
                {
                    insertAbsFile(*it) ; // multiple insertions are checked by inserAbsFile()
                }
            }

        }
    }

}
