#include <allegro.h>
#include <stdio.h>
#include <time.h>
#include "map.h"
#include "ship.h"
#include "render.h"
#include "think.h"
#include "globals.h"
#include "water.h"
#include "palette.h"
#include "data.h"
#include "screens.h"

#ifdef ALLEGRO_LINUX
# define BEL allegro_message("\007");
#else
# ifdef ALLEGRO_DOS
#  include <conio.h>
#  define BEL putch(7);
# else
// Hmmm .... Hope that BEL gets through....
#  define BEL allegro_message("\007");
# endif
#endif

#define ADV \
 if (++n==argc) Error("%s needs more arguments, try --help",argv[n-1]);


struct {
  int left;
  int right;
  int go;
} key_table[4]={
  {
    KEY_LEFT,
    KEY_RIGHT,
    KEY_UP,
  },
  {
    KEY_X,
    KEY_C,
    KEY_D,
  },
  {
    KEY_ALTGR,
    KEY_RCONTROL,
    KEY_RSHIFT,
  },
  {
    KEY_LCONTROL,
    KEY_ALT,
    KEY_LSHIFT,
  },
};

volatile int BEL_timeout=0;
void ticker()
{
  if (BEL_timeout>0) {
    BEL_timeout--;
  }
  if (BEL_timeout==-1) {
    think++;
    race_ticks++;
  }
  ticks++;
  //race_ticks++;
}
END_OF_FUNCTION(ticker)

void framer()
{
  fps=frames;
  frames=0;
}
END_OF_FUNCTION(framer)

void Ready()
{
  int n;

  for (n=0;n<nships;n++) {
    ships[n]->Init(map->startx,map->starty);
  }
}

Ship *NewShip(int ship)
{
  switch (ship) {
    case 0:
      return new ArrowShip();
    case 1:
      return new CatamaranShip();
    case 2:
      return new EvergladesShip();
    case 3:
      return new MediumShip();
    case 4:
      return new StarshipShip();
    case 5:
      return new ZodiacShip();
    default:
      Error("Invalid ship ID");
  }
  return NULL;
}

void DoScores(int load)
{
  int n;
  static char buffer[1024];
  char *section;
  static char *score_entry[4]={
    "1",
    "2",
    "3",
    "4",
  };

  replace_extension(buffer,map->filename,"",sizeof(buffer));
  section=get_filename(buffer);
  if (section[strlen(section)-1]=='.') section[strlen(section)-1]=0;
  for (n=0;n<4;n++) {
    if (load)
     scores[n]=get_config_int(section,score_entry[n],-1);
    else
     set_config_int(section,score_entry[n],scores[n]);
  }
}

void InitViewports()
{
  int n;

  if (training|clockmode) {
    ships[0]->viewer.sx=0;
    ships[0]->viewer.sy=0;
    ships[0]->viewer.sw=SCREEN_W;
    ships[0]->viewer.sh=SCREEN_H;
    return;
  }

  switch (nplayers) {
    case 1:
      ships[0]->viewer.sx=0;
      ships[0]->viewer.sy=0;
      ships[0]->viewer.sw=SCREEN_W;
      ships[0]->viewer.sh=SCREEN_H;
      break;
    case 2:
      if (horz) {
        ships[0]->viewer.sx=0;
        ships[0]->viewer.sy=0;
        ships[0]->viewer.sw=SCREEN_W;
        ships[0]->viewer.sh=SCREEN_H/2;
        ships[1]->viewer.sx=0;
        ships[1]->viewer.sy=SCREEN_H/2;
        ships[1]->viewer.sw=SCREEN_W;
        ships[1]->viewer.sh=SCREEN_H/2;
      }
      else {
        ships[0]->viewer.sx=0;
        ships[0]->viewer.sy=0;
        ships[0]->viewer.sw=SCREEN_W/2;
        ships[0]->viewer.sh=SCREEN_H;
        ships[1]->viewer.sx=SCREEN_W/2;
        ships[1]->viewer.sy=0;
        ships[1]->viewer.sw=SCREEN_W/2;
        ships[1]->viewer.sh=SCREEN_H;
      }
      break;
    case 4:
      ships[3]->viewer.sx=SCREEN_W/2;
      ships[3]->viewer.sy=SCREEN_H/2;
      ships[3]->viewer.sw=SCREEN_W/2;
      ships[3]->viewer.sh=SCREEN_H/2;
    case 3:
      ships[0]->viewer.sx=0;
      ships[0]->viewer.sy=0;
      ships[0]->viewer.sw=SCREEN_W/2;
      ships[0]->viewer.sh=SCREEN_H/2;
      ships[1]->viewer.sx=0;
      ships[1]->viewer.sy=SCREEN_H/2;
      ships[1]->viewer.sw=SCREEN_W/2;
      ships[1]->viewer.sh=SCREEN_H/2;
      ships[2]->viewer.sx=SCREEN_W/2;
      ships[2]->viewer.sy=0;
      ships[2]->viewer.sw=SCREEN_W/2;
      ships[2]->viewer.sh=SCREEN_H/2;
      break;
  }
  for (n=0;n<nplayers;n++) ships[n]->viewer.Init();
}

int main(int argc,char **argv)
{
  int n,stop=0;
  int key=-1;
  int pship[256];
  int input[256];
  int mouse=0;
  PALETTE palette;
  int winner;
  FONT *f;
  char *load_map;
  int score;
  BITMAP *cdown;
  int prev_cdown;

  srand((unsigned)time(NULL));
  unlink("log.txt");

LogExec();
  allegro_init();
LogExec();

  for (n=0;n<(int)(sizeof(pship)/sizeof(pship[0]));n++) pship[n]=3;
  for (n=0;n<(int)(sizeof(input)/sizeof(input[0]));n++) input[n]=0;

  for (n=1;n<argc;n++) {
LogExec();
    if (argv[n][0]!='-') {
      Error("Bad option: %s, try --help",argv[n]+1);
    }
    else if (!strcmp(argv[n]+1,"-help")) {
      allegro_message(
        "SpeedHack\n"
        "   [--help]                  Help\n"
        "   [--width X]               Video mode widh\n"
        "   [--height Y]              Video mode height\n"
        "   [--cdepth D]              Video mode color depth\n"
        "   [--splunge]               I don't know\n"
        "   [--players N]             Number of human/dog/alien players\n"
        "   [--ships N]               Number of ships (inc. ASS)\n"
        "   [--track <filename>]      Custom map file\n"
        "   [--pship P S]             Player P uses ship type S\n"
        "   [--vert]                  In two player mode, use verical screens\n"
        "   [--joystick N]            player N uses joystick\n"
        "   [--mouse N]               player N uses mouse\n"
        "   [--nowater]               Disables the slow water effect\n"
        "   [--midi <filemame>]       Specifies a MIDI file to play\n"
        "   [--ass N]                 Selects the Artificial Ship Sapience\n"
        "   [--nowind]                Disables wind\n"
        "   [--notrans]               Disables Transclucency\n"
        "   [--marker]                Shows direction to next waypoint\n"
        "   [--showfps]               Shows framerate\n"
        "   [--nosound]               Disables SFX\n"
        "   [--nomusic]               Disables MIDI music\n"
        "   [--vsync]                 Synchronizes with retrace\n"
      );
LogExec();
      stop=1;
    }
    else if (!strcmp(argv[n]+1,"-width")) {
      ADV
      width=atoi(argv[n]);
    }
    else if (!strcmp(argv[n]+1,"-height")) {
      ADV
      height=atoi(argv[n]);
    }
    else if (!strcmp(argv[n]+1,"-depth")) {
      ADV
      depth=atoi(argv[n]);
    }
    else if (!strcmp(argv[n]+1,"-splunge")) {
      allegro_message("What does 'splunge' means ???");
LogExec();
      stop=1;
    }
    else if (!strcmp(argv[n]+1,"-players")) {
      ADV
      nplayers=atoi(argv[n]);
    }
    else if (!strcmp(argv[n]+1,"-ships")) {
      ADV
      nships=atoi(argv[n]);
    }
    else if (!strcmp(argv[n]+1,"-track")) {
      ADV
      track=argv[n];
    }
    else if (!strcmp(argv[n]+1,"-pship")) {
      ADV
      ADV
      pship[atoi(argv[n-1])]=atoi(argv[n]);
    }
    else if (!strcmp(argv[n]+1,"-vert")) {
      horz=0;
    }
    else if (!strcmp(argv[n]+1,"-joystick")) {
      ADV
      if (joystick==num_joysticks) Error("Not enough joysticks");
      input[atoi(argv[n])-1]=joystick++;
    }
    else if (!strcmp(argv[n]+1,"-mouse")) {
      ADV
      if (mouse) Error("Only one mouse supported");
      input[atoi(argv[n])-1]=-1;
    }
    else if (!strcmp(argv[n]+1,"-nowater")) {
      water=0;
    }
    else if (!strcmp(argv[n]+1,"-midi")) {
      ADV
      if (midi) Error("Can't play more than one MIDI file");
      midi=load_midi(argv[n]);
      if (!midi) Error("Error loading MIDI file %s",midi);
    }
    else if (!strcmp(argv[n]+1,"-ass")) {
      ADV
      ass_level=atoi(argv[n]);
    }
    else if (!strcmp(argv[n]+1,"-nowind")) {
      wind=0;
    }
    else if (!strcmp(argv[n]+1,"-notrans")) {
      trans=0;
    }
    else if (!strcmp(argv[n]+1,"-marker")) {
      marker=1;
    }
    else if (!strcmp(argv[n]+1,"-showfps")) {
      showfps=1;
    }
    else if (!strcmp(argv[n]+1,"-nosound")) {
      nosound=1;
    }
    else if (!strcmp(argv[n]+1,"-nomusic")) {
      nomusic=1;
    }
    else if (!strcmp(argv[n]+1,"-vsync")) {
      wait_vsync=1;
    }
    else {
      Error("Bad option: %s, try --help",argv[n]+1);
    }
  }
LogExec();
  if (stop) return 0;
LogExec();

  if (-1==install_timer()) Error("Could not install timer support");
LogExec();
  if (-1==install_keyboard()) Error("Could not install keyboard");
LogExec();
  if (-1==install_mouse()) Error("Could not install mouse");
LogExec();
  if (!nomusic || !nosound) {
    if (-1==install_sound(DIGI_AUTODETECT,MIDI_AUTODETECT,NULL))
     Error("Could not install sound");
  }
LogExec();
  set_color_depth(depth);
  if (set_gfx_mode(GFX_AUTODETECT,width,height,0,0)) Error("%s",allegro_error);
LogExec();

  drawable=create_bitmap(SCREEN_W,SCREEN_H);
  if (!drawable) Error("Could not create drawable");
  clear(drawable);

  DATAFILE *d=load_datafile("palette.dat");
  palette_color[0]=makecol(255,0,255);
  memcpy(palette,d[pal].dat,sizeof(PALETTE));
  set_palette(palette);
  set_color_conversion(COLORCONV_TOTAL);
  data=load_datafile("data.dat");
  if (!data) Error("Could not load data");
LogExec();

  map=new Map();
  if (!map) Error("Not enough memory");
  if (!map->Load(track)) Error("Could not load track %s",track);
LogExec();

  if (nships<nplayers) Error("There must not be less ships than players");
  if (nplayers<=0 || nplayers>4) Error("Only 1 to 4 players supported");
  voices=new int[nships];
  ships=new Ship*[nships];
  if (!voices || !ships) Error("Not enough memory");

  for (n=0;n<nships;n++) ships[n]=NewShip(pship[n]);
  for (n=0;n<nplayers;n++) {
    ships[n]->key_left=key_table[n].left;
    ships[n]->key_right=key_table[n].right;
    ships[n]->key_go=key_table[n].go;
    ships[n]->input=input[n];
  }
  for (n=0;n<nships;n++) ships[n]->color=n+1;
  for (n=nplayers;n<nships;n++) ships[n]=NewShip(rand()%MAX_SHIPS);
  for (n=0;n<nplayers;n++) ships[n]->viewer.ship=ships[n];
  InitViewports();

LogExec();
  cdown=create_bitmap(32,32);

  SAMPLE *sample=(SAMPLE*)(data[dat_engine].dat);
LogExec();
  if (!sample) Error("Could not load engine sample");
LogExec();
  if (!nosound) {
    for (n=0;n<nships;n++) {
LogExec();
      voices[n]=allocate_voice(sample);
LogExec();
      if (voices[n]==-1) Error("Could not allocate voice");
LogExec();
      voice_set_playmode(voices[n],PLAYMODE_LOOP);
LogExec();
      ships[n]->voice=voices[n];
    }
LogExec();
  }
LogExec();
  LOCK_VARIABLE(ticks);
  LOCK_VARIABLE(think);
  LOCK_FUNCTION(ticker);
  if (-1==install_int_ex(ticker,BPS_TO_TIMER(GAME_SPEED))
  || -1==install_int_ex(framer,BPS_TO_TIMER(1))) {
    Error("Could not install timer");
  }
LogExec();
  set_mouse_range(0,0,SCREEN_W-1,SCREEN_H-1);

LogExec();
  bg=load_bitmap("bgsq.pcx",palette);
//line(bg,5,5,90,90,makecol(255,0,0));
  if (!bg) Error("Could not load background image");
  text_mode(-1);

LogExec();
  if (!nosound) play_sample((SAMPLE*)(data[dat_welcome].dat),255,128,1000,0);

#ifdef DEBUG
Log("Water size: %d %d\n",width/WATERSCALE,height/WATERSCALE);
#endif
  water1=create_bitmap_ex(8,width/WATERSCALE,height/WATERSCALE);
  if (!water1) Error("Not enough memory");
  clear(water1);
  water2=create_bitmap_ex(8,width/WATERSCALE,height/WATERSCALE);
  if (!water2) Error("Not enough memory");
  clear(water2);

  if (!nomusic) {
    if (midi)
     play_midi(midi,0);
    else
     play_midi((MIDI*)(data[dat_demotune].dat),0);
  }

  f=(FONT*)(data[dat_font1].dat);
  TitleScreen();

  ticks=0;
  do {
domenu:
    clockmode=0;
    training=0;
    switch (Menu()) {
      case -1:
      case 5:
        goto end;
      case 0:
        // Race
        break;
      case 1:
        // Clock
        clockmode=1;
        break;
      case 2:
        // Train
        training=1;
        break;
      case 3:
        // Map
        load_map=SelectMap();
        if (load_map) {
          map->Unload();
          map->Load(load_map);
        }
        goto domenu;
      case 4:
        DoScores(1);
        ShowScores(-1);
        goto domenu;
    }

    InitViewports();
    Ready();
    BEL_timeout=300;
    prev_cdown=4;
    if (training) BEL_timeout=0;
    think=0;
    race_ticks=0;

    if (!nosound) for (n=0;n<nships;n++) voice_start(ships[n]->voice);
    //Render();
    while (1) {
      if (BEL_timeout==0) {
        // Go !
        BEL
        BEL_timeout=-1;
      }
      while (BEL_timeout==-1 && think) {
        if (keypressed()) {
          key=readkey();
          switch (key>>8) {
            case KEY_ESC:
              goto escape;
            case KEY_PRTSCR:
              save_pcx("SpeedHack.pcx",drawable,(PALETTE)(d[pal].dat));
              break;
          }
        }
        if ((winner=Think())!=0) goto won;
        think--;
      }
      Render();
      if (BEL_timeout>0) {
        // Not time to go yet
        if (prev_cdown!=(BEL_timeout+99)/100) {
          prev_cdown=(BEL_timeout+99)/100;
          play_sample((SAMPLE*)(data[dat_cd0+prev_cdown].dat),255,128,1000,0);
        }
        clear_to_color(cdown,bitmap_mask_color(cdown));
        textprintf_centre(
          cdown,f,cdown->w/2,cdown->h/2-text_height(f)/2,makecol(255,0,0),
          "%d",(BEL_timeout+99)/100
        );
        stretch_sprite(
          drawable,cdown,
          SCREEN_W/2-cdown->w*2,SCREEN_H/2-cdown->h*2,cdown->w*4,cdown->h*4
        );
      }
      if (wait_vsync) vsync();
      blit(drawable,screen,0,0,0,0,SCREEN_W,SCREEN_H);
    }
won:
    score=race_ticks;
    if (!nosound) for (n=0;n<nships;n++) voice_stop(ships[n]->voice);
    while (keypressed()) readkey();
    if (winner-1<nplayers) WinScreen(winner); else LoseScreen();
    if (clockmode) {
      DoScores(1);
      n=4;
      while (n>0 && (scores[n-1]==-1 || scores[n-1]>score)) n--;
      if (n<4) {
        for (int l=3;l>n;l--) scores[l]=scores[l-1];
        scores[n]=score;
      }
      else n=-1;
      ShowScores(n);
      DoScores(0);
    }
escape:
    if (!nosound) for (n=0;n<nships;n++) voice_stop(ships[n]->voice);
    while (keypressed()) readkey();
  } while (1);
end:
  while (keypressed()) readkey();

  EndScreen();

  if (!nomusic) {
    play_midi(NULL,0);
    if (midi) destroy_midi(midi);
  }
  destroy_bitmap(water1);
  destroy_bitmap(water2);

  return 0;
}
END_OF_MAIN()


