#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "swis.h"
#include "debug.h"

#include "tboxlibs/wimp.h"
#include "tboxlibs/wimplib.h"
#include "tboxlibs/event.h"
#include "tboxlibs/window.h"

#include "pinboard.h"
#include "iconize.h"
#include "tinydirs.h"
#include "sticky.h"
#include "msgtrans.h"

static WimpIconizeAtMessage iconize_at_pending={-1,-1,0,0,0};

static int iconize_window_info(WimpMessage *m,void *handle);

int wimp_sprite_exists(char *spritename)
{
  _kernel_oserror *e;
  SpriteParams p;
  e=wimp_sprite_op(40,spritename,&p); // SpriteOp read sprite info
  return (e==NULL);
}

static int iconize_bounce_message(int event_code,WimpPollBlock *poll,IdBlock *id_block,void *handle)
{
  IGNORE(id_block);
  
  if (event_code!=Wimp_EUserMessageAcknowledge) return 0;
  if (poll->user_message_acknowledge.hdr.action_code!=Wimp_MWindowInfo) return 0;
  
  // reroute...
  iconize_window_info((WimpMessage *)&poll->user_message_acknowledge,handle);

  return 1;
}

static int iconize_window_info(WimpMessage *m,void *handle)
{
  _backdrop_state *state=(_backdrop_state *)handle;
  WimpMessageE *msg=(WimpMessageE *)m;
  WimpGetPointerInfoBlock mouse;
  WimpGetWindowStateBlock win;
  char spritename[12];
  char text[21];
  int tinywin=0,i,known=-1;
  pin_add_coord_type add_type=PIN_ADD_CENTRED;
  
  if (msg->data.window_info.window_handle==0) return 1; // was from us

  // remove noack wimp handler
  event_deregister_wimp_handler(-1,Wimp_EUserMessageAcknowledge,iconize_bounce_message,handle);
  
  memcpy(text,msg->data.window_info.window_name,20);
  text[msg->hdr.size-16-20]=0; // ensure termination - message may be short

  memcpy(spritename,"ic_",3);
  memcpy(spritename+3,msg->data.window_info.sprite_name,8);
  spritename[11]=0; // ensure termination


  if (!wimp_sprite_exists(spritename)) {
    strcpy(spritename,"ic_?");
  }

  if (iconize_at_pending.window_handle==msg->data.window_info.window_handle && iconize_at_pending.task_handle==msg->data.window_info.reserved) {
    if (iconize_at_pending.flags & 1) {
      // shift held down - iconize directly
      wimp_get_pointer_info(&mouse);
    } else {
      // clicked on the button, may change action here depending on settings
      switch (state->setting_iconize) {
      case ICONIZE_TO_BUTTON: 
        mouse.x=iconize_at_pending.x;
        mouse.y=iconize_at_pending.y;
        break;
        
      case ICONIZE_TO_ICONBAR:
        // add a tinywin instead
        tinywin=1;
        break;
        
      default:
        {
/*          BBox gap;
          int grid_width,grid_height;
          grid_size(state,&grid_width,&grid_height);
          gap.xmin=0;
          gap.ymin=0;
          gap.xmax=192; // max icon width
          gap.ymax=142; // sprite size 82 + textheight 44 +4+12
          sticky_update_bboxes(state);
          pin_find_gap(state,state->setting_iconize,state->setting_iconize_stack,&gap);*/
          mouse.x=-100;//gap.xmin+grid_width/2;
          mouse.y=-100;//gap.ymin+grid_height/2;
          add_type=PIN_ADD_CENTRED;
          // we'll move the pin shortly to a better spot
        }
        break;
      }
      iconize_at_pending.window_handle=-1;
      iconize_at_pending.task_handle=-1;   
    }
  } else {
    wimp_get_pointer_info(&mouse);
  }
  
  // do we already know this window?
  for (i=0;i<pinlist->count;i++) {
    if (pin_is_window(&pinlist->info[i])) {
      if (pinlist->info[i].data.window.handle==msg->data.window_info.window_handle) {
        // we have this one.
        known=i;
        break;
      }
    }
  }

  if (tinywin) {
    if (known==-1) {
      tinywin_add(msg->data.window_info.window_handle,msg->data.window_info.reserved,text,spritename);
      known=pinlist->count-1;
    }
  } else {
    // add a pin for this one...
    if (known==-1) {
      char *t=&text[0];
      char *s=&spritename[0];
      backdrop_add_pin(state,pin_type_window,mouse.x,mouse.y,add_type,-2,&s,&t,NULL,msg->data.window_info.window_handle,msg->data.window_info.reserved);
      known=pinlist->count-1;
    } else {
      // move the icon to the new position
      int oldtype;
      oldtype=pinlist->info[known].icontype;
      pinlist->info[known].icontype=pin_type_empty;
      pin_redraw(state,known);
      pinlist->info[known].icontype=oldtype;

      if (iconize_at_pending.window_handle==msg->data.window_info.window_handle && iconize_at_pending.task_handle==msg->data.window_info.reserved) {
        if (state->setting_iconize==ICONIZE_TO_BUTTON) {
          int w,h;
          w=pinlist->info[known].icondata.bbox.xmax-pinlist->info[known].icondata.bbox.xmin;
          h=pinlist->info[known].icondata.bbox.ymax-pinlist->info[known].icondata.bbox.ymin;
          pinlist->info[known].icondata.bbox.xmin=mouse.x-w/2;
          pinlist->info[known].icondata.bbox.xmax=pinlist->info[known].icondata.bbox.xmin+w;
          pinlist->info[known].icondata.bbox.ymin=mouse.y-h/2;
          pinlist->info[known].icondata.bbox.ymax=pinlist->info[known].icondata.bbox.ymin+h;
        }
      } else {
        int w,h;
        w=pinlist->info[known].icondata.bbox.xmax-pinlist->info[known].icondata.bbox.xmin;
        h=pinlist->info[known].icondata.bbox.ymax-pinlist->info[known].icondata.bbox.ymin;
        pinlist->info[known].icondata.bbox.xmin=mouse.x-w/2;
        pinlist->info[known].icondata.bbox.xmax=pinlist->info[known].icondata.bbox.xmin+w;
        pinlist->info[known].icondata.bbox.ymin=mouse.y-h/2;
        pinlist->info[known].icondata.bbox.ymax=pinlist->info[known].icondata.bbox.ymin+h;
      }
      pin_redraw(state,known);
    }
  }

  if (state->setting_iconize!=ICONIZE_TO_BUTTON && state->setting_iconize!=ICONIZE_TO_ICONBAR) {
    BBox gap;
    int oldtype;
    int w,h,xoffset;

    sticky_update_bboxes(state);
    oldtype=pinlist->info[known].icontype;
    pinlist->info[known].icontype=pin_type_empty;
    pin_redraw(state,known);
    pinlist->info[known].icontype=oldtype;

    pin_find_gap(state,state->setting_iconize,state->setting_iconize_stack,&gap);

    w=pinlist->info[known].icondata.bbox.xmax-pinlist->info[known].icondata.bbox.xmin;
    h=pinlist->info[known].icondata.bbox.ymax-pinlist->info[known].icondata.bbox.ymin;
    if (state->iconsize==ICONSIZE_SMALL) {
      pinlist->info[known].icondata.bbox.xmin=gap.xmin+4;
    } else {
      int grid_width,grid_height;
      grid_size(state,&grid_width,&grid_height);
      xoffset=(grid_width-w)/2;  // adjustment to centre it
      pinlist->info[known].icondata.bbox.xmin=gap.xmin+xoffset;
    }
    pinlist->info[known].icondata.bbox.xmax=pinlist->info[known].icondata.bbox.xmin+w;
    pinlist->info[known].icondata.bbox.ymin=gap.ymin;
    pinlist->info[known].icondata.bbox.ymax=pinlist->info[known].icondata.bbox.ymin+h;
    pin_redraw(state,known);
  }

  // move the window behind the backdrop
  win.window_handle=msg->data.window_info.window_handle;
  wimp_get_window_state(&win);
  pinlist->info[known].data.window.visible_area=win.visible_area;
  win.behind=-3; // iconized
  wimp_send_message(2,&win,win.window_handle,0,0); // send open_window_request to the window itself
  
  return 1;
}

void iconize_restore(int window_handle)
{
  WimpGetWindowStateBlock win;
  int i;

  win.window_handle=window_handle;
  wimp_get_window_state(&win);
  win.behind=-1; // on top

  // find window
  for (i=0;i<pinlist->count;i++) {
    if (pin_is_window(&pinlist->info[i])) {
      if (pinlist->info[i].data.window.handle==window_handle) {
        // we have this one.
        win.visible_area=pinlist->info[i].data.window.visible_area;
        break;
      }
    }
  }

  wimp_send_message(2,&win,win.window_handle,0,0); // send open_window_request to the window itself
}

static int iconize_message(WimpMessage *m,void *handle)
{
  WimpMessageE winfo,*msg=(WimpMessageE *)m;
  char *taskname;
  int i;
  _kernel_oserror *e;
  
  // acknowledge the message
  m->hdr.your_ref=m->hdr.my_ref;
  wimp_send_message(19,m,m->hdr.sender,0,0);

  // send Message_WindowInfo
  memset(&winfo,0,sizeof(WimpMessageE));
  winfo.hdr.size=msg->hdr.size+8;
  winfo.hdr.action_code=Wimp_MWindowInfo;
  winfo.data.window_info.window_handle=msg->data.iconize.window_handle;
  winfo.data.window_info.reserved=msg->data.iconize.task_handle;
  memcpy(winfo.data.window_info.window_name,msg->data.iconize.title,20);

  e=_swix(TaskManager_TaskNameFromHandle,_IN(0)|_OUT(0),msg->data.iconize.task_handle,&taskname);
  if (e) {
    wimp_report_error(e,1,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
    return 1;
  }
  
  for (i=0;i<8 && taskname[i]>32;i++) {
    winfo.data.window_info.sprite_name[i]=tolower(taskname[i]);
  }

  // message bounces need to go separately:
  event_register_wimp_handler(-1,Wimp_EUserMessageAcknowledge,iconize_bounce_message,handle);

  wimp_send_message(18,(WimpMessage *)&winfo,winfo.data.window_info.window_handle,0,0);

  return 1;
}

static int iconize_message_at(WimpMessage *m,void *handle)
{
  WimpMessageE *msg=(WimpMessageE *)m;
  IGNORE(handle);
  
  // acknowledge
  m->hdr.your_ref=m->hdr.my_ref;
  wimp_send_message(19,m,m->hdr.sender,0,0);
  
  // set pending stuff ready for the iconize
  iconize_at_pending=msg->data.iconize_at;
  
  // and wait for the proper iconize...
  return 1;
}

static int iconize_remove_all(WimpMessage *m,void *handle)
{
  // remove all belonging to a task
  pin_remove_all_task((_backdrop_state *)handle,m->hdr.sender);
  return 1;
}

static int iconize_remove_window(WimpMessage *m,void *handle)
{
  // window was closed
  WimpMessageE *msg=(WimpMessageE *)m;
  pin_remove_window((_backdrop_state *)handle,msg->data.window_closed.window_handle);
  return 1;
}

void iconize_init(_backdrop_state *state)
{
  event_register_message_handler(Wimp_MIconize,iconize_message,state);
  event_register_message_handler(Wimp_MIconizeAt,iconize_message_at,state);
  event_register_message_handler(Wimp_MWindowClosed,iconize_remove_window,state);
  event_register_message_handler(Wimp_MWindowInfo,iconize_window_info,state);
  event_register_message_handler(Wimp_MTaskCloseDown,iconize_remove_all,state);

  WimpMessageE msg;
  memset(&msg,0,sizeof(WimpMessageE));
  msg.hdr.size=(char *)(&msg.data.window_info.reserved)-(char *)&msg;
  msg.hdr.action_code=Wimp_MWindowInfo;
  wimp_send_message(17,(WimpMessage *)&msg,0,0,0);
}
