/**********************************************************************
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/text_input.hh"
#include "gui/text.hh"
#include "lisp/li_types.hh"
#include "lisp/li_class.hh"
#include "lisp/li_dialog.hh"
#include "file/ram_file.hh"
#include "app/app.hh"
#include "memory/array.hh"
#include "gui/image_win.hh"
#include "gui/button.hh"
#include "gui/list_box.hh"
#include "menu/textitem.hh"
#include "window/wmanager.hh"


static li_symbol_ref li_list_box("list_box");

class li_generic_edit_class : public li_type_edit_class
{
public:
  enum input_type { TEXT_INPUT,
                    LIST_BOX };

  input_type get_type(li_object *property_list, li_environment *env)
  {
    if (property_list && property_list->type()==LI_LIST &&
        li_list_box.get()==li_car(property_list,env))
      return LIST_BOX;
    else 
      return TEXT_INPUT;
  }
  

  virtual int create_edit_controls(const i4_const_str &name,
                                   li_object *o, 
                                   li_object *property_list,
                                   i4_window_class **windows, 
                                   int max_windows,
                                   li_environment *env)
  {
    if (max_windows<2) return 0;
    
    char buf[200];
    i4_ram_file_class rf(buf, 30);
    li_get_type(o->type())->print(o, &rf);
    buf[rf.tell()]=0;

    i4_graphical_style_class *style=i4_current_app->get_style();

    windows[0]=new i4_text_window_class(name, style);

    if (get_type(property_list,env)==LIST_BOX)
    {
      property_list=li_cdr(property_list, env);
    
      i4_list_box_class *lb=new i4_list_box_class(200, style, 
                                                  i4_current_app->get_window_manager());

      int on=0;
      for (;property_list; property_list=li_cdr(property_list,env), on++)
      {
        char buf[100];
        i4_ram_file_class rf(buf, 100);
        li_object *v=li_car(property_list,env);


        li_get_type(v->type())->print(v, &rf);
        buf[rf.tell()]=0;

        lb->add_item(new i4_text_item_class(buf, style)); 

        if (v->type()==o->type() && li_get_type(v->type())->equal(o,v))
          lb->set_current_item(on);
       

      }
      windows[1]=lb;   
    }    
    else
    {
      if (o->type()==LI_STRING)
      {
        buf[strlen(buf)-1]=0;   // chop of end quote
        windows[1]=new i4_text_input_class(style,buf+1, 200,200);
      }
      else
        windows[1]=new i4_text_input_class(style,buf, 200,200);
    }
                                       
    return 2;
  }

  virtual i4_bool can_apply_edit_controls(li_object *o, 
                                          li_object *property_list,
                                          i4_window_class **windows)
  {
    return i4_T;    
  }

  virtual li_object *apply_edit_controls(li_object *o, 
                                         li_object *property_list,
                                         i4_window_class **windows,
                                         li_environment *env)
  {
    if (get_type(property_list, env)==LIST_BOX)
    {
      i4_list_box_class *ib=((i4_list_box_class *)windows[1]);
      return li_nth(property_list, ib->get_current()+1, env);
    } 
    else if (get_type(property_list, env)==TEXT_INPUT)
    {
      i4_text_input_class *w=((i4_text_input_class *)windows[1]);
      
      i4_const_str::iterator i=w->get_edit_string()->begin();
      if (o->type()==LI_INT)
        return new li_int(i.read_number());
      else if (o->type()==LI_FLOAT)
        return new li_float(i.read_float());
      else if (o->type()==LI_SYMBOL)
      {
        char buf[300];
        i4_os_string(*w->get_edit_string(), buf, 100);
        return li_get_symbol(buf);
      }
      else if (o->type()==LI_STRING)
        return new li_string(*w->get_edit_string());
      else
        return o;
    }
    else 
      return o;
  }
      

} li_generic_edit_instance;


class li_class_dialog_item : public li_dialog_item
{
public:
  char *name() { return "li_class_item"; }

  i4_array items;

  li_class_dialog_item(li_class *c, li_object *_prop_list, li_environment *env)
    : items(5,5)
  {
    prop_list=_prop_list; 
    o=c;

    int t=li_class_total_members(c->type()), i, max_colums=0;
    int colums[50];
    memset(colums, 0, sizeof(colums));

    for (i=0; itype(), i);
      li_object *val=c->value(i);
      li_object *prop_list=li_class_get_property_list(c->type(), sym);
      
      li_dialog_item *item=new li_dialog_item(sym->name()->value(), val, prop_list, env);
       items.add(item);

       int t_win=items[i]->t_windows;
       if (t_win>max_colums)
         max_colums=t_win;
       for (int j=0; jwindows[j] && items[i]->windows[j]->width()>colums[j])
           colums[j]=items[i]->windows[j]->width();
     }

     int dy=0, maxw=0;
     for (i=0; it_windows; j++)
       {
         if (items[i]->windows[j])
         {
           int xoff=dx-(items[i]->windows[j]->x()-items[i]->x());
           items[i]->windows[j]->move(xoff,0);

           if (items[i]->windows[j]->height()>maxh)
             maxh=items[i]->windows[j]->height();
         }

         dx+=colums[j]+3;
         if (dx>maxw) maxw=dx;
       }
       items[i]->resize_to_fit_children();

       dy+=maxh+1;        
     }      

     private_resize(maxw,dy);
   }

   i4_bool can_apply(li_environment *env)
   {
     for (int i=0; ican_apply(env))
         return i4_F;
     return i4_T;
   }

   li_object *apply(li_environment *env)
   {
     li_class *c=(li_class *)li_new(o.get()->type());

     for (int i=0; iset_value(i, items[i]->apply(env));

     return c;
   }
 };

 class li_class_edit_class : public li_type_edit_class
 {
 public:
   int create_edit_controls(const i4_const_str &name,
                            li_object *object, 
                            li_object *property_list,
                            i4_window_class **windows, 
                            int max_windows,
                            li_environment *env)
   {
     if (max_windows)
     {
       windows[0]=new li_class_dialog_item(li_class::get(object,env), property_list, env);
       return 1;
     } else return 0;    
   }

   i4_bool can_apply_edit_controls(li_object *objectw, 
                                   li_object *property_list,
                                   i4_window_class **windows,
                                   li_environment *env)
   {
     return ((li_class_dialog_item *)windows[0])->can_apply(env);
   }

   li_object *apply_edit_controls(li_object *o, 
                                  li_object *property_list,
                                  i4_window_class **windows,
                                  li_environment *env)
   {
     return ((li_class_dialog_item *)windows[0])->apply(env);   
   }

 } li_class_edit_instance;


 class li_generic_edit_initer : public i4_init_class
 {
 public:
   int init_type() { return I4_INIT_TYPE_LISP_FUNCTIONS; }

   void init()
   {
     li_get_type(LI_INT)->editor=&li_generic_edit_instance;
     li_get_type(LI_FLOAT)->editor=&li_generic_edit_instance;
     li_get_type(LI_SYMBOL)->editor=&li_generic_edit_instance;
     li_get_type(LI_STRING)->editor=&li_generic_edit_instance;
     li_set_class_editor(&li_class_edit_instance);
   }

 } li_generic_edit_initer_instance;


 li_dialog_item::li_dialog_item() 
   : i4_color_window_class(0,0, i4_current_app->get_style()->color_hint->neutral(),
                           i4_current_app->get_style())
 { 
   windows=0; 
   t_windows=0; 
   o=0;
   prop_list=0;
 }


 li_dialog_item::li_dialog_item(const i4_const_str &name, 
                                li_object *_o, 
                                li_object *prop_list,
                                li_environment *env)

   : i4_color_window_class(0,0, i4_current_app->get_style()->color_hint->neutral(),
                           i4_current_app->get_style()),
     prop_list(prop_list)
 {
   o=_o;
   windows=0;
   t_windows=0;

   i4_window_class *w[10];

   if (li_get_type(o->type())->editor)
   {
     if (prop_list!=li_get_symbol("no_edit"))
     {
       t_windows=li_get_type(o->type())->editor->create_edit_controls(name,
                                                                      o.get(),
                                                                      prop_list,
                                                                      w, 10, env);
       if (t_windows)
       {       
         windows=(i4_window_class **)i4_malloc(sizeof(i4_window_class *) * t_windows,"");
         int x=0, i, maxh=0;
         for (i=0; iheight()>maxh)
             maxh=w[i]->height();


         for (i=0; iheight()/2 , windows[i]);
           x+=w[i]->width();
         }
         resize_to_fit_children();
       }
     }
   }
 }



 i4_bool li_dialog_item::can_apply(li_environment *env)
 {
   if (!li_get_type(o->type())->editor)  return i4_T;

   return li_get_type(o->type())->editor->can_apply_edit_controls(o.get(), prop_list, windows,env);
 }


 li_object *li_dialog_item::apply(li_environment *env)
 {
   if (li_get_type(o->type())->editor)
     return li_get_type(o->type())->editor->apply_edit_controls(o.get(), prop_list, windows, env);
   return o.get();
 }  

 li_dialog_item::~li_dialog_item()
 {
   if (windows)
     i4_free(windows);
 }


 i4_graphical_style_class *li_dialog_window_class::style()
 {
   return i4_current_app->get_style(); 
 }



 li_dialog_window_class::~li_dialog_window_class()
 {
   if (called_on_close)
   {
     if (new_value.get())
       called_on_close(li_make_list(new_value.get(), o.get(), 0),0);
     called_on_close=0;
   }
 }

 li_dialog_window_class::li_dialog_window_class(const i4_const_str &name,
                                                li_object *_o, 
                                                li_object *_prop_list,
                                                li_function_type called_on_close,
                                                li_environment *env)
   : i4_color_window_class(0,0, style()->color_hint->neutral(), style()),
     called_on_close(called_on_close), enviroment(env)
 {
   o=_o;
   prop_list=_prop_list;


   mp_handle=0;
   int t=li_get_type(o->type())->editor->create_edit_controls(name, o.get(), 
                                                             prop_list.get(), w, 10,
                                                              env);
  int x=0, maxh=0;
  for (int i=0; iheight()>maxh)
      maxh=w[i]->height();
  }

  i4_window_class *ok, *cancel;

  if (style()->icon_hint->ok_icon && style()->icon_hint->cancel_icon)
  {
    ok=new i4_image_window_class(style()->icon_hint->ok_icon);
    cancel=new i4_image_window_class(style()->icon_hint->cancel_icon);
  }
  else
  {
    ok=new i4_text_window_class(i4gets("ok"), style());
    cancel=new i4_text_window_class(i4gets("cancel"), style());
  }
    
  resize_to_fit_children();

  i4_button_class *okb=new i4_button_class(0, ok, style(), 
                                           new i4_event_reaction_class(this, 1));
  i4_button_class *cancelb=new i4_button_class(0, cancel, style(), 
                                               new i4_event_reaction_class(this, 1));
  x=width()/2-okb->width()/2-cancelb->width()/2;
  if (x<0) x=0;

  add_child(x, maxh+1, okb);
  add_child(x+okb->width(), maxh+1, cancelb);

  resize_to_fit_children();

}

void li_dialog_window_class::receive_event(i4_event *ev)
{
  if (ev->type()==i4_event::USER_MESSAGE)
  {
    if (((i4_user_message_event_class *)ev)->sub_type==1)
    {
      if (!li_get_type(o->type())->editor->can_apply_edit_controls(o.get(), prop_list.get(), w, env()))
        return;
      else
        new_value=li_get_type(o->type())->editor->apply_edit_controls(o.get(),
                                                                      prop_list.get(), w, env());

    }

    if (mp_handle)
      style()->close_mp_window(mp_handle);        

    if (called_on_close)
    {
      if (new_value.get())
        called_on_close(li_make_list( new_value.get(), o.get(),  0),0);
      called_on_close=0;
    }

  }
  else
    i4_color_window_class::receive_event(ev);
}
  


li_dialog_window_class *li_create_dialog(const i4_const_str &name,
                                         li_object *o, 
                                         li_object *prop_list,
                                         char *close_fun,
                                         li_environment *env)
{
  li_function_type fun=0;
  if (close_fun)
    fun=li_function::get(li_get_fun(li_get_symbol(close_fun), env),env)->value();

  li_dialog_window_class *d=new li_dialog_window_class(name, o,prop_list, fun, env);

  i4_parent_window_class *mp;
  mp=i4_current_app->get_style()->create_mp_window(-1,-1, d->width(), d->height(),
                                                   name, 0);
  d->mp_handle=mp;
  mp->add_child(0,0,d);
  return d;
}




li_dialog_window_class *li_create_dialog(const i4_const_str &name,
                                         li_object *o, 
                                         li_object *prop_list,
                                         li_function_type fun,
                                         li_environment *env)
{
  li_dialog_window_class *d=new li_dialog_window_class(name, o,prop_list, fun, env);

  i4_parent_window_class *mp;
  mp=i4_current_app->get_style()->create_mp_window(-1,-1, d->width(), d->height(),
                                                   name, 0);
  d->mp_handle=mp;
  mp->add_child(0,0,d);
  return d;
}