/**********************************************************************
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 "gui/scroll_bar.hh"
#include "window/colorwin.hh"
#include "image/image.hh"
#include "gui/button.hh"
#include "gui/image_win.hh"
#include "window/win_evt.hh"



inline void draw_out_deco(i4_image_class *screen,
                          i4_draw_context_class &context,
                          i4_coord x1, i4_coord y1, i4_coord x2, i4_coord y2,
                          i4_color bright, i4_color medium, i4_color dark, i4_color black)
{
  screen->add_dirty(x1,y1,x2,y2,context);

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

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


class i4_scroll_button : public i4_window_class
{
  i4_bool active,dragging;
  i4_scroll_bar *buddy;

  void reparent(i4_image_class *draw_area, i4_parent_window_class *parent)
  {
    i4_window_class::reparent(draw_area, parent);
    if (parent)
      fit_parent();
  }

  i4_bool vertical() { return buddy->vertical; }
  i4_graphical_style_class *style() { return buddy->style; }



public:
  void fit_parent()
  {   
    if (vertical())
      resize(parent->width(), 
             parent->height() * buddy->total_visible_objects / buddy->total_scroll_objects);
    else
      resize(parent->width() * buddy->total_visible_objects / buddy->total_scroll_objects,
             parent->height());
  }

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


  i4_scroll_button(i4_scroll_bar *buddy)
    : i4_window_class(0,0), 
      buddy(buddy)
  {
    active=i4_F;
    dragging=i4_F;
  }

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

    draw_out_deco(local_image,context,
                  0,0,width()-1,height()-1,
                  color->bright,
                  color->medium,
                  color->dark,
                  style()->color_hint->black);
  }

  void receive_event(i4_event *ev)
  {
    switch (ev->type())
    {
      case i4_event::WINDOW_MESSAGE :
      {
        CAST_PTR(wev,i4_window_message_class,ev);
        if (wev->sub_type==i4_window_message_class::GOT_MOUSE_FOCUS)
        {
          active=i4_T;
          request_redraw();
        }
        else if (wev->sub_type==i4_window_message_class::LOST_MOUSE_FOCUS)
        {
          active=i4_F;
          request_redraw();
        }
      } break;
      case i4_event::MOUSE_MOVE :
      {
        CAST_PTR(mev,i4_mouse_move_event_class,ev);
        if (dragging)
        {
          if (vertical())
          {
            i4_coord new_y=mev->y+y()-parent->y()-height()/2;  // convert to parent coords
            if (new_y<0)
              new_y=0;
            else if (new_y+height()>=parent->height())
              new_y=parent->height()-height();

            new_y=new_y+parent->y()-y();  // convert back to our coord system
            if (new_y!=y())
            {
              move(0,new_y);
              buddy->calc_pos();
              buddy->send_position();
            }
          }
          else
          {
            i4_coord new_x=mev->x+x()-parent->x()-width()/2;  // convert to parent coords
            if (new_x<0)
              new_x=0;
            else if (new_x+width()>=parent->width())
              new_x=parent->width()-width();

            new_x=new_x+parent->x()-x();  // convert back to our coord system
            if (new_x!=x())
            {
              move(new_x, 0); 
              buddy->calc_pos();
              buddy->send_position();
            }
          }
        }
      } break;

      case i4_event::MOUSE_BUTTON_DOWN :
      {
        CAST_PTR(bev,i4_mouse_button_down_event_class,ev);
        if (bev->but==i4_mouse_button_down_event_class::LEFT)
        {
          dragging=i4_T;
          i4_window_request_mouse_grab_class grab(this);
          i4_kernel.send_event(parent,&grab);
        }
      } break;

      case i4_event::MOUSE_BUTTON_UP :
      {
        CAST_PTR(bev,i4_mouse_button_up_event_class,ev);
        if (bev->but==i4_mouse_button_up_event_class::LEFT)
        {
          dragging=i4_F;
          i4_window_request_mouse_ungrab_class ungrab(this);
          i4_kernel.send_event(parent,&ungrab);
        }
      } break;


    }
  }

} ;


// if total items under control changes
void i4_scroll_bar::set_new_total(int total)
{
  total_scroll_objects=total;
  if (total_scroll_objects<=0)
    total_scroll_objects=1;

  scroll_but->fit_parent();
}

i4_button_class *i4_scroll_bar::create_button(i4_button_class *&b, i4_image_class *im)
{
  b=new i4_button_class(0, new i4_image_window_class(im), style);

  b->send.press=new i4_event_reaction_class(this, new i4_object_message_event_class(b));
  b->set_repeat_down(i4_T);
  b->set_popup(i4_T);
  
  return b;
}

i4_scroll_bar::i4_scroll_bar(i4_bool vertical,
                             int max_dimention_size,
                             int total_visible_objects,
                             int total_scroll_objects,    // total number of objects
                             w32 message_id,
                             i4_event_handler_class *send_to,
                             i4_graphical_style_class *style)

  : i4_parent_window_class(0,0),
    vertical(vertical),
    total_scroll_objects(total_scroll_objects),
    total_visible_objects(total_visible_objects),
    id(message_id),
    send_to(send_to),
    style(style)
{
  pos=0;
  if (vertical)
  {
    add_child(0,0, create_button(up_but, style->icon_hint->up_icon));

    create_button(down_but, style->icon_hint->down_icon);
    add_child(0, max_dimention_size-down_but->height(), down_but);

    left_but=right_but=0;

    resize(up_but->width(), max_dimention_size);
  }
  else
  {
    add_child(0,0, create_button(left_but, style->icon_hint->left_icon));

    create_button(right_but, style->icon_hint->right_icon);
    add_child(max_dimention_size-right_but->width(),0, right_but);
    up_but=down_but=0;    

    resize(max_dimention_size,  left_but->height());
  }


  int x=vertical ? 0 : left_but->width(),
    y=vertical ? up_but->height() : 0;
  int sa_w=vertical ? width() : width() - left_but->width() - right_but->height(),
    sa_h=vertical ? height() - up_but->width() - down_but->height() : height();

  // area where scroll grab button resides
  scroll_area=i4_add_color_window(this,
                                  style->color_hint->window.passive.dark,
                                  style, x,y, sa_w, sa_h);


  scroll_but=new i4_scroll_button(this);
  scroll_area->add_child(0,0,scroll_but);

}
  
void i4_scroll_bar::receive_event(i4_event *ev)
{
  if (ev->type()==i4_event::OBJECT_MESSAGE)
  {
    CAST_PTR(oev,i4_object_message_event_class,ev);
    if (oev->object==down_but || oev->object==right_but)
    {    
      if (posobject==up_but || oev->object==left_but)
    {
      if (pos>0)
      {
        pos-=1;
        set_bar_pos(pos);
        send_position();
      }           
    }

  } else i4_parent_window_class::receive_event(ev);
}

void i4_scroll_bar::set_bar_pos(sw32 pos)
{ 
  sw32 reverse_pos;

  if (vertical)
  {
    if (total_scroll_objects<=1)
      reverse_pos=0;
    else
      reverse_pos=pos*(scroll_area->height()-scroll_but->height())/(total_scroll_objects-1);

    scroll_but->move(0,reverse_pos- (scroll_but->y()-scroll_area->y()));
  }
  else
  {
    if (total_scroll_objects<=1)
      reverse_pos=0;
    else
      reverse_pos=pos*(scroll_area->width()-scroll_but->width())/(total_scroll_objects-1);

    scroll_but->move(reverse_pos- (scroll_but->x()-scroll_area->x()),0);
  }
                   
}

void i4_scroll_bar::calc_pos()
{
  if (total_visible_objects>=total_scroll_objects || total_scroll_objects<=1)
    pos=0;
  else if (vertical)
    pos=(scroll_but->y()-scroll_area->y()) * (total_scroll_objects-1) / 
      (scroll_area->height()-scroll_but->height());
  else
    pos=(scroll_but->x()-scroll_area->x()) * (total_scroll_objects-1) / 
      (scroll_area->width()-scroll_but->width());

}

void i4_scroll_bar::send_position()
{
  if (send_to)
  {
    //    i4_vscroll_button *b=(i4_vscroll_button *)scroll_but;
    i4_scroll_message message(pos, total_scroll_objects, id);
    i4_kernel.send_event(send_to,&message);
  }  
}