
#include "allegro5/allegro.h"
#include "allegro5/allegro_primitives.h"

#include <fstream>
#include <iostream>
#include <cmath>

using std::ofstream;
using std::endl;



class AllegroBitmap {
public :
   ALLEGRO_BITMAP* bmp;
   int w;
   int h;
   
   AllegroBitmap(ALLEGRO_BITMAP* bitmap) : bmp(bitmap) , w(al_get_bitmap_width(bitmap)) , h(al_get_bitmap_height(bitmap)) {}
   
   operator ALLEGRO_BITMAP*() {return bmp;}
};


ofstream log_file(".\\AnimationTestLog.txt");



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

  if (!log_file) {return 0;}
   (void)argc;
   (void)argv;
   
   if (!al_init()) {
      return 1;
   }

   if (!al_init_primitives_addon()) {
      return 2;
   }
   
   if (!al_install_keyboard()) {
      return 3;
   }
   if (!al_install_mouse()) {
      return 4;
   }
   
   al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED);
   
   int width = 800;
   int height = 600;
   
   ALLEGRO_DISPLAY* display = al_create_display(width , height);
   
   al_clear_to_color(al_map_rgb(0,0,255));
   al_flip_display();
   
   ALLEGRO_EVENT_QUEUE* queue = al_create_event_queue();
   
   ALLEGRO_TIMER* timer = al_create_timer(1.0/60.0);
   
   al_register_event_source(queue , al_get_keyboard_event_source());
   al_register_event_source(queue , al_get_timer_event_source(timer));
   al_register_event_source(queue , al_get_display_event_source(display));
   al_register_event_source(queue , al_get_mouse_event_source());


   bool keys_down[ALLEGRO_KEY_MAX];
   memset(&keys_down , 0 , sizeof(keys_down));



   /// Yes I know this is a memory leak. :P I don't care this is just a demo
   AllegroBitmap* buffer = new AllegroBitmap(al_create_bitmap(800,600));
   AllegroBitmap* pre_buffer = new AllegroBitmap(al_create_bitmap(800,600));
   AllegroBitmap* head = new AllegroBitmap(al_create_bitmap(160,80));
   AllegroBitmap* head_rotated_scaled = new AllegroBitmap(al_create_bitmap(800,600));
   AllegroBitmap* body = new AllegroBitmap(al_create_bitmap(160,320));
   AllegroBitmap* body_rotated_scaled = new AllegroBitmap(al_create_bitmap(800,600));
  
   al_set_blender(ALLEGRO_ADD , ALLEGRO_ONE , ALLEGRO_ZERO);
  
   al_set_target_bitmap(head->bmp);
   al_clear_to_color(al_map_rgba(0,0,0,0));
   al_draw_filled_circle((7*head->w)/8    , (1*head->h)/4     , 14 , al_map_rgba(0,0,0,0));
   al_draw_filled_circle((7*head->w)/8    , (3*head->h)/4     , 14 , al_map_rgba(0,0,0,0));
   
   al_draw_filled_triangle(0    ,     head->h/2     , (3*head->w) / 4 , head->h / 2 , (3*head->w) / 4 ,           0 , al_map_rgba(255,255,0,255));
   al_draw_filled_triangle(0    ,     head->h/2     , (3*head->w) / 4 , head->h / 2 , (3*head->w) / 4 , head->h - 1 , al_map_rgba(255,255,0,255));
   al_draw_filled_triangle(head->w - 1    ,     head->h/2     , (3*head->w) / 4 , head->h / 2 , (3*head->w) / 4 ,           0 , al_map_rgba(255,255,0,255));
   al_draw_filled_triangle(head->w - 1    ,     head->h/2     , (3*head->w) / 4 , head->h / 2 , (3*head->w) / 4 , head->h - 1 , al_map_rgba(255,255,0,255));
   
   al_draw_circle((7*head->w)/8    , (1*head->h)/4     , 16 , al_map_rgba(0,100,0,255) , 1.0);// dark green rim of glasses
   al_draw_circle((7*head->w)/8    , (3*head->h)/4     , 16 , al_map_rgba(0,100,0,255) , 1.0);// dark green rim of glasses
   al_draw_circle((7*head->w)/8    , (1*head->h)/4     , 15 , al_map_rgba(0,100,0,255) , 1.0);// dark green rim of glasses
   al_draw_circle((7*head->w)/8    , (3*head->h)/4     , 15 , al_map_rgba(0,100,0,255) , 1.0);// dark green rim of glasses
   al_draw_circle((7*head->w)/8    , (1*head->h)/4     , 14 , al_map_rgba(0,100,0,255) , 1.0);// dark green rim of glasses
   al_draw_circle((7*head->w)/8    , (3*head->h)/4     , 14 , al_map_rgba(0,100,0,255) , 1.0);// dark green rim of glasses
   al_draw_line((7*head->w)/8 - 1, (1*head->h)/4 + 7 , (7*head->w)/8 - 1, (3*head->h)/4 - 7 , al_map_rgba(0,100,0,255) , 1.0);
   al_draw_line((7*head->w)/8    , (1*head->h)/4 + 7 , (7*head->w)/8 , (3*head->h)/4 - 7 , al_map_rgba(0,100,0,255) , 1.0);
   al_draw_line((7*head->w)/8 + 1, (1*head->h)/4 + 7 , (7*head->w)/8 + 1, (3*head->h)/4 - 7 , al_map_rgba(0,100,0,255) , 1.0);
   al_draw_filled_circle((7*head->w)/8    , (1*head->h)/4 , 10, al_map_rgba(255,255,255,255));
   al_draw_filled_circle((7*head->w)/8    , (3*head->h)/4 , 10, al_map_rgba(255,255,255,255));
   al_draw_filled_circle((7*head->w)/8    , (1*head->h)/4 , 3 , al_map_rgba(0,0,0,255));
   al_draw_filled_circle((7*head->w)/8    , (3*head->h)/4 , 3 , al_map_rgba(0,0,0,255));

/**
  clear_to_color(head , makeacol32(0,0,0,0));
  circlefill(head , (7*head->w)/8    , (1*head->h)/4     , 14 , makeacol32(0,0,0,0));// transparent black for glasses
  circlefill(head , (7*head->w)/8    , (3*head->h)/4     , 14 , makeacol32(0,0,0,0));// transparent black for glasses
  triangle(  head ,             0    ,     head->h/2     , (3*head->w) / 4 , head->h / 2 , (3*head->w) / 4 ,           0 , makeacol32(255,255,0,255));
  triangle(  head ,             0    ,     head->h/2     , (3*head->w) / 4 , head->h / 2 , (3*head->w) / 4 , head->h - 1 , makeacol32(255,255,0,255));
  triangle(  head ,   head->w - 1    ,     head->h/2     , (3*head->w) / 4 , head->h / 2 , (3*head->w) / 4 ,           0 , makeacol32(255,255,0,255));
  triangle(  head ,   head->w - 1    ,     head->h/2     , (3*head->w) / 4 , head->h / 2 , (3*head->w) / 4 , head->h - 1 , makeacol32(255,255,0,255));
  circle(    head , (7*head->w)/8    , (1*head->h)/4     , 16 , makeacol32(0,100,0,255));// dark green rim of glasses
  circle(    head , (7*head->w)/8    , (3*head->h)/4     , 16 , makeacol32(0,100,0,255));// dark green rim of glasses
  circle(    head , (7*head->w)/8    , (1*head->h)/4     , 15 , makeacol32(0,100,0,255));// dark green rim of glasses
  circle(    head , (7*head->w)/8    , (3*head->h)/4     , 15 , makeacol32(0,100,0,255));// dark green rim of glasses
  circle(    head , (7*head->w)/8    , (1*head->h)/4     , 14 , makeacol32(0,100,0,255));// dark green rim of glasses
  circle(    head , (7*head->w)/8    , (3*head->h)/4     , 14 , makeacol32(0,100,0,255));// dark green rim of glasses
  line(      head , (7*head->w)/8 - 1, (1*head->h)/4 + 7 , (7*head->w)/8 - 1, (3*head->h)/4 - 7 , makeacol32(0,100,0,255));
  line(      head , (7*head->w)/8    , (1*head->h)/4 + 7 , (7*head->w)/8 , (3*head->h)/4 - 7 , makeacol32(0,100,0,255));
  line(      head , (7*head->w)/8 + 1, (1*head->h)/4 + 7 , (7*head->w)/8 + 1, (3*head->h)/4 - 7 , makeacol32(0,100,0,255));
  circlefill(head , (7*head->w)/8    , (1*head->h)/4 , 10, makeacol32(255,255,255,255));
  circlefill(head , (7*head->w)/8    , (3*head->h)/4 , 10, makeacol32(255,255,255,255));
  circlefill(head , (7*head->w)/8    , (1*head->h)/4 , 3 , makeacol32(0,0,0,255));
  circlefill(head , (7*head->w)/8    , (3*head->h)/4 , 3 , makeacol32(0,0,0,255));
*/  
  
  al_set_target_bitmap(body->bmp);
  al_clear_to_color(al_map_rgba(0,0,0,0));
  al_draw_filled_ellipse(body->w / 2 , body->h / 2 , body->w/2 , body->h/2 , al_map_rgba(255,255,255,255));

/**
  clear_to_color(body , makeacol32(0,0,0,0));
  //void ellipsefill(BITMAP *bmp, int x, int y, int rx, int ry, int color);
  ellipsefill(body , body->w / 2 , body->h / 2 , body->w/2 , body->h/2 , makeacol32(255,255,255,255));
*/  
  int head_source_pivot_x = 120;// position on head bitmap that this sprite will be rotated around
  int head_source_pivot_y = 40;// position on head bitmap that this sprite will be rotated around

  int body_source_pivot_x = 80;// position on body bitmap that this sprite will be rotated around
  int body_source_pivot_y = 240;// position on body bitmap that this sprite will be rotated around

  int body_buf_dest_pivot_x = 400;// position on body_rotated_scaled slide where the body sprite will be rotated onto it
  int body_buf_dest_pivot_y = 450;// position on body_rotated_scaled slide where the body sprite will be rotated onto it

  int head_relative_to_body_dest_pivot_x = 0;
  int head_relative_to_body_dest_pivot_y = -240;

  double body_pivot_to_head_pivot_initial_angle = atan2(head_relative_to_body_dest_pivot_y , head_relative_to_body_dest_pivot_x);
  double body_pivot_to_head_pivot_initial_distance = sqrt(head_relative_to_body_dest_pivot_x*head_relative_to_body_dest_pivot_x + 
                                                          head_relative_to_body_dest_pivot_y*head_relative_to_body_dest_pivot_y);
  
  double figure_angle = 0.0;
  int figure_angle_deg = 0;
  double figure_scale = 1.0;
  int figure_scale_hundredths = 100;
  int head_angle_deg = -90;
  double head_angle = -M_PI/2.0;
  
//  int head_buf_dest_pivot_x = body_buf_dest_pivot_x + head_relative_to_body_dest_pivot_x;
//  int head_buf_dest_pivot_y = body_buf_dest_pivot_y + head_relative_to_body_dest_pivot_y;
  int head_buf_dest_pivot_x = body_buf_dest_pivot_x + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*cos(body_pivot_to_head_pivot_initial_angle + figure_angle));
  int head_buf_dest_pivot_y = body_buf_dest_pivot_y + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*sin(body_pivot_to_head_pivot_initial_angle + figure_angle));
  
  const double rad_to_alfix = 256.0/(2.0*M_PI);
  const double deg_to_rad = M_PI / 180.0;
  const double deg_to_alfix = 256.0/360.0;
  const double rad_to_deg = 180 / M_PI;
  
  bool mouse_b1 = false;
  bool mouse_b2 = false;
  
  int m_pos = 0;
  int m_x = 0;
  int m_y = 0;
  int m_z = 0;
  int last_m_z = 0;
  int m_z_change = 0;
  

   al_start_timer(timer);
   
   bool redraw = true;
   
  while (!keys_down[ALLEGRO_KEY_ESCAPE]) {
    
   if (redraw) {

   al_set_blender(ALLEGRO_ADD , ALLEGRO_ONE , ALLEGRO_ZERO);
   al_set_target_bitmap(body_rotated_scaled->bmp);
   al_clear_to_color(al_map_rgba(0,0,0,0));
   al_set_target_bitmap(head_rotated_scaled->bmp);
   al_clear_to_color(al_map_rgba(0,0,0,0));
/**
    clear_to_color(body_rotated_scaled , makeacol32(0,0,0,0));
    clear_to_color(head_rotated_scaled , makeacol32(0,0,0,0));
    //void pivot_scaled_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, int cx, int cy, fixed angle, fixed scale);
    //Like rotate_scaled_sprite(), but aligns the point in the sprite given by (cx, cy) to (x, y) in the bitmap, then rotates and scales around this point. 
*/

/**
void pivot_scaled_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, int cx, int cy, fixed angle, fixed scale);

Like rotate_scaled_sprite(), but aligns the point in the sprite given by (cx, cy) to (x, y) in the bitmap, then rotates and scales around this point. 

void al_draw_scaled_rotated_bitmap(ALLEGRO_BITMAP *bitmap,
   float cx, float cy, float dx, float dy, float xscale, float yscale,
   float angle, int flags)

The point at cx/cy in the bitmap will be drawn at dx/dy and the bitmap is rotated and scaled around this point.
*/   

   al_set_blender(ALLEGRO_ADD , ALLEGRO_ALPHA , ALLEGRO_INVERSE_ALPHA);
   al_set_target_bitmap(body_rotated_scaled->bmp);
   al_draw_scaled_rotated_bitmap(body->bmp , body_source_pivot_x , body_source_pivot_y , body_buf_dest_pivot_x , body_buf_dest_pivot_y , figure_scale , figure_scale , figure_angle , 0);
   
   al_set_target_bitmap(head_rotated_scaled->bmp);
   al_draw_scaled_rotated_bitmap(head->bmp , head_source_pivot_x , head_source_pivot_y , head_buf_dest_pivot_x , head_buf_dest_pivot_y , figure_scale , figure_scale , figure_angle + head_angle , 0);

/**
    pivot_scaled_sprite(body_rotated_scaled , body , body_buf_dest_pivot_x , body_buf_dest_pivot_y , body_source_pivot_x , body_source_pivot_y , ftofix(figure_angle*rad_to_alfix) , ftofix(figure_scale));
    pivot_scaled_sprite(head_rotated_scaled , head , head_buf_dest_pivot_x , head_buf_dest_pivot_y , head_source_pivot_x , head_source_pivot_y , ftofix((figure_angle+head_angle)*rad_to_alfix) , ftofix(figure_scale));
*/

   al_set_target_bitmap(buffer->bmp);
   al_clear_to_color(al_map_rgba(0,0,0,0));
   al_draw_bitmap(body_rotated_scaled->bmp , 0 , 0 , 0);
   al_draw_bitmap(head_rotated_scaled->bmp , 0 , 0 , 0);

   al_set_target_backbuffer(display);
   al_clear_to_color(al_map_rgb(0,0,0));
   al_draw_bitmap(buffer->bmp , 0 , 0 , 0);
   al_flip_display();
   
/**   
    clear_to_color(buffer , makecol(0,0,0));
//    clear_to_color(pre_buffer , makecol(0,0,0));
    draw_trans_sprite(buffer , body_rotated_scaled , 0 , 0);
    draw_trans_sprite(buffer , head_rotated_scaled , 0 , 0);
    vsync();
    blit(buffer , screen , 0 , 0 , 0 , 0 , buffer->w , buffer->h);
*/
      redraw = false;
  }

   do {
         
      ALLEGRO_EVENT ev;
      al_wait_for_event(queue , &ev);
      
      if (ev.type == ALLEGRO_EVENT_KEY_DOWN) {
         keys_down[ev.keyboard.keycode] = true;
      }
      if (ev.type == ALLEGRO_EVENT_KEY_UP) {
         keys_down[ev.keyboard.keycode] = false;
      }

      if (ev.type == ALLEGRO_EVENT_MOUSE_AXES) {
          m_x = ev.mouse.x;
          m_y = ev.mouse.y;
          m_z_change = ev.mouse.z;
         /// adjust scale with vertical mouse wheel
         if (m_z_change != 0) {
            figure_scale_hundredths += 5*m_z_change;
            if (figure_scale_hundredths < 1) {figure_scale_hundredths = 1;}
            figure_scale = static_cast<double>(figure_scale_hundredths) / 100.0;
            head_buf_dest_pivot_x = body_buf_dest_pivot_x + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*cos(body_pivot_to_head_pivot_initial_angle + figure_angle));
            head_buf_dest_pivot_y = body_buf_dest_pivot_y + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*sin(body_pivot_to_head_pivot_initial_angle + figure_angle));
         }
      }
      if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) {
         if (ev.mouse.button == 1) {
            mouse_b1 = true;
         }
         else if (ev.mouse.button == 2) {
            mouse_b2 = true;
         }
      }
      if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) {
         if (ev.mouse.button == 1) {
            mouse_b1 = false;
         }
         else if (ev.mouse.button == 2) {
            mouse_b2 = false;
         }
      }
  
      if (ev.type == ALLEGRO_EVENT_TIMER) {
         redraw = true;
    
    /// adjust rotation of head
         m_z_change = 0;
         if (keys_down[ALLEGRO_KEY_LEFT]) {m_z_change = -1;}
         if (keys_down[ALLEGRO_KEY_RIGHT]) {m_z_change = 1;}
         if (m_z_change != 0) {
            head_angle_deg = (head_angle_deg + 3*m_z_change);
            head_angle_deg = head_angle_deg % 360;
            head_angle = ((double)head_angle_deg)*deg_to_rad;
         }

         /// adjust rotation of entire figure (rotate body and recalculate position of head's destination pivot)
         m_z_change = 0;
         if (keys_down[ALLEGRO_KEY_UP]) {m_z_change = -1;}
         if (keys_down[ALLEGRO_KEY_DOWN]) {m_z_change = 1;}
         if (m_z_change != 0) {
            figure_angle_deg = figure_angle_deg + 3*m_z_change;
            figure_angle_deg = figure_angle_deg % 360;
            figure_angle = ((double)figure_angle_deg)*deg_to_rad;
            head_buf_dest_pivot_x = body_buf_dest_pivot_x + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*cos(body_pivot_to_head_pivot_initial_angle + figure_angle));
            head_buf_dest_pivot_y = body_buf_dest_pivot_y + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*sin(body_pivot_to_head_pivot_initial_angle + figure_angle));
         }


         if (mouse_b1) {// move figure's body destination pivot
            body_buf_dest_pivot_x = m_x;
            body_buf_dest_pivot_y = m_y;
            head_buf_dest_pivot_x = body_buf_dest_pivot_x + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*cos(body_pivot_to_head_pivot_initial_angle + figure_angle));
            head_buf_dest_pivot_y = body_buf_dest_pivot_y + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*sin(body_pivot_to_head_pivot_initial_angle + figure_angle));
         }
         if (mouse_b2) {// rotate figure
            figure_angle = atan2((m_y - body_buf_dest_pivot_y) , (m_x - body_buf_dest_pivot_x)) + M_PI/2.0;
            figure_angle_deg = (int)(figure_angle*rad_to_deg);
            figure_angle_deg = figure_angle_deg % 360;
            head_buf_dest_pivot_x = body_buf_dest_pivot_x + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*cos(body_pivot_to_head_pivot_initial_angle + figure_angle));
            head_buf_dest_pivot_y = body_buf_dest_pivot_y + (int)((figure_scale*body_pivot_to_head_pivot_initial_distance)*sin(body_pivot_to_head_pivot_initial_angle + figure_angle));
         }
    
            
      }
    
   } while (!al_is_event_queue_empty(queue));
    
    
  }
  
  return 0;
}
