/* ---------------------------------------------------------------------- */
/*                                                                        */
/* [main.cpp]                      Konsole                                */
/*                                                                        */
/* ---------------------------------------------------------------------- */
/*                                                                        */
/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>        */
/*                                                                        */
/* This file is part of Konsole, an X terminal.                           */
/*                                                                        */
/* The material contained in here more or less directly orginates from    */
/* kvt, which is copyright (c) 1996 by Matthias Ettrich <ettrich@kde.org> */
/*                                                                        */
/* ---------------------------------------------------------------------- */

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

#include <qdir.h>
#include <qsessionmanager.h>
#include <qwidgetlist.h>

#include <dcopclient.h>

#include <klocale.h>
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <kimageio.h>
#include <kdebug.h>
#include <kstandarddirs.h>

#include <config.h>

#include "konsole.h"

// COMPOSITE disabled by default because the QApplication constructor
// needed to enable the ARGB32 visual has undesired side effects.
#if 0
#if defined(Q_WS_X11) && defined(HAVE_XRENDER) && QT_VERSION >= 0x030300
# define COMPOSITE
#endif
#endif

#ifdef COMPOSITE
# include <X11/Xlib.h>
# include <X11/extensions/Xrender.h>
# include <fixx11h.h>
#endif

static const char description[] =
  I18N_NOOP("X terminal for use with KDE.");

//   { "T <title>",       0, 0 },
static KCmdLineOptions options[] =
{
   { "name <name>",     I18N_NOOP("Set window class"), 0 },
   { "ls",              I18N_NOOP("Start login shell"), 0 },
   { "T <title>",       I18N_NOOP("Set the window title"), 0 },
   { "tn <terminal>",   I18N_NOOP("Specify terminal type as set in the TERM\nenvironment variable"), "xterm" },
   { "xwin",            I18N_NOOP("ignored"), 0 },
   { "noclose",         I18N_NOOP("Do not close Konsole when command exits"), 0 },
   { "nohist",          I18N_NOOP("Do not save lines in history"), 0 },
   { "nomenubar",       I18N_NOOP("Do not display menubar"), 0 },
   { "notabbar",        0, 0 },
   { "notoolbar",       I18N_NOOP("Do not display tab bar"), 0 },
   { "noframe",         I18N_NOOP("Do not display frame"), 0 },
   { "noscrollbar",     I18N_NOOP("Do not display scrollbar"), 0 },
   { "noxft",           I18N_NOOP("Do not use Xft (Anti-Aliasing)"), 0 },
#ifdef COMPOSITE
   { "noargb",          I18N_NOOP("Do not use the ARGB32 visual (Transparency)"), 0 },
#endif
   { "vt_sz CCxLL",     I18N_NOOP("Terminal size in columns x lines"), 0 },
   { "noresize",        I18N_NOOP("Terminal size is fixed"), 0 },
   { "type <type>",     I18N_NOOP("Open the given session type instead of the default shell"), 0 },
   { "types",           I18N_NOOP("List available session types"), 0 },
   { "keytab <name>",   I18N_NOOP("Use given .keytab file"), 0 },
   { "profile <name>",  I18N_NOOP("Start with given sessions profile"), 0 },
   { "profiles",        I18N_NOOP("List available profiles"), 0 },
   { "schema <name>",   I18N_NOOP("Use given .schema file"), 0 },
   { "schemas",         I18N_NOOP("List available schemas"), 0 },
   { "script",          I18N_NOOP("Enable extended DCOP functions"), 0 },
   { "workdir <dir>",   I18N_NOOP("Change working directory of the konsole to 'dir'"), 0 },
   { "!e <command>",    I18N_NOOP("Execute 'command' instead of shell"), 0 },
   // WABA: All options after -e are treated as arguments.
   { "+[args]",         I18N_NOOP("Arguments for 'command'"), 0 },
   KCmdLineLastOption
};

static bool has_noxft = false;
static bool login_shell = false;
static bool full_script = false;
static bool auto_close = true;
static bool fixed_size = false;

bool argb_visual = false;

const char *konsole_shell(QStrList &args)
{
  const char* shell = getenv("SHELL");
  if (shell == NULL || *shell == '\0') shell = "/bin/sh";
  if (login_shell)
  {
    char* t = (char*)strrchr(shell,'/');
    if (t) // see sh(1)
    {
      t = strdup(t);
      *t = '-';
      args.append(t);
      free(t);
    }
    else
      args.append(shell);
  }
  else
    args.append(shell);
  return shell;
}

/**
   The goal of this class is to prevent GUI confirmation
   on the exit when the class is called from the session
   manager.
   It must be here, because this has to be called before
   the KMainWindow's manager.

   It is also used to add "--noxft" and "--ls" to the restoreCommand
   if konsole was started with any of those options.
 */
class KonsoleSessionManaged: public KSessionManaged {
public:
    bool commitData( QSessionManager&) {
        konsole->skip_exit_query = true;
        return true;
    };

    bool saveState( QSessionManager&sm) {
        QStringList restartCommand = sm.restartCommand();
        if (has_noxft)
            restartCommand.append("--noxft");
        if (login_shell)
            restartCommand.append("--ls");
        if (full_script)
            restartCommand.append("--script");
        if (!auto_close)
            restartCommand.append("--noclose");
        if (fixed_size)
            restartCommand.append("--noresize");
        sm.setRestartCommand(restartCommand);
        return true;
    }
    Konsole *konsole;
};



/* --| main |------------------------------------------------------ */
extern "C" int kdemain(int argc, char* argv[])
{
  setgid(getgid()); setuid(getuid()); // drop privileges

  // deal with shell/command ////////////////////////////
  bool histon = true;
  bool menubaron = true;
  bool tabbaron = true;
  bool frameon = true;
  bool scrollbaron = true;
  bool showtip = true;
  QCString wname = PACKAGE;


  KAboutData aboutData( PACKAGE, I18N_NOOP("Konsole"),
    VERSION, description, KAboutData::License_GPL_V2,
    "(c) 1997-2004, Lars Doelle");
  aboutData.addAuthor("Waldo Bastian",I18N_NOOP("Maintainer"), "bastian@kde.org");
  aboutData.addAuthor("Lars Doelle",I18N_NOOP("Author"), "lars.doelle@on-line.de");
  aboutData.addCredit("Stephan Binner",
    I18N_NOOP("bug fixing and improvements"),
    "binner@kde.org");
  aboutData.addCredit("Chris Machemer",
    I18N_NOOP("bug fixing"),
    "machey@ceinetworks.com");
  aboutData.addCredit("Stephan Kulow",
    I18N_NOOP("Solaris support and work on history"),
    "coolo@kde.org");
  aboutData.addCredit("Alexander Neundorf",
    I18N_NOOP("faster startup, bug fixing"),
    "neundorf@kde.org");
  aboutData.addCredit("Peter Silva",
    I18N_NOOP("decent marking"),
    "peter.silva@videotron.ca");
  aboutData.addCredit("Lotzi Boloni",
    I18N_NOOP("partification\n"
    "Toolbar and session names"),
    "boloni@cs.purdue.edu");
  aboutData.addCredit("David Faure",
    I18N_NOOP("partification\n"
    "overall improvements"),
    "David.Faure@insa-lyon.fr");
  aboutData.addCredit("Antonio Larrosa",
    I18N_NOOP("transparency"),
    "larrosa@kde.org");
  aboutData.addCredit("Matthias Ettrich",
    I18N_NOOP("most of main.C donated via kvt\n"
    "overall improvements"),
    "ettrich@kde.org");
  aboutData.addCredit("Warwick Allison",
    I18N_NOOP("schema and selection improvements"),
    "warwick@troll.no");
  aboutData.addCredit("Dan Pilone",
    I18N_NOOP("SGI Port"),
    "pilone@slac.com");
  aboutData.addCredit("Kevin Street",
    I18N_NOOP("FreeBSD port"),
    "street@iname.com");
  aboutData.addCredit("Sven Fischer",
    I18N_NOOP("bug fixing"),
    "herpes@kawo2.rwth-aachen.de");
  aboutData.addCredit("Dale M. Flaven",
    I18N_NOOP("bug fixing"),
    "dflaven@netport.com");
  aboutData.addCredit("Martin Jones",
    I18N_NOOP("bug fixing"),
    "mjones@powerup.com.au");
  aboutData.addCredit("Lars Knoll",
    I18N_NOOP("bug fixing"),
    "knoll@mpi-hd.mpg.de");
  aboutData.addCredit("",I18N_NOOP("Thanks to many others.\n"
    "The above list only reflects the contributors\n"
    "I managed to keep track of."));

  KCmdLineArgs::init( argc, argv, &aboutData );
  KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
  //1.53 sec
  KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
  KCmdLineArgs *qtargs = KCmdLineArgs::parsedArgs("qt");
  has_noxft = !args->isSet("xft");
  TEWidget::setAntialias( !has_noxft );
  TEWidget::setStandalone( true );

#ifdef COMPOSITE
  char *display = 0;
  if ( qtargs->isSet("display"))
    display = qtargs->getOption( "display" ).data();

  Display *dpy = XOpenDisplay( display );
  if ( !dpy ) {
    kdError() << "cannot connect to X server " << display << endl;
    exit( 1 );
  }

  int screen = DefaultScreen( dpy );
  Colormap colormap = 0;
  Visual *visual = 0;
  int event_base, error_base;

  if ( args->isSet("argb") && XRenderQueryExtension( dpy, &event_base, &error_base ) )
  {
    int nvi;
    XVisualInfo templ;
    templ.screen  = screen;
    templ.depth   = 32;
    templ.c_class = TrueColor;
    XVisualInfo *xvi = XGetVisualInfo( dpy, VisualScreenMask | VisualDepthMask
		  | VisualClassMask, &templ, &nvi );

    for ( int i = 0; i < nvi; i++ ) {
      XRenderPictFormat *format = XRenderFindVisualFormat( dpy, xvi[i].visual );
      if ( format->type == PictTypeDirect && format->direct.alphaMask ) {
        visual = xvi[i].visual;
        colormap = XCreateColormap( dpy, RootWindow( dpy, screen ), visual, AllocNone );
        argb_visual = true;
        break;
      }
    }
  }

  KApplication a( dpy, Qt::HANDLE( visual ), Qt::HANDLE( colormap ) );
#else
  KApplication a;
#endif

  KImageIO::registerFormats(); // add io for additional image formats
  //2.1 secs

  QString title;
  if(args->isSet("T")) {
    title = QFile::decodeName(args->getOption("T"));
  }
  if(qtargs->isSet("title")) {
    title = QFile::decodeName(qtargs->getOption("title"));
  }

  QString term = "";
  if(args->isSet("tn")) {
    term=QString::fromLatin1(args->getOption("tn"));
  }
  login_shell = args->isSet("ls");

  QStrList eargs;

  const char* shell = 0;
  if (!args->getOption("e").isEmpty())
  {
     if (args->isSet("ls"))
        KCmdLineArgs::usage(i18n("You can't use BOTH -ls and -e.\n"));
     shell = strdup(args->getOption("e"));
     eargs.append(shell);
     for(int i=0; i < args->count(); i++)
       eargs.append( args->arg(i) );

     if (title.isEmpty() &&
         (kapp->caption() == kapp->aboutData()->programName()))
     {
        title = QFile::decodeName(shell);  // program executed in the title bar
     }
     showtip = false;
  }

  QCString sz = "";
  sz = args->getOption("vt_sz");
  histon = args->isSet("hist");
  menubaron = args->isSet("menubar");
  tabbaron = args->isSet("tabbar") && args->isSet("toolbar");
  frameon = args->isSet("frame");
  scrollbaron = args->isSet("scrollbar");
  wname = qtargs->getOption("name");
  full_script = args->isSet("script");
  auto_close = args->isSet("close");
  fixed_size = !args->isSet("resize");

  if (!full_script)
	a.dcopClient()->setQtBridgeEnabled(false);

  QCString type = "";

  if(args->isSet("type")) {
    type = args->getOption("type");
  }
  if(args->isSet("types")) {
    QStringList types = KGlobal::dirs()->findAllResources("appdata", "*.desktop", false, true);
    types.sort();
    for(QStringList::ConstIterator it = types.begin();
        it != types.end(); ++it)
    {
       QString file = *it;
       file = file.mid(file.findRev('/')+1);
       if (file.endsWith(".desktop"))
          file = file.left(file.length()-8);
       printf("%s\n", QFile::encodeName(file).data());
    }
    return 0;
  }
  if(args->isSet("schemas")) {
    ColorSchemaList colors;
    colors.checkSchemas();
    for(int i = 0; i < (int) colors.count(); i++)
    {
       ColorSchema *schema = colors.find(i);
       QString relPath = schema->relPath();
       if (!relPath.isEmpty())
          printf("%s\n", QFile::encodeName(relPath).data());
    }
    return 0;
  }

  QString workDir = QFile::decodeName( args->getOption("workdir") );

  QString keytab = "";
  if (args->isSet("keytab"))
    keytab = args->getOption("keytab");

  QString schema = "";
  if (args->isSet("schema"))
    schema = args->getOption("schema");

  KConfig * sessionconfig = 0;
  QString profile = "";
  if (args->isSet("profile")) {
    profile = args->getOption("profile");
    QString path = locate( "data", "konsole/profiles/" + profile );
    if ( QFile::exists( path ) )
      sessionconfig=new KConfig( path, true );
    else
      profile = "";
  }
  if (args->isSet("profiles"))
  {
     QStringList profiles = KGlobal::dirs()->findAllResources("data", "konsole/profiles/*", false, true);
     profiles.sort();
     for(QStringList::ConstIterator it = profiles.begin();
         it != profiles.end(); ++it)
     {
        QString file = *it;
        file = file.mid(file.findRev('/')+1);
        printf("%s\n", QFile::encodeName(file).data());
     }
     return 0;
  }


  //FIXME: more: font

  args->clear();

  int c = 0, l = 0;
  if ( !sz.isEmpty() )
  {
    char *ls = (char*)strchr( sz.data(), 'x' );
    if ( ls != NULL )
    {
       *ls='\0';
       ls++;
       c=atoi(sz.data());
       l=atoi(ls);
    }
    else
    {
       KCmdLineArgs::usage(i18n("expected --vt_sz <#columns>x<#lines> ie. 80x40\n"));
    }
  }

  if (!kapp->authorizeKAction("size"))
    fixed_size = true;

  // ///////////////////////////////////////////////

  // Ignore SIGHUP so that we don't get killed when
  // our parent-shell gets closed.
  signal(SIGHUP, SIG_IGN);

  putenv((char*)"COLORTERM="); // to trigger mc's color detection
  KonsoleSessionManaged *ksm = new KonsoleSessionManaged();

  if (a.isRestored() || !profile.isEmpty())
  {
    if (!shell)
       shell = konsole_shell(eargs);

    if (profile.isEmpty())
      sessionconfig = a.sessionConfig();
    sessionconfig->setDesktopGroup();
    wname = sessionconfig->readEntry("class",wname).latin1();
//    RESTORE( Konsole(wname,shell,eargs,histon,tabbaron) )
    int n = 1;

    int session_count = sessionconfig->readNumEntry("numSes");
    int counter = 0;
    QString key;
    QString sTitle;
    QString sPgm;
    QString sTerm;
    QString sIcon;
    QString sCwd;
    int     n_tabbar;

    while (KMainWindow::canBeRestored(n) || !profile.isEmpty())
    {
	sessionconfig->setDesktopGroup();
        sPgm = sessionconfig->readEntry("Pgm0", shell);
        sessionconfig->readListEntry("Args0", eargs);
        sTitle = sessionconfig->readEntry("Title0", title);
        sTerm = sessionconfig->readEntry("Term0");
        sIcon = sessionconfig->readEntry("Icon0","openterm");
        sCwd = sessionconfig->readPathEntry("Cwd0");
	n_tabbar = QMIN(sessionconfig->readUnsignedNumEntry("tabbar",Konsole::TabBottom),2);
        Konsole *m = new Konsole(wname,sPgm,eargs,histon,menubaron,tabbaron,frameon,scrollbaron,sIcon,sTitle,0/*type*/,sTerm,true,n_tabbar,sCwd);
        m->enableFullScripting(full_script);
        m->enableFixedSize(fixed_size);
	m->restore(n);
        m->makeGUI();
        m->setSchema(sessionconfig->readEntry("Schema0"));
        m->initSessionFont(sessionconfig->readNumEntry("Font0", -1));
        m->initSessionKeyTab(sessionconfig->readEntry("KeyTab0"));
        m->initMonitorActivity(sessionconfig->readBoolEntry("MonitorActivity0",false));
        m->initMonitorSilence(sessionconfig->readBoolEntry("MonitorSilence0",false));
        m->initMasterMode(sessionconfig->readBoolEntry("MasterMode0",false));
        counter++;

        while (counter < session_count)
        {
          sessionconfig->setDesktopGroup();
          key = QString("Title%1").arg(counter);
          sTitle = sessionconfig->readEntry(key, title);
          key = QString("Args%1").arg(counter);
          sessionconfig->readListEntry(key, eargs);
          key = QString("Pgm%1").arg(counter);
          sPgm = sessionconfig->readEntry(key, shell);
          key = QString("Term%1").arg(counter);
          sTerm = sessionconfig->readEntry(key);
          key = QString("Icon%1").arg(counter);
          sIcon = sessionconfig->readEntry(key,"openterm");
          key = QString("Cwd%1").arg(counter);
          sCwd = sessionconfig->readPathEntry(key);
          m->newSession(sPgm, eargs, sTerm, sIcon, sCwd);
          m->initSessionTitle(sTitle);
          key = QString("Schema%1").arg(counter);
          m->setSchema(sessionconfig->readEntry(key));
          key = QString("Font%1").arg(counter);
          m->initSessionFont(sessionconfig->readNumEntry(key, -1));
          key = QString("KeyTab%1").arg(counter);
          m->initSessionKeyTab(sessionconfig->readEntry(key));
          key = QString("MonitorActivity%1").arg(counter);
          m->initMonitorActivity(sessionconfig->readBoolEntry(key,false));
          key = QString("MonitorSilence%1").arg(counter);
          m->initMonitorSilence(sessionconfig->readBoolEntry(key,false));
          key = QString("MasterMode%1").arg(counter);
          m->initMasterMode(sessionconfig->readBoolEntry(key,false));
                  counter++;
        }
        m->setDefaultSession( sessionconfig->readEntry("DefaultSession","shell.desktop") );

        ksm->konsole = m;
        ksm->konsole->initFullScreen();
        if ( !profile.isEmpty() ) {
          ksm->konsole->callReadPropertiesInternal(sessionconfig,1);
          profile = "";
          // Hack to work-around sessions initialized with minimum size
          for (int i=0;i<counter;i++)
            m->activateSession( i );
          m->setColLin(c,l); // will use default height and width if called with (0,0)
        }
	// works only for the first one, but there won't be more.
        n++;
        m->activateSession( sessionconfig->readNumEntry("ActiveSession",0) );
        m->show();
	m->setAutoClose(auto_close);
    }
  }
  else
  {
    Konsole*  m = new Konsole(wname,(shell ? QFile::decodeName(shell) : QString::null),eargs,histon,menubaron,tabbaron,frameon,scrollbaron,QString::null,title,type,term, false, 0, workDir);
    m->enableFullScripting(full_script);
    m->enableFixedSize(fixed_size);
    //3.8 :-(
    //exit(0);

    if (!keytab.isEmpty())
      m->initSessionKeyTab(keytab);

    if (!schema.isEmpty()) {
      if (schema.right(7)!=".schema")
        schema+=".schema";
      m->setSchema(schema);
    }

    ksm->konsole = m;
    m->setColLin(c,l); // will use default height and width if called with (0,0)

    m->initFullScreen();
    m->show();
    if (showtip)
      m->showTipOnStart();
    m->setAutoClose(auto_close);
  }

  int ret = a.exec();

 //// Temporary code, waiting for Qt to do this properly

  // Delete all toplevel widgets that have WDestructiveClose
  QWidgetList *list = QApplication::topLevelWidgets();
  // remove all toplevel widgets that have a parent (i.e. they
  // got WTopLevel explicitly), they'll be deleted by the parent
  list->first();
  while( list->current())
  {
    if( list->current()->parentWidget() != NULL || !list->current()->testWFlags( Qt::WDestructiveClose ) )
    {
        list->remove();
        continue;
    }
    list->next();
  }
  QWidgetListIt it(*list);
  QWidget * w;
  while( (w=it.current()) != 0 ) {
     ++it;
     delete w;
  }
  delete list;

  return ret;
}
