/******************************************************************************
* FILE:   main.cc
*         The main function and general functionality of the tetris game.
*
* Author: Dana Vrajitoru, IUSB
* Class:  C481 B581 Computer Graphics
******************************************************************************/

#include <gnome.h>
#include <iostream.h>
#include <stdlib.h>
#include <time.h>
#include "tetris_brick.h"
#include "gui_handle.h"
#include "draw.h"
#include "main.h"

Table_type the_table;
Tetris_brick the_brick;
int game_end = 0, score = 0, level = 1;
const int MAX_INTERVAL = 400;
int interval = MAX_INTERVAL;
GtkWidget *status_bar;
guint timeout_id = 0;

int main(int argc, char **argv)
{
  int i;
  GtkWidget *app;
  GtkWidget *area;
  
  srand(time(NULL));
  gnome_init("tetris", "1.0", argc, argv);
  app = gnome_app_new("tetris", "Gnome Tetris");

  area = gtk_drawing_area_new();
  gtk_widget_set_usize(area, T_WIDTH*C_WIDTH, T_HEIGHT*C_HEIGHT);
  gnome_app_set_contents(GNOME_APP(app),area);
  Make_menus(area);

  status_bar = gtk_statusbar_new();
  gnome_app_set_statusbar(GNOME_APP(app), status_bar);

  Insert_menu(app);
  Connect_signals(app, area);

  gtk_widget_show_all(app);
  Init_gc(area);
  Init_pixmaps(area);

  Make_empty_table(the_table);
  Draw_table(the_table);
  Draw_brick(the_brick);
  timeout_id = gtk_timeout_add(interval, Next_frame, (gpointer) area);
  gtk_main();
  exit(0);
}

// Defines the events to be applied to the application.
void Connect_signals(GtkWidget *app, GtkWidget *area)
{
  gtk_signal_connect(GTK_OBJECT(app), "delete_event", 
		     GTK_SIGNAL_FUNC(Event_delete), NULL);
  gtk_signal_connect(GTK_OBJECT(app), "destroy", 
		     GTK_SIGNAL_FUNC(Event_destroy), NULL);
  gtk_signal_connect(GTK_OBJECT(app), "key_press_event",
		     GTK_SIGNAL_FUNC(Event_press_key), (gpointer)area);
  gtk_signal_connect(GTK_OBJECT(status_bar), "event", 
                     GTK_SIGNAL_FUNC(Event_score), NULL);
}

// The most important function in the game that defines the next move.
gint Next_frame(gpointer data)
{
  int old_x, old_y, rows;
  if (!game_end) {
    old_x = the_brick.pos_x;
    old_y = the_brick.pos_y;
    if (!the_brick.Move_down(the_table)) {
      rows = the_brick.Write_brick(the_table);
      if (rows > 0) {
	Draw_table(the_table);
	score += rows * rows * 10 * level;
      }
      score += level; // counting the brick itself too
      Adjust_interval(data);
      Event_score((gpointer) status_bar);
      the_brick.Set_brick();
      if (!the_brick.Check_position(the_table))
	game_end = 1;
    }
    else {
      Erase_brick(the_brick, old_x, old_y);
      Draw_brick(the_brick);
    }
    Draw((GtkWidget *)data);
  }
}

// Increases the level and the speed based on the score.
void Adjust_interval(gpointer data)
{
  if (score > 150 * level * level && interval > 50) {
    level++;
    interval -= 50;
    gtk_timeout_remove(timeout_id);
    timeout_id = gtk_timeout_add(interval, Next_frame, data);
  }
}

// Callback for a key being pressed.
gint Event_press_key(GtkWidget *widget, GdkEvent *event, gpointer data)
{
  GdkEventKey *key = (GdkEventKey *) event;
  if (key->keyval == GDK_Left)
    Event_move_left((GtkWidget *)data);
  else if (key->keyval == GDK_Right)
    Event_move_right((GtkWidget *)data);
  else if (key->keyval == GDK_space)
    Event_drop((GtkWidget *)data);
  else if (key->keyval == GDK_Down)
    Event_rotate((GtkWidget *)data);
}

// Move the brick to the left.
void Event_move_left(GtkWidget *widget)
{
  int old_x = the_brick.pos_x;
  int old_y = the_brick.pos_y;
  if (the_brick.Move_left(the_table)) {
    Erase_brick(the_brick, old_x, old_y);
    Draw_brick(the_brick);
    Draw(widget);
  }
}

// Move the brick to the right.
void Event_move_right(GtkWidget *widget)
{
  int old_x = the_brick.pos_x;
  int old_y = the_brick.pos_y;
  if (the_brick.Move_right(the_table)) {
    Erase_brick(the_brick, old_x, old_y);
    Draw_brick(the_brick);
    Draw(widget);
  }
}

// Move the brick to the right.
void Event_rotate(GtkWidget *widget)
{
  int old_x = the_brick.pos_x;
  int old_y = the_brick.pos_y;
  Erase_brick(the_brick, old_x, old_y);
  the_brick.Rotate_CCW();
  if (!the_brick.Check_position(the_table)) 
    the_brick.Rotate_CW();
  Draw_brick(the_brick);
  Draw(widget);
}

// Move the brick all the way down.
void Event_drop(GtkWidget *widget)
{
  int old_x = the_brick.pos_x;
  int old_y = the_brick.pos_y;
  while (the_brick.Move_down(the_table));
  Erase_brick(the_brick, old_x, old_y);
  Draw_brick(the_brick);
  Draw(widget);
}

// Function to update the score shown on the status bar
gint Event_score(gpointer data)
{
  int context_id;
  char score_str[50];

  // get a context ID for successive calls
  context_id = gtk_statusbar_get_context_id((GtkStatusbar *)data, "StatusBar");

  // pop off the old message from the status bar stack
  gtk_statusbar_pop((GtkStatusbar *)data, context_id);

  // construct the message that we want to output
  if (!game_end)
    sprintf(score_str, "Level %d    Score  %d", level, score);
  else
    sprintf(score_str, "Level %d    Total score %d", level, score);

  // push the message onto the status bar stack
  gtk_statusbar_push((GtkStatusbar *)data, context_id, score_str);

  // returning "true" keeps the timer going
  return (true);
} 

// Callback for the new game
void New_game(gpointer area)
{
  game_end = 0;
  score = 0;
  level = 1;
  Make_empty_table(the_table);
  Draw_table(the_table);
  the_brick.Set_brick();
  Draw_brick(the_brick);
  interval = MAX_INTERVAL;
  gtk_timeout_remove(timeout_id);
  timeout_id = gtk_timeout_add(interval, Next_frame, area);
}
