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


#ifdef __linux__
#include <getopt.h>
#endif

using namespace std;

SDL_Surface *Screen;
SDL_Surface *ScreenTransition;
SDL_Surface *Tiles;
SDL_Surface *Font;
SDL_Surface *Texture;

SDL_Joystick *Joystick = NULL;

int CoinCount;
int CoinsTaken;
coin_s *Coin;

int LastTick;
int LevelStartTick;

float XPos;
float YPos;

float XMov;
float YMov;

float StartX;
float StartY;

float XRoll;
float YRoll;

float Roll;

int KeyLeft;
int KeyRight;
int KeyUp;
int KeyDown;

int Score;
int CurrentLevel;

float CameraX;
float CameraY;

unsigned char *Level;

char *LevelName;
int LevelTime;

int LevelWidth;
int LevelHeight;

int FontOfs[256];
int FontWidth[256];

vertex_s *Vertex;
vertex_s *RVertex;

int NextState;
int GameState;
int StateStartTick;
int TransitionStartTick = 0;

int Lock = 1;
int Tick = 0;
int Sound_Lock = 1;
int Sound_Lock_1 = 1;
int FramesRendered = 0;

char caption[5];
const char * ptr = caption;

void init()
{
	// Ze względu na obecność operacji na bitach i brak wsparcia
	// dla procesorów innych niż little endian (eg. x86) sprawdzamy
	// kompatybilność procesora z programem
	cout << "Processor type: "
			 << ((SDL_BYTEORDER == SDL_LIL_ENDIAN) ? "Little" : "Big")
			 << " endian." << endl;
	if (SDL_BYTEORDER != SDL_LIL_ENDIAN)
	{
		cerr << "This game is limited to work on little endian processors such as x86."
				 << endl;
		exit(EXIT_FAILURE);
	}

	// Pobieramy informacje o sterowniku graficznym
	char * driver_name_buffer = new char[8];
	SDL_VideoDriverName(driver_name_buffer, 8);

	const SDL_VideoInfo *info = SDL_GetVideoInfo();

	if (driver_name_buffer != NULL)
	{
		cout << "Video Driver Name: " << driver_name_buffer << endl;
		delete[] driver_name_buffer;
	}
	else
		cerr << "Unable to get Video Driver Name!";

	cout << "Total Video Memory: "
			 << info->video_mem << "." << endl;
	cout << "Hardware Surfaces: "
			 << ((info->hw_available == 0) ? "No." : "Yes.")
			 << endl;
	cout << "Hardware Blit Acceleration: "
			 << ((info->blit_hw == 0) ? "No." : "Yes.")
			 << endl;
	cout << "Hardware Colorkey Blit Acceleration: "
			 << ((info->blit_hw_CC == 0) ? "No." : "Yes.")
			 << endl;
	cout << "Hardware Alpha Blit Acceleration: "
			 << ((info->blit_hw_A == 0) ? "No." : "Yes.")
			 << endl;
	cout << "Software to Hardware Blit Acceleration: "
			 << ((info->blit_sw == 0) ? "No." : "Yes.")
			 << endl;
	cout << "Software to Hardware Colorkey Blit Acceleration: "
			 << ((info->blit_sw_CC == 0) ? "No." : "Yes.")
			 << endl;
	cout << "Software to Hardware Alpha Blit Acceleration: "
			 << ((info->blit_sw_A == 0) ? "No." : "Yes.")
			 << endl;
	cout << "Color Fill Acceleration: "
			 << ((info->blit_fill == 0) ? "No." : "Yes.")
			 << endl;

	// Sprawdzamy dostępność joystick'a
	cout << "Joysticks: ";
  if (SDL_NumJoysticks() > 0)
  {
    Joystick = SDL_JoystickOpen(0);
    if (SDL_JoystickNumAxes(Joystick) < 2)
    {
      SDL_JoystickClose(Joystick);
      Joystick = NULL;
			cout << "Available, but unsufficient axes (need at least two)."
					 << endl;
    }
		cout << "Available, using default one." << endl;
  }
	else
		cout << "Not Available." << endl;

	// Tworzymy wierzchołki na piłeczce gracza
  Vertex = new vertex_s[BALL_VERTEX_COUNT];
  RVertex = new vertex_s[BALL_VERTEX_COUNT];
  int i, j, k;

  for (i = 0; i < BALL_VERTEX_COUNT; i++)
  {
    Vertex[i].X = (rand() % 32768) - 16384.0f;
    Vertex[i].Y = (rand() % 32768) - 16384.0f;
    Vertex[i].Z = (rand() % 32768) - 16384.0f;
    float len = (float)sqrt(Vertex[i].X * Vertex[i].X +
                            Vertex[i].Y * Vertex[i].Y +
                            Vertex[i].Z * Vertex[i].Z);
    if (len != 0)
    {
      Vertex[i].X /= len;
      Vertex[i].Y /= len;
      Vertex[i].Z /= len;
    }
  }

	// Ustawiamy niezbędne zmienne
  Coin = NULL;
  Level = NULL;
  LevelName = NULL;
  Score = 0;
  CurrentLevel = 0;
  GameState = S_INIT;
  NextState = S_ENTRY;

	// Ładujemy  pliki graficzne z teksturami planszy...
  SDL_Surface *temp = SDL_LoadBMP("tiles.bmp");
	Tiles = SDL_ConvertSurface(temp, Screen->format, SDL_HWSURFACE);
  SDL_FreeSurface(temp);

	// ... oraz z czcionką
  temp = SDL_LoadBMP("font.bmp");
	Font = SDL_ConvertSurface(temp, Screen->format, SDL_SWSURFACE );
  SDL_FreeSurface(temp);

	// Inicjujemy czcionkę
  if (SDL_MUSTLOCK(Font))
    if (SDL_LockSurface(Font) < 0)
      return;

  for (k = 0; k < Font->h / Font->w; k++)
  {
    FontOfs[k] = Font->w;
    FontWidth[k] = 0;
    for (i = 0; i < Font->w; i++)
    {
      for (j = 0; j < Font->w; j++)
      {
        if (((unsigned int*)Font->pixels)[(i + k * Font->w) *
                                           (Font->pitch / 4) + j] != 0)
        {
          if (j < FontOfs[k])
            FontOfs[k] = j;

          if (j > FontWidth[k])
            FontWidth[k] = j;
        }
      }
    }
    FontWidth[k] -= FontOfs[k];
  }

  if (SDL_MUSTLOCK(Font))
    SDL_UnlockSurface(Font);
}

void deinit()
{
  if (Joystick)
    SDL_JoystickClose(Joystick);
    Close_Audio();
}

void changestate()
{
    // Rzeczy, które potrzebujemy zrobić gdy opuszczamy stan
    switch (GameState)
    {
			case S_ENDLEVEL:
				CurrentLevel++;
				break;
			case S_TRANSITION_OUT:
				reset();
				break;
			case S_TRANSITION_IN:
				Lock = 1;
				break;
    }

    // Przełączamy stan
    GameState = NextState;

    // Rzeczy, które potrzebujemy zrobić gdy przechodzimy do nowego stanu
    switch (GameState)
    {
			case S_INGAME:
				LastTick = SDL_GetTicks();
				LevelStartTick = SDL_GetTicks();
				break;
			case S_TRANSITION_OUT:
				ScreenTransition = SDL_ConvertSurface(Screen, Screen->format, SDL_SWSURFACE);
				TransitionStartTick = SDL_GetTicks();
				break;
			case S_TRANSITION_IN:
				TransitionStartTick = SDL_GetTicks();
				break;
			case S_ENTRY:
				reset();
				break;
    }

    StateStartTick = SDL_GetTicks();
}

void render()
{
    while (NextState != GameState)
      changestate();
    switch (GameState)
    {
			case S_ENTRY:
			case S_READY:
			case S_INGAME:
			case S_FALLOFF:
			case S_ENDLEVEL:
			case S_TRANSITION_IN:
			case S_TRANSITION_OUT:
					rendergame();
					break;
    }
}

int main(int argc, char *argv[])
{

	// Pod linuksem możemy włączyć akcelerację sprzętową poprzez
	// dga, jeśli obsługa dga jest obecna w systemie
#ifdef __linux__
  // Parse command line arguments.
  opterr = 1;
  const struct option long_options[] =
  {
    { "verbose", 0, NULL, 'v' },
    { "use-dga", 0, NULL, 'd' },
    { "use-x11", 0, NULL, 'x' },
    { "usage", 0, NULL, 'u' },
    { NULL, 0, NULL, 0 }
  };

  const char* const short_options = "vdxu";
  int option_index = 0, next_option;
	accel_enum linux_accel = VIDEO_X11;

  do {
    next_option = getopt_long(argc, argv, short_options,
                                  long_options,
                                  &option_index);
    switch ( next_option )
    {
      case 'u':
        //usage();
        exit(EXIT_SUCCESS);
      case 'd':
        linux_accel = VIDEO_DGA;
        break;
      case 'x':
        break;
      case 'v':
        break;
      default:
        break;
    }
  }
  while (next_option != -1);

  if (linux_accel == VIDEO_X11)
    putenv("SDL_VIDEODRIVER=x11");
  else
    putenv("SDL_VIDEODRIVER=dga");
#endif

	// Pod windowsem kompilator, zwróci nam uwagę, że
	// argc i argv są nieużywane. W poniższy sposób się
	// go pozbywamy ;)
  argc = 0;
  argv = NULL;

  // Inicjalizujemy podsystemy SDL
  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0)
  {
    cerr << "Unable to init SDL: " << SDL_GetError() << endl;
    exit(EXIT_FAILURE);
  }

  // Rejestrujemy SDL_Quit, aby była uruchomiona podczas zamykania
	// programu. Dzięki temu mamy pewność, że nie pozostawimy po sobie
	// żadnych śmieci.
  atexit(SDL_Quit);

  // Upewnijmy się, że nasz deinit() zostanie wykonany podczas
	// zamykania programu
  atexit(deinit);

  // Tworzymy okno o rozmiarach WIDTHxHEIGHT i 32 bitowej
	// głębi kolorów.
	Screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_SWSURFACE);

  if (Screen == NULL)
  {
    cerr << "Unable to set up video: " << SDL_GetError() << endl;
    exit(EXIT_FAILURE);
  }

	init();

// Włączamy muzykę
	Play_Music();
	Mix_PlayMusic(music,-1);

  // Główna pętla gry
  while (1)
  {
    render();

    // Obsługa zdarzeń
    SDL_Event event;
    while (SDL_PollEvent(&event))
    {
      switch (event.type)
      {
      case SDL_KEYDOWN:
        switch (event.key.keysym.sym)
        {
						case SDLK_LEFT:
							KeyLeft = 1;
							break;
						case SDLK_RIGHT:
							KeyRight = 1;
							break;
						case SDLK_UP:
							KeyUp = 1;
							break;
						case SDLK_DOWN:
							KeyDown = 1;
							break;
						default:
							break;
        }
        break;
      case SDL_KEYUP:
        switch (event.key.keysym.sym)
        {
					case SDLK_ESCAPE:
						return EXIT_SUCCESS;
					case SDLK_RETURN:
					case SDLK_SPACE:
						if (GameState == S_ENDLEVEL)
						{
							NextState = S_TRANSITION_OUT;
						}
						break;
						case SDLK_LEFT:
							KeyLeft = 0;
							break;
						case SDLK_RIGHT:
							KeyRight = 0;
							break;
						case SDLK_UP:
							KeyUp = 0;
							break;
						case SDLK_DOWN:
							KeyDown = 0;
							break;
					default:
						break;
        }
        break;
      case SDL_QUIT:
				return EXIT_SUCCESS;
      default:
				break;
      }
    }
  }
  return EXIT_FAILURE;
}
