/*
   patience -- main program
     Copyright (C) 1995  Paul Olav Tvete

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted,
   provided that the above copyright notice appear in all copies and that
   both that copyright notice and this permission notice appear in
   supporting documentation.

   This file is provided AS IS with no warranties of any kind.  The author
   shall have no liability with respect to the infringement of copyrights,
   trade secrets or any patents by this file or any part thereof.  In no
   event will the author be liable for any lost revenue or profits or
   other special, indirect and consequential damages.


   Heavily modified by Mario Weilguni <mweilguni@sime.com>
*/

#include <stdio.h>

#include <tqregexp.h>
#include <tqtimer.h>
#include <tqimage.h>

#include <tdeapplication.h>
#include <tdelocale.h>
#include <tdeaction.h>
#include <kdebug.h>
#include <kcarddialog.h>
#include <kinputdialog.h>
#include <kstandarddirs.h>
#include <tdefiledialog.h>
#include <tdetempfile.h>
#include <tdeio/netaccess.h>
#include <tdemessagebox.h>
#include <kstatusbar.h>
#include <tdeaccelmanager.h>
#include <tdemenubar.h>

#include "pwidget.h"
#include "version.h"
#include "dealer.h"
#include "cardmaps.h"
#include "speeds.h"
#include "gamestatsimpl.h"


static pWidget *current_pwidget = 0;


void saveGame(int) {
    current_pwidget->saveGame();
}

pWidget::pWidget()
    : TDEMainWindow(0, "pwidget"), dill(0)
{
    current_pwidget = this;
    // TDECrash::setEmergencySaveFunction(::saveGame);
    KStdAction::quit(TQT_TQOBJECT(kapp), TQT_SLOT(quit()), actionCollection(), "game_exit");

    undo = KStdAction::undo(TQT_TQOBJECT(this), TQT_SLOT(undoMove()),
                     actionCollection(), "undo_move");
    undo->setEnabled(false);
    (void)KStdAction::openNew(TQT_TQOBJECT(this), TQT_SLOT(newGame()),
                              actionCollection(), "new_game");
    (void)KStdAction::open(TQT_TQOBJECT(this), TQT_SLOT(openGame()),
                           actionCollection(), "open");
    recent = KStdAction::openRecent(TQT_TQOBJECT(this), TQT_SLOT(openGame(const KURL&)),
                                    actionCollection(), "open_recent");
    recent->loadEntries(TDEGlobal::config());
    (void)KStdAction::saveAs(TQT_TQOBJECT(this), TQT_SLOT(saveGame()),
                           actionCollection(), "save");
    (void)new TDEAction(i18n("&Choose Game..."), 0, TQT_TQOBJECT(this), TQT_SLOT(chooseGame()),
                      actionCollection(), "choose_game");
    (void)new TDEAction(i18n("Restart &Game"), TQString::fromLatin1("reload"), 0,
                      TQT_TQOBJECT(this), TQT_SLOT(restart()),
                      actionCollection(), "restart_game");
    (void)KStdAction::help(TQT_TQOBJECT(this), TQT_SLOT(helpGame()), actionCollection(), "help_game");

    games = new TDESelectAction(i18n("&Game Type"), 0, TQT_TQOBJECT(this),
                              TQT_SLOT(newGameType()),
                              actionCollection(), "game_type");
    TQStringList list;
    TQValueList<DealerInfo*>::ConstIterator it;
    uint max_type = 0;

    for (it = DealerInfoList::self()->games().begin();
         it != DealerInfoList::self()->games().end(); ++it)
    {
        // while we develop, it may happen that some lower
        // indices do not exist
        uint index = (*it)->gameindex;
        for (uint i = 0; i <= index; i++)
            if (list.count() <= i)
                list.append("unknown");
        list[index] = i18n((*it)->name);
        if (max_type < index)
            max_type = index;
    }
    games->setItems(list);

    TDEGlobal::dirs()->addResourceType("wallpaper", TDEStandardDirs::kde_default("data") + "kpat/backgrounds/");
    TDEGlobal::dirs()->addResourceType("wallpaper", TDEStandardDirs::kde_default("data") + "ksnake/backgrounds/");
    wallpapers = new TDESelectAction(i18n("&Change Background"), 0, TQT_TQOBJECT(this),
                              TQT_SLOT(changeWallpaper()),
                              actionCollection(), "wallpaper");
    list.clear();
    wallpaperlist.clear();
    TQStringList wallpaperlist2 = TDEGlobal::dirs()->findAllResources("wallpaper", TQString(),
                                                                   false, true, list);
    TQStringList list2;
    for (TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
	TQString file = *it;
	int rindex = file.findRev('.');
	if (rindex != -1) {
	  TQString ext = file.mid(rindex + 1).lower();
	  if (ext == "jpeg" || ext == "png" || ext == "jpg") {
	     list2.append(file.left(rindex));
             wallpaperlist.append( file );
          }
	}
    }

    wallpapers->setItems(list2);
    wallpapers->setCurrentItem(list2.findIndex("No-Ones-Laughing-3"));

    changeWallpaper();

    (void)new cardMap(midcolor);

    backs = new TDEAction(i18n("&Switch Cards..."), 0, TQT_TQOBJECT(this),
                        TQT_SLOT(changeBackside()),
                        actionCollection(), "backside");
	 stats = new TDEAction(i18n("&Statistics"), 0, TQT_TQOBJECT(this), TQT_SLOT(showStats()),
			 actionCollection(),"game_stats");

    animation = new TDEToggleAction(i18n( "&Animation on Startup" ),
                                  0, TQT_TQOBJECT(this), TQT_SLOT(animationChanged()),
                                  actionCollection(), "animation");
    dropaction = new TDEToggleAction(i18n("&Enable Autodrop"),
                                   0, TQT_TQOBJECT(this), TQT_SLOT(enableAutoDrop()),
                                   actionCollection(), "enable_autodrop");
    dropaction->setCheckedState(i18n("Disable Autodrop"));

    TDEConfig *config = kapp->config();
    TDEConfigGroupSaver cs(config, settings_group );

    TQString bgpath = config->readPathEntry("Background");
    kdDebug(11111) << "bgpath '" << bgpath << "'" << endl;
    if (bgpath.isEmpty())
        bgpath = locate("wallpaper", "No-Ones-Laughing-3.jpg");
    background = TQPixmap(bgpath);

    bool animate = config->readBoolEntry( "Animation", true);
    animation->setChecked( animate );

    bool autodrop = config->readBoolEntry("Autodrop", true);
    dropaction->setChecked(autodrop);

    uint game = config->readNumEntry("DefaultGame", 0);
    if (game > max_type)
        game = max_type;
    games->setCurrentItem(game);

    statusBar()->insertItem( "", 1, 0, true );

    createGUI(TQString(), false);
    TDEAcceleratorManager::manage(menuBar());

    newGameType();
    adjustSize();
    setAutoSaveSettings();
}

pWidget::~pWidget()
{
    delete dill;
}

void pWidget::undoMove() {
    if( dill )
        dill->undo();
}

void pWidget::helpGame()
{
    if (!dill)
        return;
    kapp->invokeHelp(dill->anchorName());
}

void pWidget::undoPossible(bool poss)
{
    undo->setEnabled(poss);
}

void pWidget::changeBackside() {
    TDEConfig *config = kapp->config();
    TDEConfigGroupSaver kcs(config, settings_group);

    TQString deck = config->readEntry("Back", KCardDialog::getDefaultDeck());
    TQString cards = config->readEntry("Cards", KCardDialog::getDefaultCardDir());
    if (KCardDialog::getCardDeck(deck, cards, this, KCardDialog::Both) == TQDialog::Accepted)
    {
        TQString imgname = KCardDialog::getCardPath(cards, 11);

        TQImage image;
        image.load(imgname);
        if( image.isNull()) {
            kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in " << cards << "\n";
            return;
        }

        bool change = false;
        if (image.width() != cardMap::CARDX() || image.height() != cardMap::CARDY())
        {
            change = true;
            if (KMessageBox::warningContinueCancel(this, i18n("The cards you have chosen have a different "
                                                              "size than the ones you are currently using. "
                                                              "This requires the current game to be restarted.")) == KMessageBox::Cancel)
                return;
        }
        setBackSide(deck, cards);
        if (change) {

            newGameType();
        }
    }

}

void pWidget::changeWallpaper()
{
    TQString bgpath=locate("wallpaper", wallpaperlist[wallpapers->currentItem()]);
    if (bgpath.isEmpty())
        return;
    background = TQPixmap(bgpath);
    if (background.isNull()) {
        KMessageBox::sorry(this, i18n("<qt>Couldn't load wallpaper<br/>%1</qt>").arg(bgpath));
        return;
    }

    TQImage bg = background.convertToImage().convertDepth(8, 0);
    if (bg.isNull() || !bg.numColors())
        return;
    long r = 0;
    long g = 0;
    long b = 0;
    for (int i = 0; i < bg.numColors(); ++i)
    {
        TQRgb rgb = bg.color(i);
        r += tqRed(rgb);
        g += tqGreen(rgb);
        b += tqBlue(rgb);
    }
    r /= bg.numColors();
    b /= bg.numColors();
    g /= bg.numColors();
    midcolor = TQColor(r, b, g);

    if (dill) {
        TDEConfig *config = kapp->config();
        TDEConfigGroupSaver kcs(config, settings_group);

        TQString deck = config->readEntry("Back", KCardDialog::getDefaultDeck());
        TQString dummy = config->readEntry("Cards", KCardDialog::getDefaultCardDir());
        setBackSide(deck, dummy);

	config->writePathEntry("Background", bgpath);
        dill->setBackgroundPixmap(background, midcolor);
        dill->canvas()->setAllChanged();
        dill->canvas()->update();
    }
}

void pWidget::animationChanged() {
    bool anim = animation->isChecked();
    TDEConfig *config = kapp->config();
    TDEConfigGroupSaver cs(config, settings_group );
    config->writeEntry( "Animation", anim);
}

void pWidget::enableAutoDrop()
{
    bool drop = dropaction->isChecked();
    TDEConfig *config = kapp->config();
    TDEConfigGroupSaver cs(config, settings_group );
    config->writeEntry( "Autodrop", drop);
    dill->setAutoDropEnabled(drop);
}

void pWidget::newGame()
{
    // Check if the user is already running a game, and if she is,
    // then ask if she wants to abort it.
    if (!dill->isGameWon() && !dill->isGameLost()
	&& KMessageBox::warningContinueCancel(0,
                                     i18n("You are already running an unfinished game.  "
                                          "If you abort the old game to start a new one, "
                                          "the old game will be registered as a loss in "
                                          "the statistics file.\n"
                                          "What do you want to do?"),
                                     i18n("Abort Current Game?"),
                                     i18n("Abort Old Game"),
                                     "careaboutstats" )  == KMessageBox::Cancel)
        return;

    dill->setGameNumber(kapp->random());
    setGameCaption();
    restart();
}


void pWidget::restart()
{
    statusBar()->clear();
    dill->startNew();
}

void pWidget::setGameCaption()
{
    TQString name = games->currentText();
    TQString newname;
    TQString gamenum;
    gamenum.setNum( dill->gameNumber() );
    for (uint i = 0; i < name.length(); i++)
        if (name.at(i) != TQChar('&'))
            newname += name.at(i);

    setCaption( newname + " - " + gamenum );
}

void pWidget::newGameType()
{
    delete dill;
    dill = 0;
    slotUpdateMoves();

    uint id = games->currentItem();
    for (TQValueList<DealerInfo*>::ConstIterator it = DealerInfoList::self()->games().begin(); it != DealerInfoList::self()->games().end(); ++it) {
        if ((*it)->gameindex == id) {
            dill = (*it)->createGame(this);
            TQString name = (*it)->name;
            name = name.replace(TQRegExp("[&']"), "");
            name = name.replace(TQRegExp("[ ]"), "_").lower();
            dill->setAnchorName("game_" + name);
            connect(dill, TQT_SIGNAL(saveGame()), TQT_SLOT(saveGame()));
            connect(dill, TQT_SIGNAL(gameInfo(const TQString&)),
                    TQT_SLOT(slotGameInfo(const TQString &)));
            connect(dill, TQT_SIGNAL(updateMoves()),
                    TQT_SLOT(slotUpdateMoves()));
            dill->setGameId(id);
            dill->setupActions();
            dill->setBackgroundPixmap(background, midcolor);
            dill->startNew();
            break;
        }
    }

    if (!dill) {
        kdError() << "unimplemented game type " << id << endl;
        dill = DealerInfoList::self()->games().first()->createGame(this);
    }

    connect(dill, TQT_SIGNAL(undoPossible(bool)), TQT_SLOT(undoPossible(bool)));
    connect(dill, TQT_SIGNAL(gameWon(bool)), TQT_SLOT(gameWon(bool)));
    connect(dill, TQT_SIGNAL(gameLost()), TQT_SLOT(gameLost()));

    dill->setAutoDropEnabled(dropaction->isChecked());

    // it's a bit tricky - we have to do this here as the
    // base class constructor runs before the derived class's
    dill->takeState();

    setGameCaption();

    TDEConfig *config = kapp->config();
    TDEConfigGroupSaver kcs(config, settings_group);
    config->writeEntry("DefaultGame", id);

    TQSize min(700,400);
    min = min.expandedTo(dill->minimumCardSize());
    dill->setMinimumSize(min);
    dill->resize(min);
    updateGeometry();
    setCentralWidget(dill);
    dill->show();
}

void pWidget::showEvent(TQShowEvent *e)
{
    if (dill)
        dill->setMinimumSize(TQSize(0,0));
    TDEMainWindow::showEvent(e);
}

void pWidget::slotGameInfo(const TQString &text)
{
    statusBar()->message(text, 3000);
}

void pWidget::slotUpdateMoves()
{
    int moves = 0;
    if ( dill ) moves = dill->getMoves();
    statusBar()->changeItem( i18n("1 move", "%n moves", moves), 1 );
}

void pWidget::setBackSide(const TQString &deck, const TQString &cards)
{
    TDEConfig *config = kapp->config();
    TDEConfigGroupSaver kcs(config, settings_group);
    TQPixmap pm(deck);
    if(!pm.isNull()) {
        cardMap::self()->setBackSide(pm, false);
        config->writeEntry("Back", deck);
        bool ret = cardMap::self()->setCardDir(cards);
        if (!ret) {
            config->writeEntry("Back", "");

        }
        config->writeEntry("Cards", cards);
        cardMap::self()->setBackSide(pm, true);
    } else
        KMessageBox::sorry(this,
                           i18n("Could not load background image!"));

    if (dill) {
        dill->canvas()->setAllChanged();
        dill->canvas()->update();
    }
}

void pWidget::chooseGame()
{
    bool ok;
    long number = KInputDialog::getText(i18n("Game Number"), i18n("Enter a game number (FreeCell deals are the same as in the FreeCell FAQ):"), TQString::number(dill->gameNumber()), 0, this).toLong(&ok);
    if (ok) {
        dill->setGameNumber(number);
        setGameCaption();
        restart();
    }
}

void pWidget::gameWon(bool withhelp)
{
    TQString congrats;
    if (withhelp)
        congrats = i18n("Congratulations! We have won!");
    else
        congrats = i18n("Congratulations! You have won!");
#if TEST_SOLVER == 0
    KMessageBox::information(this, congrats, i18n("Congratulations!"));
#endif
    TQTimer::singleShot(0, TQT_TQOBJECT(this), TQT_SLOT(newGame()));
#if TEST_SOLVER == 1
    dill->demo();
#endif
}

void pWidget::gameLost()
{
    TQString   dontAskAgainName = "gameLostDontAskAgain";

    // The following code is taken out of tdemessagebox.cpp in tdeui.
    // Is there a better way?
    TDEConfig  *config = 0;
    TQString grpNotifMsgs = TQString::fromLatin1("Notification Messages");

    config = TDEGlobal::config();
    TDEConfigGroupSaver saver(config,
			    TQString::fromLatin1("Notification Messages"));
    TQString dontAsk = config->readEntry(dontAskAgainName).lower();

    // If we are ordered never to ask again and to continue the game,
    // then do so.
    if (dontAsk == "no")
	return;
    // If it says yes, we ask anyway. Just starting a new game would
    // be incredibly annoying.
    if (dontAsk == "yes")
	dontAskAgainName = TQString();

    if (KMessageBox::questionYesNo(this, i18n("You could not win this game, "
                                              "but there is always a second try.\nStart a new game?"),
                                   i18n("Could Not Win!"),
                                   i18n("New Game"),
				   KStdGuiItem::cont(),
				   dontAskAgainName) == KMessageBox::Yes) {

        TQTimer::singleShot(0, TQT_TQOBJECT(this), TQT_SLOT(newGame()));
    }
}

void pWidget::openGame(const KURL &url)
{
    TQString tmpFile;
    if( TDEIO::NetAccess::download( url, tmpFile, this ) )
    {
        TQFile of(tmpFile);
        of.open(IO_ReadOnly);
        TQDomDocument doc;
        TQString error;
        if (!doc.setContent(&of, &error))
        {
            KMessageBox::sorry(this, error);
            return;
        }
        uint id = doc.documentElement().attribute("id").toUInt();

        if (id != (TQ_UINT32)games->currentItem()) {
            games->setCurrentItem(id);
            newGameType();
            if (!dill) {
                KMessageBox::error(this, i18n("The saved game is of unknown type!"));
                games->setCurrentItem(0);
                newGameType();
            }
        }
        dill->openGame(doc);
        setGameCaption();
        TDEIO::NetAccess::removeTempFile( tmpFile );
        recent->addURL(url);
        recent->saveEntries(TDEGlobal::config());
    }
}

void pWidget::openGame()
{
    KURL url = KFileDialog::getOpenURL();
    openGame(url);
}

void pWidget::saveGame()
{
    KURL url = KFileDialog::getSaveURL();
    KTempFile file;
    TQDomDocument doc("kpat");
    dill->saveGame(doc);
    TQTextStream *stream = file.textStream();
    *stream << doc.toString();
    file.close();
    TDEIO::NetAccess::upload(file.name(), url, this);
    recent->addURL(url);
    recent->saveEntries(TDEGlobal::config());
}

void pWidget::showStats()
{
	GameStatsImpl* dlg = new GameStatsImpl(this,"statistics dialog");
	if (dill)
		dlg->showGameType(dill->gameId());
	dlg->exec();
}

#include "pwidget.moc"