/*
 * GPLv2 zecke@handhelds.org
 */

#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <qdir.h>

#include <kaction.h>
#include <klocale.h>
#include <kdebug.h>
#include <kpopupmenu.h>
#include "ktempdir.h"
#include <kprocess.h>
#include <kmessagebox.h>

#include <kurl.h>
#include <kio/netaccess.h>

#include <kdevproject.h>
#include <envvartools.h>
#include <kdevmakefrontend.h>

#include "trollprojectpart.h"
#include "trollprojectwidget.h"
#include "opiepackagingstuff.h"

/*
 * We lay out the package in a new temp directory

 */
class TempPackaging {
public:
    TempPackaging();
    ~TempPackaging();

    QString tmpDirName()const;

/*
 * copy the content from path to dest if there is content in path
 * it also creates dirs for sounds, pics,share,etc
 */
    void copyIfExists( const QString& path, const QString& dest );

/*
 * create new dir but not recursively
 */
    void addDir( const QString& );

/*
 * add a file from string
 */
    void addFile( const QString& dir, const QString& file,
                  const QString& app, int perm = -1 );

/*
 * copy existing file
 */
    void addFile( const QString& dir, const QString& file,
                  const QFileInfo& file, int perm = -1 );

private:
    DropKTempDir m_dir;
};

TempPackaging::TempPackaging() {
    m_dir.setAutoDelete( true );
}
TempPackaging::~TempPackaging() {
}
QString TempPackaging::tmpDirName()const {
    return m_dir.name();
}

void TempPackaging::addDir( const QString& str) {
    QDir *dir = m_dir.qDir();
    dir->mkdir( str );
}

void TempPackaging::addFile( const QString& dir, const QString& filename,
                             const QString& context, int perm ) {
    QFile file( m_dir.qDir()->absPath()+"/"+dir+"/"+filename );
    if(!file.open(IO_WriteOnly) )
        return;
    QCString utf = context.utf8();
    file.writeBlock( utf.data(), utf.length() );

     if ( perm != -1 )
         ::fchmod( file.handle(), perm );

    file.close();



}

void TempPackaging::addFile( const QString& dir, const QString& file,
                             const QFileInfo& fileinfo, int perm ) {
    kdDebug() << " Add File " << dir << " " << file << " " << fileinfo.absFilePath() << endl;

    /* if it is a symlink we need to keep it*/
    if ( fileinfo.isSymLink() ) {
        QString di = QDir::currentDirPath();
        QDir::setCurrent(m_dir.qDir()->absPath()+"/"+dir);
        ::symlink(QFile::encodeName(fileinfo.readLink()).data(),
                  QFile::encodeName(file).data() );
        QDir::setCurrent(di);
        return;
    }

    KURL src  = KURL::fromPathOrURL(  fileinfo.absFilePath() );
    KURL dest = KURL::fromPathOrURL(  m_dir.qDir()->absPath()+"/"+dir+"/"+file );

    //KIO::NetAccess::file_copy( src, dest, perm );
    KIO::NetAccess::copy( src, dest);
    if( perm != -1 )
    	::chmod( QFile::encodeName(m_dir.qDir()->absPath()+"/"+dir+"/"+file).data(),
		 perm );
}


void TempPackaging::copyIfExists( const QString& path,
                                  const QString& dest ) {
    QDir d( path );
    if ( !d.exists()  || d.entryList().count() <= 2 )
        return;

    if ( path.contains("/pics/")  )
        addDir( "opt/QtPalmtop/pics/" );
    else if ( path.contains( "/sounds/" ))
        addDir( "opt/QtPalmtop/sounds/" );
    else if ( path.contains("/apps/"))
        addDir( "opt/QtPalmtop/apps/" );
    else if ( path.contains( "/etc/" ) )
        addDir( "opt/QtPalmtop/etc/" );
    else if ( path.contains( "/share/" ) )
        addDir( "opt/QtPalmtop/share" );

    addDir(dest);

    QStringList lst = d.entryList(QDir::Files);
    lst.remove( "." );
    lst.remove( ".." );
    for ( QStringList::Iterator it = lst.begin(); it != lst.end();++it )
        addFile( dest, *it, QFileInfo(d, *it) );


}


/*
 *
 */


OpiePackagingStuff::OpiePackagingStuff(  TrollProjectPart* parent )
    : QObject( parent )
{
    m_part = parent;
}


/*
 * convert int to Priority string
 */
static QString prio( int i) {
    switch( i ) {
    default:
        return QString::fromLatin1("optional");
    }
}


/*
 * Add the control file
 */
void OpiePackagingStuff::addControl( TempPackaging* pack ) {
    QDomDocument *doc = m_part->projectDom();
    QString control;

    QString pri = prio( DomUtil::readIntEntry(*doc, "/kdevtrollproject/packaging/owner/prio" ) );

    control += QString("Package: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/package" ));
    control += QString("Priority: %1\n").arg(pri);
    control += QString("Section: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/section"));
    control += QString("Maintainer: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/maintainer" ) );
    control += QString("Architecture: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/arch" ));
    control += QString("Depends: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/depends" ));
    control += QString("Description: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/desc" ));
    control += QString("Version: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/version" ));
    control += QString("Installed-Size: " );

    pack->addFile( "CONTROL/", "control", control );
}


/*
 * Adds the pre/post inst/rem scripts
 */
void OpiePackagingStuff::addScripts(  TempPackaging* pack,
                                      const QString& dest,const QString& key ){
    QString str = DomUtil::readEntry(*m_part->projectDom(), "/kdevtrollproject/packaging/scripts/"+key );
    if ( str.isEmpty() )
        return;
    pack->addFile( "CONTROL", dest, str, 0555 );
}

/*
 * check if quick app or not
 * and then installs binary or libs
 * also copies the .desktop file
 */
void OpiePackagingStuff::installApp( TempPackaging* pack, const QString& app,
                                     const QString& pro  ) {
    if ( m_part->m_quickLaunch ) {
        pack->addDir( "opt/QtPalmtop/plugins" );
        pack->addDir( "opt/QtPalmtop/plugins/application" );
        QDir dir(pro,"lib"+app+".so*");
        QStringList lst = dir.entryList();
        for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it )
            pack->addFile( "opt/QtPalmtop/plugins/application", *it,
                           QFileInfo( pro+"/"+*it ), 0755 );
    }else {
        pack->addDir( "opt/QtPalmtop/bin");
        pack->addFile( "opt/QtPalmtop/bin", app,
                       QFileInfo(pro+"/"+app ), 0755 );
    }

    pack->copyIfExists( pro+"/apps/",
                        "opt/QtPalmtop/apps/"+DomUtil::readEntry(*m_part->projectDom(),
                                                                 "/kdevtrollproject/packaging/appcategory") );
}


/*
 * install the project as lib
 */
void OpiePackagingStuff::installLib( TempPackaging* pack,
                                     const QString& app, const QString& pro ) {
    QDir dir(pro,"lib"+app+".so*");
    QStringList lst = dir.entryList();
    pack->addDir( "opt/QtPalmtop/lib" );
    for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it )
        pack->addFile( "opt/QtPalmtop/lib", *it,
                       QFileInfo( pro+"/"+*it ), 0755 );
}

/*
 * install as applet to plugins/applets
 */
void OpiePackagingStuff::installApplet( TempPackaging* pack,
                                        const QString& app, const QString& pro ) {
    pack->addDir( "opt/QtPalmtop/plugins" );
    pack->addDir( "opt/QtPalmtop/plugins/applets" );

    QDir dir(pro,"lib"+app+".so*");
    QStringList lst = dir.entryList();
    for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it )
        pack->addFile( "opt/QtPalmtop/plugins/applets", *it,
                       QFileInfo( pro+"/"+*it ), 0755 );
}


/**
 * install custom library to $DEST
 */
void OpiePackagingStuff::installCustom(  TempPackaging* pack,
                                         const QString& app, const QString& pro,
                                         const QString& dest ) {
    QStringList sp = QStringList::split( "/", dest );
    QString str =  "opt/QtPalmtop";
    for ( QStringList::Iterator it = sp.begin(); it != sp.end(); ++it ) {
        str += "/"+*it;
        pack->addDir( str );
    }

    QDir dir(pro,"lib"+app+".so*");
    QStringList lst = dir.entryList();
    for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it )
        pack->addFile( "opt/QtPalmtop/"+dest,  *it,
                       QFileInfo( pro+"/"+*it ), 0755 );
}

void OpiePackagingStuff::slotBuildPackage() {
    QString proDir( m_part->projectDirectory() );
    QString name  ( m_part->m_widget->m_rootSubproject->configuration.m_target);
    TempPackaging pack;
    pack.addDir( "CONTROL" );
    pack.addDir( "opt" );
    pack.addDir( "opt/QtPalmtop" );
    pack.copyIfExists( proDir+"/pics/"+name, "opt/QtPalmtop/pics/"+name );
    pack.copyIfExists( proDir+"/sounds/"+name, "opt/QtPalmtop/sounds/"+name );
    pack.copyIfExists( proDir+"/etc/", "opt/QtPalmtop/etc" );
    pack.copyIfExists( proDir+"/etc/"+name, "opt/QtPalmtop/etc/"+name );
    pack.copyIfExists( proDir+"/share", "opt/QtPalmtop/share" );
    pack.copyIfExists( proDir+"/share/"+name, "opt/QtPalmtop/share/"+name);

    addControl( &pack );
    addScripts( &pack, "preinst", "pre" );
    addScripts( &pack, "postinst", "post" );
    addScripts( &pack, "prerm", "pre_rem" );
    addScripts( &pack, "postrm", "post_rem" );

    int project = DomUtil::readIntEntry(*m_part->projectDom(), "/kdevtrollproject/packaging/template");

    switch( project ) {
    default:
    case 0:
        installApp( &pack, name, proDir );
        break;
    case 1:
        installLib( &pack, name, proDir );
        break;
    case 2:
        installApplet( &pack, name, proDir );
        break;
    case 3:
        installCustom( &pack, name, proDir,
                       DomUtil::readEntry(*m_part->projectDom(), "/kdevtrollproject/packaging/custom_dest" ) );
        break;
    }

    /* now generate the size */
    QString shell = QString("mkfs.jffs2 -r %1 | wc -c >> %2").arg(KProcess::quote(pack.tmpDirName() ))
                                                             .arg(KProcess::quote(pack.tmpDirName()+"/CONTROL/control" ));
    KProcess _proc;
    _proc.setUseShell(true);
    _proc << shell;
    _proc.start( KProcess::Block );

    /* run ipk-create       */
    /* I'm scared to reinitialize the process */
    m_outp = QString::null;
    KProcess proc;
    connect(&proc, SIGNAL(receivedStdout(KProcess *, char *,int)),
            this, SLOT( slotOutPut(KProcess*, char*, int)));
    connect(&proc, SIGNAL(receivedStderr(KProcess *, char *,int)),
            this, SLOT( slotOutPut(KProcess*, char*, int)));
    proc << "ipkg-build";
    proc << "-oroot " << "-groot " << pack.tmpDirName();
//    proc.setUseShell(true);
    proc.setWorkingDirectory( m_part->projectDirectory() );
    if ( !proc.start( KProcess::Block,KProcess::AllOutput ) )
        KMessageBox::error(0, i18n( "Calling ipkg-build failed. Make sure it is available." ) );


    KMessageBox::information( 0, m_outp, i18n( "Output of ipkg-build" ) );

    /* unlink the dir       */

}

/*
 * package all languages into one package
 * For future versions we will fix that
 */
void OpiePackagingStuff::slot18nPackage() {
    QString proDir = m_part->projectDirectory();
    QString name  =  m_part->m_widget->m_rootSubproject->configuration.m_target;
    QString prefix = m_part->m_widget->m_rootSubproject->configuration.m_template == QTMP_LIBRARY ?
                     "lib" : QString::null;
    QString lang_base = basePath();

    QDir d(lang_base, QString::null, QDir::Name | QDir::IgnoreCase,
           QDir::Dirs );
    QStringList lst = d.entryList();
    lst.remove(".");
    lst.remove("..");

    KProcess __proc;
    __proc.setWorkingDirectory( proDir );
    __proc.setEnvironment( "OPIE_CREATE_LANGS", lst.join(":") );
    __proc.setEnvironment( "OPIE_PROJECT_LANG_DIR", proDir );

    __proc << "opie-lrelease";
    __proc << proFile(m_part->projectDirectory() );

    if ( !__proc.start( KProcess::Block ) ) {
        KMessageBox::error( 0, i18n( "Could not start opie-rlease. Make sure it exists" ) );
        return;
    }

    TempPackaging pack;
    pack.addDir( "CONTROL" );
    pack.addDir( "opt" );
    pack.addDir( "opt/QtPalmtop" );
    pack.addDir( "opt/QtPalmtop/i18n" );

    for (QStringList::Iterator it= lst.begin(); it != lst.end(); ++it) {
        QString file  = proDir+"/i18n/"+*it+"/"+prefix+name+".qm";
        kdDebug() << "Language file is " << file << endl;
        if ( QFile::exists(file) ) {
            pack.addDir( "opt/QtPalmtop/i18n/"+*it);
            pack.addFile( "opt/QtPalmtop/i18n/"+*it, prefix+name+".qm",
                          QFileInfo(file ) );

        }

    }

/*
 * generate a control file
 */
    QDomDocument *doc = m_part->projectDom();
    QString control;
    control += QString("Package: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/package" )+"lang" );
    control += QString("Priority: optional\n");
    control += QString("Section: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/section"));
    control += QString("Maintainer: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/maintainer" ) );
    control += QString("Architecture: all\n");
    control += QString("Depends: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/package" ));
    control += QString("Description: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/desc" ));
    control += QString("Version: %1\n").arg(DomUtil::readEntry(*doc, "/kdevtrollproject/packaging/owner/version" ));
    control += QString("Installed-Size: " );

    pack.addFile( "CONTROL/", "control", control );
    /* now generate the size */
    QString shell = QString("mkfs.jffs2 -r %1 | wc -c >> %2").arg(KProcess::quote(pack.tmpDirName() ))
                                                             .arg(KProcess::quote(pack.tmpDirName()+"/CONTROL/control" ));
    KProcess _proc;
    _proc.setUseShell(true);
    _proc << shell;
    _proc.start( KProcess::Block );


    /* run ipk-create       */
    /* I'm scared to reinitialize the process */
    m_outp = QString::null;

    KProcess proc;
    connect(&proc, SIGNAL(receivedStdout(KProcess *, char *,int)),
            this, SLOT( slotOutPut(KProcess*, char*, int)));
    connect(&proc, SIGNAL(receivedStderr(KProcess *, char *,int)),
            this, SLOT( slotOutPut(KProcess*, char*, int)));
    proc << "ipkg-build";
    proc << "-oroot " << "-groot " << pack.tmpDirName();
//    proc.setUseShell(true);
    proc.setWorkingDirectory( m_part->projectDirectory() );
    if ( !proc.start( KProcess::Block,KProcess::AllOutput ) )
        KMessageBox::error(0, i18n( "Calling ipkg-build failed. Make sure it is available." ) );


    KMessageBox::information( 0, m_outp, i18n( "Output of ipkg-build" ) );

    /* unlink the dir       */
}


/*
 * Add the supported languages to the KActionMenu
 */
void OpiePackagingStuff::slotFillLanguage() {
    KActionMenu* men = m_part->m_actMenu;

    KAction* act = new KAction(i18n("All Available Languages"), KShortcut(),
                               this, SLOT(slotGenerateAll()),
                               m_part->actionCollection(), "add_all_ts_files" );
    men->insert( act );
    men->popupMenu()->insertSeparator();

    QStringList lst = QStringList::split(':', QString(::getenv("OPIE_LANGUAGES")));
    for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
        act = new KAction( *it, KShortcut(), this, SLOT( slotAddLanguage()),
                           m_part->actionCollection(), "add_one_ts_file" );
        men->insert( act );
    }
    connect(men->popupMenu(),SIGNAL(activated(int)),
            this,SLOT(slotAddLanguage(int)));
}

/*
 * find the language subdirs
 * call opie-lupdate
 */
void OpiePackagingStuff::slotUpdateAndRelease() {
    QString lang_base = basePath();

    QDir d(lang_base, QString::null, QDir::Name | QDir::IgnoreCase,
           QDir::Dirs );
    QStringList lst = d.entryList();
    lst.remove(".");
    lst.remove("..");
    generateLang(lst.join(":") );

}

void OpiePackagingStuff::slotGenerateAll() {
    QStringList lst = QStringList::split(':', QString(::getenv("OPIE_LANGUAGES")));
    mkdirs( lst );
    generateLang(lst.join(":"));
}

void OpiePackagingStuff::slotAddLanguage(int id) {

    QPopupMenu* pop =  m_part->m_actMenu->popupMenu();
    QString text = pop->text(id);
    kdDebug() << "The Text is " << text << endl;
    if(text == i18n("All Available Languages"))
        return;

    mkdirs( text );
    generateLang(text);

}

void OpiePackagingStuff::generateLang( const QString& lang  ){
    QString lang_base = basePath() + lang;
      kdDebug() << "Path is " << lang_base << endl;


      QString pro = " opie-lupdate ";
      pro += proFile(m_part->projectDirectory());

      QString dircmd = "cd ";
      dircmd += m_part->projectDirectory();
      dircmd += " && ";

      pro.prepend( " OPIE_CREATE_LANGS="+EnvVarTools::quote(lang) +
                   " OPIE_PROJECT_LANG_DIR="+ EnvVarTools::quote(m_part->projectDirectory()));
      m_part->makeFrontend()->queueCommand(basePath(),
                                           dircmd + pro );

}

QString OpiePackagingStuff::basePath()const {
     QString pro_dir = m_part->projectDirectory();
     pro_dir += "/i18n/";

     kdDebug() << "Base path is " << pro_dir << endl;

     QDir dir(pro_dir);
     if(!dir.exists()){
          kdDebug() << "Did not exist " << endl;
          dir.mkdir(pro_dir);
     }

     return pro_dir;
}



QString OpiePackagingStuff::proFile(const QString& dir){
       /* note. normally I won't write such code and duplicate it
       */
      QFileInfo fi(dir);
      QDir d(dir);
      QStringList l = d.entryList("*.pro");

      return l.count()?l[0]:(fi.baseName() + ".pro");
}


void OpiePackagingStuff::mkdirs( const QStringList& lst ) {
    QString base = basePath();
    QString lang;
    for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
        lang = base + *it;
        QDir dir(lang);
        if(!dir.exists())
            dir.mkdir(lang);
    }
}

void OpiePackagingStuff::slotOutPut( KProcess*, char* buffer, int len ) {
    m_outp += QString::fromLocal8Bit(buffer, len );
    kdDebug() << "Foo bar " << m_outp << endl;
}

#include "opiepackagingstuff.moc"
