//-----------------------------------------------------------------------------
//
// TDE xscreensaver configuration dialog
//
// Copyright (c)  Martin R. Jones <mjones@kde.org> 1999
//
// 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;
// version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301, USA.

// This file contains code copied from xscreensaver.  The xscreensaver
// copyright notice follows.

/*
 * xscreensaver, Copyright (c) 1993-2002 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 */

#include <config.h>

#include <stdlib.h>
#include <tqlayout.h>
#include <tqtimer.h>
#include <tqvbox.h>
#include <tqlabel.h>
#include <tqfileinfo.h>

#include <kdebug.h>
#include <tdeapplication.h>
#include <tdeconfig.h>
#include <kdialogbase.h>
#include <tdeprocess.h>
#include <tdestandarddirs.h>
#include <tdelocale.h>
#include <tdecmdlineargs.h>
#include <tdemessagebox.h>

#include "kxsconfig.h"
#include "kxscontrol.h"
#include "kxsxml.h"

template class TQPtrList<KXSConfigItem>;

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/IntrinsicP.h>
#include <X11/ShellP.h>

int ignoreXError(Display *, XErrorEvent *);

//===========================================================================

const uint widgetEventMask =                 // X event mask
(uint)(
       ExposureMask |
       PropertyChangeMask |
       StructureNotifyMask
      );

KXSConfigDialog::KXSConfigDialog(const TQString &filename, const TQString &name)
  : KDialogBase(Plain, name, Ok| Cancel, Ok, 0, 0, false),
    mFilename(filename), mPreviewProc(0), mKilled(true)
{
    int slash = filename.findRev('/');
    if (slash >= 0)
	mConfigFile = filename.mid(slash+1);
    else
	mConfigFile = filename;

    mExeName = mConfigFile;
    mConfigFile += "rc";
}

bool KXSConfigDialog::create()
{
    TQVBoxLayout *topLayout = new TQVBoxLayout(plainPage(), spacingHint());
    TQHBoxLayout *layout = new TQHBoxLayout(topLayout, spacingHint());
    TQVBox *controlLayout = new TQVBox(plainPage());
    controlLayout->setSpacing(spacingHint());
    layout->addWidget(controlLayout);
    ((TQBoxLayout*)controlLayout->layout())->addStrut(120);

    TDEConfig config(mConfigFile);

    TQString xmlFile = "/doesntexist";
    xmlFile += "/" + mExeName + ".xml";
#ifdef XSCREENSAVER_CONFIG_DIRS
    TQFileInfo checkXml;
    TQStringList saverConfigPaths = TQStringList::split(':', XSCREENSAVER_CONFIG_DIRS);
    for (TQStringList::ConstIterator it = saverConfigPaths.begin(); it != saverConfigPaths.end(); ++it)
    {
        checkXml.setFile((*it) + "/" + mExeName + ".xml");
        if (checkXml.exists())
        {
            xmlFile = checkXml.absFilePath();
            break;
        }
    }
#endif

    if ( TQFile::exists( xmlFile ) ) {
	// We can use the xscreensaver xml config files.
	KXSXml xmlParser(controlLayout);
	xmlParser.parse( xmlFile );
	mConfigItemList = *xmlParser.items();
	TQWidget *spacer = new TQWidget(controlLayout);
	controlLayout->setStretchFactor(spacer, 1 );
	TQString descr = xmlParser.description();
	if ( !descr.isEmpty() ) {
	    descr.replace('\n',' ');
	    descr = descr.simplifyWhiteSpace();
	    TQLabel *l = new TQLabel( i18n( descr.utf8() ), plainPage() );
	    l->setAlignment ( WordBreak );
 	    topLayout->addWidget( l );
 	}
    } else {
        // fall back to TDE's old config files.
	int idx = 0;
	while (true) {
	    TQString group = TQString("Arg%1").arg(idx);
	    if (config.hasGroup(group)) {
		config.setGroup(group);
		TQString type = config.readEntry("Type");
		if (type == "Range") {
		    KXSRangeControl *rc = new KXSRangeControl(controlLayout, group, config);
		    mConfigItemList.append(rc);
		} else if (type == "DoubleRange") {
		    KXSDoubleRangeControl *rc =
			new KXSDoubleRangeControl(controlLayout, group, config);
		    mConfigItemList.append(rc);
		} else if (type == "Check") {
		    KXSCheckBoxControl *cc = new KXSCheckBoxControl(controlLayout, group,
			    config);
		    mConfigItemList.append(cc);
		} else if (type == "DropList") {
		    KXSDropListControl *dl = new KXSDropListControl(controlLayout, group,
			    config);
		    mConfigItemList.append(dl);
		}
	    } else {
		break;
	    }
	    idx++;
	}
	if ( idx == 0 )
	    return false;
    }

    TQPtrListIterator<KXSConfigItem> it( mConfigItemList );
    KXSConfigItem *item;
    while ( (item = it.current()) != 0 ) {
	++it;
	item->read( config );
        TQWidget *i = dynamic_cast<TQWidget*>(item);
        if (i) {
            connect( i, TQ_SIGNAL(changed()), TQ_SLOT(slotChanged()) );
        }
    }

    mPreviewProc = new TDEProcess;
    connect(mPreviewProc, TQ_SIGNAL(processExited(TDEProcess *)),
	    TQ_SLOT(slotPreviewExited(TDEProcess *)));

    mPreviewTimer = new TQTimer(this);
    connect(mPreviewTimer, TQ_SIGNAL(timeout()), TQ_SLOT(slotNewPreview()));

    mPreview = new TQWidget(plainPage());
    mPreview->setFixedSize(250, 200);
    //  mPreview->setBackgroundMode(TQWidget::NoBackground);
    mPreview->setBackgroundColor(TQt::black);

    layout->add(mPreview);
    show();

    // So that hacks can XSelectInput ButtonPressMask
    XSelectInput(tqt_xdisplay(), mPreview->winId(), widgetEventMask );

    slotPreviewExited(0);
    return true;
}

//---------------------------------------------------------------------------
KXSConfigDialog::~KXSConfigDialog()
{
  if (mPreviewProc && mPreviewProc->isRunning()) {
    int pid = mPreviewProc->pid();
    mPreviewProc->kill();
    waitpid(pid, (int *)0, 0);
    delete mPreviewProc;
  }
}

//---------------------------------------------------------------------------
TQString KXSConfigDialog::command()
{
  TQString cmd;
  KXSConfigItem *item;

  for (item = mConfigItemList.first(); item != 0; item = mConfigItemList.next())
  {
    cmd += " " + item->command();
  }

  return cmd;
}

//---------------------------------------------------------------------------
void KXSConfigDialog::slotPreviewExited(TDEProcess *)
{
    if ( mKilled ) {
	mKilled = false;
	mPreviewProc->clearArguments();
	TQString saver;
	saver.sprintf( "%s -window-id 0x%lX", mFilename.latin1(), long(mPreview->winId()) );
	saver += command();
	kdDebug() << "Command: " <<  saver << endl;

	unsigned int i = 0;
	TQString word;
	saver = saver.stripWhiteSpace();
	while ( !saver[i].isSpace() ) word += saver[i++];

	// find the xscreensaver executable
	//work around a KStandarDirs::findExe() "feature" where it looks in $TDEDIR/bin first no matter what and sometimes finds the wrong executable
	TQString path;
	TQFileInfo checkExe;
	TQStringList saverPaths = TQStringList::split(':', XSCREENSAVER_HACKS_DIRS);
	for (TQStringList::ConstIterator it = saverPaths.begin(); it != saverPaths.end(); ++it)
	{
		checkExe.setFile((*it) + "/" + word);
		if (checkExe.exists() && checkExe.isExecutable() && checkExe.isFile())
		{
			path = checkExe.absFilePath();
			break;
		}
	}

	if (path.isEmpty())
	{
		// Executable not present in XScreenSaver directory!
		// Try standard paths
		path = TDEStandardDirs::findExe(word);
	}

	if (!path.isEmpty()) {
	    (*mPreviewProc) << path;

	    bool inQuotes = false;
	    while ( i < saver.length() ) {
		word = "";
		while ( saver[i].isSpace() && i < saver.length() ) i++;
		while ( (!saver[i].isSpace() || inQuotes) && i < saver.length() ) {
		    if ( saver[i] == '\"' ) {
			inQuotes = !inQuotes;
		    } else {
			word += saver[i];
		    }
		    i++;
		}
		if (!word.isEmpty()) {
		    (*mPreviewProc) << word;
		}
	    }

	    mPreviewProc->start();
	}
    } else {
	// stops us from spawning the hack really fast, but still not the best
	TQString path = TDEStandardDirs::findExe(mFilename, XSCREENSAVER_HACKS_DIRS);
	if ( TQFile::exists(path) ) {
	    mKilled = true;
	    slotChanged();
	}
    }
}

//---------------------------------------------------------------------------
void KXSConfigDialog::slotNewPreview()
{
  if (mPreviewProc->isRunning()) {
    mKilled = true;
    mPreviewProc->kill(); // restarted in slotPreviewExited()
  } else {
    slotPreviewExited(0);
  }
}

//---------------------------------------------------------------------------
void KXSConfigDialog::slotChanged()
{
    if (mPreviewTimer->isActive())
	mPreviewTimer->changeInterval(1000);
    else
	mPreviewTimer->start(1000, true);
}

//---------------------------------------------------------------------------
void KXSConfigDialog::slotOk()
{
  KXSConfigItem *item;
  TDEConfig config(mConfigFile);

  for (item = mConfigItemList.first(); item != 0; item = mConfigItemList.next())
  {
    item->save(config);
  }

  tdeApp->quit();
}

//---------------------------------------------------------------------------
void KXSConfigDialog::slotCancel()
{
  tdeApp->quit();
}


//===========================================================================

static const char appName[] = "kxsconfig";

static const char description[] = I18N_NOOP("TDE X Screen Saver Configuration tool");

static const char version[] = "3.0.0";

static const TDECmdLineOptions options[] =
{
   {"+screensaver", I18N_NOOP("Filename of the screen saver to configure"), 0},
   {"+[savername]", I18N_NOOP("Optional screen saver name used in messages"), 0},
   TDECmdLineLastOption
};

static const char *defaults[] = {
#include "XScreenSaver_ad.h"
 0
};

const char *progname = 0;
const char *progclass = "XScreenSaver";
XrmDatabase db;

int main(int argc, char *argv[])
{
  TDECmdLineArgs::init(argc, argv, appName, I18N_NOOP("KXSConfig"), description, version);

  TDECmdLineArgs::addCmdLineOptions(options);

  TDEApplication app;

  TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
  if(args->count()==0)
    exit(1);

  /* We must read exactly the same resources as xscreensaver.
     That means we must have both the same progclass *and* progname,
     at least as far as the resource database is concerned.  So,
     put "xscreensaver" in argv[0] while initializing Xt.
   */
  const char *dummyargs[] = { "xscreensaver" };
  int dummyargc = 1;
  progname = dummyargs[0];

  // Teach Xt to use the Display that TQt has already opened.
  XtToolkitInitialize ();
  XtAppContext xtApp = XtCreateApplicationContext ();
  Display *dpy = tqt_xdisplay();
  XtAppSetFallbackResources (xtApp, const_cast<char**>(defaults));
  XtDisplayInitialize (xtApp, dpy, progname, progclass, 0, 0,
                       &dummyargc,
                       const_cast<char**>(dummyargs));
  Widget toplevel_shell = XtAppCreateShell (progname, progclass,
	  applicationShellWidgetClass,
	  dpy, 0, 0);
  dpy = XtDisplay (toplevel_shell);
  db = XtDatabase (dpy);
  XtGetApplicationNameAndClass (dpy, const_cast<char**>(&progname),
                                const_cast<char**>(&progclass));

  TQString name = TQString::fromLocal8Bit(args->arg(args->count() - 1));
  KXSConfigDialog *dialog=new KXSConfigDialog(args->arg(0), name);
  if ( dialog->create() ) {
      dialog->show();
      app.setMainWidget(dialog);
      app.exec();
  } else {
      KMessageBox::sorry(0,
	      i18n("No configuration available for %1").arg(name),
	      name );
  }

  delete dialog;
}

#include "kxsconfig.moc"
