/*
 * KAsteroids - Copyright (c) Martin R. Jones 1997
 *
 * Part of the KDE project
 */

#include <tqlabel.h>
#include <tqlayout.h>
#include <tqlcdnumber.h>
#include <tqspinbox.h>
#include <tqcheckbox.h>

#include <kdebug.h>
#include <tdeapplication.h>
#include <kstandarddirs.h>
#include <tdeglobalsettings.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <kkeydialog.h>
#include <tdeaction.h>
#include <kstdgameaction.h>
#include <kaudioplayer.h>
#include <highscore/kscoredialog.h>
#include <tdeconfigdialog.h>

#include "settings.h"
#include "toplevel.h"
#include "ledmeter.h"

#include "toplevel.moc"

#if defined TQ_WS_X11 && ! defined K_WS_TQTONLY
#include <X11/Xlib.h>
#endif
#include <fixx11h.h>

#define SB_SCORE	1
#define SB_LEVEL	2
#define SB_SHIPS	3

struct SLevel
{
    int    nrocks;
    double rockSpeed;
};

#define MAX_LEVELS	16

SLevel levels[MAX_LEVELS] =
{
    { 1, 0.4 },
    { 1, 0.6 },
    { 2, 0.5 },
    { 2, 0.7 },
    { 2, 0.8 },
    { 3, 0.6 },
    { 3, 0.7 },
    { 3, 0.8 },
    { 4, 0.6 },
    { 4, 0.7 },
    { 4, 0.8 },
    { 5, 0.7 },
    { 5, 0.8 },
    { 5, 0.9 },
    { 5, 1.0 }
};

#ifdef KA_ENABLE_SOUND
#include <arts/dispatcher.h>
#include <arts/soundserver.h>

Arts::SimpleSoundServer *soundServer = 0;
#endif


KAstTopLevel::KAstTopLevel()
{
    TQWidget *mainWin = new TQWidget( this );
    mainWin->setFixedSize(640, 480);

    view = new KAsteroidsView( mainWin );
    connect( view, TQ_SIGNAL( shipKilled() ), TQ_SLOT( slotShipKilled() ) );
    connect( view, TQ_SIGNAL( rockHit(int) ), TQ_SLOT( slotRockHit(int) ) );
    connect( view, TQ_SIGNAL( rocksRemoved() ), TQ_SLOT( slotRocksRemoved() ) );
    connect( view, TQ_SIGNAL( updateVitals() ), TQ_SLOT( slotUpdateVitals() ) );

    TQVBoxLayout *vb = new TQVBoxLayout( mainWin );
    TQHBoxLayout *hb = new TQHBoxLayout;
    TQHBoxLayout *hbd = new TQHBoxLayout;
    vb->addLayout( hb );

    TQFont labelFont( TDEGlobalSettings::generalFont().family(), 24 );
    TQColorGroup grp( darkGreen, black, TQColor( 128, 128, 128 ),
	    TQColor( 64, 64, 64 ), black, darkGreen, black );
    TQPalette pal( grp, grp, grp );

    mainWin->setPalette( pal );

    hb->addSpacing( 10 );

    TQLabel *label;
    label = new TQLabel( i18n("Score"), mainWin );
    label->setFont( labelFont );
    label->setPalette( pal );
    label->setFixedWidth( label->sizeHint().width() );
    hb->addWidget( label );

    scoreLCD = new TQLCDNumber( 6, mainWin );
    scoreLCD->setFrameStyle( TQFrame::NoFrame );
    scoreLCD->setSegmentStyle( TQLCDNumber::Flat );
    scoreLCD->setFixedWidth( 150 );
    scoreLCD->setPalette( pal );
    hb->addWidget( scoreLCD );
    hb->addStretch( 10 );

    label = new TQLabel( i18n("Level"), mainWin );
    label->setFont( labelFont );
    label->setPalette( pal );
    label->setFixedWidth( label->sizeHint().width() );
    hb->addWidget( label );

    levelLCD = new TQLCDNumber( 2, mainWin );
    levelLCD->setFrameStyle( TQFrame::NoFrame );
    levelLCD->setSegmentStyle( TQLCDNumber::Flat );
    levelLCD->setFixedWidth( 70 );
    levelLCD->setPalette( pal );
    hb->addWidget( levelLCD );
    hb->addStretch( 10 );

    label = new TQLabel( i18n("Ships"), mainWin );
    label->setFont( labelFont );
    label->setFixedWidth( label->sizeHint().width() );
    label->setPalette( pal );
    hb->addWidget( label );

    shipsLCD = new TQLCDNumber( 1, mainWin );
    shipsLCD->setFrameStyle( TQFrame::NoFrame );
    shipsLCD->setSegmentStyle( TQLCDNumber::Flat );
    shipsLCD->setFixedWidth( 40 );
    shipsLCD->setPalette( pal );
    hb->addWidget( shipsLCD );

    hb->addStrut( 30 );

    TQFrame *sep = new TQFrame( mainWin );
    sep->setMaximumHeight( 5 );
    sep->setFrameStyle( TQFrame::HLine | TQFrame::Raised );
    sep->setPalette( pal );

    vb->addWidget( sep );

    vb->addWidget( view, 10 );

// -- bottom layout:
    TQFrame *sep2 = new TQFrame( mainWin );
    sep2->setMaximumHeight( 1 );
    sep2->setFrameStyle( TQFrame::HLine | TQFrame::Raised );
    sep2->setPalette( pal );
    vb->addWidget( sep2, 1 );

    vb->addLayout( hbd );

    TQFont smallFont( TDEGlobalSettings::generalFont().family(), 14 );
    hbd->addSpacing( 10 );

    TQString sprites_prefix =
        TDEGlobal::dirs()->findResourceDir("sprite", "rock1/rock10000.png");
/*
    label = new TQLabel( i18n( "T" ), mainWin );
    label->setFont( smallFont );
    label->setFixedWidth( label->sizeHint().width() );
    label->setPalette( pal );
    hbd->addWidget( label );

    teleportsLCD = new TQLCDNumber( 1, mainWin );
    teleportsLCD->setFrameStyle( TQFrame::NoFrame );
    teleportsLCD->setSegmentStyle( TQLCDNumber::Flat );
    teleportsLCD->setPalette( pal );
    teleportsLCD->setFixedHeight( 20 );
    hbd->addWidget( teleportsLCD );

    hbd->addSpacing( 10 );
*/
    TQPixmap pm( sprites_prefix + "powerups/brake.png" );
    label = new TQLabel( mainWin );
    label->setPixmap( pm );
    label->setFixedWidth( label->sizeHint().width() );
    label->setPalette( pal );
    hbd->addWidget( label );

    brakesLCD = new TQLCDNumber( 1, mainWin );
    brakesLCD->setFrameStyle( TQFrame::NoFrame );
    brakesLCD->setSegmentStyle( TQLCDNumber::Flat );
    brakesLCD->setPalette( pal );
    brakesLCD->setFixedHeight( 20 );
    hbd->addWidget( brakesLCD );

    hbd->addSpacing( 10 );

    pm.load( sprites_prefix + "powerups/shield.png" );
    label = new TQLabel( mainWin );
    label->setPixmap( pm );
    label->setFixedWidth( label->sizeHint().width() );
    label->setPalette( pal );
    hbd->addWidget( label );

    shieldLCD = new TQLCDNumber( 1, mainWin );
    shieldLCD->setFrameStyle( TQFrame::NoFrame );
    shieldLCD->setSegmentStyle( TQLCDNumber::Flat );
    shieldLCD->setPalette( pal );
    shieldLCD->setFixedHeight( 20 );
    hbd->addWidget( shieldLCD );

    hbd->addSpacing( 10 );

    pm.load( sprites_prefix + "powerups/shoot.png" );
    label = new TQLabel( mainWin );
    label->setPixmap( pm );
    label->setFixedWidth( label->sizeHint().width() );
    label->setPalette( pal );
    hbd->addWidget( label );

    shootLCD = new TQLCDNumber( 1, mainWin );
    shootLCD->setFrameStyle( TQFrame::NoFrame );
    shootLCD->setSegmentStyle( TQLCDNumber::Flat );
    shootLCD->setPalette( pal );
    shootLCD->setFixedHeight( 20 );
    hbd->addWidget( shootLCD );

    hbd->addStretch( 1 );

    label = new TQLabel( i18n( "Fuel" ), mainWin );
    label->setFont( smallFont );
    label->setFixedWidth( label->sizeHint().width() + 10 );
    label->setPalette( pal );
    hbd->addWidget( label );

    powerMeter = new KALedMeter( mainWin );
    powerMeter->setFrameStyle( TQFrame::Box | TQFrame::Plain );
    powerMeter->setRange( MAX_POWER_LEVEL );
    powerMeter->addColorRange( 10, darkRed );
    powerMeter->addColorRange( 20, TQColor(160, 96, 0) );
    powerMeter->addColorRange( 70, darkGreen );
    powerMeter->setCount( 40 );
    powerMeter->setPalette( pal );
    powerMeter->setFixedSize( 200, 12 );
    hbd->addWidget( powerMeter );

    initTDEAction();

    setCentralWidget( mainWin );
		
    setupGUI( TDEMainWindow::Save | Create );
    
		setFocusPolicy( TQWidget::StrongFocus );
    setFocus();

#ifdef KA_ENABLE_SOUND
    soundServer = new Arts::SimpleSoundServer(
                         Arts::Reference("global:Arts_SimpleSoundServer") );
#endif

    loadSettings();
    slotNewGame();
    installEventFilter(this);
}

KAstTopLevel::~KAstTopLevel()
{
#if defined TQ_WS_X11 && ! defined K_WS_TQTONLY
    XAutoRepeatOn( tqt_xdisplay() );
#endif
    soundDict.setAutoDelete(true);
    soundDict.clear();
#ifdef KA_ENABLE_SOUND
    delete soundServer;
#endif
}

void KAstTopLevel::initTDEAction()
{
// game
    KStdGameAction::gameNew( this, TQ_SLOT( slotNewGame() ), actionCollection() );
    KStdGameAction::highscores( this, TQ_SLOT( slotShowHighscores() ), actionCollection() );
    KStdGameAction::pause( this, TQ_SLOT( slotPause() ), actionCollection() );
    KStdGameAction::quit(this, TQ_SLOT( close() ), actionCollection());

// settings
    KStdAction::keyBindings(this, TQ_SLOT( slotKeyConfig() ), actionCollection());
    KStdAction::preferences(this, TQ_SLOT( slotPref() ), actionCollection());

// keyboard-only actions
    keycodes.insert(Thrust, new TDEAction(i18n("Thrust"), TQt::Key_Up, 0, 0, actionCollection(), "Thrust"));
    keycodes.insert(RotateLeft, new TDEAction(i18n("Rotate Left"), TQt::Key_Left, 0, 0, actionCollection(), "RotateLeft"));
    keycodes.insert(RotateRight, new TDEAction(i18n("Rotate Right"), TQt::Key_Right, 0, 0, actionCollection(), "RotateRight"));
    keycodes.insert(Shoot, new TDEAction(i18n("Shoot"), TQt::Key_Space, 0, 0, actionCollection(), "Shoot"));
//     keycodes.insert(Teleport, new TDEAction(i18n("Teleport"), TQt::Key_Z, 0, 0, actionCollection(), "Teleport"));
    keycodes.insert(Brake, new TDEAction(i18n("Brake"), TQt::Key_X, 0, 0, actionCollection(), "Brake"));
    keycodes.insert(Shield, new TDEAction(i18n("Shield"), TQt::Key_S, 0, 0, actionCollection(), "Shield"));
    launchAction = new TDEAction(i18n("Launch"), TQt::Key_L, this, TQ_SLOT(slotLaunch()), actionCollection(), "Launch");
}


void KAstTopLevel::loadSettings()
{
    soundDict.insert("ShipDestroyed", 
                 new TQString( locate("sounds", Settings::soundShipDestroyed())) );
    soundDict.insert("RockDestroyed", 
                 new TQString( locate("sounds", Settings::soundRockDestroyed())) );

    shipsRemain = Settings::numShips();
}

void KAstTopLevel::playSound( const char *snd )
{
// KAudioPlayer sometimes seem to be a little bit...slow
// it takes a moment until the sound is played - maybe an arts problem
// but it's a good temporary solution
    if (!Settings::playSounds())
        return;

    TQString *filename = soundDict[ snd ];
    if (filename) {
        KAudioPlayer::play(*filename);
    }

return; // remove this and the above when the sound below is working correctly
#ifdef KA_ENABLE_SOUND
    TQString *filename = soundDict[ snd ];
    if (filename) {
        kdDebug(12012)<< "playing " << *filename << endl;
        if(!soundServer->isNull()) soundServer->play(filename->latin1());
    }
#endif
}

bool KAstTopLevel::eventFilter( TQObject* /* object */, TQEvent *event )
{
	TQKeyEvent *e = static_cast<TQKeyEvent*>(event);
	if (event->type() == TQEvent::AccelOverride)
	{
		if (processKeyPress(e)) return true;
		else return false;
	}
	else if (event->type() == TQEvent::KeyRelease)
	{
		if (processKeyRelease(e)) return true;
		else return false;
	}
	return false;
}

bool KAstTopLevel::processKeyPress( TQKeyEvent *event )
{
    KKey key(event);
    Action a = Invalid;
    TQMap<Action, TDEAction*>::Iterator it = keycodes.begin();
    for (; it != keycodes.end(); ++it)
    {
       if ( (*it)->shortcut().contains(key) )
       {
           a = it.key();
           break;
       }
    }
    switch ( a )
    {
        case RotateLeft:
            view->rotateLeft( true );
            break;

        case RotateRight:
            view->rotateRight( true );
            break;

        case Thrust:
            view->thrust( true );
            break;

        case Shoot:
            if (gameOver)
               slotNewGame();
            else
               view->shoot( true );
            break;

        case Shield:
            view->setShield( true );
            break;

/*        case Teleport:
            view->teleport( true );
            break;*/

        case Brake:
            view->brake( true );
            break;

        case Invalid:
        default:
            return false;
    }
    return true;
}

bool KAstTopLevel::processKeyRelease( TQKeyEvent *event )
{
    KKey key(event);
    Action a = Invalid;
    TQMap<Action, TDEAction*>::Iterator it = keycodes.begin();
    for (; it != keycodes.end(); ++it)
    {
       if ( (*it)->shortcut().contains(key) )
       {
           a = it.key();
           break;
       }
    }
    switch ( a )
    {
        case RotateLeft:
            view->rotateLeft( false );
            break;

        case RotateRight:
            view->rotateRight( false );
            break;

        case Thrust:
            view->thrust( false );
            break;

        case Shoot:
            view->shoot( false );
            break;

        case Brake:
            view->brake( false );
            break;

        case Shield:
            view->setShield( false );
            break;

/*        case Teleport:
            view->teleport( false );
            break;*/

        case Invalid:
        default:
            return false;
    }

    return true;
}

void KAstTopLevel::focusInEvent( TQFocusEvent *e )
{
    view->pause( false );
#if defined TQ_WS_X11 && ! defined K_WS_TQTONLY
    XAutoRepeatOff( tqt_xdisplay() );
#endif
    TDEMainWindow::focusInEvent(e);
}

void KAstTopLevel::focusOutEvent( TQFocusEvent *e )
{
    view->pause( true );
#if defined TQ_WS_X11 && ! defined K_WS_TQTONLY
    XAutoRepeatOn( tqt_xdisplay() );
#endif
    TDEMainWindow::focusOutEvent(e);
}

void KAstTopLevel::slotNewGame()
{
    loadSettings();
    score = 0;
    scoreLCD->display( 0 );
    level = 0;
    levelLCD->display( level );
    shipsLCD->display( shipsRemain-1 );
    view->newGame();
    view->setRockSpeed( levels[0].rockSpeed );
    view->addRocks( levels[0].nrocks );
    view->showText( i18n( "Press %1 to launch." )
                    .arg(launchAction->shortcut().seq(0).toString()),
                    yellow );
    waitShip = true;
    gameOver = false;
}

bool KAstTopLevel::queryExit()
{
#if defined TQ_WS_X11 && ! defined K_WS_TQTONLY
    XAutoRepeatOn( tqt_xdisplay() );
#endif
    return true;
}

void KAstTopLevel::slotShipKilled()
{
    shipsRemain--;
    shipsLCD->display( shipsRemain-1 );

    playSound( "ShipDestroyed" );

    if ( shipsRemain )
    {
        waitShip = true;
        view->showText( i18n( "Ship Destroyed. Press %1 to launch.")
                        .arg(launchAction->shortcut().seq(0).toString()),
                        yellow );
    }
    else
    {
        view->showText( i18n("Game Over!"), red );
        view->endGame();
	doStats();
	KScoreDialog d(KScoreDialog::Name | KScoreDialog::Level | KScoreDialog::Score, this);
        KScoreDialog::FieldInfo scoreInfo;
        scoreInfo[KScoreDialog::Level].setNum(level);

        if (d.addScore(score, scoreInfo) || Settings::showHiscores())
        {
           // Show highscore and ask for name.
           d.exec();
           slotGameOver();
        }
        else
        {
           TQTimer::singleShot(1000, this, TQ_SLOT(slotGameOver()));
        }
    }
}

void KAstTopLevel::slotGameOver()
{
   gameOver = true;
}

void KAstTopLevel::slotRockHit( int size )
{
    switch ( size )
    {
	case 0:
	    score += 10;
	     break;

	case 1:
	    score += 20;
	    break;

	default:
	    score += 40;
      }

    playSound( "RockDestroyed" );

    scoreLCD->display( score );
}

void KAstTopLevel::slotRocksRemoved()
{
    level++;

    if ( level >= MAX_LEVELS )
	level = MAX_LEVELS - 1;

    view->setRockSpeed( levels[level-1].rockSpeed );
    view->addRocks( levels[level-1].nrocks );

    levelLCD->display( level );
}

void KAstTopLevel::slotKeyConfig()
{
    KKeyDialog::configure( actionCollection(), this );
    if ( waitShip ) view->showText( i18n( "Press %1 to launch." )
                    .arg(launchAction->shortcut().seq(0).toString()),
                    yellow, false );
}

void KAstTopLevel::slotPref()
{
  if(TDEConfigDialog::showDialog("settings"))
    return;

  TDEConfigDialog *dialog = new TDEConfigDialog(this, "settings", Settings::self(), KDialogBase::Swallow);

  /* Make widget */
  TQWidget *w = new TQWidget(0, "Settings");
  TQVBoxLayout *page = new TQVBoxLayout( w, 11 );

  TQHBoxLayout *hb = new TQHBoxLayout( page, 11 );
  TQLabel *label = new TQLabel( i18n("Start new game with"), w );
  TQSpinBox* sb = new TQSpinBox( 1, 5, 1, w, "kcfg_numShips" );
  sb->setValue(3);
  TQLabel *lb = new TQLabel( i18n(" ships."), w );
  TQSpacerItem* hspacer = new TQSpacerItem( 16, 20, TQSizePolicy::Minimum, TQSizePolicy::Expanding );
  hb->addWidget(label);
  hb->addWidget(sb);
  hb->addWidget(lb);
  hb->addItem(hspacer);

  TQCheckBox *f1 = new TQCheckBox(i18n("Show highscores on Game Over"), w, "kcfg_showHiscores");
  TQCheckBox *f2 = new TQCheckBox(i18n("Player can destroy Powerups"), w, "kcfg_canDestroyPowerups");
  f2->setChecked(true);
  page->addWidget(f1);
  page->addWidget(f2);
  TQSpacerItem* spacer = new TQSpacerItem( 20, 16, TQSizePolicy::Minimum, TQSizePolicy::Expanding );
  page->addItem( spacer );
  /* Done */

  dialog->addPage(w, i18n("General"), "package_settings");
  connect(dialog, TQ_SIGNAL(settingsChanged()), this, TQ_SLOT(loadSettings()));
  dialog->show();
}

void KAstTopLevel::slotShowHighscores()
{
    KScoreDialog d(KScoreDialog::Name | KScoreDialog::Level | KScoreDialog::Score, this);
    d.exec();
}

void KAstTopLevel::doStats()
{
    TQString r;
    if ( view->shots() )
	 r = TDEGlobal::locale()->formatNumber(( (float)view->hits() /
					(float)view->shots() ) * 100, 2 );
    else
	r = TDEGlobal::locale()->formatNumber( 0.0, 2 );

    TQString s = i18n( "Game Over\n\n"
		      "Shots fired:\t%1\n"
		      "  Hit:\t%2\n"
		      "  Missed:\t%3\n"
		      "Hit ratio:\t%4 %\t\t")
      .arg(view->shots()).arg(view->hits())
      .arg(view->shots() - view->hits())
      .arg(r);

    view->showText( s, green, FALSE );
}

void KAstTopLevel::slotUpdateVitals()
{
    brakesLCD->display( view->brakeCount() );
    shieldLCD->display( view->shieldCount() );
    shootLCD->display( view->shootCount() );
//    teleportsLCD->display( view->teleportCount() );
    powerMeter->setValue( view->power() );
}

void KAstTopLevel::slotPause()
{
    KMessageBox::information( this,
                              i18n("KAsteroids is paused."),
                              i18n("Paused") );
    //Hack (montel@kde.org)
    //To create "pause" action we display a messagebox
    //and we "play" when we close this messagebox
    //so before this action was 'checked/uncheched'
    //so I force to unchecked it when we close messagebox.
    TDEToggleAction * act = (TDEToggleAction *)(sender());
    act->setChecked(false);
}

void KAstTopLevel::slotLaunch()
{
    if ( waitShip )
    {
        view->newShip();
        waitShip = false;
        view->hideText();
    }
}
