/***************************************************************************
                        loader.cpp  -  loader application for Amarok
                           -------------------
  begin                : 2004/02/19
  copyright            : (C) 2004 by Mark Kretschmann
  email                : markey@web.de
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 <cstdlib>
#include <iostream>
#include "loader.h"
#include <tqfile.h>
#include <tqmessagebox.h>
#include <tqprocess.h>
#include <tqstring.h>
#include <tdeinstance.h>
#include <tdeglobal.h>
#include <kstandarddirs.h>
#include <ksplashscreen.h>

extern "C"
{
    #include <unistd.h> //usleep
}


int
main( int argc, char *argv[] )
{
    //NOTE this list doesn't include argv[0] ("amarok")
    TQStringList args;
    for( int i = 1; i < argc; i++ ) {
        args += TQString::fromLocal8Bit(argv[i]);
    }

    const bool isRunning = amarokIsRunning();

    // first check the arguments, we don't need a splashscreen
    // for arguments like --help, --version, etc.

    if( !args.isEmpty() )
    {
        // These arguments cannot be passed to Amarok, or Amarok will exit
        // after processing them.
        TQStringList longs; longs
                << "-help" << "-help-qt" << "-help-kde" << "-help-all" << "-author" << "-version" << "-license" << "-v";

        // both --arg and -arg are valid
        {
            TQStringList longlongs;
            foreach( longs ) {
                longlongs += TQChar('-') + *it;
            }

            longs += longlongs;
        }

        foreach( args ) {
            const TQString arg = *it;
            foreach( longs )
                if( arg == *it ) {
                    // this argument cannot be passed to the running amarokapp
                    // or TDECmdLineArgs would exit the application

                    TQProcess proc( TQString("amarokapp") );
                    proc.setCommunication( 0 ); //show everything
                    proc.addArgument( arg );
                    proc.start();

                    while( proc.isRunning() )
                        ::usleep( 100 );

                    return 0; //exit success!
                }
        }

        // these arguments are deemed safe for dcop, but if
        // there is no amarokapp running, we'll start a new
        // instance and the above checks were not necessary
    }

    if ( isRunning ) {
        TQStringList dcop_args;
        dcop_args << "dcop" << "amarok" << "player" << "transferCliArgs" << "[";

        // We transmit our DESKTOP_STARTUP_ID, so amarokapp can stop the startup animation
        dcop_args += std::getenv( "DESKTOP_STARTUP_ID" ); //will be interptreted as latin1

        // relative URLs should be interpreted correctly by amarokapp
        // so we need to pass the current working directory
        dcop_args << "--cwd" << TQDir::currentDirPath();

        dcop_args += args;
        dcop_args += "]";

        TQProcess proc( dcop_args );
        if ( !proc.start() ) {
            printf("[ERROR] Unable to launch DCOP for communication with running Amarok instance!\n\r");
        }
        while( proc.isRunning() ) {
            ::usleep( 100 );
        }
        if ( !proc.normalExit() || proc.exitStatus() ) {
            printf("[ERROR] DCOP communication failure with running Amarok instance!\n\r");
            while ( proc.canReadLineStdout() ) {
                printf("%s\n\r", proc.readLineStdout().ascii());
            }
            while ( proc.canReadLineStderr() ) {
                printf("%s\n\r", proc.readLineStderr().ascii());
            }
        }

        return 0;
    }
    else {
        // no amarokapp is running, start one, show
        // a splashscreen and pass it the arguments

        return Loader( args ).exec();
    }
}

bool
amarokIsRunning()
{
    TQProcess proc( TQString( "dcop" ) );
    proc.start();
    while( proc.isRunning() )
        ::usleep( 100 );

    while( proc.canReadLineStdout() ) {
        if ( proc.readLineStdout() == "amarok" ) {
            return true;
        }
    }

    return false;
}



static int _argc = 0;

Loader::Loader( TQStringList args )
        : TQApplication( _argc, 0 )
        , m_counter( 0 )
        , m_splash( 0 )
{
    // we transmit the startup_id, so amarokapp can stop the startup animation
    //FIXME TQCString str( ::getenv( "DESKTOP_STARTUP_ID" ) );

     if( !TQApplication::isSessionRestored())
     {
        TDEInstance instance("amarok"); // TDEGlobal::dirs() crashes without
        if( isSplashEnabled() )
        {
            m_splash = new KSplashScreen( TQPixmap( TDEStandardDirs().findResource("data", "amarok/images/splash_screen.jpg")));
            m_splash->show();
        }
     }

    args.prepend( "amarokapp" );

    m_proc = new TQProcess( args, this );
    m_proc->setCommunication( TQProcess::Stdout );

    std::cout << "Amarok: [Loader] Starting amarokapp..\n";
    std::cout << "Amarok: [Loader] Don't run gdb, valgrind, etc. against this binary! Use amarokapp.\n";

    if( !m_proc->start() )
    {
        delete m_splash; // hide the splash

        TQMessageBox::critical( 0, "Amarok",
                "Amarok could not be started!\n" //FIXME this needs to be translated
                    "This may be because the amarokapp binary is not in your PATH.\n"
                    "Try locating and running amarokapp from a terminal.",
                TQMessageBox::Ok, 0 );

        std::exit( 1 ); //event-loop is not yet being processed
    }

    startTimer( INTERVAL );
}

Loader::~Loader()
{
    // must be deleted before TQApplication closes our Xserver connection
    // thus we cannot make it a child of the TQApplication and must
    // delete it manually
    delete m_splash;
}

void
Loader::timerEvent( TQTimerEvent* )
{
    if( m_proc->isRunning() )
    {
         if( ++m_counter == (30000 / INTERVAL) ) {
             // 30 seconds have passed
             std::cerr << "Amarok: [Loader] Amarok is taking a long time to load! Perhaps something has gone wrong?\n";
         }

        while( m_proc->canReadLineStdout() ) {
            if( m_proc->readLineStdout() == "STARTUP" ) {
                TQApplication::exit( 0 );
            }
        }
    }
    else if( !m_proc->normalExit() ) {
        // no reason to show messagebox, as amarokapp should start drkonqi
        std::cerr << "Amarok: [Loader] amarokapp probably crashed!\n";

        TQApplication::exit( 3 );
    }
    else {
        // if we get here, then either we didn't receive STARTUP through
        // the pipe, or amarokapp exited normally before the STARTUP was
        // written to stdout (possibly possible)
        TQApplication::exit( 0 );
    }
}

bool
isSplashEnabled()
{
    //determine whether splash-screen is enabled in amarokrc
    (void)TDEGlobal::config(); // the kubuntu special directory is not present without this
    TQStringList dirs = TDEGlobal::dirs()->findAllResources( "config", "amarokrc" );

    for( TQStringList::iterator path = dirs.begin();
            path != dirs.end();
            ++path )
    {
        TQFile file( *path );
        if ( file.open( IO_ReadOnly ) )
        {
            TQString line;
            while( file.readLine( line, 2000 ) != -1 )
                if ( line.contains( "Show Splashscreen" ) )
                {
                    if( line.contains( "false" ) )
                        return false;
                    else
                        return true;
                }
        }
    }

    //if we fail to open anything, just show the splash
    return true;
}
