/**********************************************************************
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 "window/style.hh"
#include "window/window.hh"
#include "device/kernel.hh"
#include "window/dragwin.hh"
#include "window/colorwin.hh"
#include "image/image.hh"
#include "menu/boxmenu.hh"
#include "window/win_evt.hh"
#include "gui/text.hh"
#include "gui/deco_win.hh"
#include "app/app.hh"
#include "gui/gradiant.hh"

class i4_mwm_context_help : public i4_window_class
{
  i4_graphical_style_class *style;
  i4_str *text;
public:
  i4_event_handler_reference_class shadow;

  void draw(i4_draw_context_class &context)
  {
    i4_font_class *f=style->font_hint->small_font;
    local_image->clear(style->color_hint->text_background, context);
    local_image->rectangle(0,0, width()-1, height()-1, 0, context);
    
    f->set_color(style->color_hint->text_foreground);
    f->put_string(local_image, 2,2, *text, context);
  }

  i4_mwm_context_help(i4_graphical_style_class *style, const i4_const_str text)
    :  style(style),
       i4_window_class(style->font_hint->small_font->width(text)+4,
                       style->font_hint->small_font->height(text)+4),
       text(new i4_str(text))
  {    
  }

  ~i4_mwm_context_help() 
  { 
    delete text; 
    if (shadow.get())
      i4_kernel.delete_handler(shadow.get());
  }

  char *name() { return "i4_mwm_context_help"; }
};

class i4_mwm_style_class : public i4_graphical_style_class
{
public:
  i4_image_class *close_icon;

  virtual void uninit()
  {
    if (close_icon)
    {
      delete close_icon;
      close_icon=0;
    }
    i4_graphical_style_class::uninit();
  }

  char *name() { return "MWM"; }
  virtual i4_parent_window_class *create_mp_window(i4_coord x, i4_coord y, 
                                                   w16 w, w16 h, 
                                                   const i4_const_str &title,
                                                   i4_event_reaction_class *on_delete=0
                                                   );

  virtual i4_bool close_mp_window(i4_parent_window_class *created_window)
  {
    if (!created_window) return i4_F;
    i4_parent_window_class *to_del=created_window->get_parent();
    if (!to_del) return i4_F;
    i4_parent_window_class *to_del_parent=to_del->get_parent();
    if (!to_del_parent) return i4_F;
    
    i4_kernel.delete_handler(to_del);

    return i4_T;
  }

  virtual i4_bool available_for_display(i4_display_class *whom) { return i4_T; }

  virtual i4_menu_class *create_menu(i4_bool hide_on_pick)
  { 
    return new i4_box_menu_class(this,hide_on_pick);
  }


  virtual void get_in_deco_size(w32 &left, w32 &top, w32 &right, w32 &bottom)
  {
    left=right=top=bottom=2;
  }

  virtual void get_out_deco_size(w32 &left, w32 &top, w32 &right, w32 &bottom)
  {
    left=right=top=bottom=2;
  }

  virtual void deco_neutral_fill(i4_image_class *screen,
                                 sw32 x1, sw32 y1, sw32 x2, sw32 y2, 
                                 i4_draw_context_class &context)
  {
    

    i4_rect_list_class sub_clip(&context.clip,0,0);
    sub_clip.intersect_area(x1,y1,x2,y2);
    sub_clip.swap(&context.clip);

    i4_image_class *im=icon_hint->background_bitmap;
    if (im)
    {
      int iw=im->width(), ih=im->height();
      int dx1=-context.xoff;
      int dy1=-context.yoff;
      while (dx1+iwput_image(screen, x,y, context);        
    }
    else
      screen->clear(color_hint->neutral(), context);


    sub_clip.swap(&context.clip);
  }

  // draw a decoration around an area that looks like it's pressed into the screen
  virtual void draw_in_deco(i4_image_class *screen, 
                            i4_coord x1, i4_coord y1,
                            i4_coord x2, i4_coord y2,
                            i4_bool active,
                            i4_draw_context_class &context)
  {
    i4_color_hint_class::bevel *color;
    if (active)
      color=&color_hint->window.active;
    else 
      color=&color_hint->window.passive;

    screen->add_dirty(x1,y1,x2,y1+1,context);
    screen->add_dirty(x1,y1+2,x1+1,y2,context);
    screen->add_dirty(x1+2,y2-1,x2,y2,context);
    screen->add_dirty(x1-1,y1+2,x2,y2-2,context);

    screen->bar(x1,y1,x2,y1,color->dark,context);
    screen->bar(x1,y1,x1,y2,color->dark,context);

    screen->bar(x1+1,y1+1,x2-1,y1+1,color_hint->black,context);
    screen->bar(x1+1,y1+1,x1+1,y2-1,color_hint->black,context);

    screen->bar(x1+1,y2,x2,y2,color->bright,context);
    screen->bar(x2,y1+1,x2,y2,color->bright,context);

    screen->bar(x1+2,y2-1,x2-1,y2-1,color->medium,context);
    screen->bar(x2-1,y1+1,x2-1,y2-1,color->medium,context);   
  }

  // draw a decoration around an area that looks like it sticks out the screen
  virtual void draw_out_deco(i4_image_class *screen, 
                             i4_coord x1, i4_coord y1,
                             i4_coord x2, i4_coord y2,
                             i4_bool active,
                             i4_draw_context_class &context)
  {
    i4_color_hint_class::bevel *color;
    if (active)
      color=&color_hint->window.active;
    else 
      color=&color_hint->window.passive;


    screen->add_dirty(x1,y1,x2,y1+1,context);
    screen->add_dirty(x1,y1+2,x1+1,y2,context);
    screen->add_dirty(x1+2,y2-1,x2,y2,context);
    screen->add_dirty(x1-1,y1+2,x2,y2-2,context);

    screen->bar(x1,y1,x2,y1,color->bright,context);
    screen->bar(x1,y1,x1,y2,color->bright,context);

    screen->bar(x1+1,y1+1,x2-1,y1+1,color->medium,context);
    screen->bar(x1+1,y1+1,x1+1,y2-1,color->medium,context);

    screen->bar(x1+1,y2,x2,y2,color->dark,context);
    screen->bar(x2,y1+1,x2,y2,color->dark,context);

    screen->bar(x1+2,y2-1,x2-1,y2-1,color_hint->black,context);
    screen->bar(x2-1,y1+1,x2-1,y2-1,color_hint->black,context);
  
  }

  // this will create a temporary (quick) context help window at the mouse cursor
  // you are responsible for deleting the window
  virtual i4_window_class *create_quick_context_help(int mouse_x, int mouse_y,
                                                     const i4_const_str &str)
  {
    i4_parent_window_class *parent=i4_current_app->get_root_window();
    i4_mwm_context_help *w=new i4_mwm_context_help(this, str);

    if (w->width()+mouse_x+3 > parent->width())
      mouse_x=parent->width()-w->width()-3;
    if (w->height()+mouse_y+3 > parent->height())
      mouse_y=parent->height()-w->height()-3;

    i4_window_class *cw=new i4_color_window_class(w->width(), w->height(), 0, this);
    w->shadow=cw;

    parent->add_child(mouse_x+3, mouse_y+3, cw);   
    parent->add_child(mouse_x, mouse_y, w);



    return w;
  }

} mwm_style;


class i4_mwm_event_class : public i4_user_message_event_class
{
public:
  enum {
    CLOSE_YOURSELF,
    MAXIMIZE_YOURSELF
  } ;
  i4_window_class *from;

  i4_mwm_event_class(w8 sub_type, i4_window_class *from) 
    : i4_user_message_event_class(sub_type),from(from) {}

  virtual i4_event  *copy() { return new i4_mwm_event_class(sub_type,from); }

};


static void widget(i4_image_class *im,
                   i4_coord x1, i4_coord y1, i4_coord x2, i4_coord y2,
                   i4_color bright, i4_color med, i4_color dark,
                   i4_draw_context_class &context)
{
  // to keep from creating a dirty for each operation below
  im->add_dirty(x1,y1,x2,y2,context);      

  im->bar(x1,y1,x2,y1,bright,context);
  im->bar(x1,y1+1,x1,y2,bright,context);
  im->bar(x2,y1+1,x2,y2,dark,context);
  im->bar(x1+1,y2,x2-1,y2,dark,context);
  im->bar(x1+1,y1+1,x2-1,y2-1,med,context);
}


class i4_mwm_close_button_class : public i4_window_class
{  
private:
  i4_graphical_style_class *hint;
  i4_bool active;

public:
  char *name() { return "close_button"; }

  i4_mwm_close_button_class(w16 w, w16 h,i4_graphical_style_class *hint) 
    : i4_window_class (w,h),hint(hint)
  { 
    active=i4_F;
  }

  void activate(i4_bool yes)
  {
    active=yes;
    request_redraw();
  }
    

  virtual void receive_event(i4_event *ev)
  {
    i4_mwm_event_class c(i4_mwm_event_class::CLOSE_YOURSELF,this);
    if (ev->type()==i4_event::MOUSE_BUTTON_DOWN && parent)
      i4_kernel.send_event(parent,&c);
  }
 
  void draw(i4_draw_context_class &context)
  {
    i4_color_hint_class::bevel *color;
    /*    if (active)
      color=&hint->color_hint->window.active;
    else */
    color=&hint->color_hint->window.passive;

    local_image->add_dirty(0,0,width()-1,height()-1,context);



    widget(local_image, 0,0,width()-2,height()-2,color->bright,color->medium,color->dark,context);
    local_image->bar(0,height()-1,width()-1,height()-1,hint->color_hint->black,context);
    local_image->bar(width()-1,0,width()-1,height()-1,hint->color_hint->black,context);

    mwm_style.icon_hint->close_icon->put_image(local_image,2,2,context);    
  }

  virtual void show_self(w32 indent) 
  {    
    char fmt[50];
    sprintf(fmt,"%%%ds mwm_close_button",indent);
    i4_warning(fmt," ");
  }
} ;


static inline int get_res_num(char *name, int def)
{
  i4_const_str s=i4_string_man.get(name);
  if (s.null()) return def;
  i4_const_str::iterator i=s.begin();
  int r=i.read_number();
  int g=i.read_number();
  int b=i.read_number();
  return (r<<16)|(g<<8)|b;
}

// this is the top draggable part of a window
class i4_mwm_drag_bar_class : public i4_window_class
{
private:
  i4_graphical_style_class *hint;
  i4_bool active,dragging;
public:
  i4_const_str title;

  char *name() { return "drag_bar"; }

  enum 
  {
    MIN_WIDTH=15 
  };

  i4_mwm_drag_bar_class(w16 w, w16 h, const i4_const_str &title, i4_graphical_style_class *hint) : 
    i4_window_class(w,h), hint(hint), title(title)
  { 
    active=i4_F;
    dragging=i4_F;
  }

  void activate(i4_bool yes)
  {
    active=yes;
    request_redraw();
  }

  void draw(i4_draw_context_class &context)
  {
    i4_color_hint_class::bevel *color;
    if (active)
      color=&hint->color_hint->window.active;
    else 
      color=&hint->color_hint->window.passive;


    local_image->add_dirty(0,0,width()-1,height()-1,context);
    local_image->rectangle(0,0,width()-1,height()-1,hint->color_hint->black,context);

    i4_color sc,ec;

    if (active)
    { 
      sc=get_res_num("drag_bar_gradiant_active_start", 0x80); 
      ec=get_res_num("drag_bar_gradiant_active_end", 0x20); 
    }
    else
    {
      sc=get_res_num("drag_bar_gradiant_start", 0x707070); 
      ec=get_res_num("drag_bar_gradiant_end", 0x202020); 
    }

    i4_gradiant_bar(local_image, 1,1,width()-2,height()-2, sc, ec, context);


//     if (dragging)
//       local_image->widget(1,1,width()-2,height()-2,
//                           color->dark,color->medium,color->bright,context);
//     else
//       local_image->widget(1,1,width()-2,height()-2,
//                           color->bright,color->medium,color->dark,context);

    i4_font_class *font=hint->font_hint->normal_font;
    w16 strw=font->width(title);
    w16 strh=font->height(title);

    font->set_color(0xffffff);

    font->put_string(local_image,width()/2-strw/2,height()/2-strh/2,title,context);
  }
    
  virtual void receive_event(i4_event *ev)
  {
    if (ev->type()==i4_event::MOUSE_BUTTON_DOWN)
    {
      if (!dragging)
      {
	i4_window_request_drag_start_class drag(this);
	i4_kernel.send_event(parent,&drag);
	if (drag.return_result)
	{
	  dragging=i4_T;
	  request_redraw();
	}
      }
    } else if (ev->type()==i4_event::MOUSE_BUTTON_UP)
    {
      if (dragging)
      {
        i4_window_request_drag_end_class end_drag(this);
	i4_kernel.send_event(parent,&end_drag);
	dragging=i4_F;
	request_redraw();
      }
    } 
  }

};


class i4_mwm_window_class : public i4_draggable_window_class
{
private:
  i4_graphical_style_class *hint;
  i4_mwm_drag_bar_class *drag;
  i4_mwm_close_button_class *close_button;
  i4_bool active,draw_frame;
  w16 left,right,top,bottom;
  i4_parent_window_class *user_area;
  i4_event_reaction_class *on_delete;
 
  w32 close_button_width(i4_graphical_style_class *style)   
  { return 2+style->icon_hint->close_icon->width()+3; }

  w32 close_button_height(i4_graphical_style_class *style)   
  { return 2+style->icon_hint->close_icon->height()+3; }

  
  w32 dragbar_height(i4_graphical_style_class *style, const i4_const_str &title)
  {    
    return hint->font_hint->normal_font->height(title)+4+1;
  }

public:
  ~i4_mwm_window_class()
  {
    if (on_delete)
      delete on_delete;
    on_delete = 0;
  }

  i4_parent_window_class *user_window() { return user_area; }

  i4_mwm_window_class(w16 width, w16 height, 
                      const i4_const_str &title, 
                      i4_graphical_style_class *hint,
                      i4_event_reaction_class *on_delete)
    : i4_draggable_window_class(width,height), hint(hint),
      on_delete(on_delete)
  {   
    left=2;
    if (close_button_height(hint)>dragbar_height(hint,title))
      top=close_button_height(hint);
    else
      top=dragbar_height(hint,title);

    top+=2;  // top border

    right=2;
    bottom=2;

    
    active=i4_F;
    draw_frame=i4_T;
    
    resize(w+left+right,h+top+bottom);
    
    drag=new i4_mwm_drag_bar_class(w-left-right-close_button_width(hint),
                                   top-2,
                                   title,hint);
    add_child(2,2,drag);

    close_button=new i4_mwm_close_button_class(close_button_width(hint),
                                               close_button_height(hint),
                                               hint);

    add_child(w-right-close_button_width(hint),2,close_button);

    user_area=i4_add_color_window(this, hint->color_hint->window.passive.medium,
                                  hint,
                                  left, top, 
                                  width, height);
  } 

  virtual void parent_draw(i4_draw_context_class &context)
  {    
    if (draw_frame ||
	!undrawn_area.clipped_away(0,0,0+width()-1,0+1)     ||
	!undrawn_area.clipped_away(0,0,0+1,0+height()-1)  ||
	!undrawn_area.clipped_away(0+1,0+height()-2,0+width()-1,0+height()-1)  ||
	!undrawn_area.clipped_away(0+width()-2,0,0+width()-1,0+height()-1))
	
    {
      i4_color_hint_class::bevel *color;
      if (active)
        color=&hint->color_hint->window.active;
      else 
        color=&hint->color_hint->window.passive;


      local_image->add_dirty(0,0,width()-1,1,context);
      local_image->add_dirty(0,2,1,height()-1,context);
      local_image->add_dirty(2,height()-2,width()-1,height()-1,context);
      local_image->add_dirty(width()-2,2,width()-1,height()-3,context);

      local_image->bar(0,0,width()-1,0,color->medium,context);
      local_image->bar(0,1,0,height()-1,color->medium,context);

      local_image->bar(1,1,width()-2,1,color->bright,context);
      local_image->bar(1,2,1,height()-2,color->bright,context);

      local_image->bar(width()-2,2,width()-2,height()-2,color->medium,context);
      local_image->bar(2,height()-2,width()-3,height()-2,color->medium,context);

      local_image->bar(width()-1,0,width()-1,height()-1,hint->color_hint->black,context);
      local_image->bar(0,height()-1,width()-2,height()-1,hint->color_hint->black,context);
      draw_frame=i4_F;
    }


    if (!undrawn_area.empty())
      local_image->bar(2,2,width()-3,height()-3,hint->color_hint->window.passive.medium,context);

  }

  virtual i4_bool need_redraw() 
  { 
    return (i4_bool)(i4_parent_window_class::need_redraw()|draw_frame); 
  }

  void receive_event(i4_event *ev)
  {
    if (ev->type()==i4_event::WINDOW_MESSAGE)
    {
      CAST_PTR(f,i4_window_message_class,ev);
      if (f->sub_type==i4_window_message_class::GOT_MOUSE_FOCUS)
      {
	active=i4_T;
        drag->activate(i4_T);
	close_button->activate(i4_T);
	draw_frame=i4_T;

      }
      else if (f->sub_type==i4_window_message_class::LOST_MOUSE_FOCUS)
      {
        drag->activate(i4_F);
	close_button->activate(i4_F);
	active=i4_F;
	draw_frame=i4_T;

      } 
      else if (f->sub_type==i4_window_message_class::NOTIFY_RESIZE)
      {
        CAST_PTR(res,i4_window_notify_resize_class,ev);

        i4_mwm_window_class *new_parent=new i4_mwm_window_class(res->new_width,
                                                                res->new_height,
                                                                drag->title,
                                                                hint,
                                                                on_delete ? 
                                                                on_delete->copy() : 0);

        user_area->transfer_children(new_parent->user_area,0,0);

        // this will be post-poned
        i4_kernel.delete_handler(this);

        parent->add_child(x(),y(),new_parent);

      }

      i4_draggable_window_class::receive_event(ev);
    } else if (ev->type()==i4_event::USER_MESSAGE)
    {
      CAST_PTR(f,i4_mwm_event_class,ev);
      if (f->sub_type==i4_mwm_event_class::CLOSE_YOURSELF)
      {
        if (on_delete)
          i4_kernel.send(on_delete);

        i4_kernel.delete_handler(this);
      }
    } else i4_draggable_window_class::receive_event(ev);    
  }

} ;


i4_parent_window_class *i4_mwm_style_class::create_mp_window(i4_coord x, i4_coord y, 
                                                             w16 w, w16 h, 
                                                             const i4_const_str &title,
                                                             i4_event_reaction_class *on_delete)
{
    i4_parent_window_class *parent=i4_current_app->get_root_window();

  i4_mwm_window_class *win=new i4_mwm_window_class(w,h,title,this,on_delete);
  if (x==-1) 
    x=parent->width()/2-w/2;
  if (y==-1)
    y=parent->height()/2-h/2;

  parent->add_child(x,y,win);
  return win->user_window();
}