#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>

#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_audio.h>
#include <allegro5/allegro_acodec.h>


#include "chapters.h"


//#define DEBUG

#ifdef DEBUG

#  define ASSERT(cond) if (!(cond)) { printf("\n\n" __FILE__ " : %d   ASSERT FAILED: " #cond "\n\n\n", __LINE__); abort(); }
#  define DEBUG_PRINTF printf

#  define WIN_MIN_W 300
#  define WIN_MIN_H 300

#else

#  define ASSERT
#  define DEBUG_PRINTF

#  define WIN_MIN_W 400
#  define WIN_MIN_H 400

#endif


#define FPS 50

#define MAIN_FONTFILE "snowstorm.otf"

#define MEBIBYTE (1024.0f * 1024.0f)

#define RADIAN (6.283185307)
#define FULL_CIRCLE 1

#define DEF_THICC 5.0f


typedef struct edge_t edge_t;
typedef struct vert_t vert_t;
typedef struct poly_t poly_t;
typedef struct trans_t trans_t;
typedef struct palette_t palette_t;


/*
  The coordinate system spans from (-10,-10) (top left) to (+10,+10)
  (bottom right), with (0,0) being the center of the screen.

  If the physical window size has other proportions than 1:1 it is
  padded with black.

  Polar coordinates simply use 1 as a full circle (0.5 is 180 degrees,
  etc), not some bullshit radians.
*/


/*
  Describes a vertex, i.e. a point on screen.
  
  Vertices can be of three types:

  - cartesian, an (x,y) coordinate pair
  - polar, a vector with angle and distance
  - anchor, mirroring the position of another vertex

  Vertices have a logical position (of the types listed above) and a
  "screen" position (in pixels). The screen position is calculated by
  update_poly() and the value is used directly by render_poly() for
  drawing, but also used by update_poly() for relative positioning of
  polys (which have the vertex as origin) and anchor vertices (which
  directly copy the position of the anchor, regardless of the position
  or rotation of their parent poly).
*/
struct vert_t
{
  int polar;
  float angl;
  float dist;

  float x;
  float y;

  float screen_x;
  float screen_y;
  float temp_angl;

  vert_t * anchor;
};


/*
  Describes an edge, i.e. a line between two vertices.

  Several edges originate from the same vertex. There can even be
  several edges between the same pair of vertices, but this is
  pointless since they will overlap on screen.
*/
struct edge_t
{
  vert_t * start;
  vert_t * end;
  
  // Edges belonging to the same poly are stored as a linked list
  edge_t * next;

  // Each edge can have an individual color.
  // Overridden by poly_t.single_color
  ALLEGRO_COLOR color;
};


/*
  Describes a poly(gon), shape, or whatever.
*/
struct poly_t
{
  // The base used for relative positioning of all edges in this poly
  vert_t * origin;

  // First edge, rest will follow in a linked list
  // Each poly has zero or more edges
  edge_t * first_edge;

  // Tail of linked list, for quick access
  edge_t * last_edge;

  // Added 
  float rotation;

  // Multiplier for all coordinates belonging to this poly.  Note that
  // this also affects the distance from origin, e.g. 2 will make the
  // object appear twice as large, but also twice as far away!
  float scale;

  int visible;
  //int piggyback;

  // Set true if this poly should re-use the .angl of the origin
  int link_angl;

  // Thickness of (all) edges in this poly
  float thicc;

  // Apply a slight fluctuation in color each frame
  int flicker;

  // If .single_color is set .color is used for all edges
  // Text always uses .color, 
  int single_color;
  ALLEGRO_COLOR color;

  // Used for text objects
  char * text;
  int text_flags;
  int font;
};



// Pointer to a function that should run each frame
typedef void (*anim_func_ptr)(void * arg);

/*
  Describes a transition used for animation.

  Each transition has a "from" and a "to" value. For each frame the
  transition is run it will interpolate these values for the current
  frame and store it at the destination address.

  Transitions can be of four types
  - integer
  - float
  - color (i.e. 4 integers 0-255, mapped to an Allegro RGBA color)
  - custom function (with void* argument) to call each frame

  Transitions can have inertia, i.e. retain a delta and
  accelerate/brake, instead of setting an absolute value.

  Each scene is made up of a linked list of transitions. The scene
  ends once all transitions have finished.
*/
struct trans_t
{
  // Number of frames, from scene start, until it should start running
  int delay;

  // Number of frames the transition should run
  int length;

  // Current frame
  int frame;

  // What kind of curve to use for interpolation
  int curve;

  // Float interpolation
  float * dest_f;
  float from_f;
  float to_f;

  int inertia;
  trans_t * inertia_chain;
  float vel_f;
  float acc_f;

  // Integer interpolation
  int * dest_i;
  int from_i[4];
  int to_i[4];

  // Destination for color value
  ALLEGRO_COLOR * dest_c;

  // Run a custom function
  anim_func_ptr custom_func;
  void * custom_arg;

  trans_t * next;
};


enum
{
  CURVE_LINEAR,
  CURVE_SQUARE,
  CURVE_CUBIC,
  CURVE_RSQUARE,
  CURVE_RCUBIC,
  CURVE_EXP,
  CURVE_REXP,
  CURVE_BUMP,
  CURVE_RBUMP
};


// palette.c
void init_palettes(void);
void apply_pal(poly_t * poly, int index);

#define COLORS_PER_PAL 100

enum palettes_t
{
  pal_green,
  pal_blood,
  pal_magenta,
  pal_yellow,
  pal_rainbow,
  pal_deep_green,
  pal_black,
  pal_orange,
  pal_blue,
  pal_max
};

struct palette_t
{
  int colors;
  ALLEGRO_COLOR color[COLORS_PER_PAL];
};

extern palette_t pal[pal_max];


// scenes.c
extern int current_scene;
extern char scene_name[50];

trans_t * init_scene(int chapter);
void set_scene_name(char * s);
void format_frame_count(void * arg);


// mem.c
extern int used_verts;
extern int used_edges;
extern int used_polys;
extern int used_trans;

extern vert_t * all_verts;
extern edge_t * all_edges;
extern poly_t * all_polys;
extern trans_t * all_trans;

void alloc_all(void);
void free_all(void);
vert_t * get_new_vert(void);
edge_t * get_new_edge(void);
poly_t * get_new_poly(void);
trans_t * get_new_trans(void);


// trans.c
trans_t * new_trans(trans_t * list, float * dest, float from, float to, int length, int delay);
trans_t * new_trans_i(trans_t * list, int * dest, int from, int to, int length, int delay);
trans_t * new_trans_func(trans_t * list, anim_func_ptr func, void * arg, int length, int delay);
trans_t * new_trans_ca(trans_t * list, ALLEGRO_COLOR * dest,
                       int r1, int g1, int b1, int a1,
                       int r2, int g2, int b2, int a2,
                       int length, int delay);
trans_t * new_trans_c(trans_t * list, ALLEGRO_COLOR * dest,
                      int r1, int g1, int b1,
                      int r2, int g2, int b2,
                      int length, int delay);
int run_trans(trans_t * list);
float curve_coeff(float input, int curve_type);


// geometry.c
vert_t * make_fixpoint(float x, float y);
vert_t * make_polar(float angl, float dist);
vert_t * make_anchor(vert_t * original);
vert_t * clone_vert(vert_t * original);
vert_t * clone_mirr(vert_t * original);
poly_t * clone_poly(poly_t * original);
poly_t * make_poly(vert_t * origin);
edge_t * add_edge(poly_t * poly, vert_t * start, vert_t * end);
poly_t * make_ngon(vert_t * origin, float radius, int sides, int step);
edge_t * add_fedge(poly_t * poly, float from_x, float from_y, float to_x, float to_y);
edge_t * add_cedge(poly_t * poly, float to_x, float to_y);
void join_edges(poly_t * a, poly_t * b);
poly_t * make_text(vert_t * origin, char * text);
vert_t * fix_to_polar(float origin_x, float origin_y, float fix_x, float fix_y);
void dump_vert(vert_t * vert);
void dump_poly(poly_t * poly);
vert_t * edge_end(edge_t * edge);
poly_t * convert_polar(poly_t * orig, float origin_x, float origin_y);
vert_t * vert_to_polar(vert_t * v, float origin_x, float origin_y);
void normalize_origin(poly_t * poly);


// demo.c
extern int frame;
extern int start_fullscreen;
extern int have_music;
extern int display_timecode;

extern vert_t * centerpoint;
extern poly_t * first_to_render;
extern poly_t * last_to_render;
extern trans_t * current_trans;

void animate(void);
int system_event(ALLEGRO_EVENT * ev);
void get_timecode(char * dest, size_t len);


// gfx.c
extern ALLEGRO_TIMER * fpstimer;
extern ALLEGRO_EVENT_QUEUE * event_queue;
extern ALLEGRO_DISPLAY * realdisp;
extern ALLEGRO_AUDIO_STREAM * music;
extern ALLEGRO_COLOR bg_color;
extern ALLEGRO_COLOR cl_black;
extern ALLEGRO_COLOR cl_white;
extern ALLEGRO_FONT * font;
extern ALLEGRO_FONT * font_big;

extern float fps_avg;
extern int fps_frames;
extern int fps_secs;

int gfx_init(void);
void gfx_shutdown(void);
void configure_canvas(void);
void blank_screen(void);
void update_poly(poly_t * poly);
void render(void);
void render_poly(poly_t * poly);
int reload_fonts(void);


// random.c
int get_rand8(void);
int get_rand16(void);
