
#include <tqfileinfo.h>
#include <tqlistview.h>
#include <tqstringlist.h>
#include <tqtimer.h>
#include <tqvbox.h>
#include <tqprogressbar.h>
#include <tqwhatsthis.h>

#include <kgenericfactory.h>
#include <tdeapplication.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <kstatusbar.h>
#include <kdialogbase.h>
#include <kiconloader.h>

#include <fstream>
#include <sstream>

#include "kdevgenericfactory.h"
#include "kdevcore.h"
#include "kdevproject.h"
#include "kdevmainwindow.h"
#include "kdevpartcontroller.h"
#include "codemodel.h"
#include "adasupportpart.h"
#include "problemreporter.h"
#include "backgroundparser.h"

#include "AdaLexer.h"
#include "AdaParser.h"
#include "AdaStoreWalker.h"
#include "AdaAST.h"

#include <kdevplugininfo.h>

enum { KDEV_DB_VERSION = 6 };
enum { KDEV_PCS_VERSION = 6 };

typedef KDevGenericFactory<AdaSupportPart> AdaSupportPartFactory;

static const KDevPluginInfo pluginData("kdevadasupport");
K_EXPORT_COMPONENT_FACTORY (libkdevadasupport, AdaSupportPartFactory (pluginData))


struct AdaSupportPartData {
    ProblemReporter* problemReporter;

    AdaSupportPartData () : problemReporter (0) {}
};

AdaSupportPart::AdaSupportPart (TQObject *parent, const char *name, const TQStringList &)
    : KDevLanguageSupport (&pluginData, parent, name ? name : "AdaSupportPart"), d (new AdaSupportPartData())
{
    setInstance (AdaSupportPartFactory::instance ());

    d->problemReporter = new ProblemReporter (this);
//    connect (core (), TQ_SIGNAL (configWidget (KDialogBase*)),
//             d->problemReporter, TQ_SLOT (configWidget (KDialogBase*)));
    d->problemReporter->setIcon( SmallIcon("application-vnd.tde.info") );
    mainWindow( )->embedOutputView( d->problemReporter, i18n("Problems"), i18n("Problem reporter"));
    TQWhatsThis::add(d->problemReporter, i18n("<b>Problem reporter</b><p>This window shows various \"problems\" in your project. "
        "It displays errors reported by a language parser."));

    setXMLFile ("adasupportpart.rc");

    connect (core (), TQ_SIGNAL (projectOpened ()), this, TQ_SLOT (projectOpened ()));
    connect (core (), TQ_SIGNAL (projectClosed ()), this, TQ_SLOT (projectClosed ()));

    connect (partController (), TQ_SIGNAL (savedFile (const KURL&)),
             this, TQ_SLOT (savedFile (const KURL&)));

//    connect (core (), TQ_SIGNAL (configWidget (KDialogBase*)), this, TQ_SLOT (configWidget (KDialogBase*)));
    connect( core(), TQ_SIGNAL(configWidget(KDialogBase*)),
             d->problemReporter, TQ_SLOT(configWidget(KDialogBase*)) );

    // a small hack (robe)
    //classStore ()->globalScope ()->setName ("(default packages)");
    //classStore ()->addScope (classStore ()->globalScope ());
    //classStore ()->globalScope ()->setName (TQString());
}


AdaSupportPart::~AdaSupportPart ()
{
    mainWindow ()->removeView (d->problemReporter);
    delete (d->problemReporter);
    d->problemReporter = 0;

    delete (d);
    d = 0;
}


KDevLanguageSupport::Features AdaSupportPart::features ()
{
    return KDevLanguageSupport::Features
        (  // TBD: Classes |
	 Functions | Namespaces);
}

void AdaSupportPart::projectOpened ()
{
    connect (project (), TQ_SIGNAL (addedFilesToProject (const TQStringList &)),
            this, TQ_SLOT (addedFilesToProject (const TQStringList &)));
    connect (project (), TQ_SIGNAL (removedFilesFromProject (const TQStringList &)),
            this, TQ_SLOT (removedFilesFromProject (const TQStringList &)));
    connect( project( ), TQ_SIGNAL( changedFilesInProject( const TQStringList & ) ),
             this, TQ_SLOT( changedFilesInProject( const TQStringList & ) ) );

    TQTimer::singleShot (0, this, TQ_SLOT (initialParse ()));
}


void AdaSupportPart::projectClosed ()
{
    saveProjectSourceInfo();
}


void AdaSupportPart::initialParse ()
{
    kdDebug () << "------------------------------------------> initialParse ()" << endl;

    if (project ())
    {
        mainWindow()->statusBar()->message( i18n("Updating...") );
        kapp->processEvents( );
        kapp->setOverrideCursor (waitCursor);

        int n = 0;
        TQStringList files = project ()->allFiles ();

        TQProgressBar* bar = new TQProgressBar( files.count( ), mainWindow( )->statusBar( ) );
        bar->setMinimumWidth( 120 );
        bar->setCenterIndicator( true );
        mainWindow( )->statusBar( )->addWidget( bar );
        bar->show( );

        for (TQStringList::Iterator it = files.begin (); it != files.end (); ++it) {
            bar->setProgress( n++ );

            TQString fn = project ()->projectDirectory () + "/" + *it;
            maybeParse (fn);
            kapp->processEvents (500);
        }

        emit updatedSourceInfo();

        mainWindow( )->statusBar( )->removeWidget( bar );
        delete bar;

        kapp->restoreOverrideCursor ();
        mainWindow( )->statusBar( )->message( i18n( "Done" ), 2000 );
/*        mainWindow ()->statusBar ()->message
            (i18n ("Found 1 problem", "Found %n problems", d->problemReporter->childCount ()));*/
    }
}

TQStringList AdaSupportPart::fileExtensions ()
{
    return TQStringList () << "ads" << "adb";
}

void AdaSupportPart::maybeParse (const TQString &fileName)
{
    kdDebug () << "AdaSupportPart::maybeParse: " << fileName << endl;

    if (!fileExtensions ().contains (TQFileInfo (fileName).extension ()))
        return;

//    mainWindow ()->statusBar ()->message (i18n ("Parsing file: %1").arg (fileName));
    parse (fileName);
}


void AdaSupportPart::addedFilesToProject (const TQStringList &fileList)
{
        TQStringList::ConstIterator it;

        for (it = fileList.begin (); it != fileList.end (); ++it)
        {
            TQString path = project ()->projectDirectory () + "/" + (*it);
            maybeParse (path);
            emit addedSourceInfo( path );
        }
}


void AdaSupportPart::removedFilesFromProject (const TQStringList &fileList)
{
        TQStringList::ConstIterator it;

        for (it = fileList.begin (); it != fileList.end (); ++it)
        {
            kdDebug () << "AdaSupportPart::removedFileFromProject () -- " << (*it) << endl;
            TQString path = project ()->projectDirectory () + "/" + (*it);

            if( codeModel()->hasFile(path) )
            {
                emit aboutToRemoveSourceInfo( path );
                codeModel()->removeFile( codeModel()->fileByName(path) );
            }
        }

//        emit updatedSourceInfo();
}


void AdaSupportPart::parse (const TQString &fileName)
{
    kdDebug () << "AdaSupportPart::parse () -- " << fileName << endl;

    std::ifstream stream (TQFile::encodeName( fileName ).data());
    TQCString _fn = fileName.utf8 ();
    std::string fn (_fn.data ());

    AdaLexer lexer (stream);
    lexer.setFilename (fn);
    lexer.setProblemReporter (d->problemReporter);

    AdaParser parser (lexer);
    parser.setFilename (fn);
    parser.setProblemReporter (d->problemReporter);

    // make an ast factory
    antlr::ASTFactory ast_factory;
    // initialize and put it in the parser...
    parser.initializeASTFactory (ast_factory);
    parser.setASTFactory (&ast_factory);
    // parser.setASTNodeType ("RefAdaAST");

    try {
        // old: parser.setASTNodeFactory (AdaAST::factory);
        lexer.resetErrors ();
        parser.resetErrors ();

        parser.compilation_unit ();
        int errors = lexer.numberOfErrors () + parser.numberOfErrors ();

        RefAdaAST ast = RefAdaAST (parser.getAST ());

        if (errors == 0 && ast != antlr::nullAST) {
            kdDebug () << "-------------------> start StoreWalker" << endl;
            AdaStoreWalker walker;
            walker.setFileName (fileName);
            walker.setCodeModel (codeModel ());
            walker.compilation_unit (ast);
        }
    } catch (antlr::ANTLRException& ex) {
        kdDebug () << "*exception*: " << ex.toString ().c_str () << endl;
        d->problemReporter->reportError (TQString::fromLatin1( ex.getMessage ().c_str() ),
                                         fileName,
                                         lexer.getLine (),
                                         lexer.getColumn ());
    }
}

void AdaSupportPart::parseContents (const TQString& contents, const TQString& fileName)
{
    kdDebug () << "AdaSupportPart::parseContents () -- " << fileName << endl;

    TQCString _fn = TQFile::encodeName (fileName);
    std::string fn (_fn.data ());

    TQCString text = contents.utf8 ();
    std::istringstream stream ((const char *)text);

    AdaLexer lexer (stream);
    lexer.setFilename (fn);
    lexer.setProblemReporter (d->problemReporter);

    AdaParser parser (lexer);
    parser.setFilename (fn);
    parser.setProblemReporter (d->problemReporter);

    try {
        lexer.resetErrors ();
        parser.resetErrors ();

        parser.compilation_unit ();
        int errors = lexer.numberOfErrors () + parser.numberOfErrors ();
        Q_UNUSED( errors );

    } catch (antlr::ANTLRException& ex) {
        kdDebug () << "*exception*: " << ex.toString ().c_str () << endl;
        d->problemReporter->reportError (TQString::fromLatin1( ex.getMessage().c_str() ),
                                         fileName,
                                         lexer.getLine (),
                                         lexer.getColumn ());
    }
}



void AdaSupportPart::savedFile (const KURL& fileName)
{
    kdDebug () << "AdaSupportPart::savedFile ()" << endl;

    if (project ()->allFiles ().contains (fileName.path().mid (project ()->projectDirectory ().length () + 1))) {
        maybeParse (fileName.path());
        emit updatedSourceInfo();
    }
}

KMimeType::List AdaSupportPart::mimeTypes( )
{
    KMimeType::List list;

    list << KMimeType::mimeType( "text/x-adasrc" );

    return list;
}

//@todo adymo: implement source info loading and saving
//hint: check javasupport for an example
//      and modify initialParse() method
void AdaSupportPart::saveProjectSourceInfo( )
{
/*    const FileList fileList = codeModel()->fileList();

    if( !project() || fileList.isEmpty() )
        return;

    TQFile f( project()->projectDirectory() + "/" + project()->projectName() + ".pcs" );
    if( !f.open( IO_WriteOnly ) )
        return;

    TQDataStream stream( &f );
    TQMap<TQString, TQ_ULONG> offsets;

    TQString pcs( "PCS" );
    stream << pcs << KDEV_PCS_VERSION;

    stream << int( fileList.size() );
    for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ){
        const FileDom dom = (*it);
#if [[[TQT_VERSION IS DEPRECATED]]] >= 0x030100
        stream << dom->name() << m_timestamp[ dom->name() ].toTime_t();
#else
        stream << dom->name() << toTime_t(m_timestamp[ dom->name() ]);
#endif
        offsets.insert( dom->name(), stream.device()->at() );
        stream << (TQ_ULONG)0; // dummy offset
    }

    for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ){
        const FileDom dom = (*it);
        int offset = stream.device()->at();

        dom->write( stream );

        int end = stream.device()->at();

        stream.device()->at( offsets[dom->name()] );
        stream << offset;
        stream.device()->at( end );
    }*/
}

void AdaSupportPart::changedFilesInProject( const TQStringList & fileList )
{
    TQStringList files = fileList;

    for ( TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
    {
        TQString path = project ()->projectDirectory () + "/" + *it ;

        maybeParse( path );
        emit addedSourceInfo( path );
    }
}


#include "adasupportpart.moc"
