/* This file is part of OpenBubbles.
 *
 * OpenBubbles is an SDL clone of Bubbles.
 * Copyright (C) 2004  Benny Sperisen
 *
 * 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.
 *
 * gameloop.cpp is the implementation file of gameloop.h.
 *
 * Written by:
 *  Benny Sperisen
 *  lasindi@gmail.com
 *  www.freewebs.com/lasindi
 *****************************************************************************/

#include "gameloop.h"

string datadir;

int bubbleSound;
int missleSound;
int gameoverSound;

double gameloop()
{
  double score=0.0,highscore=0.0;
  SDL_Surface* gameover;
  SDL_Surface* youwin;
  SDL_Rect src,dest;
  SDL_Event event;
  string title;
  playerBubble* player=new playerBubble(SCREEN_WIDTH/2,SCREEN_HEIGHT/2,0,255,0,
                                        5);
  bool spaceDown=false,quitnow=false;
  int rand_radius,rand_vel1,rand_vel2,time1,time2,diff_time,pause_time,
      delay=30;
  char sharky_exists=0;
  srand((unsigned)time(NULL)); // Seed the random number generator.
  // Load the "Game Over" and "You Win!" surfaces.
  datadir=DATA_PREFIX;
  datadir+="/gameover.png";
  gameover=IMG_Load(datadir.c_str());
  if(gameover==NULL)
  {
    cerr<<"ERROR: unable to load gameover.\n";
    exit(1);
  }
  datadir=DATA_PREFIX;
  datadir+="/youwin.png";
  youwin=IMG_Load(datadir.c_str());
  if(youwin==NULL)
  {
    cerr<<"ERROR: unable to load youwin.\n";
    exit(1);
  }
  for(;;)
  {
    time1=clock()/CLOCKS_PER_SEC; //get time at start of loop
    while(SDL_PollEvent(&event))
      if(event.type==SDL_QUIT)
        exit(0);
    // Put the score in the title bar.
    if(player->state()==-1)
    {
      title="OpenBubbles - Score: ";
      // Currently the score is just the radius of the player's bubble minus 5.
      title+=numberToString((int)score);
    }
    else
      title="OpenBubbles - You Lose";
    SDL_WM_SetCaption(title.c_str(),title.c_str());
    SDL_PumpEvents();
    if(SDL_GetKeyState(NULL)[SDLK_q]==1) // If the user presses Q, quit.
      quitnow=true;
    // slow motion
    if((SDL_GetKeyState(NULL)[SDLK_m]==1)&&(score>0.0))
    {
      score-=0.5;
      delay=100;
    }
    else
      delay=30;
		// The player fired a missle.
    if((SDL_GetKeyState(NULL)[SDLK_SPACE]==1)&&!spaceDown&&(score>=10))
    {
      missles.push_back(new missleBubble((int)player->x(),(int)player->y(),255,
																				255,255,5,player->x_velocity()*1.5,
																				player->y_velocity()*1.5));
      score-=10;
      spaceDown=true;
			audio->playSound(missleSound);
    }
    else if(SDL_GetKeyState(NULL)[SDLK_SPACE]!=1)
      spaceDown=false;
    // Clear the screen with a black background.
    boxRGBA(screen,0,0,SCREEN_WIDTH-1,SCREEN_HEIGHT-1,0,0,0,255);
    // Randomly generate bubbles until there are 13.
    while(bubs.size()<=NUM_OF_BUBBLES)
    {
      // Randomly generate a radius plus or minus 3 from that of the player->
      rand_radius=random_int(6)-3;
      rand_radius+=(int)player->radius()+1;
      if(rand_radius<=0) // It needs to have some area.
        rand_radius=1;
      rand_vel1=random_int(16)-8;
      if(rand_vel1==0)
        rand_vel1=1;
      rand_vel2=random_int(8)+4;
      switch(random_int(30))
      {
      // Occaisonally add a bouncer instead.
      case 0: case 1: case 2: case 3: case 4: case 5: case 6:
        switch(random_int(4))
        {
          case TOP:
            bubs.push_back(new bouncerBubble(random_int(SCREEN_WIDTH),
                                             0-rand_radius,255,186,0,
                                             rand_radius,rand_vel1,
                                             rand_vel2));
            break;
          case BOTTOM:
            bubs.push_back(new bouncerBubble(random_int(SCREEN_WIDTH),
                                             SCREEN_HEIGHT+rand_radius-1,255,
                                             186,0,rand_radius,
                                             rand_vel1,
                                             -rand_vel2));
            break;
          case RIGHT:
            bubs.push_back(new bouncerBubble(0-rand_radius,
																						 random_int(SCREEN_HEIGHT),255,186,
																						 0,rand_radius,rand_vel2,
																						 rand_vel1));
            break;
          case LEFT:
            bubs.push_back(new bouncerBubble(SCREEN_WIDTH-1,
																						 random_int(SCREEN_HEIGHT),255,186,
																						 0,rand_radius,-rand_vel2,
																						 rand_vel1));
        }
        break;
      case 7: // Add the rare bonus bubbles.
        switch(random_int(4))
        {
          case TOP:
            bubs.push_back(new bonusBubble(random_int(SCREEN_WIDTH),
																					 0-rand_radius,0,255,0,rand_radius,
																					 rand_vel1,rand_vel2));
            break;
          case BOTTOM:
            bubs.push_back(new bonusBubble(random_int(SCREEN_WIDTH),
																					 SCREEN_HEIGHT+rand_radius-1,0,255,0,
																					 rand_radius,rand_vel1,-rand_vel2));
            break;
          case RIGHT:
            bubs.push_back(new bonusBubble(0-rand_radius,
																					 random_int(SCREEN_HEIGHT),
																					 0,255,0,rand_radius,rand_vel2,
																					 rand_vel1));
            break;
          case LEFT:
            bubs.push_back(new bonusBubble(SCREEN_WIDTH+rand_radius-1,
																					 random_int(SCREEN_HEIGHT),0,255,0,
																					 rand_radius,-rand_vel2,rand_vel1));
        }
        break;
      default:
        switch(random_int(4))
        {
        case TOP:
          bubs.push_back(new bubble(random_int(SCREEN_WIDTH),0-rand_radius,255,
                                    0,0,rand_radius,rand_vel1,rand_vel2));
          break;
        case BOTTOM:
          bubs.push_back(new bubble(random_int(SCREEN_WIDTH),
                                    SCREEN_HEIGHT+rand_radius-1,255,0,0,
																		rand_radius,rand_vel1,-rand_vel2));
          break;
        case RIGHT:
          bubs.push_back(new bubble(0-rand_radius,random_int(SCREEN_HEIGHT),
                                    255,0,0,rand_radius,rand_vel2,rand_vel1));
          break;
        case LEFT:
          bubs.push_back(new bubble(SCREEN_WIDTH+rand_radius-1,
																		random_int(SCREEN_HEIGHT),255,0,0,
																		rand_radius,-rand_vel2,rand_vel1));
				}
      }
		}
    // Add a sharky if there isn't one at random.
/*    if((!sharky_exists)&&(random_int(500)==0))
    {
      switch(random_int(4))
      {
        case TOP:
          bubs.push_back(new sharkyBubble(random_int(SCREEN_WIDTH),0,255,0,0,
																					player->radius()+2,player));
          break;
        case BOTTOM:
          bubs.push_back(new sharkyBubble(random_int(SCREEN_HEIGHT),
																					SCREEN_HEIGHT-1,255,0,0,
																					player->radius()+2,player));
          break;
        case RIGHT:
          bubs.push_back(new sharkyBubble(0,random_int(SCREEN_HEIGHT),255,0,0,
																					player->radius()+2,player));
          break;
        case LEFT:
          bubs.push_back(new sharkyBubble(SCREEN_WIDTH-1,
																					random_int(SCREEN_HEIGHT),255,0,0,
																					player->radius()+2,player));
      }
      sharky_exists=1;
			}*/
    // Handle crumbs.
    for(list<crumb>::iterator i=crumbs.begin();i!=crumbs.end();i++)
    {
      (*i).move();
      (*i).draw();
      // The player sucked a crumb in.
      if((*i).testCollision(player)&&(!(*i).isExploding()||
                                       !(*i).hasSucker())&&
         !player->immune()&&(player->state()==-1))
      {
        player->setRadius(player->radius()+(*i).value()*0.02);
        score+=(*i).value();
        i=crumbs.erase(i);
        i--;
      }
    }
    // Handle any missles that exist.
    for(list<missleBubble*>::iterator i=missles.begin();i!=missles.end();i++)
    {
      (*i)->move();
      (*i)->draw();
			// A missle hit a bubble.
      for(list<bubble*>::iterator j=bubs.begin();j!=bubs.end();j++)
        if((*i)->testCollision(*j))
        {
          if(strcmp((*j)->identify(),"sharkyBubble")==0)
            sharky_exists=0;
          sprayCrumbs((int)(*j)->x(),(int)(*j)->y(),1,(int)player->radius()*3,
											(*j)->r(),(*j)->g(),(*j)->b());
					delete *i;
          i=missles.erase(i);
          i--;
					delete *j;
          j=bubs.erase(j);
					audio->playSound(bubbleSound);
          break;
        }
    }
    if(player->state()==-1) // The player hasn't died, so procede normally.
    {
      player->move();
      player->draw();
    }
    // Move, draw, and erase bubbles when they go off the screen.
    /* Note: there are debugging statements in this loop commented out. I think
     * that there is a bug that will make the screen go black, but if it
     * exists, it happens so rarely that it's difficult to test. If you would
     * like to help me find the bug, uncomment these comments (the ones that
     * start with "cout<<"), recompile/install, and start the program from a
     * terminal and play until the black screen comes. Email me what the last
     * me what the last output was. If you do this, I will greatly appreciate
     * it. */
    for(list<bubble*>::iterator i=bubs.begin();i!=bubs.end();i++)
    {
//      cout<<"begin\n";
      (*i)->move();
      (*i)->draw();
      // If this is a bouncerBubble,
      if(strcmp((*i)->identify(),"bouncerBubble")==0)
      {
        (*i)->number=1;
        // Bounce it against all the other bubbles if they're close enough ...
        for(list<bubble*>::iterator j=bubs.begin();j!=bubs.end();j++)
          if((*j)->number!=1) // except for itself.
            bounceOff(*i,*j);
        (*i)->number=0;
        bounceOff(*i,player); // Bounce the player, too.
      }
//      cout<<"before ifs\n";
      // The player hit a bubble.
      if((((*i)->x()<-(*i)->radius()*2)||((*i)->y()<-(*i)->radius()*2)||
          ((*i)->x()>=SCREEN_WIDTH+(*i)->radius()*2)||
          ((*i)->y()>=SCREEN_HEIGHT+(*i)->radius()*2))&&
          (!(*i)->immune()))
      { // The bubble moved off of the screen, so delete it.
//        cout<<"if1\n";
				delete *i;
        i=bubs.erase(i);
        i--;
//        cout<<"endif1\n";
      }
      else if((!player->immune())&&(player->state()==-1)&&
/*              (sqrt((double)(((*i)->x()-player->x())*((*i)->x()-player->x())+
              ((*i)->y()-player->y())*((*i)->y()-player->y())))<(*i)->radius()+
                player->radius())&&*/
              player->testCollision(*i)&&
              (strcmp((*i)->identify(),"bouncerBubble")!=0))
      {
        // The player hit a bonus bubble.
        if(strcmp((*i)->identify(),"bonusBubble")==0)
        {
          if(!(*i)->immune())
          {
            getBonus(*i,player);
            if((*i)->radius()<1.0)
						{
							delete *i;
              i=bubs.erase(i);
						}
						audio->playSound(bubbleSound);
            i--;
					}
          continue;
        }
//        cout<<"if2\n";
        // The player hit a smaller bubble.
        if((*i)->radius()<=player->radius())
        {
//          cout<<"if21\n";
          if(strcmp((*i)->identify(),"sharkyBubble")==0)
            sharky_exists=0;
          sprayCrumbs((int)(*i)->x(),(int)(*i)->y(),1,(int)(*i)->radius()*3,
                      (*i)->r(),(*i)->g(),(*i)->b(),player);
					delete *i;
          i=bubs.erase(i);
          i--;
					audio->playSound(bubbleSound);
//          cout<<"endif21\n";
        }
        // The player hit a bigger bubble.
        else
        {
//          if<<"if22\n";
          sprayCrumbs((int)player->x(),(int)player->y(),1,
                      (int)player->radius()*2,0,255,0);
//          cout<<"if22check\n";
          player->setRadius(player->radius()-4);
          score-=player->radius()*10;
          player->setImmune();
          if((player->radius()<=4)&&(player->state()==-1)) // The player has 
          {                                                // died,
            player->kill(); // so start the dying animation.
            player->setRadius(player->radius()+4); // Do this so that we don't
                                                   // see very tiny bubbles
                                                   // generated in the dying
                                                   // animation.
            for(list<crumb>::iterator i=crumbs.begin();i!=crumbs.end();i++)
              (*i).setSuck(NULL);
						audio->playSound(gameoverSound);
          }
//          cout<<"endif22\n";
        }
      }
//      cout<<"end\n";
    }
    if(score>=3000) // The player wins.
    {
      src.x=0;
      src.y=0;
      src.w=youwin->w;
      src.h=youwin->h;
      dest.x=SCREEN_WIDTH/2-youwin->w/2;
      dest.y=SCREEN_HEIGHT/2-youwin->h/2;
      dest.w=src.w;
      dest.h=src.h;
      SDL_BlitSurface(youwin,&src,screen,&dest);
      SDL_UpdateRect(screen,0,0,screen->w,screen->h);
      SDL_Delay(3000);
      quitnow=true;
    }
    if(player->state()==DEAD_TIME) // The game is finally over.
    {
      src.x=0;
      src.y=0;
      src.w=gameover->w;
      src.h=gameover->h;
      dest.x=SCREEN_WIDTH/2-gameover->w/2;
      dest.y=SCREEN_HEIGHT/2-gameover->h/2;
      dest.w=src.w;
      dest.h=src.h;
      SDL_BlitSurface(gameover,&src,screen,&dest);
      SDL_UpdateRect(screen,0,0,screen->w,screen->h);
      SDL_Delay(750);
      quitnow=true;
    }
    else if(player->state()>=0)
      player->kill();
    SDL_UpdateRect(screen,0,0,screen->w,screen->h);
    if(score<0.0)
      score=0.0;
    if(highscore<score)
      highscore=score;
    if(quitnow)
      break;
    time2=clock()/CLOCKS_PER_SEC; //get time at end of loop
    diff_time=time2-time1;    //compare...
    pause_time=delay-diff_time;
    SDL_Delay(pause_time);    //and pause for required amount of time
  }
  src.x=src.y=dest.x=dest.y=0;
  src.w=dest.w=SCREEN_WIDTH;
  src.h=dest.h=SCREEN_HEIGHT;
  SDL_FreeSurface(gameover);
  SDL_FreeSurface(youwin);
	player->freeSurfaces();
  return highscore;
}
