//	--- toplevel.cpp ---
#include <kapp.h>
#include <kstddirs.h>
#include <qaccel.h>
#include <qmessagebox.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qlcdnumber.h>
#include <qpushbutton.h>
#include <kstatusbar.h>
#include <ktoolbar.h>
#include <ktmainwindow.h>
#include <kmenubar.h>
#include <khelpmenu.h>
#include <kconfig.h>
#include <klocale.h>
#include <kaccel.h>
#include <kkeydialog.h>

// sound support
#include <kaudioplayer.h>

#include "toplevel.h"
#include "ledmeter.h"
#include "preferences.h"
#include "version.h"

#include "toplevel.moc"

#include <X11/Xlib.h>
#include <klocale.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.6 },
    { 1, 0.8 },
    { 2, 0.7 },
    { 2, 0.8 },
    { 2, 0.9 },
    { 3, 0.7 },
    { 3, 0.8 },
    { 3, 0.9 },
    { 4, 0.7 },
    { 4, 0.8 },
    { 4, 0.9 },
    { 5, 0.7 },
    { 5, 0.8 },
    { 5, 0.9 },
    { 5, 1.0 }
};

const char *soundEvents[] = 
{
    "ShipDestroyed",
    "RockDestroyed",
    0
};

const char *soundDefaults[] = 
{
    "Explosion.wav",
    "ploop.wav",
    0
};

KAstTopLevel::KAstTopLevel() : KTMainWindow()
{
    setCaption( kapp->caption() );

    QWidget *mainWin = new QWidget( this );
    mainWin->setFixedSize(640, 480);

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

    QVBoxLayout *vb = new QVBoxLayout( mainWin );
    QHBoxLayout *hb = new QHBoxLayout;
    QHBoxLayout *hbd = new QHBoxLayout;
    vb->addLayout( hb );

    QFont labelFont( "helvetica", 24 );
    QColorGroup grp( darkGreen, black, QColor( 128, 128, 128 ),
	    QColor( 64, 64, 64 ), black, darkGreen, black );
    QPalette pal( grp, grp, grp );

    mainWin->setPalette( pal );

    hb->addSpacing( 10 );

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

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

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

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

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

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

    hb->addStrut( 30 );

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

    vb->addWidget( sep );

    vb->addWidget( view, 10 );

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

    vb->addLayout( hbd );

    QFont smallFont( "helvetica", 14 );
    hbd->addSpacing( 10 );

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

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

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

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

    hbd->addSpacing( 10 );

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

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

    hbd->addStretch( 1 );

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

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

    highscore = new HighscoreList( this );
    connect( highscore, SIGNAL(dialogClosed()), this, SLOT(slotReturn()) );

    accel = new KAccel( this );
    actions.insert( "Thrust", Thrust );
    accel->insertItem( i18n("Thrust"), "Thrust", Qt::Key_Up );
    actions.insert( "RotateLeft", RotateLeft );
    accel->insertItem( i18n("Rotate Left"), "RotateLeft", Qt::Key_Left );
    actions.insert( "RotateRight", RotateRight );
    accel->insertItem( i18n("Rotate Right"), "RotateRight", Qt::Key_Right );
    actions.insert( "Shoot", Shoot );
    accel->insertItem( i18n("Shoot"), "Shoot", Qt::Key_Space );
    actions.insert( "Teleport", Teleport );
    accel->insertItem( i18n("Teleport"), "Teleport", Qt::Key_Z );
    actions.insert( "Brake", Brake);
    accel->insertItem( i18n("Brake"), "Brake", Qt::Key_X );
    actions.insert( "Shield", Shield );
    accel->insertItem( i18n("Shield"), "Shield", Qt::Key_S );
    actions.insert( "Pause", Pause );
    accel->insertItem( i18n("Pause"), "Pause", Qt::Key_P );
    actions.insert( "Launch", Launch );
    accel->insertItem( i18n("Launch"), "Launch", Qt::Key_L );

    accel->readSettings();

    createMenuBar();

    setView( mainWin );
    setMenu( menu );

    setFocusPolicy( StrongFocus );
    setFocus();

    sound = false;

    readSettings();
}

KAstTopLevel::~KAstTopLevel()
{
}

void KAstTopLevel::createMenuBar()
{
    menu = new KMenuBar( this );

    QPopupMenu *fileMenu = new QPopupMenu;
    fileMenu->insertItem( i18n( "&New Game" ), this,
                          SLOT(slotNewGame()), CTRL+Key_N );
    fileMenu->insertItem( i18n("Show &Highscores..."), this,
                          SLOT( slotShowHighscores() ), CTRL+Key_H );
    fileMenu->insertSeparator( );
    fileMenu->insertItem( i18n( "&Quit" ), this, SLOT(slotQuit()),
                          CTRL+Key_Q );

    QPopupMenu *optionsMenu = new QPopupMenu( NULL, "popupOptions" );
    optionsMenu->insertItem( i18n( "Configure &Keys..."), this,
                             SLOT( slotKeyConfig() ) );
    optionsMenu->insertItem( i18n( "Game &Preferences..." ), this,
                             SLOT( slotPref() ) );

    KHelpMenu *help = new KHelpMenu(this,
                                    i18n("Asteroids %1\n\n"
                                         "by Martin R. Jones (mjones@kde.org)")
                                    .arg(KASTEROIDS_VERSION));

    menu->insertItem( i18n( "&File" ), fileMenu );
    menu->insertItem( i18n( "&Options" ), optionsMenu );
    menu->insertSeparator();
    menu->insertItem( i18n( "&Help" ), help->menu() );
}


void KAstTopLevel::readSettings()
{
    KConfig *config = kapp->config();
    config->setGroup( "Sounds" );

    QString qs;

    for ( int i = 0; soundEvents[i]; i++ )
    {
	qs = config->readEntry( soundEvents[i] );
	if ( !qs.isEmpty() )
	    soundDict.insert( soundEvents[i], 
		new QString( locate("appdata", qs)) );
	else
	    soundDict.insert( soundEvents[i], 
		new QString( locate("appdata", soundDefaults[i])) );
    }

    qs = config->readEntry( "PlaySounds" );
    if ( qs.isEmpty() || qs != "Yes" )
	sound = false;
    else
	sound = true;
	
    config->setGroup( "Preferences" );

    showStats = config->readBoolEntry( "showStats", true );
    showHiscores = config->readBoolEntry( "showHiscores", true );
    shipsRemain = config->readNumEntry( "numShips", 3 );
}

void KAstTopLevel::playSound( const char *snd )
{
    if ( sound ) {
        QString *filename = soundDict[ snd ];
        if (filename)
            KAudioPlayer::play( *filename );
    }
}

void KAstTopLevel::keyPressEvent( QKeyEvent *event )
{
    QString id = accel->findKey( event->key() );

    if ( id.isEmpty() )
    {
        event->ignore();
        return;
    }

    Action a = actions[ id ];

    switch ( a )
    {
        case RotateLeft:
            view->rotateLeft( true );
            break;

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

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

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

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

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

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

        default:
            event->ignore();
            return;
    }
    event->accept();
}

void KAstTopLevel::keyReleaseEvent( QKeyEvent *event )
{
    QString id = accel->findKey( event->key() );

    if ( id.isEmpty() )
    {
        event->ignore();
        return;
    }

    Action a = actions[ id ];

    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 Launch:
            if ( waitShip )
            {
                view->newShip();
                waitShip = false;
                view->hideText();
            }
            else
            {
                event->ignore();
                return;
            }
            break;

        case Pause:
            {
                view->pause( true );
                QMessageBox::warning( this, i18n("Paused"),
                        i18n("KAsteroids is paused"), i18n("Continue") );
                view->pause( false );
            }
            break;

        default:
            event->ignore();
            return;
    }

    event->accept();
}

void KAstTopLevel::focusInEvent( QFocusEvent * )
{
    XAutoRepeatOff( qt_xdisplay() );
}

void KAstTopLevel::focusOutEvent( QFocusEvent * )
{
    XAutoRepeatOn( qt_xdisplay() );
}

void KAstTopLevel::slotNewGame()
{
    readSettings();
    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 L to launch." ), yellow );
    waitShip = true;
    isPaused = false;
}

void KAstTopLevel::slotQuit()
{
    kapp->quit();
}

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

    playSound( "ShipDestroyed" );

    if ( shipsRemain )
    {
        waitShip = true;
        view->showText( i18n( "Ship Destroyed.  Press L to launch."),
                        yellow );
    }
    else
    {
        QMessageBox::information(0, i18n("Asteriods"), i18n("Game Over!"),
                                 i18n("OK"));
        view->endGame();
        if ( showStats )
            doStats();
        highscore->addEntry( score, level, showHiscores );
    }
}

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::configureKeys( accel, true, this );
}

void KAstTopLevel::slotReturn()
{
  view->startTimer( view->refreshRate );
}

void KAstTopLevel::slotPref()
{
    PrefDialog *pref = new PrefDialog( this );
    if ( pref->exec() )
        readSettings();
    delete pref;
}

void KAstTopLevel::slotShowHighscores()
{
    highscore->show();
}

void KAstTopLevel::doStats()
{
    QDialog *dlg = new QDialog( this, "Stats", true );

    dlg->setCaption( i18n( "Game stats" ) );

    QVBoxLayout *vb = new QVBoxLayout( dlg, 10 );

    QLabel *lab = new QLabel( dlg );
    vb->addWidget( lab );

    QString r = KGlobal::locale()->formatNumber(( (float)view->hits() /
					(float)view->shots() ) * 100, 2 );

    QString s = i18n( "kasteroids game stats:\n\n "
		      "Score:\t%1\n "
		      "Level:\t\t%2\n "
		      "Shots fired:\t%3\n "
		      "o Hit:\t%4\n "
		      "o Missed:\t%5\n "
		      "Hit ratio:\t%6 %%\t\t")
      .arg(score).arg(level)
      .arg(view->shots()).arg(view->hits())
      .arg(view->shots() - view->hits())
      .arg(r);

    lab->setText( s );

    QPushButton *ok = new QPushButton( i18n( "OK" ), dlg );
    vb->addWidget( ok );
    ok->setFocus();
    ok->setDefault( true );

    connect( ok, SIGNAL( clicked() ), dlg, SLOT( accept() ) );

    dlg->exec();

    delete dlg;
}

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