/*
 *    Example program for the Allegro library.
 *
 *    This program blitting to/from sub-bitmaps.
 *
 */

#include "allegro5/allegro.h"
#include "allegro5/allegro_image.h"
#include <allegro5/allegro_primitives.h>

#include "common.c"

#define MIN(x,y)     (((x) < (y)) ? (x) : (y))
#define MAX(x,y)     (((x) > (y)) ? (x) : (y))
#define CLAMP(x,y,z) MAX((x), MIN((y), (z)))

typedef enum {
   PLAIN_BLIT,
   SCALED_BLIT
} Mode;

enum {
   SRC_WIDTH  = 640,
   SRC_HEIGHT = 480,
   SRC_X = 160,
   SRC_Y = 120,
   DST_WIDTH  = 640,
   DST_HEIGHT = 480
};


ALLEGRO_DISPLAY *src_display;
ALLEGRO_DISPLAY *dst_display;
ALLEGRO_EVENT_QUEUE *queue;
ALLEGRO_BITMAP *src_bmp;

int src_x1 = SRC_X;
int src_y1 = SRC_Y;
int src_x2 = SRC_X + 319;
int src_y2 = SRC_Y + 199;
int dst_x1 = 0;
int dst_y1 = 0;
int dst_x2 = DST_WIDTH-1;
int dst_y2 = DST_HEIGHT-1;

Mode mode = PLAIN_BLIT;
int draw_flags = 0;

int main(int argc, const char *argv[])
{
   ALLEGRO_BITMAP *src_subbmp[2] = {NULL, NULL};
   ALLEGRO_BITMAP *dst_subbmp[2] = {NULL, NULL};
   ALLEGRO_EVENT event;
   bool mouse_down;
   bool recreate_subbitmaps;
   bool redraw;
   bool use_memory;
   const char *image_filename = NULL;

   if (!al_init()) {
      abort_example("Could not init Allegro.\n");
   }
   al_init_primitives_addon();
   al_init_image_addon();

   open_log();

   al_set_new_display_flags(ALLEGRO_GENERATE_EXPOSE_EVENTS);
   src_display = al_create_display(SRC_WIDTH, SRC_HEIGHT);
   if (!src_display) {
      abort_example("Error creating display\n");
   }
   al_set_window_title(src_display, "Source");

   dst_display = al_create_display(DST_WIDTH, DST_HEIGHT);
   if (!dst_display) {
      abort_example("Error creating display\n");
   }
   al_set_window_title(dst_display, "Destination");

   {
      int i;
      for (i = 1; i < argc; ++i) {
         if (!image_filename)
            image_filename = argv[i];
         else
            abort_example("Unknown argument: %s\n", argv[i]);
      }
      
      if (!image_filename) {
         image_filename = "data/mysha.pcx";
      }
   }

   src_bmp = al_load_bitmap(image_filename);
   if (!src_bmp) {
      abort_example("Could not load image file\n");
   }
   
   src_x2 = src_x1 + al_get_bitmap_width(src_bmp);
   src_y2 = src_y1 + al_get_bitmap_height(src_bmp);

   al_install_keyboard();
   al_install_mouse();

   queue = al_create_event_queue();
   al_register_event_source(queue, al_get_keyboard_event_source());
   al_register_event_source(queue, al_get_mouse_event_source());
   al_register_event_source(queue, al_get_display_event_source(src_display));
   al_register_event_source(queue, al_get_display_event_source(dst_display));

   mouse_down = false;
   recreate_subbitmaps = true;
   redraw = true;
   use_memory = false;

   log_printf("Highlight sub-bitmap regions with left mouse button.\n");
   log_printf("Press 'm' to toggle memory bitmaps.\n");
   log_printf("Press '1' to perform plain blits.\n");
   log_printf("Press 's' to perform scaled blits.\n");
   log_printf("Press 'h' to flip horizontally.\n");
   log_printf("Press 'v' to flip vertically.\n");

   while (true) {
      if (recreate_subbitmaps) {
         int l, r, t, b, sw, sh;

         al_destroy_bitmap(src_subbmp[0]);
         al_destroy_bitmap(dst_subbmp[0]);
         al_destroy_bitmap(src_subbmp[1]);
         al_destroy_bitmap(dst_subbmp[1]);

         l = MIN(src_x1, src_x2);
         r = MAX(src_x1, src_x2);
         t = MIN(src_y1, src_y2);
         b = MAX(src_y1, src_y2);
         
         l -= SRC_X;
         t -= SRC_Y;
         r -= SRC_X;
         b -= SRC_Y;

         src_subbmp[0] = al_create_sub_bitmap(src_bmp, l, t, r - l + 1,
            b - t + 1);
         sw = al_get_bitmap_width(src_subbmp[0]);
         sh = al_get_bitmap_height(src_subbmp[0]);
         src_subbmp[1] = al_create_sub_bitmap(src_subbmp[0], 2, 2,
            sw - 4, sh - 4);

         l = MIN(dst_x1, dst_x2);
         r = MAX(dst_x1, dst_x2);
         t = MIN(dst_y1, dst_y2);
         b = MAX(dst_y1, dst_y2);

         al_set_target_backbuffer(dst_display);
         dst_subbmp[0] = al_create_sub_bitmap(al_get_backbuffer(dst_display),
            l, t, r - l + 1, b - t + 1);
         dst_subbmp[1] = al_create_sub_bitmap(dst_subbmp[0],
            2, 2, r - l - 3, b - t - 3);

         recreate_subbitmaps = false;
      }

      if (redraw && al_is_event_queue_empty(queue)) {
         al_set_target_backbuffer(dst_display);
         al_clear_to_color(al_map_rgb(0, 0, 0));

         al_set_target_bitmap(dst_subbmp[1]);
         switch (mode) {
            case PLAIN_BLIT:
            {
               al_draw_bitmap(src_subbmp[1], 0, 0, draw_flags);
               break;
            }
            case SCALED_BLIT:
            {
               al_draw_scaled_bitmap(src_subbmp[1],
                  0, 0, al_get_bitmap_width(src_subbmp[1]),
                  al_get_bitmap_height(src_subbmp[1]),
                  0, 0, al_get_bitmap_width(dst_subbmp[1]),
                  al_get_bitmap_height(dst_subbmp[1]),
                  draw_flags);
               break;
            }
         }

         #define SWAP_GREATER(f1, f2) { \
            if (f1 > f2) { \
               float tmp = f1; \
               f1 = f2; \
               f2 = tmp; \
            } \
         }

         {
            /* pixel center is at 0.5/0.5 */
            float x = dst_x1 + 0.5;
            float y = dst_y1 + 0.5;
            float x_ = dst_x2 + 0.5;
            float y_ = dst_y2 + 0.5;
            SWAP_GREATER(x, x_)
            SWAP_GREATER(y, y_)
            al_set_target_backbuffer(dst_display);
            al_draw_rectangle(x, y, x_, y_,
               al_map_rgb(0, 255, 255), 0);
            al_draw_rectangle(x + 2, y + 2, x_ - 2, y_ - 2,
               al_map_rgb(255, 255, 0), 0);
            al_flip_display();
         }

         {
            /* pixel center is at 0.5/0.5 */
            float x = src_x1 + 0.5;
            float y = src_y1 + 0.5;
            float x_ = src_x2 + 0.5;
            float y_ = src_y2 + 0.5;
            SWAP_GREATER(x, x_)
            SWAP_GREATER(y, y_)
            al_set_target_backbuffer(src_display);
            al_clear_to_color(al_map_rgb(0, 0, 0));
            al_draw_bitmap(src_bmp, SRC_X, SRC_Y, 0);
            al_draw_rectangle(x, y, x_, y_,
               al_map_rgb(0, 255, 255), 0);
            al_draw_rectangle(x + 2, y + 2, x_ - 2, y_ - 2,
               al_map_rgb(255, 255, 0), 0);
            al_flip_display();
         }

         #undef SWAP_GREATER

         redraw = false;
      }

      al_wait_for_event(queue, &event);
      if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
         break;
      }
      if (event.type == ALLEGRO_EVENT_KEY_CHAR) {
         if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
            break;
         }
         if (event.keyboard.unichar == '1') {
            mode = PLAIN_BLIT;
            redraw = true;
         }
         else if (event.keyboard.unichar == 's') {
            mode = SCALED_BLIT;
            redraw = true;
         }
         else if (event.keyboard.unichar == 'h') {
            draw_flags ^= ALLEGRO_FLIP_HORIZONTAL;
            redraw = true;
         }
         else if (event.keyboard.unichar == 'v') {
            draw_flags ^= ALLEGRO_FLIP_VERTICAL;
            redraw = true;
         }
         else if (event.keyboard.unichar == 'm') {
            ALLEGRO_BITMAP *temp = src_bmp;
            use_memory = !use_memory;
            log_printf("Using a %s bitmap.\n", use_memory ? "memory" : "video");
            al_set_new_bitmap_flags(use_memory ?
               ALLEGRO_MEMORY_BITMAP : ALLEGRO_VIDEO_BITMAP);
            src_bmp = al_clone_bitmap(temp);
            al_destroy_bitmap(temp);
            redraw = true;
            recreate_subbitmaps = true;
         }
      }
      else if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN &&
            event.mouse.button == 1) {
         if (event.mouse.display == src_display) {
            src_x1 = src_x2 = event.mouse.x;
            src_y1 = src_y2 = event.mouse.y;
         }
         else if (event.mouse.display == dst_display) {
            dst_x1 = dst_x2 = event.mouse.x;
            dst_y1 = dst_y2 = event.mouse.y;
         }
         mouse_down = true;
         redraw = true;
      }
      else if (event.type == ALLEGRO_EVENT_MOUSE_AXES) {
         if (mouse_down) {
            if (event.mouse.display == src_display) {
               src_x2 = event.mouse.x;
               src_y2 = event.mouse.y;
            }
            else if (event.mouse.display == dst_display) {
               dst_x2 = event.mouse.x;
               dst_y2 = event.mouse.y;
            }
            redraw = true;
         }
      }
      else if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP &&
            event.mouse.button == 1) {
         mouse_down = false;
         recreate_subbitmaps = true;
         redraw = true;
      }
      else if (event.type == ALLEGRO_EVENT_DISPLAY_EXPOSE) {
         redraw = true;
      }
   }

   al_destroy_event_queue(queue);

   close_log(false);
   return 0;
}


/* vim: set sts=3 sw=3 et: */
