/*
  Copyright (C) 2003  Taiga Yonekura, taiga@col.hi-ho.ne.jp

  For more information about this appliation please look at:
   http://www.pat.hi-ho.ne.jp/kyonekura

  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; either version 2
  of the License, or (at your option) any later version.

  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; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
#include <qpe/qpeapplication.h>
#include <qtimer.h>
#include <qmessagebox.h>
#include <qwidget.h>
#include <qpainter.h>
#include <qkeycode.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <qdir.h>

#include "config.h"

#ifdef SOUND_ENABLE
#include <qpe/resource.h>
#include <qsound.h>
#endif

#include "gameWidget.h"

#define UNDER_BOOT       0
#define TITLE            1
#define GAME_START       2
#define UNDER_PLAY       3
#define GAME_OVER        4
#define GAME_CLEAR       5
#define REGIST_SCORE     6
#define DISP_HISCOREHARD 7
#define DISP_HISCOREEASY 8

#define MAX_DAMAGE 900

// GAME LEVEL
#define LEVEL_HARD 0
#define LEVEL_EASY 1

#define NET_DAMAGE_HARD 3700 // For Hard Level
#define NET_DAMAGE_EASY 5000 // For Easy Level

//Score Files
#define SCORE_DIR        ".qfish"
#define EASY_SCORE_FNAME ".qfish/qfisheasy.dat"
#define HARD_SCORE_FNAME ".qfish/qfishhard.dat"

//Drawing Interval
#define REFRESH_INTERVAL 100 //100msec

#define GET_TIME(x) x/REFRESH_INTERVAL
#define LOCK_TIME 2000 //2sec
#define TITLE_DISPTIME  10000
#define HILIST_DISPTIME 5000

CStringDisplay* CGameWidget::m_pStringDisp;

CGameWidget::CGameWidget(QWidget *parent, const char *name, WFlags f) :
    QWidget(parent, name, f)
{
    int i;

    m_gamemode = UNDER_BOOT;
    m_allfish  = FISH_NUM;
    m_freefish = m_allfish;
    m_level = LEVEL_EASY;

    // setup score directory
    QDir dir;
    if( !dir.exists( SCORE_DIR ) ){
      dir.mkdir( SCORE_DIR );
    }

    m_DispStayTime = GET_TIME( TITLE_DISPTIME );

    m_pStringDisp = new CStringDisplay();

    // create score Manager
    m_pScoreMng[0] = new CScoreMng( EASY_SCORE_FNAME );
    m_pScoreMng[0]->SetOrder( false );
    m_pScoreMng[1] = new CScoreMng( HARD_SCORE_FNAME );
    m_pScoreMng[1]->SetOrder( false );
    if( !m_pScoreMng[0]->DownLoadHiScore() ){
      // read easy level scorefile failed.
      registHiScoreData( false );
    }
    if( !m_pScoreMng[1]->DownLoadHiScore() ){
      // read hard level scorefile failed.
      registHiScoreData( true );
    }

    m_pHiScoreReg = new CHiScoreReg( m_pStringDisp );

    m_pDisplay = new CDisplay();

    /* initialize random number generator */
    srand(::time(0));

    m_timer = new QTimer(this);
    m_pixmap = 0;

    m_netObj = new Cnet( m_pDisplay->GetNetImages(), NET_DAMAGE_EASY);

    for(i = 0;i < m_allfish;++i){
      int type = choiceFish();
      m_pfish[i] = new CFish( SCREENW, SCREENH - SCREEN_HEGIHT_ADJUSTMENT, 
			      m_pDisplay->GetFishWidth(type), m_pDisplay->GetFishHeight(type), 
			      m_pDisplay->GetFishImages(type) );
      setUpHitArea( m_pfish[i], type );
    }

    m_pfireworks[0] = new CFireWorks(SCREENW,SCREENH);
    m_pfireworks[1] = new CFireWorks(SCREENW,SCREENH);
    m_pfireworks[2] = new CFireWorks(SCREENW,SCREENH);

    //
    m_pstartBtn = new CButton(  m_pDisplay->GetStartBtnImages(), 
				STARTBTN_POSX, STARTBTN_POSY );

    connect(m_timer, SIGNAL(timeout()), this, SLOT(timerTick()));

    m_timer->start( REFRESH_INTERVAL );
    
}
CGameWidget::~CGameWidget()
{
    delete m_timer;
    delete m_pixmap;
    delete m_pScoreMng[0];
    delete m_pScoreMng[1];
    delete m_pHiScoreReg;
    delete m_pStringDisp;
    delete m_pstartBtn;
    delete m_pDisplay;
}


void CGameWidget::timerTick(void)
{

  if( m_gamemode == TITLE || m_gamemode == DISP_HISCOREHARD || m_gamemode == DISP_HISCOREEASY ){

     if( !(--m_DispStayTime) ){

       switch( m_gamemode ){

       case TITLE:
	 m_gamemode = DISP_HISCOREHARD;
	 m_DispStayTime = GET_TIME( HILIST_DISPTIME );
	 break;

       case DISP_HISCOREHARD:
	 m_gamemode = DISP_HISCOREEASY;
	 m_DispStayTime = GET_TIME( HILIST_DISPTIME );
	 break;

       case DISP_HISCOREEASY:
	 m_gamemode = TITLE;
	 m_DispStayTime = GET_TIME( TITLE_DISPTIME );
	 break;
       }

     }

   }

   repaint(false);
}

void CGameWidget::paintEvent(QPaintEvent *event)
{
  paint();
}
void CGameWidget::resizeEvent(QResizeEvent *event)
{
    QWidget::resizeEvent(event);
    if (!m_pixmap || event->size() != event->oldSize()) {
	if (m_pixmap) {
	    delete m_pixmap;
	}
	m_pixmap = new QPixmap(event->size().width(), event->size().height());
    }
}
void CGameWidget::mousePressEvent(QMouseEvent *event)
{

  CScoreMng *pScoreMng;
  int time;
  int type;
  int i;

  switch( m_gamemode ){

  case TITLE:
    /* */
    m_DispStayTime = GET_TIME( TITLE_DISPTIME );
    m_levelselector.ClickLevel( event->x(), event->y() );
    if( m_pstartBtn->isOn( event->x(), event->y() )){

      m_gamemode = GAME_START;
      
      if( m_levelselector.IsHardSelected() ){
	m_level = LEVEL_HARD;
      } else {
	m_level = LEVEL_EASY;
      }

      if( m_level == LEVEL_EASY ){
	m_netObj->init(event->x() - 24, event->y() - 24,  NET_DAMAGE_EASY);
      } else {
	m_netObj->init(event->x() - 24, event->y() - 24,  NET_DAMAGE_HARD);
      }
      
      // change fish kind
      for( i = 0; i < m_allfish; ++i){
	type = choiceFish();
	m_pfish[i]->setImageList( m_pDisplay->GetFishImages(type) );
	setUpHitArea( m_pfish[i], type );	
      }

      initFishSpeed( m_level );
      m_stopwatch.start();
    }

    break;

  case DISP_HISCOREHARD:
  case DISP_HISCOREEASY:
    m_gamemode = TITLE;
    m_DispStayTime = GET_TIME( TITLE_DISPTIME );
    break;

  case UNDER_PLAY:
    /* no oparation*/
    break;

  case GAME_OVER:
    /* return Title */
    setNewGame();
    m_gamemode = TITLE;
    break;

  case GAME_CLEAR:

    /* check rank in */
    pScoreMng = getCurrentScoreMng();

    if( pScoreMng->IsRankIn( m_stopwatch.getClearTime() ) ){
      m_gamemode = REGIST_SCORE;
    } else {
      setNewGame();
      m_gamemode = TITLE;
    }

    break;

  case REGIST_SCORE:
    if( m_pHiScoreReg->OnTap( event->x(), event->y() ) ){
      // go to title
      time = m_stopwatch.getClearTime();
      pScoreMng = getCurrentScoreMng();
      pScoreMng->RegistHiScore( m_pHiScoreReg->GetName(), time );
      pScoreMng->SaveHiScore();
      setNewGame();
      m_gamemode = TITLE;
      m_DispStayTime = GET_TIME( HILIST_DISPTIME );
    }
    break;

  }

}
void CGameWidget::mouseMoveEvent(QMouseEvent *event)
{
	m_netObj->setPos(event->x() - 24 ,event->y() - 24 );
}

void CGameWidget::mouseReleaseEvent(QMouseEvent *event)
{

  switch(m_gamemode){

  case GAME_START:
    m_gamemode = UNDER_PLAY;
    break;

  case UNDER_PLAY:

    if(m_netObj->isInWater()){
      /* net under water */
      m_netObj->setinwater(false);

#ifdef SOUND_ENABLE
      QSound::play( Resource::findSound( QString("qfish2/") + "poo" ) );
#endif

      pickUpWater();
    } else {
      m_netObj->setinwater(true);
      intoWater();
    }
    break;
  }


}

void CGameWidget::setNewGame()
{
	int i;

	for(i = 0;i < m_allfish;++i){
		m_pfish[i]->setVisible(true);
	}

	m_freefish = m_allfish;

}
void CGameWidget::clearGame()
{
  m_stopwatch.stop();
  m_gamemode = GAME_CLEAR;

  m_pfireworks[0]->reset();
  m_pfireworks[1]->reset();
  m_pfireworks[2]->reset();

}
void CGameWidget::paint(void)
{

    QPainter painter_pixmap(m_pixmap);
    QPainter painter_widget(this);
    QString Scoretitle;
    int i;
    int h,m,s;
    QColor black(0, 0, 0);

    if(m_gamemode == UNDER_BOOT){
      // Draw Boot Logo
      m_pDisplay->DrawBootLogo(&painter_pixmap, width(), height() );
      painter_pixmap.flush();
      painter_widget.drawPixmap(0, 0, *m_pixmap);
      sleep(2);
      m_gamemode = TITLE;
      return;
    }

    for(i=0;i<FISH_NUM;i++){

      if( m_level == LEVEL_HARD ){
	m_pfish[i]->movefast();
      } else {
	m_pfish[i]->moveslow();
      }

    }

    m_pDisplay->setBackGround( &painter_pixmap, width(), height() );

    switch(m_gamemode){
    
    case TITLE:
      //display title
      drawFish( &painter_pixmap );
      m_pDisplay->setTitle( &painter_pixmap );
      m_levelselector.Draw( &painter_pixmap );
      m_pstartBtn->draw( &painter_pixmap );

      //display damage bar
      m_netObj->drawDamageBar(&painter_pixmap,DAMAGEBAR_POSX,
			      DAMAGEBAR_POSY,DAMAGEBAR_WIDTH,DAMAGEBAR_COLOR_HEIGHT);
      m_pDisplay->drawFishList( &painter_pixmap, m_allfish, m_allfish - m_freefish );

      break;

    case DISP_HISCOREHARD:
      drawFish( &painter_pixmap );
      Scoretitle = QString("HARD LEVEL BEST");
      m_pDisplay->DrawHiScoreList( &painter_pixmap, m_pScoreMng[1], Scoretitle );
      break;

    case DISP_HISCOREEASY:
      drawFish( &painter_pixmap );
      Scoretitle = QString("EASY LEVEL BEST");
      m_pDisplay->DrawHiScoreList( &painter_pixmap, m_pScoreMng[0], Scoretitle );
      break;

    case UNDER_PLAY:

      if(m_netObj->isInWater()){

	//net in water
	m_netObj->draw( &painter_pixmap );
	drawFish( &painter_pixmap, true );

      } else {

	drawFish( &painter_pixmap );
	m_netObj->draw( &painter_pixmap );

      }

      //display damage bar
      m_netObj->drawDamageBar(&painter_pixmap,DAMAGEBAR_POSX,
			      DAMAGEBAR_POSY,DAMAGEBAR_WIDTH,DAMAGEBAR_COLOR_HEIGHT);
      m_pDisplay->drawFishList( &painter_pixmap, m_allfish, m_allfish - m_freefish );

      break;

    case GAME_OVER:

	// Game Over
	drawFish( &painter_pixmap );
	m_netObj->draw(&painter_pixmap);
	setGameOverMsg(&painter_pixmap);

	//display damage bar
	m_netObj->drawDamageBar(&painter_pixmap,DAMAGEBAR_POSX,
				DAMAGEBAR_POSY,DAMAGEBAR_WIDTH,DAMAGEBAR_COLOR_HEIGHT);
	m_pDisplay->drawFishList( &painter_pixmap, m_allfish, m_allfish - m_freefish );
	break;

    case GAME_CLEAR:
	// Clear Game
	//int h,m,s;
	//QColor black(0, 0, 0);
	painter_pixmap.fillRect(0, 0, width(), height(), black);	
	m_pfireworks[0]->dispFire(&painter_pixmap);
	m_pfireworks[1]->dispFire(&painter_pixmap);
	m_pfireworks[2]->dispFire(&painter_pixmap);
	m_stopwatch.getClearTime(&h, &m, &s);
	m_pDisplay->drawClearTime( &painter_pixmap, h, m, s);
      
	//display damage bar
	m_netObj->drawDamageBar(&painter_pixmap,DAMAGEBAR_POSX,
				DAMAGEBAR_POSY,DAMAGEBAR_WIDTH,DAMAGEBAR_COLOR_HEIGHT);
	m_pDisplay->drawFishList( &painter_pixmap, m_allfish, m_allfish - m_freefish );
	break;
	    
    case REGIST_SCORE:
      m_pHiScoreReg->DrawRegistView( &painter_pixmap, width(), height());
      break;
    }


    painter_pixmap.flush();
    painter_widget.drawPixmap(0, 0, *m_pixmap);



}
void CGameWidget::intoWater(void)
{

  int i;

  CHitArea Area = m_netObj->GetHitArea();
  int ret;

  for(i = 0;i < m_allfish;++i){

    if(m_pfish[i]->getVisible()){

      ret = m_pfish[i]->checkUpperNet(&Area);

      if( ret == CROSS_NET_FISH || ret == WITHIN_NET_FISH ){
	m_netObj->setdamage(0);
	m_gamemode = GAME_OVER;
	break;
      }
	
    }

  }

}
void CGameWidget::pickUpWater(void)
{
  int i;

  CHitArea Area = m_netObj->GetHitArea();
  int ret;

  for(i = 0;i < m_allfish;++i){

    if(m_pfish[i]->getVisible()){

      ret = m_pfish[i]->checkUpperNet(&Area);

      
      if( ret == CROSS_NET_FISH ){

	m_netObj->minusdamage(MAX_DAMAGE);

	if(m_netObj->getdamage() < 1){
	  m_gamemode = GAME_OVER;
	  break;
	}

      } else if (ret == WITHIN_NET_FISH ){

	m_freefish--;
	m_pfish[i]->setVisible(false);
	if(m_freefish < 1) clearGame();

      }
    }
  }

}
//
// Draw Fishes
//
void CGameWidget::drawFish( QPainter *paint, bool bNetInWater )
{
  int i;

  for(i=0;i<FISH_NUM;i++){

    m_pfish[i]->draw( paint );
    if(bNetInWater && m_netObj->minusdamage()){
      //net is broken
      m_gamemode = GAME_OVER;
      m_netObj->setinwater( false );
    }

  }
  
}
void CGameWidget::setGameOverMsg(QPainter *paint)
{
  QColor black(0,0,0);
  QString str = "You caught ";
  QString numStr;

  numStr.setNum(FISH_NUM - m_freefish, 10);
  str += numStr +" fishes, Let's try again !!";
  paint->setPen( black );
  paint->drawText( 150, 350, str);

}
//
// Init ScoreData
//
void CGameWidget::registHiScoreData( bool IsHard )
{
  if(IsHard){
    // Hard Level initial data
    m_pScoreMng[1]->RegistHiScore( QString("TUX")  ,TIME_TO_SEC(0,4,0) );
    m_pScoreMng[1]->RegistHiScore( QString("ZAURU"),TIME_TO_SEC(0,4,30) );
    m_pScoreMng[1]->RegistHiScore( QString("SENNA"),TIME_TO_SEC(0,4,40) );
    m_pScoreMng[1]->RegistHiScore( QString("KEN"), TIME_TO_SEC(0,4,50) );
    m_pScoreMng[1]->RegistHiScore( QString("JUN"), TIME_TO_SEC(0,5,0) );
    m_pScoreMng[1]->RegistHiScore( QString("KING"),TIME_TO_SEC(0,5,10) );
    m_pScoreMng[1]->RegistHiScore( QString("TAIGA"),TIME_TO_SEC(0,5,20) );
    m_pScoreMng[1]->RegistHiScore( QString("KENGO"),TIME_TO_SEC(0,5,30) );
    m_pScoreMng[1]->RegistHiScore( QString("TAKU"), TIME_TO_SEC(0,5,40) );
    m_pScoreMng[1]->RegistHiScore( QString("BILL"), TIME_TO_SEC(0,5,50) );
  } else {
    // Easy Level initial data
    m_pScoreMng[0]->RegistHiScore( QString("TUX"),  TIME_TO_SEC(0,4,0));
    m_pScoreMng[0]->RegistHiScore( QString("ZAURU"),TIME_TO_SEC(0,4,30));
    m_pScoreMng[0]->RegistHiScore( QString("SENNA"),TIME_TO_SEC(0,4,40));
    m_pScoreMng[0]->RegistHiScore( QString("KEN"),  TIME_TO_SEC(0,4,50));
    m_pScoreMng[0]->RegistHiScore( QString("JUN"),  TIME_TO_SEC(0,5,0));
    m_pScoreMng[0]->RegistHiScore( QString("KING"), TIME_TO_SEC(0,5,10));
    m_pScoreMng[0]->RegistHiScore( QString("TAIGA"),TIME_TO_SEC(0,5,20));
    m_pScoreMng[0]->RegistHiScore( QString("KENGO"),TIME_TO_SEC(0,5,30));
    m_pScoreMng[0]->RegistHiScore( QString("TAKU"), TIME_TO_SEC(0,5,40));
    m_pScoreMng[0]->RegistHiScore( QString("BILL"), TIME_TO_SEC(0,5,50));
  }
}
CScoreMng* CGameWidget::getCurrentScoreMng()
{

  if( m_level == LEVEL_EASY ){
    return m_pScoreMng[0];
  } else {
    return m_pScoreMng[1];
  }

}
void CGameWidget::setUpWHFish( CFish *fish )
{

  CHitArea* HitArea[3];

  HitArea[0] = new CHitArea( WR_UP_HITBOX1 );
  fish->setHitArea( CFish::UP, HitArea, 1);
  delete HitArea[0];

  HitArea[0] = new CHitArea( WR_DOWN_HIBOX1 );
  fish->setHitArea( CFish::DOWN, HitArea, 1);
  delete HitArea[0];

  HitArea[0] = new CHitArea( WR_LEFT_HIBOX1 );
  fish->setHitArea( CFish::LEFT, HitArea, 1);
  delete HitArea[0];

  HitArea[0] = new CHitArea( WR_RIGHT_HIBOX1 );
  fish->setHitArea( CFish::RIGHT, HitArea, 1);
  delete HitArea[0];

  HitArea[0] = new CHitArea( WR_LEFT_UP_HITBOX1 );
  HitArea[1] = new CHitArea( WR_LEFT_UP_HITBOX2 );
  HitArea[2] = new CHitArea( WR_LEFT_UP_HITBOX3 );

  fish->setHitArea( CFish::LEFT_UP, HitArea, 3);

  delete HitArea[0];
  delete HitArea[1];
  delete HitArea[2];
  
  
  HitArea[0] = new CHitArea( WR_LEFT_DOWN_HITBOX1 );
  HitArea[1] = new CHitArea( WR_LEFT_DOWN_HITBOX2 );
  HitArea[2] = new CHitArea( WR_LEFT_DOWN_HITBOX3 );

  fish->setHitArea( CFish::LEFT_DOWN, HitArea, 3); 

  delete HitArea[0];
  delete HitArea[1];
  delete HitArea[2];

  HitArea[0] = new CHitArea( WR_RIGHT_UP_HITBOX1 );
  HitArea[1] = new CHitArea( WR_RIGHT_UP_HITBOX2 );
  HitArea[2] = new CHitArea( WR_RIGHT_UP_HITBOX3 );

  fish->setHitArea( CFish::RIGHT_UP, HitArea, 3);

  delete HitArea[0];
  delete HitArea[1];
  delete HitArea[2];
  
  
  HitArea[0] = new CHitArea( WR_RIGHT_DOWN_HITBOX1 );
  HitArea[1] = new CHitArea( WR_RIGHT_DOWN_HITBOX2 );
  HitArea[2] = new CHitArea( WR_RIGHT_DOWN_HITBOX3 );

  fish->setHitArea( CFish::RIGHT_DOWN, HitArea, 3); 

  delete HitArea[0];
  delete HitArea[1];
  delete HitArea[2];
}
void CGameWidget::setUpBlackFish( CFish *fish )
{

  CHitArea* HitArea[3];

  HitArea[0] = new CHitArea( BLACK_UP_HITBOX1 );
  fish->setHitArea( CFish::UP, HitArea, 1);
  delete HitArea[0];

  HitArea[0] = new CHitArea( BLACK_DOWN_HIBOX1 );
  fish->setHitArea( CFish::DOWN, HitArea, 1);
  delete HitArea[0];

  HitArea[0] = new CHitArea( BLACK_LEFT_HIBOX1 );
  fish->setHitArea( CFish::LEFT, HitArea, 1);
  delete HitArea[0];

  HitArea[0] = new CHitArea( BLACK_RIGHT_HIBOX1 );
  fish->setHitArea( CFish::RIGHT, HitArea, 1);
  delete HitArea[0];

  HitArea[0] = new CHitArea( BLACK_LEFT_UP_HITBOX1 );
  HitArea[1] = new CHitArea( BLACK_LEFT_UP_HITBOX2 );
  HitArea[2] = new CHitArea( BLACK_LEFT_UP_HITBOX3 );

  fish->setHitArea( CFish::LEFT_UP, HitArea, 3);

  delete HitArea[0];
  delete HitArea[1];
  delete HitArea[2];
  
  
  HitArea[0] = new CHitArea( BLACK_LEFT_DOWN_HITBOX1 );
  HitArea[1] = new CHitArea( BLACK_LEFT_DOWN_HITBOX2 );
  HitArea[2] = new CHitArea( BLACK_LEFT_DOWN_HITBOX3 );

  fish->setHitArea( CFish::LEFT_DOWN, HitArea, 3); 

  delete HitArea[0];
  delete HitArea[1];
  delete HitArea[2];

  HitArea[0] = new CHitArea( BLACK_RIGHT_UP_HITBOX1 );
  HitArea[1] = new CHitArea( BLACK_RIGHT_UP_HITBOX2 );
  HitArea[2] = new CHitArea( BLACK_RIGHT_UP_HITBOX3 );

  fish->setHitArea( CFish::RIGHT_UP, HitArea, 3);

  delete HitArea[0];
  delete HitArea[1];
  delete HitArea[2];
  
  
  HitArea[0] = new CHitArea( BLACK_RIGHT_DOWN_HITBOX1 );
  HitArea[1] = new CHitArea( BLACK_RIGHT_DOWN_HITBOX2 );
  HitArea[2] = new CHitArea( BLACK_RIGHT_DOWN_HITBOX3 );

  fish->setHitArea( CFish::RIGHT_DOWN, HitArea, 3); 

  delete HitArea[0];
  delete HitArea[1];
  delete HitArea[2];
}
void CGameWidget::setUpHitArea( CFish *fish, int type )
{
  switch(type){
  case 0:
    setUpWHFish( fish );
    break;
  case 1:
    setUpBlackFish( fish );
    break;
  }
}
//
// return fish type
//
int CGameWidget::choiceFish()
{
  return (rand() % 2);
}
//
// Set fish speed
//
void CGameWidget::initFishSpeed( int level )
{
  int i;

  for( i = 0; i < FISH_NUM; i++ ){

    if( level == LEVEL_HARD ){
      m_pfish[i]->initSpeed( CFish::HARD );  
    } else {
      m_pfish[i]->initSpeed( CFish::EASY );
    }

  }
}
