#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <math.h>
#include "SDL/SDL.h"
#include "SDL/SDL_mixer.h"
#include "include.h"


using namespace std;


void coincollision()
{
  int i;
  for (i = 0; i < CoinCount; i++)
  {
    if (Coin[i].Taken == 0)
    {
      if (sqrt((Coin[i].X - XPos) *
               (Coin[i].X - XPos) +
               (Coin[i].Y - YPos) *
               (Coin[i].Y - YPos)) <
          RADIUS + Coin[i].Radius)
      {
	Mix_PlayChannel(0,s_coin,0);
        CoinsTaken++;
        Coin[i].Taken = 1;
        Score += 10;
      }
    }
  }
}

void reset()
{
  XMov = 0;
  YMov = 0;
  XRoll = 0;
  YRoll = 0;
  Roll = 0;

  KeyLeft = 0;
  KeyRight = 0;
  KeyUp = 0;
  KeyDown = 0;

  char name[80];
  FILE * f;
  do
  {
    sprintf(name, "level%d.txt", CurrentLevel);
    f = fopen(name, "rb");
    if (f == NULL)
    {
      if (CurrentLevel == 0)
        exit(0);
      CurrentLevel = 0;
    }
  }
  while (f == NULL);

  LevelWidth = -1;
  int i = 0;
  while (i != '\n' && i != '\r')
  {
    i = fgetc(f);
    LevelWidth++;
  }
  LevelHeight = 1;
  int ch = 0;
  while (!feof(f) && ch != '@')
  {
    ch = fgetc(f);
    if (ch == i)
      LevelHeight++;
  }
  fseek(f, 0, SEEK_SET);

  delete[] Level;
  Level = new unsigned char[LevelWidth * LevelHeight];

  int p = 0;
  while (p < LevelWidth * LevelHeight && !feof(f))
  {
    int v = fgetc(f);
    if (v > 32)
    {
      switch (v)
      {
      case '.':
        Level[p] = L_DROP;
        break;
      case '_':
        Level[p] = L_GROUND;
        break;
      case 'S':
        Level[p] = L_START;
        break;
      case 'E':
        Level[p] = L_END;
        break;
      case 'o':
        Level[p] = L_COIN;
        break;
      case '>':
        Level[p] = L_RIGHT;
        break;
      case '<':
        Level[p] = L_LEFT;
        break;
      case 'v':
        Level[p] = L_DOWN;
        break;
      case '^':
        Level[p] = L_UP;
        break;
      case '~':
        Level[p] = L_ROUGH;
        break;
      case '=':
        Level[p] = L_SMOOTH;
        break;
      }
      p++;
    }
  }
  delete[] LevelName;
  LevelName = NULL;
  // default time limit
  LevelTime = TIMELIMIT;

  ch = 0;
  while (!feof(f) && ch != '@')
    ch = fgetc(f);
  if (ch == '@')
  {
    i = 0;
    ch = 0;
    while (!feof(f) && ch != '@')
    {
      ch = fgetc(f);
      name[i] = ch;
      i++;
    }
    name[i-1] = 0;
    LevelTime = atoi(name);

    i = 0;
    ch = 0;
    while (!feof(f) && ch != '@')
    {
      ch = fgetc(f);
      name[i] = ch;
      i++;
    }
    name[i-1] = 0;
  }

  int len = (int)strlen(name);
  LevelName = new char[len + 1];
  memcpy(LevelName, name, len + 1);

  fclose(f);

  CoinCount = 0;
  for (i = 0; i < LevelWidth * LevelHeight; i++)
  {
    if (Level[i] == L_START)
    {
      StartX = (float)((i % LevelWidth) * TILESIZE + TILESIZE / 2);
      StartY = (float)((i / LevelWidth) * TILESIZE + TILESIZE / 2);
    }
    if (Level[i] == L_COIN)
      CoinCount++;
  }

  delete[] Coin;
  Coin = new coin_s[CoinCount];
  CoinCount = 0;
  for (i = 0; i < LevelWidth * LevelHeight; i++)
  {
    if (Level[i] == L_COIN)
    {
      Coin[CoinCount].X =
        (float)((i % LevelWidth) * TILESIZE + TILESIZE / 2);
      Coin[CoinCount].Y =
        (float)((i / LevelWidth) * TILESIZE + TILESIZE / 2);
      Coin[CoinCount].Color = COIN_COLOR;
      Coin[CoinCount].Radius = COIN_RADIUS;
      Coin[CoinCount].Taken = 0;
      CoinCount++;
    }
  }

  memcpy(RVertex, Vertex, sizeof(vertex_s) * BALL_VERTEX_COUNT);

  CoinsTaken = 0;

  XPos = StartX;
  YPos = StartY;

  CameraX = (WIDTH / 2) - XPos;
  CameraY = (HEIGHT / 2) - YPos;

  LastTick = SDL_GetTicks();
  LevelStartTick = SDL_GetTicks();
}

void gamephysics(int tick)
{
  if (tick <= LastTick)
  {
    SDL_Delay(1);
    return;
  }

  while (LastTick < tick)
  {
    int currenttile = (((int)YPos) / TILESIZE) * LevelWidth + ((int)XPos) / TILESIZE;

    if (KeyLeft) XRoll -= THRUST;
    if (KeyRight) XRoll += THRUST;
    if (KeyUp) YRoll -= THRUST;
    if (KeyDown) YRoll += THRUST;

    if (Joystick)
    {
			if((SDL_JoystickGetAxis(Joystick, 0)) > DEADZONE || (SDL_JoystickGetAxis(Joystick, 0)) < -DEADZONE)
      	XRoll += (SDL_JoystickGetAxis(Joystick, 0) / 32768.0f) * THRUST;
			if((SDL_JoystickGetAxis(Joystick, 1)) > DEADZONE || (SDL_JoystickGetAxis(Joystick, 1)) < -DEADZONE)
      	YRoll += (SDL_JoystickGetAxis(Joystick, 1) / 32768.0f) * THRUST;
    }

    switch (Level[currenttile])
    {
    case L_DROP:
      // player fell off
      NextState = S_FALLOFF;
			if(Sound_Lock)
			{
				Mix_PlayChannel(1,s_fall,0);
				Sound_Lock = 0;
			}
      break;
    case L_END:
      {
        int secondsleft = LevelTime - (LastTick - LevelStartTick) / 1000;
        if (secondsleft < 0)
          secondsleft = 0;
        Score += 100 + secondsleft * 25;
        NextState = S_ENDLEVEL;
      }
      break;
    case L_LEFT:
      XMov -= SLIDE_POWER;
      XRoll -= SLIDE_POWER;
      break;
    case L_RIGHT:
      XMov += SLIDE_POWER;
      XRoll += SLIDE_POWER;
      break;
    case L_UP:
      YMov -= SLIDE_POWER;
      YRoll -= SLIDE_POWER;
      break;
    case L_DOWN:
      YMov += SLIDE_POWER;
      YRoll += SLIDE_POWER;
      break;
    }

    switch (Level[currenttile])
    {
    case L_SMOOTH:
      XMov = (XMov * 63 + XRoll) / 64;
      YMov = (YMov * 63 + YRoll) / 64;
      XRoll = (XMov + XRoll * 63) / 64;
      YRoll = (YMov + YRoll * 63) / 64;
      break;
    case L_ROUGH:
      XMov *= SLOWDOWN_ROUGH;
      YMov *= SLOWDOWN_ROUGH;
      XMov = (XMov + XRoll) / 2;
      YMov = (YMov + YRoll) / 2;
      XRoll = (XMov + XRoll) / 2;
      YRoll = (YMov + YRoll) / 2;
      break;
    default:
      XMov *= SLOWDOWN;
      YMov *= SLOWDOWN;
      XMov = (XMov * 7 + XRoll) / 8;
      YMov = (YMov * 7 + YRoll) / 8;
      XRoll = (XMov + XRoll * 7) / 8;
      YRoll = (YMov + YRoll * 7) / 8;
    }

    XPos += XMov;
    YPos += YMov;

    float targetx = (WIDTH / 2) - (XPos + XMov * 25);
    float targety = (HEIGHT / 2) - (YPos + YMov * 25);
    CameraX = (targetx + CameraX * 19) / 20;
    CameraY = (targety + CameraY * 19) / 20;

    Roll += (float)sqrt(XRoll * XRoll + YRoll * YRoll);

    // Collision with the level borders
    if (XPos > LevelWidth * TILESIZE ||
        XPos < 0 ||
        YPos > LevelHeight * TILESIZE ||
        YPos < 0)
    {
      NextState = S_FALLOFF;
			if(Sound_Lock)
			{
				Mix_PlayChannel(1,s_fall,0);
				Sound_Lock = 0;
			}
    }

    coincollision();

    LastTick += 1000 / PHYSICS_IPS;
  }
}

void rendergame()
{
  // Pytamy SDL o czas jaki upłynął w milisekundach
  Tick = SDL_GetTicks();

  if (GameState == S_INGAME)
    gamephysics(Tick);

  if (SDL_MUSTLOCK(Screen))
    if (SDL_LockSurface(Screen) < 0)
      return;

  // Wypełniamy tło
  drawbackground();

  if (GameState == S_FALLOFF)
  {
    float scale = (Tick - StateStartTick) / 1000.0f;
    if (scale > 1)
    {
      scale = 1;
      NextState = S_ENTRY;
			Sound_Lock = 1;
    }
    scale = 1 - scale;
    scale = scale * scale;

		drawball((int)((XPos + CameraX) * (1 - scale) + (XPos + CameraX) * scale),
							(int)((YPos + CameraY) * (1 - scale) + (YPos + CameraY) * scale),
             (int)(RADIUS * scale), BALL_COLOR, BALL_LOCOLOR, BALL_HICOLOR,
             0, 0);
  }

	int i, j;
	for (i = 0; i < LevelHeight; i++)
	{
		for (j = 0; j < LevelWidth; j++)
		{
			if (Level[i * LevelWidth + j] != 0)
			{
				int tile = 0;

				switch (Level[i * LevelWidth + j])
				{
					case L_START:
						tile = 2;
						break;
					case L_END:
						tile = 1;
						break;
					case L_UP:
						tile = 3;
						break;
					case L_RIGHT:
						tile = 4;
						break;
					case L_DOWN:
						tile = 5;
						break;
					case L_LEFT:
						tile = 6;
						break;
					case L_ROUGH:
						tile = 7;
						break;
					case L_SMOOTH:
						tile = 8;
						break;
					case L_GROUND:
					case L_COIN:
					default:
						// tile = 0;
						break;
				}
				drawtile((int)(j * TILESIZE + CameraX),
								(int)(i * TILESIZE + CameraY), tile);
			}
		}
	}

	// Rysujemy monety
	for (i = 0; i < CoinCount; i++)
	{
		if (Coin[i].Taken == 0)
		{
			drawcircle((int)(Coin[i].X + 2 + CameraX),
						(int)(Coin[i].Y + 2 + CameraY),
						Coin[i].Radius,
						0);
			drawcircle((int)(Coin[i].X + CameraX),
						(int)(Coin[i].Y + CameraY),
						Coin[i].Radius,
						Coin[i].Color);
		}
	}

	// Wypisujemy status
	char statusstring[80];
	sprintf(statusstring, "'%s', time limit:%ds", LevelName, LevelTime);
	drawstring(5, 5, statusstring);

	sprintf(statusstring, "Score:%d", Score);
	drawstring(5, 22, statusstring);

	int secondsleft = LevelTime - (LastTick - LevelStartTick) / 1000;
	if (secondsleft < 0)
		secondsleft = 0;
	sprintf(statusstring, "Time:%d", secondsleft);
	drawstring(5, 39, statusstring);

	// Rysujemy piłeczkę gracza
	switch (GameState)
	{
		case S_FALLOFF:
			break;
		case S_ENTRY:
		{
			float scale = (Tick - StateStartTick) / 1000.0f;
			if (scale > 1)
			{
				scale = 1;
				NextState = S_READY;
			}
			scale = 1 - scale;
			float pscale = scale * scale;

			drawball((int)(XPos + CameraX), (int)(YPos + CameraY),
							(int)(RADIUS + pscale * 200), BALL_COLOR, BALL_LOCOLOR, BALL_HICOLOR,
							0.4f * scale, scale * PI);
		}
			break;
		case S_TRANSITION_IN:
			break;
		default:
			drawball((int)(XPos + CameraX), (int)(YPos + CameraY),
							RADIUS, BALL_COLOR, BALL_LOCOLOR, BALL_HICOLOR,
							Roll/10.0f, (float)atan2(YRoll, XRoll));
			break;
	}

  Roll = 0;

  if (GameState == S_ENTRY || GameState == S_READY)
  {
      drawstring(200, 140, "Get ready!");
  }

  if (GameState == S_READY)
  {
Sound_Lock_1 = 1;
      drawstring(230, 160, "Go!");
      if (Tick - StateStartTick > 500)
          NextState = S_INGAME;
  }

  if (GameState == S_FALLOFF)
  {
    drawstring(200, 140, "Fall off!");
  }

 // if (GameState == S_FALLOFF)
 // {
 //   Mix_PlayChannel(1,s_fall,0);
 // }

  if (GameState == S_ENDLEVEL)
  {
			if(Sound_Lock_1)
			{
				Mix_PlayChannel(2,s_winner,0);
				Sound_Lock_1 = 0;
			}
    int eventpos = Tick - StateStartTick;
    char temp[80];
    drawstring(160, 120, "Level finished!");
    if (eventpos > 500)
    {
      float seconds = (LastTick - LevelStartTick) / 1000.0f;
      int bonus = (int)(LevelTime - seconds) * 25;
      if (bonus < 0)
        bonus = 0;
      sprintf(temp, "Time:%0.2fs Bonus:%d", seconds, bonus);
      drawstring(160, 140, temp);
      if (eventpos > 1000)
      {
        sprintf(temp, "Coins:%d%%", CoinsTaken * 100 / CoinCount);
        drawstring(160, 160, temp);
        if (eventpos > 2000 && (eventpos % 2000 > 500))
          drawstring(160, 200, "Hit space for next level!");
      }
    }
  }

	if (GameState == S_TRANSITION_OUT)
	{
		float v = (Tick-TransitionStartTick)/4000.0f;
		transition(v);
		if (v > 1.0f)
			NextState = S_TRANSITION_IN;
	}

	if (GameState == S_TRANSITION_IN)
	{
		if (Lock == 1)
		{
			ScreenTransition = SDL_ConvertSurface(Screen, Screen->format, SDL_SWSURFACE);
			Lock = 0;
		}
		float v = ((Tick-TransitionStartTick)/4000.0f) - 1.0f;
		transition(v);
		if (v > 0.0f)
			NextState = S_ENTRY;
	}

	if (SDL_MUSTLOCK(Screen))
		SDL_UnlockSurface(Screen);

	// Uaktualnijmy cały ekran
	SDL_UpdateRect(Screen, 0, 0, WIDTH, HEIGHT);

	FramesRendered++;
	sprintf(caption, "FPS: %.0f",((float)FramesRendered/(float)(Tick/1000)));
	SDL_WM_SetCaption(ptr, "Icon");

}
