/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include "video/win32/win32.hh"
#include "device/event.hh"
#include "main/win_main.hh"

class gdi_accelerator_class : public win32_accelerator_class
{
  I4_SCREEN_TYPE  *back_buffer;
  BITMAPINFOHEADER back_buffer_bmp;
  BITMAPINFO       dc_bmpinfo;
  i4_bool          win_is_open;
  HBITMAP          bitmap;

public:
  class gdi_mode : public i4_display_class::mode
  {
  public:
    gdi_accelerator_class *assoc;      // pointer to use, so we can confirm we created this mode

  };
  gdi_mode amode, cur_mode;


  virtual w16 width() const
  {
    return back_buffer->width();
  }
  
  virtual w16 height() const
  {
    return back_buffer->height();
  }
  
  void setup_back_buffer_bmp(w32 width, w32 height)
  {
    if (back_buffer)
    {
      delete back_buffer;
      DeleteObject(bitmap);
    }

    HDC         dc;
    dc=GetDC(win32_display_instance.window_handle);

    memset(&back_buffer_bmp,0,sizeof(back_buffer_bmp));
    back_buffer_bmp.biSize=sizeof(back_buffer_bmp);
    back_buffer_bmp.biWidth=width;
    back_buffer_bmp.biHeight=-height;
    back_buffer_bmp.biPlanes=1;
    back_buffer_bmp.biBitCount=I4_BYTES_PER_PIXEL*8;
    back_buffer_bmp.biCompression=BI_RGB;
    back_buffer_bmp.biSizeImage=width*height*I4_BYTES_PER_PIXEL;

    memcpy(&dc_bmpinfo.bmiHeader,&back_buffer_bmp,sizeof(back_buffer_bmp));

    void *bmp_data_address;
     
    bitmap=CreateDIBSection(dc,
                            &dc_bmpinfo,DIB_RGB_COLORS,
                            &bmp_data_address,           // where bitmap's data will be stored
                            0,                           // don't use a file
                            0);
    if (!bitmap)
      i4_error("Error CreateDIBSection failed!");

    i4_pal_handle_class pal;

#if (I4_SCREEN_DEPTH==15 || I4_SCREEN_DEPTH==16)
    pal.set_type(i4_pal_handle_class::ID_16BIT);
#else
#error add code here
#endif
    
    back_buffer=new I4_SCREEN_TYPE(width,height,
                                   pal,
                                   width*I4_BYTES_PER_PIXEL,   // bytes per line
                                   (w8 *)bmp_data_address);
    if (context)
      delete context;

    context=new i4_draw_context_class(0,0,width-1,height-1);
    context->both_dirty=new i4_rect_list_class;
    context->both_dirty->add_area(0,0,width-1,height-1);
    context->single_dirty=new i4_rect_list_class;
  }
  
  virtual void init()
  {
    back_buffer=0;
    win32_accelerator_class::init();
  }
  
  // called when windows gives us a WM_MOVE message, this tells everyone else interested
  virtual void move_screen(i4_coord x, i4_coord y)               
  {
    // We don't care about moves in this case
    // Because we are drawing an off-screen buffer, window moves don't affect anything
  }
  
  // called when windows gives us a WM_MOVE message, this tells everyone else interested
  virtual void resize_screen(i4_coord w, i4_coord h)             
  {
    // If the window resizes, we need to resize our off-screen buffer and send a message 
    // to everyone who is interested
    setup_back_buffer_bmp(w,h);

    delete context;
    context=new i4_draw_context_class(0,0,w-1,h-1);
    context->both_dirty=new i4_rect_list_class;
    context->both_dirty->add_area(0,0,w-1,h-1);
    context->single_dirty=new i4_rect_list_class;

    i4_display_change_event_class ev(&win32_display_instance,i4_display_change_event_class::SIZE_CHANGE);
    win32_display_instance.input.send_event_to_agents(&ev,i4_device_class::FLAG_DISPLAY_CHANGE);
  }
  

  virtual mode *current_mode()
  {
    return &cur_mode;
  }

  virtual i4_display_class::mode *get_first_mode()
  {
    memset(&amode,0,sizeof(amode));
    strcpy(amode.name,"Win32 GDI window");
    amode.flags=i4_display_class::mode::RESOLUTION_DETERMINED_ON_OPEN;

    amode.red_mask=(31<<10);
    amode.green_mask=(31<<5);
    amode.blue_mask=(31<<0);

    amode.bits_per_pixel=16;

    amode.xres=640;
    amode.yres=480;

    amode.assoc=this;          // so we know in 'initialize_mode' that we created this mode

    return &amode;
  }
  
  virtual i4_display_class::mode *get_next_mode(i4_display_class::mode *last_mode)
  {
    return 0;
  }
  
  virtual i4_bool initialize_mode(i4_display_class::mode *which_one)
  {
    if (!back_buffer)
    {
      memcpy(&cur_mode, which_one, sizeof(cur_mode));
      win32_display_instance.window_handle = CreateWindowEx(
                                                            0,
                                                            //                                                             WS_EX_TOPMOST,
                                                            win32_display_instance.class_name(),
                                                            win32_display_instance.class_name(),
                                                            WS_OVERLAPPEDWINDOW,
                                                            0,
                                                            0,
                                                            which_one->xres,
                                                            which_one->yres,
                                                            NULL,
                                                            NULL,
                                                            i4_win32_instance,
                                                            NULL );


      ShowWindow( win32_display_instance.window_handle, SW_SHOW );
      UpdateWindow( win32_display_instance.window_handle );

      setup_back_buffer_bmp(which_one->xres,
                            which_one->yres);
     

      return i4_T;
    } 
    else
      return i4_F;
  }
  
  virtual i4_bool close()
  {
    if (back_buffer)
    {
      DestroyWindow(win32_display_instance.window_handle);
      delete back_buffer;
      back_buffer=0;
      return i4_T;
    } else return i4_F;
  }
  
  virtual i4_image_class *get_screen()
  {
    return back_buffer;
  }
  
  virtual void flush()
  {
    PAINTSTRUCT paint_s;
    HDC         dc;
    dc=GetDC(win32_display_instance.window_handle);


    SelectObject(dc,bitmap);

    HDC hdcMem = CreateCompatibleDC(dc);
    bitmap = SelectObject(hdcMem,bitmap);


    for (i4_rect_list_class::area_iter a=context->single_dirty->list.begin();
         a!=context->single_dirty->list.end();
         ++a)
      context->both_dirty->add_area(a->x1, a->y1, a->x2, a->y2);

    context->single_dirty->delete_list();

    context->both_dirty->intersect_area(0,0,width()-1,height()-1);



    a=context->both_dirty->list.begin();
    for (;a!=context->both_dirty->list.end();++a)
      BitBlt(dc,a->x1,a->y1,
             a->x2-a->x1+1,a->y2-a->y1+1,
             hdcMem,a->x1,a->y1,SRCCOPY);

    // this is to reduce the flicker of the mouse cursor
    POINT p;
    GetCursorPos(&p);
    SetCursorPos(p.x, p.y);
  
    context->both_dirty->delete_list();
    bitmap = SelectObject(hdcMem,bitmap);         // windows says to do this

    DeleteDC(hdcMem);
    
    ReleaseDC(win32_display_instance.window_handle,dc);
  }
  
  virtual i4_bool set_mouse_shape(i4_cursor_class *cursor)
  {
    return i4_F;
  }
  
  virtual i4_bool realize_palette(i4_pal_handle_class pal_id)
  {
    return i4_T;   // pretend like this works for now
  }
  
  virtual w32 priority()
  {
    return 1;
  }
  
  gdi_accelerator_class() { context=0; }
};

static gdi_accelerator_class gdi_accelerator_instance;