#include <ncurses.h>
#include <time.h>
#include <stdlib.h>

#include "galaga.h"
#include "ship.h"
#include "alien.h"

#define MAX_BULLETS 100

// The possible game states
typedef enum {
  PLAYING,
  FINISHED
} state_t;

// Global variables
typedef struct {
  int width, height;
  state_t state;
  ship_t *ship;
  bullet_t *bullets[MAX_BULLETS];
  int nbullets;
  agroup_t *ag;
} galaga_t;

// The game variable struct
galaga_t g;

void setup(int width, int height);
void draw();
void update();
void process_input();
double time_in_milli();

// Begin the game, contain the game loop
void start_game(int width, int height)
{
  setup(width, height);

  // The game loop, with code to control fps
  double start = time_in_milli();
  double dt;
  while(g.state != FINISHED) {
    dt = time_in_milli() - start;
    if(dt > 33) {
      draw();
      update();
      process_input();
      start = time_in_milli();
    }
  }
}

// Closes curses
void end_game()
{
  endwin();
}

void setup(int width, int height)
{
  // ----- NCURSES INIT ----- //
  initscr();            // Initialize ncurses library and clear the screen
  cbreak();             // Disable terminal buffering
  keypad(stdscr, true); // Enables special keyboard characters
  noecho();             // Disable the echoing of characters
  curs_set(false);      // Disable the cursor   
  timeout(false);       // Enable nonblocking input
  // ------------------------ //
  
  // ----- INIT GAME VARIABLES ----- //
  g.width = width;
  g.height = height;
  g.state = PLAYING;
  g.ship = new_ship(g.width / 2, g.height - 1);
  g.nbullets = 0;
  g.ag = new_agroup();
  // ------------------------------- //
}

void draw()
{
  clear();    // Clear the previous frame

  // Draw the ship
  draw_ship(g.ship);

  // Draw the bullets
  for(int i = 0; i < g.nbullets; ++i) {
    draw_bullet(g.bullets[i]);
  }

  // Draw aliens
  draw_agroup(g.ag);

  // Draw the game border
  mvhline(0, 1, '-', g.width); 
  mvvline(0, 0, '|', g.height + 1); 
  mvhline(g.height, 1, '-', g.width);
  mvvline(0, g.width, '|', g.height + 1);

  // Write the changes to the screen
  refresh();
}

void update()
{
  if(g.ship->x >= g.width) g.ship->x = g.width - 1;
  else if(g.ship->x <= 0) g.ship->x = 1;

  // Update the bullets
  for(int i = 0; i < g.nbullets; ++i) {
    update_bullet(g.bullets[i]);
    if(g.bullets[i]->y <= 0) {
      free(g.bullets[i]);
      g.bullets[i] = g.bullets[--g.nbullets];
    }
  }
}

void process_input()
{
  int c = getch();

  switch(c) {
    case KEY_LEFT:
      g.ship->x--;
      break;
    case KEY_RIGHT:
      g.ship->x++;
      break;
    case ' ':
      if(g.nbullets < MAX_BULLETS) {
        bullet_t *b = shoot(g.ship);
        g.bullets[g.nbullets++] = b;
      }
      break;
  }
}

double time_in_milli()
{
  return (clock() / (double)CLOCKS_PER_SEC) * 1000.0;
}