#include <graphics.h>
#include <conio.h>    // getch, kbhit
#include <stdio.h>    // sprintf
#include <stdlib.h>   // itoa, randomize
#include <ctype.h>    // toupper
#include <dos.h>      // delay, sound
#include <math.h>     // sin
#include "worm.hpp"

const int maxplr      =  4;
const int numlevels   =  3;
const int WORM_LENGTH = 30;
const int col[4]      = { YELLOW, GREEN, LIGHTBLUE, RED };
const int frame_color = MAGENTA;
unsigned long MemLeft;

struct inst_type {               // Inst„llningar f”r anv„ndaren
  int     players;
  int     speed;
  int     growth_speed;
  Boolean sound;
  Boolean autotail;
} inst = { 0, 5, 5, true, true };


int update_setup(Boolean FirstTime)
{
  char   ch = '\x0';
  char   buffer[10];

  if (!FirstTime)
    ch = toupper(getch());

  setfillstyle(SOLID_FILL, BLACK);
  switch (ch) {
    case 'N' : if (++inst.players >= maxplr)
                 inst.players = 0;
               bar(350, 300, 374, 308); break;
    case 'W' : if (++inst.speed > 10)
                 inst.speed = 1;
               bar(350, 320, 374, 328); break;
    case 'S' : if (inst.sound)
                 inst.sound = false;
               else
                 inst.sound = true;
               bar(350, 340, 374, 348); break;
    case 'T' : if (inst.autotail)
                 inst.autotail = false;
               else
                 inst.autotail = true;
               bar(350, 360, 398, 368); break;
    case 'G' : if (++inst.growth_speed > 10)
                 inst.growth_speed = 1;
               bar(350, 380, 374, 388); break;

    case '\x1b' : return false;
    case ' '    : return true;
  }

  setcolor(WHITE);
  sprintf(buffer, "%3d", inst.players+1);
  outtextxy(350, 300, buffer);
  sprintf(buffer, "%3d", inst.speed*10);
  outtextxy(350, 320, buffer);
  if (inst.sound)
    outtextxy(350, 340, "YES");
  else
    outtextxy(350, 340, "NO ");
  if (inst.autotail)
    outtextxy(350, 360, "AUTO  ");
  else
    outtextxy(350, 360, "MANUAL");
  sprintf(buffer, "%3d", inst.growth_speed);
  outtextxy(350, 380, buffer);
  setcolor(YELLOW);

  return 2;
}

int draw_worms(int &i, int faktor, int x, Point P[4][20])
{
  int loop;
  const double pi = 3.141;
  const int punkter = 20;

  if (kbhit())
    switch(update_setup(false))
    {
      case 0 : return false;
      case 1 : return true;
    }
  i++; if (i==punkter) i=0;           // R„kna modulo 20
  double y = 200 + faktor*sin(pi*2*x/400);

  P[0][i].MoveTo(130+x,y);
  if (inst.players > 0)
    P[1][i].MoveTo(110+x,y);
  if (inst.players > 1)
    P[2][i].MoveTo( 90+x,y);
  if (inst.players > 2)
    P[3][i].MoveTo( 70+x,y);

  loop = 0;
  do {
    delay(5);
    loop += 5;
  } while (loop < 100-inst.speed*10);

  return 2;
}

// ---------------------------------------------- V„LKOMSTBILD

int welcome_screen()
{
  int     i, x, answer;
  Point   P[maxplr][20];         // Initiera punkterna
  for (i=0; i<20; i++)
  {
    P[0][i].SetColor(col[0]);
    P[1][i].SetColor(col[1]);
    P[2][i].SetColor(col[2]);
    P[3][i].SetColor(col[3]);
  }

  cleardevice();
  settextstyle(TRIPLEX_FONT, HORIZ_DIR, 4);
  setcolor(YELLOW);
  outtextxy(150,50,"THE WORMFIGHT");

  setcolor(BROWN);
  rectangle(210, 270, 390, 285);
  rectangle(170, 285, 430, 400);
  settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
  setcolor(CYAN);
  outtextxy(190,300,"Number of players :");
  outtextxy(190,320,"Worm speed        :     km/h");
  outtextxy(190,340,"Sound effects     :");
  outtextxy(190,360,"Tailexpaning      :");
  outtextxy(190,380,"Growth speed      :"); //

  setcolor(WHITE);
  outtextxy(265, 275, "SETTINGS");
  outtextxy(190, 300, "N");
  outtextxy(190, 320, "W");
  outtextxy(190, 340, "S");
  outtextxy(190, 360, "T");
  outtextxy(190, 380, "G");

  update_setup(true);

  while (true) {
    i = 0;
    for (x=0; x<=400; x=x+5) {
      answer = draw_worms(i, 60, x, P);
      if (answer != 2) return answer;
    }
    for (x-=10; x>=0; x=x-5) {
      answer = draw_worms(i, -60, x, P);
      if (answer != 2) return answer;
    }
  }
} // welcome_screen


// ---------------------------------------------- INITIERA SPELPLANEN

void fat_rectangle(int x1, int y1, int x2, int y2)
{
  rectangle(x1,   y1,   x2,   y2);
  rectangle(x1+1, y1+1, x2-1, y2-1);
  rectangle(x1+2, y1+2, x2-2, y2-2);
  rectangle(x1+3, y1+3, x2-3, y2-3);
  rectangle(x1+4, y1+4, x2-4, y2-4);
}
void fat_vert_line(int x1, int y1, int x2, int y2)
{
  line(x1,   y1, x2,   y2);
  line(x1+1, y1, x2+1, y2);
  line(x1+2, y1, x2+2, y2);
  line(x1+3, y1, x2+3, y2);
  line(x1+4, y1, x2+4, y2);
}
void fat_hori_line(int x1, int y1, int x2, int y2)
{
  line(x1, y1,   x2, y2);
  line(x1, y1+1, x2, y2+1);
  line(x1, y1+2, x2, y2+2);
  line(x1, y1+3, x2, y2+3);
  line(x1, y1+4, x2, y2+4);
}

void init_battlefield(int level)
{
  cleardevice();
  setcolor(frame_color);
  fat_rectangle(  0,  10, 639, 469);
  switch(level) { 
    case 2 : fat_vert_line(320, 100, 320, 379); break;
    case 3 : fat_vert_line(320, 100, 320, 379);
             fat_hori_line(180, 240, 459, 240); break;
  }

  settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);

  setcolor(col[0]);
  outtextxy(15,0,"Player 1");
  outtextxy(15,472,"\x1b""Q W\x1a");
  if (!inst.autotail) outtextxy(71,472,"A");

  setcolor(col[1]);
  if (inst.players > 0) {
    outtextxy(175,0,"Player 2");
    outtextxy(175,472,"\x1b""8 9\x1a");
    if (!inst.autotail) outtextxy(231,472,"6");
  }
  setcolor(col[2]);
  if (inst.players > 1) {
    outtextxy(335,0,"Player 3");
    outtextxy(335,472,"\x1b""F G\x1a");
    if (!inst.autotail) outtextxy(391,472,"V");
  }
  setcolor(col[3]);
  if (inst.players > 2) {
    outtextxy(495,0,"Player 4");
    outtextxy(495,472,"\x1b""0 +\x1a");
    if (!inst.autotail) outtextxy(551,472,"P");
  }
} // init_battlefield

void adjust_speed(Worm P[maxplr], int *killed)
{
  int loop;
  int i = 0;

  do { // Om en mask „r d”d och inte „r begravd begrav den
    for (loop=0; loop<=inst.players; loop++)
      if (P[loop].Bury(inst.sound)) (*killed)++;

    delay(5);
    i += 5;
  } while (i < 100-inst.speed*10);
} // adjust_speed

void update_score(int score, int player_nr)
{
  char score_txt[5];
  itoa(score, score_txt, 10);
  setfillstyle(SOLID_FILL, BLACK);
  bar(95 + player_nr*160, 0, 135 + player_nr*160, 7);
  setcolor(WHITE);
  outtextxy(95 + player_nr*160, 0, score_txt);
}

// ---------------------------------------------- SPELA SPELET

Boolean play_game(int &level, int *scores)
{
  int     loop;
  char    ch;
  int     killed = maxplr - inst.players - 1;
  int     autoinc = 0;
  Worm    P[maxplr];
  Target  T(10, LIGHTCYAN, 4);
  int     sound_loop = 0;

  if (inst.players >= 0)
    P[0].Setup(31, 47, right, col[0], 'W', 'Q', 'A');

  if (inst.players > 0)
    P[1].Setup(95, 47, left,  col[1], '9', '8', '6');

  if (inst.players > 1)
    P[2].Setup(63, 15, down,  col[2], 'G', 'F', 'V');

  if (inst.players > 2)
    P[3].Setup(63, 79, up,    col[3], '+', '0', 'P');

  for (loop=0; loop<maxplr; loop++) {
    P[loop].score = scores[loop];
    if (inst.players >= loop)
      update_score(P[loop].score, loop);
  }

  ch = '\x0';
  while ((ch != '\x1b') &&
	!((killed == 4) && (inst.players == 0)) &&
	!((killed == 3) && (inst.players > 0)))
  {

    ch = '\x0';
    if (kbhit())             // Kolla tangentbordet
    {
      ch = toupper(getch());
      for (loop=0; loop<=inst.players; loop++)
        P[loop].CheckKeys(ch);
    }

    if (sound_loop > 0)
    {
      sound_loop--;
      sound(1000 + sound_loop*100);
    }
    if (sound_loop == 0)
      nosound();

    for (loop=0; loop<=inst.players; loop++)
      if (!P[loop].IsDead())
      {
      	if (P[loop].GetLength() < WORM_LENGTH)
          P[loop].grow = true;

       	if (sound_loop == 0)
          T.RandomPlace();

        P[loop].CalcNewPos();           // R„kna ut ny position
        if (P[loop].Kollision(T.color))   // Kolla efter krock
        {
          if (T.Hit(P[loop].head_x, P[loop].head_y)) // Žr m†ltavla T tr„ffad
          {
	          P[loop].score += T.value;
            update_score(P[loop].score, loop);
	          if (inst.sound)
            {
              sound(1500);
              sound_loop = 5;
	          }
	          T.Hide();
	        }
	        if (P[loop].score == level*50 && level < numlevels)
          {
	          level++;
	          for (loop=0; loop<maxplr; loop++)
	            scores[loop] = P[loop].score;
	          return true; // spelet forts„tter p† n„sta bana
	        }
	      }
	      else // Kollision med mask eller ram
          if (P[loop].Kollision(col[0], col[1], col[2], col[3], frame_color))
	          P[loop].Kill();
        P[loop].AddPoint();           // Masken v„xer
      	P[loop].WormMove();           // Masken flyttar sig
      }

    adjust_speed(P, &killed);

    // ™ka alla maskars l„ngd automatiskt
    if (inst.autotail && ++autoinc == inst.growth_speed)
    {
      autoinc = 0;
      for (loop=0; loop<=inst.players; loop++)
      	P[loop].grow = true;
    }
  }  // Tills krock eller ESC

  nosound();

  settextstyle(TRIPLEX_FONT, HORIZ_DIR, 4);
  setcolor(YELLOW);
  outtextxy(250, 200, "Game Over");
  delay(1000);
  while(kbhit())
    getch();
  getch();

  return false; // Spelet „r slut
} // play_game


// ############################################## HUVUDPROGRAM

void main()
{
  int gdriver = DETECT, gmode;
  registerbgidriver(EGAVGA_driver);
  initgraph(&gdriver, &gmode, "");
  registerbgifont(triplex_font);
  registerbgifont(sansserif_font);

  int level = 1;
  randomize();

  while (welcome_screen())        // Visa v„lkomstbilden
  {
    int scores[maxplr] = { 0, 0, 0, 0 };
    do
      init_battlefield(level);         // Rita upp spelplanen
    while (play_game(level, scores));  // Spela spelet
  } // Tills avbryt i welcome

  closegraph();
} // Main
