/* Copyright (C) 2003 Oliver Kellogg
 * okellogg@users.sourceforge.net
 *
 * 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 "adaproject_part.h"

#include <tqdom.h>
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqvaluestack.h>
#include <tqregexp.h>
#include <tqvbox.h>

#include <kiconloader.h>
#include <tdelocale.h>
#include <tdeaction.h>
#include <kgenericfactory.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <tdemessagebox.h>
#include <klibloader.h>
#include <kprocess.h>
#include <kservice.h>
#include <tdeconfig.h>

#include "domutil.h"
#include "kdevcore.h"
#include "kdevmainwindow.h"
#include "kdevmakefrontend.h"
#include "kdevappfrontend.h"
#include "kdevpartcontroller.h"
#include "kdevlanguagesupport.h"
#include "kdevcompileroptions.h"
#include "kdevgenericfactory.h"
#include <kdevplugininfo.h>

#include "adaproject_widget.h"
#include "adaprojectoptionsdlg.h"
#include "adaglobaloptionsdlg.h"

typedef KDevGenericFactory<AdaProjectPart> AdaProjectFactory;
static const KDevPluginInfo pluginData("kdevadaproject");
K_EXPORT_COMPONENT_FACTORY( libkdevadaproject, AdaProjectFactory( pluginData ) )

AdaProjectPart::AdaProjectPart(TQObject *parent, const char *name, const TQStringList& )
    :KDevBuildTool(&pluginData, parent, name ? name : "AdaProjectPart" )
{
    setInstance(AdaProjectFactory::instance());
    setXMLFile("kdevadaproject.rc");

    TDEAction *action;
    action = new TDEAction( i18n("&Build Project"), "make_tdevelop", Key_F8,
                          this, TQT_SLOT(slotBuild()),
                          actionCollection(), "build_build" );
    action = new TDEAction( i18n("Execute Program"), "application-x-executable", 0,
                          this, TQT_SLOT(slotExecute()),
                          actionCollection(), "build_execute" );

    connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)),
             this, TQT_SLOT(projectConfigWidget(KDialogBase*)) );

    connect( core(), TQT_SIGNAL(configWidget(KDialogBase*)),
             this, TQT_SLOT(configWidget(KDialogBase*)) );

//  m_widget = new AdaProjectWidget(this);

//  TQWhatsThis::add(m_widget, i18n("WHAT DOES THIS PART DO?"));

  // now you decide what should happen to the widget. Take a look at kdevcore.h
  // or at other plugins how to embed it.

  // if you want to embed your widget as an outputview, simply uncomment
  // the following line.

  // mainWindow()->embedOutputView( m_widget, "name that should appear", "enter a tooltip" );

}

AdaProjectPart::~AdaProjectPart()
{
//  delete m_widget;
}

/**
 * This should really be merged with FileTreeWidget::matchesHidePattern()
 * and put in its own class. Currently this is repeated in scriptprojectpart.cpp, pascalproject_part.cpp, adaproject_part.cpp
 */
static bool matchesPattern(const TQString &fileName, const TQStringList &patternList)
{
    TQStringList::ConstIterator it;
    for (it = patternList.begin(); it != patternList.end(); ++it) {
        TQRegExp re(*it, true, true);
        if (re.search(fileName) == 0 && re.matchedLength() == (int)fileName.length())
            return true;
    }

    return false;
}

void AdaProjectPart::openProject(const TQString &dirName, const TQString &projectName)
{
    m_buildDir = dirName;
    m_projectDir = dirName;
    m_projectName = projectName;

    TQDomDocument &dom = *projectDom();
    // Set the default directory radio to "executable"
    if (DomUtil::readEntry(dom, "/kdevadaproject/run/directoryradio") == "" ) {
        DomUtil::writeEntry(dom, "/kdevadaproject/run/directoryradio", "executable");
    }

    loadProjectConfig();

    // Put all files from all subdirectories into file list
    TQValueStack<TQString> s;
    int prefixlen = m_projectDir.length()+1;
    s.push(m_projectDir);

    TQStringList includepatternList;

    if ( languageSupport() )
    {
	KMimeType::List list = languageSupport()->mimeTypes();
	KMimeType::List::Iterator it = list.begin();
	while( it != list.end() ){
	    includepatternList += (*it)->patterns();
	    ++it;
	}
    }

    TQString excludepatterns = "*~";
    TQStringList excludepatternList = TQStringList::split(",", excludepatterns);

    TQDir dir;
    do {
        dir.setPath(s.pop());
        kdDebug() << "AdaProjectPart::openProject examining: " << dir.path() << endl;
        const TQFileInfoList *dirEntries = dir.entryInfoList();
	if( !dirEntries )
	    break;

        TQPtrListIterator<TQFileInfo> it(*dirEntries);
        for (; it.current(); ++it) {
            TQString fileName = it.current()->fileName();
            if (fileName == "." || fileName == "..")
                continue;
            TQString path = it.current()->absFilePath();
            if (it.current()->isDir()) {
                kdDebug() << "AdaProjectPart::openProject pushing: " << path << endl;
                s.push(path);
            }
            else {
                if (matchesPattern(path, includepatternList)
                    && !matchesPattern(path, excludepatternList)) {
                    kdDebug() << "AdaProjectPart::openProject adding: " << path << endl;
                    m_sourceFiles.append(path.mid(prefixlen));
                } else {
                    kdDebug() << "AdaProjectPart::openProject ignoring: " << path << endl;
                }
            }
        }
    } while (!s.isEmpty());

    KDevProject::openProject( dirName, projectName );
}

void AdaProjectPart::closeProject()
{
}

/** Retuns a PairList with the run environment variables */
DomUtil::PairList AdaProjectPart::runEnvironmentVars() const
{
    return DomUtil::readPairListEntry(*projectDom(), "/kdevadaproject/run/envvars", "envvar", "name", "value");
}


/** Retuns the currently selected run directory
  * The returned string can be:
  *   if run/directoryradio == executable
  *        The directory where the executable is
  *   if run/directoryradio == build
  *        The directory where the executable is relative to build directory
  *   if run/directoryradio == custom
  *        The custom directory absolute path
  */
TQString AdaProjectPart::runDirectory() const
{
    TQString cwd = defaultRunDirectory("kdevadaproject");
    if (cwd.isEmpty())
      cwd = buildDirectory();
    return cwd;
}


/** Retuns the currently selected main program
  * The returned string can be:
  *   if run/directoryradio == executable
  *        The executable name
  *   if run/directoryradio == build
  *        The path to executable relative to build directory
  *   if run/directoryradio == custom or relative == false
  *        The absolute path to executable
  */
TQString AdaProjectPart::mainProgram() const
{
    TQDomDocument * dom = projectDom();

    if ( !dom ) return TQString();

    TQString DomMainProgram = DomUtil::readEntry( *dom, "/kdevadaproject/run/mainprogram");

    if ( DomMainProgram.isEmpty() ) return TQString();

    if ( DomMainProgram.startsWith("/") )   // assume absolute path
    {
        return DomMainProgram;
    }
    else // assume project relative path
    {
        return projectDirectory() + "/" + DomMainProgram;
    }

    return TQString();
}

/** Retuns a TQString with the run command line arguments */
TQString AdaProjectPart::debugArguments() const
{
    return DomUtil::readEntry(*projectDom(), "/kdevadaproject/run/globaldebugarguments");
}


/** Retuns a TQString with the run command line arguments */
TQString AdaProjectPart::runArguments() const
{
    return DomUtil::readEntry(*projectDom(), "/kdevadaproject/run/programargs");
}

TQString AdaProjectPart::mainSource() const
{
    return projectDirectory() + "/" + m_mainSource;
}

void AdaProjectPart::setMainSource(TQString fullPath)
{
    TQString olddir = activeDirectory();
    m_mainSource = fullPath.replace(TQRegExp(TQString(projectDirectory() + TQString("/"))),"");
    emit activeDirectoryChanged( olddir, activeDirectory() );
}

TQString AdaProjectPart::projectDirectory() const
{
    return m_projectDir;
}

TQString AdaProjectPart::projectName() const
{
    return m_projectName;
}

TQString AdaProjectPart::activeDirectory() const
{
    TQFileInfo fi(mainSource());
    return fi.dirPath(true).replace(TQRegExp(projectDirectory()),"");
}

TQString AdaProjectPart::buildDirectory() const
{
    TQFileInfo fi(mainSource());
    return fi.dirPath(true);
}

void AdaProjectPart::listOfFiles(TQStringList &result, TQString path) const
{
    TQDir d(path);
    if (!d.exists())
        return;

    const TQFileInfoList *entries = d.entryInfoList(TQDir::Dirs | TQDir::Files | TQDir::Hidden);
    if( !entries )
        return;

    TQFileInfoListIterator it( *entries );
    while( const TQFileInfo* fileInfo = it.current() )
    {
        ++it;

        if (fileInfo->isDir() && fileInfo->filePath() != path)
        {
            kdDebug() << "entering dir " << fileInfo->dirPath() << endl;
            listOfFiles(result, fileInfo->dirPath());
        }
        else
        {
            kdDebug() << "adding to result: " << fileInfo->filePath() << endl;
            result << fileInfo->filePath();
        }
    }
}

TQStringList AdaProjectPart::allFiles() const
{
//    TQStringList files;

//    listOfFiles(files, projectDirectory());

//    return files;
    return m_sourceFiles;
}

void AdaProjectPart::addFile(const TQString& /*fileName*/)
{
}

void AdaProjectPart::addFiles(const TQStringList& /*fileList*/)
{
}

void AdaProjectPart::removeFile(const TQString& /*fileName*/)
{
}

void AdaProjectPart::removeFiles(const TQStringList& /*fileList*/)
{
}

void AdaProjectPart::slotBuild()
{
    if (partController()->saveAllFiles()==false)
       return; //user cancelled

    TQString cmdline = m_compilerExec + " " + m_compilerOpts + " ";

    if (cmdline.isEmpty())
    {
        KMessageBox::sorry(0, i18n("Could not find ada compiler.\nCheck if your compiler settings are correct."));
        return;
    }

    TQFileInfo fi(mainSource());
    cmdline += fi.fileName();

    TQString dircmd = "cd ";
    dircmd += TDEProcess::quote(buildDirectory());
    dircmd += " && ";

    makeFrontend()->queueCommand(buildDirectory(), dircmd + cmdline);
}

void AdaProjectPart::slotExecute()
{
    partController()->saveAllFiles();
    TQString program = "./";
    appFrontend()->startAppCommand(buildDirectory(), mainProgram(), true);
}

void AdaProjectPart::changedFiles( const TQStringList & fileList )
{
    KDevProject::changedFiles(fileList);
}

void AdaProjectPart::changedFile( const TQString & fileName )
{
    KDevProject::changedFile(fileName);
}

void AdaProjectPart::projectConfigWidget( KDialogBase * dlg )
{
    TQVBox *vbox;
    vbox = dlg->addVBoxPage(i18n("Ada Compiler"));
    AdaProjectOptionsDlg *w = new AdaProjectOptionsDlg(this, vbox);
    connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) );
    connect( dlg, TQT_SIGNAL(okClicked()), this, TQT_SLOT(loadProjectConfig()) );
}

void AdaProjectPart::loadProjectConfig( )
{
    TQDomDocument &dom = *(projectDom());

    TQString config = DomUtil::readEntry(dom, "/kdevadaproject/general/useconfiguration", "default");
    m_mainSource = DomUtil::readEntry(dom, TQString("/kdevadaproject/configurations/") + config + TQString("/mainsource") );
    m_compilerOpts = DomUtil::readEntry(dom, TQString("/kdevadaproject/configurations/") + config + TQString("/compileroptions"));
    m_compilerExec = DomUtil::readEntry(dom, TQString("/kdevadaproject/configurations/") + config + TQString("/compilerexec"));

    if (m_compilerExec.isEmpty())
    {
        TDETrader::OfferList offers = TDETrader::self()->query("TDevelop/CompilerOptions", "[X-TDevelop-Language] == 'Ada'");
        TQValueList<KService::Ptr>::ConstIterator it;
        for (it = offers.begin(); it != offers.end(); ++it) {
            if ((*it)->property("X-TDevelop-Default").toBool()) {
                m_compilerExec = (*it)->exec();
                break;
            }
        }
    }
}

void AdaProjectPart::configWidget( KDialogBase * dlg )
{
    TQVBox *vbox;
    vbox = dlg->addVBoxPage(i18n("Ada Compiler"));
    AdaGlobalOptionsDlg *w = new AdaGlobalOptionsDlg(this, vbox);
    connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) );
}

KDevCompilerOptions *AdaProjectPart::createCompilerOptions(const TQString &name)
{
    KService::Ptr service = KService::serviceByDesktopName(name);
    if (!service) {
        kdDebug() << "AdaProjectPart::createCompilerOptions can't find service " << name;
        return 0;
    }

    KLibFactory *factory = KLibLoader::self()->factory(TQFile::encodeName(service->library()));
    if (!factory) {
        TQString errorMessage = KLibLoader::self()->lastErrorMessage();
        KMessageBox::error(0, i18n("There was an error loading the module %1.\n"
                                   "The diagnostics are:\n%2").arg(service->name()).arg(errorMessage));
        exit(1);
    }

    TQStringList args;
    TQVariant prop = service->property("X-TDevelop-Args");
    if (prop.isValid())
        args = TQStringList::split(" ", prop.toString());

    TQObject *obj = factory->create(this, service->name().latin1(),
                                   "KDevCompilerOptions", args);

    if (!obj->inherits("KDevCompilerOptions")) {
        kdDebug() << "AdaProjectPart::createCompilerOptions: component does not inherit KDevCompilerOptions" << endl;
        return 0;
    }
    KDevCompilerOptions *dlg = (KDevCompilerOptions*) obj;

    return dlg;
}

TQString AdaProjectPart::defaultOptions( const TQString compiler )
{
    TDEConfig *config = TDEGlobal::config();
    config->setGroup("Ada Compiler");
    return config->readPathEntry(compiler);
}

#include "adaproject_part.moc"


/*!
    \fn AdaProjectPart::distFiles() const
 */
TQStringList AdaProjectPart::distFiles() const
{
	TQStringList sourceList = allFiles();
	// Scan current source directory for any .pro files.
	TQString projectDir = projectDirectory();
	TQDir dir(projectDir);
	TQStringList files = dir.entryList( "Makefile");
	return sourceList + files;
}