/***************************************************************************
                          game.cpp  -  description
                             -------------------
    begin                : Tue Feb 29 2000
    copyright            : (C) 2000 by Michael Speck
    email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <sys/timeb.h>
#include <stdlib.h>
#include <string.h>
#include "game.h"
#include "breakout.h"
#include "menumanager.h"
#include "level.h"

#ifdef SOUND
extern SndSrv sndsrv;
#endif
extern Sdl sdl;
extern int fast_quit;
extern DrawRgn dr_src, dr_dst;

Game::Game()
{
	//video mode
#ifdef SOUND	
	Sdl_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
#else	
	Sdl_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
#endif
	if (Sdl_SetVideoMode(450, 325, 16, SDL_HWSURFACE))
		exit(1);
	SDL_WM_SetCaption("LBreakout", 0);
		
	//load title
	title = SSur_Load("title.bmp", SDL_HWSURFACE);
	SDL_SetColorKey(title, 0, 0);
	if (title == 0) {
		title = SSur_Create(sdl.scr->w, sdl.scr->h, SDL_HWSURFACE);
		DR_SETFULLDST(title);
		SSur_Fill(0x0);
	}
	f_copy = SFnt_Load("font_s.sdlfnt");
	f_copy->algn = TA_X_RIGHT | TA_Y_BOTTOM;
	SFnt_Write(f_copy, title, title->w-3, title->h-1, "(C) 2000 Michael Speck", 0);
	f_copy->algn = TA_X_LEFT | TA_Y_BOTTOM;
	SFnt_Write(f_copy, title, 0, title->h-1, "http://lgames.sourceforge.net", 0);
    SFnt_Free(f_copy);

    //load logo
    logo = SSur_Load("logo.bmp", SDL_HWSURFACE);
//  SDL_SetAlpha(logo, SDL_SRCALPHA, 16);
    if (logo == 0)
    	exit(1);
    logo_x = (sdl.scr->w - logo->w) / 2;
    logo_y = 40;
    	
	//load fonts
	f_menu_w = SFnt_LoadFixed("f_yellow.bmp", 32, 96, 10);
	f_menu_w->algn = TA_X_CENTER | TA_Y_CENTER;
	f_menu_b = SFnt_LoadFixed("f_white.bmp", 32, 96, 10);
	f_menu_b->algn = TA_X_CENTER | TA_Y_CENTER;
	f_hiscore = SFnt_LoadFixed("f_yellow.bmp", 32, 96, 10);
	f_hiscore->algn = TA_X_LEFT | TA_Y_TOP;
	f_enlgt_hiscore = SFnt_LoadFixed("f_white.bmp", 32, 96, 10);
	f_enlgt_hiscore->algn = TA_X_LEFT | TA_Y_TOP;
	
	//restricted keys
	memset(val_ctrl_keys, 0, sizeof(val_ctrl_keys));
	for (int i = 32; i < 128; i++)
		val_ctrl_keys[i] = 1;
	val_ctrl_keys[SDLK_RETURN] = 0;
	val_ctrl_keys[SDLK_ESCAPE] = 0;
	val_ctrl_keys[SDLK_q] = 0;
	val_ctrl_keys[SDLK_r] = 0;
	val_ctrl_keys[SDLK_f] = 0;
	val_ctrl_keys[SDLK_p] = 0;
	val_ctrl_keys[SDLK_UP] = 1;
	val_ctrl_keys[SDLK_DOWN] = 1;
	val_ctrl_keys[SDLK_RIGHT] = 1;
	val_ctrl_keys[SDLK_LEFT] = 1;

	//breakout
	breakout = new BreakOut();
		
	//create menumanager
	char    *str_gfx[] = {
	    "Off",
	    "Low",
	    "High"
	};
	char    *str_ctrls[] = {
	    "Only Keyboard",
	    "Only Mouse",
	    "Keyboard&Mouse"
	};
	char    *str_diff[] = {
	    "Easy",
	    "Medium",
	    "Hard"
	};
	mm = new MenuManager(title, sdl.scr->w / 2, 240, f_menu_w, f_menu_b, 9, val_ctrl_keys);
	//create menus
	Menu *menu[9];
	menu[0] = new Menu("main", 4);
	menu[1] = new Menu("newgame", 4);
	menu[2] = new Menu("options", 6);
	menu[3] = new Menu("graphics", 8);
	menu[4] = new Menu("audio", 4);
	menu[5] = new Menu("controls", 8);
	menu[6] = new Menu("game", 10);
	menu[7] = new Menu("level", 4);
	menu[8] = new Menu("mouse", 5);
    menu[0]->InsertItem(0, new MenuItem("New Game", menu[1]));
    menu[0]->InsertItem(1, new MenuItem("HiScores", AT_HISCORE));
    menu[0]->InsertItem(2, new MenuItem("Options", menu[2]));
    menu[0]->InsertItem(3, new MenuItem("Quit", AT_QUIT));
    menu[1]->InsertItem(0, new MenuItem("New Game", AT_NEWGAME));
    menu[1]->InsertItem(1, new MenuItem("Name", breakout->Setup()->name, 10));
    menu[1]->InsertItem(2, new MenuItem());
    menu[1]->InsertItem(3, new MenuItem("Back", menu[0]));
    menu[2]->InsertItem(0, new MenuItem("Game", menu[6]));
    menu[2]->InsertItem(1, new MenuItem("Controls", menu[5]));
    menu[2]->InsertItem(2, new MenuItem("Graphics", menu[3]));
#ifdef SOUND
    menu[2]->InsertItem(3, new MenuItem("Audio", menu[4]));
#else
    menu[2]->InsertItem(3, new MenuItem("Audio"));
#endif
    menu[2]->InsertItem(4, new MenuItem());
    menu[2]->InsertItem(5, new MenuItem("Back", menu[0]));
    menu[3]->InsertItem(0, new MenuItem("Transparency:", &breakout->Setup()->trp, "Off", "On"));
    menu[3]->InsertItem(1, new MenuItem("Animations:", &breakout->Setup()->anim, str_gfx, 3));
    menu[3]->InsertItem(2, new MenuItem("Bonus Information:", &breakout->Setup()->no_exdisp, "On", "Off"));
	menu[3]->InsertItem(3, new MenuItem());
    menu[3]->InsertItem(4, new MenuItem("Background:", &breakout->Setup()->bkgnd, "Off", "On"));
    menu[3]->InsertItem(5, new MenuItem("Display:", &breakout->Setup()->fullscreen, "Window", "Fullscreen"));
	menu[3]->InsertItem(6, new MenuItem());
    menu[3]->InsertItem(7, new MenuItem("Back", menu[2]));
    menu[4]->InsertItem(0, new MenuItem("Sound:", &breakout->Setup()->snd_on, "Off", "On"));
    menu[4]->InsertItem(1, new MenuItem("Volume:", &breakout->Setup()->snd_vol, 1, 8));
    menu[4]->InsertItem(2, new MenuItem());
    menu[4]->InsertItem(3, new MenuItem("Back", menu[2]));
	menu[5]->InsertItem(0, new MenuItem("Left", &breakout->Setup()->k_left, 0));
	menu[5]->InsertItem(1, new MenuItem("Right", &breakout->Setup()->k_right, 0));
	menu[5]->InsertItem(2, new MenuItem("Fire", &breakout->Setup()->k_fire, 0));
	menu[5]->InsertItem(3, new MenuItem());
    menu[5]->InsertItem(4, new MenuItem("Mouse", menu[8]));
	menu[5]->InsertItem(5, new MenuItem("Use:", &breakout->Setup()->control, str_ctrls, 3));
	menu[5]->InsertItem(6, new MenuItem());
	menu[5]->InsertItem(7, new MenuItem("Back", menu[2]));
    menu[6]->InsertItem(0, new MenuItem("Difficulty:", &breakout->Setup()->diff, str_diff, 3));
    menu[6]->InsertItem(1, new MenuItem("Starting Level:", &breakout->Setup()->startlevel, 0, LEVEL_NUM-1, 5));
    menu[6]->InsertItem(2, new MenuItem("Paddle:", &breakout->Setup()->convex, "Flat Surface", "Convex Surface"));
    menu[6]->InsertItem(3, new MenuItem("Ball's Starting Angle:", &breakout->Setup()->rnd_start, "50 Degree", "Random"));
	menu[6]->InsertItem(4, new MenuItem());
    menu[6]->InsertItem(5, new MenuItem("", &breakout->Setup()->lvls_frm_file, "Play Original Levels", "Play Own Levels"));
	menu[6]->InsertItem(6, new MenuItem("Level File", menu[7]));
	menu[6]->InsertItem(7, new MenuItem("(not selected)"));
	menu[6]->InsertItem(8, new MenuItem());
	menu[6]->InsertItem(9, new MenuItem("Back", menu[2]));
    menu[7]->InsertItem(0, new MenuItem("Path", breakout->Setup()->lvl_path, 31));
    menu[7]->InsertItem(1, new MenuItem("File", breakout->Setup()->lvl_file, 19));
	menu[7]->InsertItem(2, new MenuItem());
	menu[7]->InsertItem(3, new MenuItem("Back", menu[6]));
    menu[8]->InsertItem(0, new MenuItem("Warp Mouse:", &breakout->Setup()->warp, "Off", "On"));
    menu[8]->InsertItem(1, new MenuItem("Invert Mouse:", &breakout->Setup()->invert, "Off", "On"));
    menu[8]->InsertItem(2, new MenuItem("Motion Modifier:", &breakout->Setup()->motion_mod, 40, 100, 10));
	menu[8]->InsertItem(3, new MenuItem());
	menu[8]->InsertItem(4, new MenuItem("Back", menu[5]));
    //add to manager
    mm->InsertMenu(0, menu[0]);
    mm->InsertMenu(1, menu[1]);
    mm->InsertMenu(2, menu[2]);
    mm->InsertMenu(3, menu[3]);
    mm->InsertMenu(4, menu[4]);
    mm->InsertMenu(5, menu[5]);
    mm->InsertMenu(6, menu[6]);
    mm->InsertMenu(7, menu[7]);
    mm->InsertMenu(8, menu[8]);
    mm->Activate();

    level_sw = menu[6]->Item(5);
    level_menu = menu[7]->Item(3);
    level_menu->SetUsed(1); level_sw->SetUsed(1);
    CheckLevelFile();

    //randomize
    timeb t;
    ftime(&t);
    srand((unsigned int)t.time);
	
#ifdef SOUND	
	//load waves
    snd_menu = Wave_Load("click.wav");
#endif	
}

Game::~Game()
{
	if (breakout) delete breakout;
	if (title) SDL_FreeSurface(title);
	if (logo) SDL_FreeSurface(logo);
	if (mm) delete mm;
	if (f_menu_w) SFnt_Free(f_menu_w);
	if (f_menu_b) SFnt_Free(f_menu_b);
	if (f_hiscore) SFnt_Free(f_hiscore);
	if (f_enlgt_hiscore) SFnt_Free(f_enlgt_hiscore);
#ifdef SOUND
	if (snd_menu) Wave_Free(snd_menu);
#endif	
    Sdl_Quit();
}

void Game::Run()
{
#ifdef SOUND
    sndsrv.spec.freq = 22050;
    sndsrv.spec.format = AUDIO_U8;
    sndsrv.spec.samples = 128;
    sndsrv.spec.channels = 1;
	SndSrv_Open(snd_menu->spec);
#endif
	
	mm->Prepare();

	DR_SETFULLDST(sdl.scr);
	DR_SETFULLSRC(title);
	SSur_Blit();
	DR_SETDST(sdl.scr, logo_x, logo_y, logo->w, logo->h);
	DR_SETSRC(logo, 0, 0);
	SSur_Blit();
	
	SDL_UNDIM();

	mm->CurMenu()->Show(1);
	//loop
	SDL_Event	event;
	int			go_on = 1;
	int         rank;
	int         ret;
	while (go_on && !fast_quit) {
	    ret = MR_CONTINUE;
		if (SDL_PollEvent(&event)) {
		    switch (event.type) {
				case SDL_MOUSEMOTION:
				    mm->CurMenu()->MouseMotion(event.motion.x, event.motion.y);
				    break;
				case SDL_MOUSEBUTTONUP:
#ifdef SOUND
    				SndSrv_Play(snd_menu, 0);
#endif
				    ret = mm->ButtonEvent(event.button);
				    break;
#ifdef __riscos__
// Need to work on Keydown as RISC OS driver only does Unicode conversion
// on keydown (as specified in the SDL documentation)
                case SDL_KEYDOWN:
#else
		        case SDL_KEYUP:
#endif

#ifdef SOUND
    				SndSrv_Play(snd_menu, 0);
#endif
    				if (event.key.keysym.sym == SDLK_ESCAPE) {
    				    go_on = 0;
    				    break;
    				}
    				ret = mm->KeyEvent(&event.key);
                break;
				case SDL_QUIT:
				    fast_quit = 1;
				    break;
				default:
				    break;
			}	
   			switch (ret) {
				case MR_CONTINUE:
   					break;
				case AT_HISCORE:
   					SDL_DIM();
    				ShowHiScore(0);
                  	DR_SETFULLDST(sdl.scr);
                  	DR_SETFULLSRC(title);
                  	SSur_Blit();
                    DR_SETDST(sdl.scr, logo_x, logo_y, logo->w, logo->h);
                    DR_SETSRC(logo, 0, 0);
                    SSur_Blit();
   					SDL_UNDIM();
    				mm->CurMenu()->Show(1);
	    			break;
		    	case AT_QUIT:
					go_on = 0;
    				break;
    			case AT_NEWGAME:
	    			SDL_DIM();
		    		rank = breakout->Run(); //game
					if (!fast_quit) {
  					    if (rank != 0)
    					    ShowHiScore(rank);
                       	DR_SETFULLDST(sdl.scr);
                       	DR_SETFULLSRC(title);
                       	SSur_Blit();
                        DR_SETDST(sdl.scr, logo_x, logo_y, logo->w, logo->h);
                        DR_SETSRC(logo, 0, 0);
                        SSur_Blit();
         			    SDL_UNDIM();
					    mm->CurMenu()->ClearState();
  					    mm->SetCurMenu(0);
    				    mm->CurMenu()->Prepare(0);
					    mm->CurMenu()->Show(1);
   					}
    		        break;
   			}		
		}
			        	
		if (fast_quit) break;
		
		CheckLevelFile();
		
	 	//compute
 	    mm->CurMenu()->Compute();
 	    mm->CurMenu()->Update(1);
	 	
	 	SDL_Delay(8);
	
#ifdef SOUND
		SndSrv_SetActive(breakout->Setup()->snd_on);
 		SndSrv_SetVolume(breakout->Setup()->snd_vol);
#endif
	}
	
    SDL_DIM();

#ifdef SOUND
	SndSrv_Close();
#endif
}

void Game::ShowHiScore(int r)
{
	int i;
	int a_y = 100;
	int off = 50;
	int entry_h = f_hiscore->lh + 2;
	char buffer[12];
	HiScoreEntry	entry;
	SFnt			*font;
	
	for (int page = 0; page<2; page++) {
    DR_SETFULLDST(sdl.scr);
    DR_SETFULLSRC(title);
    SSur_Blit();
	SDL_UNDIM();
	
    f_hiscore->algn = TA_X_CENTER | TA_Y_CENTER;
    const char* txt;
    if (0==page) txt="Highest Scores";
    else txt="Highest Levels";
    SFnt_Write(f_hiscore, sdl.scr, sdl.scr->w / 2, 50, const_cast<char*>(txt), 0);
    f_hiscore->algn = 0;
    for (i = 0; i < 10; i++) {
        if ((0==page && r%10 == i+1)
            || (1==page && r/10 == i+1))
            font = f_enlgt_hiscore;
        else
            font = f_hiscore;
        entry = breakout->GetHiScore()->Entry(i,page);
        font->algn = TA_X_LEFT | TA_Y_TOP;
        SFnt_Write(font, sdl.scr, off, a_y + i * entry_h, entry.name, 0);
        font->algn = TA_X_CENTER | TA_Y_TOP;
        sprintf(buffer, "%i", entry.level);
        SFnt_Write(font, sdl.scr, sdl.scr->w / 2, a_y + i * entry_h, buffer, 0);
        font->algn = TA_X_RIGHT | TA_Y_TOP;
        sprintf(buffer, "%i", entry.score);
        SFnt_Write(font, sdl.scr, sdl.scr->w - off, a_y + i * entry_h, buffer, 0);
    }
    Sdl_FullUpdate();
    Sdl_WaitForClick();
    		
	SDL_DIM();
	}
}

void Game::CheckLevelFile()
{
     // strange and bad code, I know... //
    if (!level_sw->Used() && !level_menu->Used()) return;
    level_menu->Used();

    int rep = mm->GetMenu(6) == mm->CurMenu() ? 1 : 0;
    if (!breakout->Setup()->lvls_frm_file) {
        mm->GetMenu(6)->Item(7)->SetString("(not selected)", rep);
        mm->GetMenu(6)->Item(1)->SetRange(0, LEVEL_NUM - 1, 5, rep);
        return;
    }
    char str[256];
    FILE *f;
    if ((f = breakout->Levels_OpenFile(str)) == 0) {
        mm->GetMenu(6)->Item(7)->SetString("(not found)", rep);
        mm->GetMenu(6)->Item(1)->SetRange(0, LEVEL_NUM - 1, 5, rep);
        return;
    }
    else
        mm->GetMenu(6)->Item(7)->SetString("(found)", rep);


    int lev_num = 0;
    char *cur_pos;

    // load file into mem //
    fseek(f, 0, SEEK_END);
    int fsize = ftell(f);
    char *fbuf = new char[fsize];
    fseek(f, 0, SEEK_SET);
    fread(fbuf, fsize, 1, f);
    cur_pos = fbuf;
    fclose(f);

    // count
    while (cur_pos < fbuf + fsize) {
        if (!strncmp(cur_pos, "[LEV", 4))
            lev_num++;
        cur_pos = breakout->NextLine(0, cur_pos, fbuf + fsize);
    }

    if (lev_num > 0)
        mm->GetMenu(6)->Item(1)->SetRange(0, lev_num - 1, 5, rep);

    delete fbuf;
}
