#include "demo.h"

ALLEGRO_DISPLAY * realdisp = NULL;
ALLEGRO_EVENT_QUEUE * event_queue = NULL;
ALLEGRO_EVENT_QUEUE * system_queue = NULL;
ALLEGRO_TIMER * timer = NULL;
ALLEGRO_TIMER * fpstimer = NULL;

float fps_avg = 0;
int fps_frames = 0;
int fps_secs = 1;

ALLEGRO_AUDIO_STREAM * music;

ALLEGRO_COLOR bg_color;
ALLEGRO_COLOR cl_black;
ALLEGRO_COLOR cl_white;

float center_x = 0;
float center_y = 0;

float base_scale = 100;

int canvas_w = 0;
int canvas_h = 0;

int window_w = WIN_MIN_W;
int window_h = WIN_MIN_H;

// Fonts are rendered by Allegro at fixed pixel heights.
float font_scale = 0.074074;
float font_big_scale = 0.111111;

int font_h = 0.074074;
int font_big_h = 0.111111;

ALLEGRO_FONT * font = NULL;
ALLEGRO_FONT * font_big = NULL;



int gfx_init()
{
#define BAIL_OUT(reason) { printf("%s\n", reason); goto it_broke; }
  
  if (al_init() == 0) BAIL_OUT("Failed to initialize Allegro (al_init)!");
  if (al_install_keyboard() == 0) BAIL_OUT("Failed to initialize Allegro (keyboard)!");

  if ((timer = al_create_timer(1.0 / (float)FPS)) == NULL ||
      (fpstimer = al_create_timer(1)) == NULL)
    BAIL_OUT("Failed to initialize Allegro (timers)!");
  
  if (al_init_image_addon() == 0)
  {
    printf("Warning: Failed to initialize Allegro (image_addon)!\n");
  }

  if ((event_queue = al_create_event_queue()) == NULL ||
      (system_queue = al_create_event_queue()) == NULL)
    BAIL_OUT("Failed to initialize Allegro (queues)!");
  
  if (al_init_primitives_addon() == 0)
    BAIL_OUT("Failed to initialize Allegro (primitives)!");
  
  al_init_font_addon();

  if (al_init_ttf_addon() == 0)
    BAIL_OUT("Failed to initialize Allegro (ttf_addon)!");

  if (al_install_audio() == 0)     BAIL_OUT("Failed to initialize Allegro (install_audio)!");
  if (al_init_acodec_addon() == 0) BAIL_OUT("Failed to initialize Allegro (acodec)!");
  if (al_reserve_samples(5) == 0)  BAIL_OUT("Failed to initialize Allegro (samples)!");

  music = al_load_audio_stream("rymdsyra.ogg", 4, 4096);

  if (music == NULL) BAIL_OUT("Failed to load music!");

  al_set_audio_stream_playing(music, false);
  al_attach_audio_stream_to_mixer(music, al_get_default_mixer());
  al_set_audio_stream_playmode(music, ALLEGRO_PLAYMODE_ONCE);
  
  bg_color = cl_black = al_map_rgb(0, 0, 0);
  cl_white = al_map_rgb(255, 255, 255);

  al_set_new_display_flags(ALLEGRO_OPENGL
                           | ALLEGRO_RESIZABLE
                           | (start_fullscreen ? ALLEGRO_FULLSCREEN_WINDOW : 0));
  realdisp = al_create_display(window_w, window_h);

  al_hide_mouse_cursor(realdisp);

  if (realdisp == NULL) BAIL_OUT("Failed to create display.");

  al_set_window_title(realdisp, "I'm 14 and this is cool as SHIT");

  configure_canvas();

  if (reload_fonts() == 0)
    BAIL_OUT("Failed to load fonts.");

  /* Set up the event queue */
  al_register_event_source(event_queue, al_get_display_event_source(realdisp));
  al_register_event_source(event_queue, al_get_keyboard_event_source());
  al_register_event_source(event_queue, al_get_timer_event_source(timer));
  al_register_event_source(event_queue, al_get_timer_event_source(fpstimer));

  al_start_timer(timer);
  al_start_timer(fpstimer);

  init_palettes();

  blank_screen();

  return 0;

it_broke:
  exit(1);

  return 1;
}



void gfx_shutdown()
{
  al_destroy_font(font);
  al_destroy_font(font_big);

  if (event_queue)
    al_destroy_event_queue(event_queue);

  if (system_queue)
    al_destroy_event_queue(system_queue);

  //al_set_audio_stream_playing(music, false);
  al_detach_audio_stream(music);
  al_destroy_audio_stream(music);

  al_uninstall_system();

  free_all();

  exit(0);
}



int reload_fonts()
{
  al_destroy_font(font);
  al_destroy_font(font_big);

  font_h = -font_scale * canvas_h;
  font_big_h = -font_big_scale * canvas_h;

  DEBUG_PRINTF("Reloading fonts at %d and %d px...\n", font_h, font_big_h);
  
  font = al_load_ttf_font(MAIN_FONTFILE, font_h, 0);
  font_big = al_load_ttf_font(MAIN_FONTFILE, font_big_h, 0);

  return !(font == NULL || font_big == NULL);
}



/*
  Figure out the dimensions of the physical display and what offset
  and clipping is needed to make the canvas appear as large as
  possible at its center.
*/
void configure_canvas()
{
  window_w = al_get_display_width(realdisp);
  window_h = al_get_display_height(realdisp);

  canvas_w = canvas_h = (window_w < window_h ? window_w : window_h);

  center_x = window_w / 2.0f;
  center_y = window_h / 2.0f;

  base_scale = canvas_w / 20.0f;

  DEBUG_PRINTF("Configured canvas to %dx%d in %dx%d window\n",
               canvas_w, canvas_h, window_w, window_h);

  // We need to flip and clear BOTH sides of the screen buffer, to
  // avoid either ending up full of crap after window resize
  
  al_reset_clipping_rectangle();
  al_clear_to_color(cl_black);
  al_flip_display();
  al_clear_to_color(cl_black);
  al_set_clipping_rectangle(center_x - canvas_w / 2, center_y - canvas_h / 2, canvas_w, canvas_h);

  return;
}



/*
  Update the screen coordinates of poly.
*/
void update_poly(poly_t * poly)
{
  ASSERT(poly != NULL);

  float origin_x = 0;
  float origin_y = 0;
  float origin_r = 0;

  // polys must always have an origin (even if it's "centerpoint")
  ASSERT(poly->origin != NULL);

  if (poly->origin)
  {
    origin_x = poly->origin->x;
    origin_y = poly->origin->y;
    
    if (poly->link_angl)
      origin_r = poly->origin->temp_angl;
    else
      origin_r = poly->origin->angl;
  }
    
  vert_t * v[2];

  float rot = poly->rotation;
  float scale = poly->scale * base_scale;

  for (edge_t * edge = poly->first_edge; edge; edge = edge->next)
  {
    /*
      This is not optimal since vertices might be processed multiple
      times if they are used in multiple edges. A cheaper way could be
      to have a finalize_poly() function to build a list of unique
      vertices and process each one only once. However, it would
      complicate the poly_t structure with a variable-sized vertices
      list, and I think it would be a neglible optimization anyway...
    */
    
    v[0] = edge->start;
    v[1] = edge->end;

    for (int i = 0; i < 2; i++)
    {
      if (v[i]->polar)
      {
        v[i]->x = origin_x + (v[i]->dist * cosf((v[i]->angl + rot + origin_r) * RADIAN));
        v[i]->y = origin_y + (v[i]->dist * sinf((v[i]->angl + rot + origin_r) * RADIAN));
        v[i]->screen_x = center_x + scale * (v[i]->x);
        v[i]->screen_y = center_y + scale * (v[i]->y);
        v[i]->temp_angl = (v[i]->angl + origin_r + rot);
      }
      else if (v[i]->anchor)
      {
        // An anchored vertex will just mirror the coordinates of the anchor
        // This overrides poly origin
        v[i]->screen_x = v[i]->anchor->screen_x;
        v[i]->screen_y = v[i]->anchor->screen_y;
      }
      else
      {
        v[i]->screen_x = center_x + scale * (v[i]->x + origin_x);
        v[i]->screen_y = center_y + scale * (v[i]->y + origin_y);
      }
    }
  }
}



#define FLICKER_COLOR(color)                                            \
  if (poly->flicker)                                                    \
  { float r, g, b;                                                      \
    float rnd = 1.0 + ((float)get_rand8()/(float)(256)) * 0.2;          \
    al_unmap_rgb_f(color, &r, &g, &b);                                  \
    color = al_map_rgb_f(r * rnd,                                       \
                         g * rnd,                                       \
                         b * rnd                                        \
                         ); }

void render_poly(poly_t * poly)
{
  ASSERT(poly != NULL);

  float thicc = poly->thicc;
  ALLEGRO_COLOR color = cl_white;

  if (poly->text)
  {
    color = poly->color;
    FLICKER_COLOR(color);

    ALLEGRO_FONT * use_font = font;
    int offset = font_h * 0.5;
  
    if (poly->font == 1)
    {
      use_font = font_big;
      offset = font_big_h * 0.5;
    }

    al_draw_text(use_font, color,
                 center_x + poly->origin->x * base_scale,
                 center_y + poly->origin->y * base_scale + offset,
                 poly->text_flags, poly->text);
  }
  else
  {
    for (edge_t * edge = poly->first_edge; edge; edge = edge->next)
    {
      if (poly->single_color)
        color = poly->color;
      else
        color = edge->color;
      
      FLICKER_COLOR(color);

      al_draw_line(edge->start->screen_x, edge->start->screen_y,
                   edge->end->screen_x, edge->end->screen_y,
                   color, thicc);
    }
  }
}



void render()
{
  al_clear_to_color(bg_color);

  for (poly_t * r = first_to_render; r && r <= last_to_render; r++)
  {
    update_poly(r);

    if (r->visible)
      render_poly(r);
  }

  if (display_timecode)
  {
    char fps[10];
    snprintf(fps, sizeof(fps), "%2.1f", fps_avg);
    
    char timecode[50];
    get_timecode(timecode, sizeof(timecode));
    char * divider = strchr(timecode, ' ');
    ASSERT(divider != NULL);
    *divider++ = 0;

    al_draw_text(font, cl_white,
                 center_x - 9 * base_scale,
                 center_y + 9 * base_scale + font_h / 2,
                 ALLEGRO_ALIGN_LEFT, timecode);

    al_draw_text(font, cl_white,
                 center_x + 9 * base_scale,
                 center_y + 9 * base_scale + font_h / 2,
                 ALLEGRO_ALIGN_RIGHT, divider);

    al_draw_text(font, cl_white,
                 center_x + 9 * base_scale,
                 center_y - 9 * base_scale + font_h / 2,
                 ALLEGRO_ALIGN_RIGHT, fps);
  }
  
  al_flip_display();

  fps_frames++;
  
  return;
}



void blank_screen()
{
  al_clear_to_color(al_map_rgb(0, 0, 0));
  al_flip_display();
}
