/**********************************************************************
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/window.hh"
#include "device/event.hh"
#include "gui/text.hh"
#include "gui/text_input.hh"
#include "window/colorwin.hh"
#include "error/alert.hh"
#include "gui/button.hh"
#include "gui/butbox.hh"
#include "gui/deco_win.hh"

char *tokens[]={"=", "(", ")",
                "[", "]", "right", "down", "up_deco", "down_deco", "'", "text_input", "text",
                "button", "butbox", "obj_ev", "user_ev", "x+", "y+", 0};

enum {
  TK_EQUAL, TK_LPAREN, TK_RPAREN,
  TK_LBRACE, TK_RBRACE, TK_RIGHT, TK_DOWN, TK_UP_DECO, TK_DOWN_DECO, TK_TICK, 
  TK_TEXT_INPUT, TK_TEXT,
  TK_BUTTON, TK_BUTBOX, TK_OBJ_EV,  TK_USER_EV,
  TK_XPLUS, TK_YPLUS, TK_NUMBER, TK_POINTER, TK_NONE };
  


void i4_expected(char *why, i4_const_str::iterator i, const i4_const_str &s)
{
  i4_str *er=new i4_str(i4gets("expected"),2000);
  i4_str::iterator x=er->end();
  
  while (i!=s.end())
  {
    x.set(i.get());
    ++i;
    ++x;
    er->set_length(er->length()+1);
  }

  i4_warning(why);
  i4_alert(*er, 2000);
  i4_error("expected error : see error output");
}

int i4_is_space(i4_const_str::iterator i)
{
  if (i.get().value()==' ' ||
      i.get().value()=='\t' ||
      i.get().value()=='\n' ||
      i.get().value()=='\r')
    return 1;
  else return 0;
}

int i4_cmp_char_str(i4_const_str::iterator i, const i4_const_str &s, char *c)
{
  while (*c && i!=s.end())
  {
    if (i.get().value()!=*c)
      return 0;

    c++;
    ++i;
  }

  if (*c)
    return 0;
  else return 1;

}


int i4_read_dlg_token(i4_const_str::iterator &i,
                      const i4_const_str &fmt,
                      sw32 &num,
                      void *&ptr,
                      va_list &ap)
{
  while (i!=fmt.end() && i4_is_space(i))
    ++i;

  if (i==fmt.end()) return TK_NONE;

  if (i.get().value()=='%')
  {
    ++i;
    if (i.get().value()=='d')
    {
      ++i;
      num=va_arg(ap,int);
      return TK_NUMBER;
    }
    else if (i.get().value()=='p')
    {
      ++i;
      ptr=va_arg(ap,void *);
      return TK_POINTER;
    }
    else i4_error("expecting p or d after %");
  }
  else
  {
    if ((i.get().value()>='0' && i.get().value()<='9') || i.get().value()=='-')
    {
      int neg=0;
      if (i.get().value()=='-')
      {
        neg=1;
        ++i;
      }

      num=0;
      while (i!=fmt.end() && !i4_is_space(i) && i.get().value()>='0' && i.get().value()<='9')
      {
        num=num*10+i.get().value()-'0';
        ++i;
      }

      if (neg) num=-num;
      return TK_NUMBER;
    }

    for (int j=0; tokens[j]; j++)
      if (i4_cmp_char_str(i, fmt, tokens[j]))
      {
        int l=strlen(tokens[j]);
        while (l) 
        {
          ++i;
          l--;
        }
            
        return j;
      }

    i4_expected("unknown token", i, fmt);

  }
  return TK_NONE;
}


int i4_next_token_is_rbrace(i4_const_str::iterator i,
                            const i4_const_str &fmt)
{
  while (i!=fmt.end() && i4_is_space(i))
    ++i;

  if (i==fmt.end()) return 1;

  if (i.get().value()==']') return 1;
  else return 0;
}


i4_str *i4_read_str(i4_const_str::iterator &i, const i4_const_str &fmt, va_list &ap)
{
  sw32 n; void *p;

  if (i4_read_dlg_token(i, fmt, n, p, ap)!=TK_TICK)
    i4_expected("'",i,fmt);

  i4_str *s=new i4_str(i4_string_man.get(0), 200);
  i4_str::iterator x=s->begin();
  while (i!=fmt.end() && i.get().value()!='\'')
  {
    x.set(i.get().value());
    ++i;
    ++x;
    s->set_length(s->length()+1);
  }
  ++i;

  i4_str *ret=s->vsprintf(200, ap);

  delete s;
  return ret;
}

i4_event_reaction_class *i4_read_reaction(i4_const_str::iterator &i, 
                                          const i4_const_str &fmt, 
                                          va_list &ap)
{
  sw32 x,id; void *p, *from, *to;
  int t=i4_read_dlg_token(i, fmt, x, p, ap);

  if (t==TK_OBJ_EV)
  {
    if (i4_read_dlg_token(i, fmt, x, p, ap)!=TK_LPAREN)
      i4_expected("(",i,fmt);

    if (i4_read_dlg_token(i, fmt, x, from, ap)!=TK_POINTER)
      i4_expected("pointer",i,fmt);

    if (i4_read_dlg_token(i, fmt, x, to, ap)!=TK_POINTER)
      i4_expected("pointer",i,fmt);

    if (i4_read_dlg_token(i, fmt, id, p, ap)!=TK_NUMBER)
      i4_expected("number",i,fmt);
  
    if (i4_read_dlg_token(i, fmt, x, p, ap)!=TK_RPAREN)
      i4_expected(")",i,fmt);
    
    i4_object_message_event_class *om;
    om=new i4_object_message_event_class((i4_event_handler_class *)from, id);
    return new i4_event_reaction_class((i4_event_handler_class *)to, om);
  }
  else if (t==TK_USER_EV)
  {
    if (i4_read_dlg_token(i, fmt, x, p, ap)!=TK_LPAREN)
      i4_expected("(",i,fmt);

    if (i4_read_dlg_token(i, fmt, x, to, ap)!=TK_POINTER)
      i4_expected("pointer",i,fmt);

    if (i4_read_dlg_token(i, fmt, id, p, ap)!=TK_NUMBER)
      i4_expected("number",i,fmt);

    if (i4_read_dlg_token(i, fmt, x, p, ap)!=TK_RPAREN)
      i4_expected(")",i,fmt);
    
    i4_user_message_event_class *uev;
    uev=new i4_user_message_event_class(id);

    return new i4_event_reaction_class((i4_event_handler_class *)to, uev);
  }
  else 
  {
    i4_expected("obj_ev or user_ev",i,fmt);
    return 0;
  }
}

i4_window_class *i4_read_object(i4_parent_window_class *parent,
                                i4_graphical_style_class *style,
                                sw32 &cx, sw32 &cy,
                                i4_const_str::iterator &i, const i4_const_str &fmt,
                                va_list &ap,
                                int in_buttonbox)
{
  sw32 x;
  void *p;
  i4_const_str::iterator start_i=i;
  i4_window_class *ret=0;


  int token=i4_read_dlg_token(i, fmt, x, p, ap);
  switch (token)
  {
    case TK_POINTER :
    {
      token=i4_read_dlg_token(i, fmt, x, p, ap);
      if (token!=TK_EQUAL)
        i4_expected("expected = after %p", start_i, fmt);

      i4_window_class *r=i4_read_object(parent, style, cx, cy, i, fmt, ap, in_buttonbox);
      *((i4_window_class **)p)=r;
      ret=r;
    } break;

    case TK_LBRACE :
    {
      int dir=i4_read_dlg_token(i, fmt, x, p, ap);
      if (dir!=TK_RIGHT && dir!=TK_DOWN)
        i4_expected("right or down after [", start_i, fmt);
     
      sw32 ncx=cx, ncy=cy;
      int max_w=0, max_h=0;
      i4_window_class *r;
      while (!i4_next_token_is_rbrace(i, fmt))
      {
        r=i4_read_object(parent, style, ncx, ncy, i, fmt, ap, in_buttonbox);
        if (r)
        {
          if (!ret) ret=r;
          if (dir==TK_RIGHT)
            ncx+=r->width();
          else ncy+=r->height();
        }        
      }
      i4_read_dlg_token(i, fmt, x, p, ap);

    } break;

    case TK_RIGHT :
    case TK_DOWN :
    case TK_NUMBER :
    case TK_RBRACE :
      i4_expected("out of place token",start_i,fmt);
      break;

    case TK_XPLUS :
    {
      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_LPAREN)
        i4_expected("(",start_i,fmt);

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_NUMBER)
        i4_expected("number",start_i,fmt);

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_RPAREN)
        i4_expected(")",start_i,fmt);

      cx+=x;
    } break;

    case TK_YPLUS :
    {
      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_LPAREN)
        i4_expected("(",start_i,fmt);

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_NUMBER)
        i4_expected("number",start_i,fmt);
      cy+=x;

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_RPAREN)
        i4_expected(")",start_i,fmt);

    } break;

    case TK_UP_DECO :
    {
      sw32 w,h;
      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_LPAREN)
        i4_expected("(",start_i,fmt);

      if (i4_read_dlg_token( i, fmt, w, p, ap)!=TK_NUMBER)
        i4_expected("number",start_i,fmt);

      if (i4_read_dlg_token( i, fmt, h, p, ap)!=TK_NUMBER)
        i4_expected("number",start_i,fmt);

      i4_deco_window_class *cw=new i4_deco_window_class(w,h, i4_T, style);

      sw32 ncx=0, ncy=0;
      i4_window_class *r=i4_read_object(cw, style, ncx, ncy, i, fmt, ap, 0);
      
      parent->add_child(cx,cy, cw);

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_RPAREN)
        i4_expected(")",start_i,fmt);

      ret=cw;
    } break;

    case TK_TEXT :
    {
      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_LPAREN)
        i4_expected("(",start_i,fmt);

      i4_str *s=i4_read_str(i, fmt, ap);
      if (s)
      {
        i4_text_window_class *tw=new i4_text_window_class(*s, style);
        delete s;
        parent->add_child(cx, cy, tw);
        ret=tw;
      }

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_RPAREN)
        i4_expected(")",start_i,fmt);

    } break;

    case TK_TEXT_INPUT :
    {
      sw32 w;
      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_LPAREN)
        i4_expected("(",start_i,fmt);

      if (i4_read_dlg_token( i, fmt, w, p, ap)!=TK_NUMBER)
        i4_expected("number",start_i,fmt);

      i4_str *s=i4_read_str(i, fmt, ap);
      if (s)
      {
        i4_text_input_class *ti=new i4_text_input_class(style, *s, w, 256);
        delete s;
        parent->add_child(cx, cy, ti);
        ret=ti;
      }

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_RPAREN)
        i4_expected(")",start_i,fmt);
    } break;

      
    case TK_BUTTON :
    {
      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_LPAREN)
        i4_expected("(",start_i,fmt);

      i4_color_window_class tmp_win(0,0,0,style);
      i4_window_class *r=i4_read_object(&tmp_win, style, cx, cy, i, fmt, ap, in_buttonbox);      
      i4_event_reaction_class *re=i4_read_reaction(i, fmt, ap);
      
      if (r)
      {
        tmp_win.remove_child(r);

        i4_button_class *b=new i4_button_class(0, r, style, re);
        
        if (in_buttonbox)
          ((i4_button_box_class *)parent)->add_button(cx, cy, b);
        else
        {
          b->set_popup(i4_T);
          parent->add_child(cx,cy, b);
        }
        ret=b;
      }

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_RPAREN)
        i4_expected(")",start_i,fmt);


    } break;


    case TK_BUTBOX :
    {
      sw32 def_down;

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_LPAREN)
        i4_expected("(",start_i,fmt);

      if (i4_read_dlg_token( i, fmt, def_down, p, ap)!=TK_NUMBER)
        i4_expected("number",start_i,fmt);

      
      i4_button_box_class *bbox=new i4_button_box_class(0);
      
      sw32 ncx=0,ncy=0;
      i4_window_class *r=i4_read_object(bbox, style, ncx, ncy, i, fmt, ap, 1);
      bbox->resize_to_fit_children();

      i4_button_class *b=(i4_button_class *)bbox->get_nth_window(def_down);
      if (b)
        bbox->push_button(b,0);

      parent->add_child(cx, cy, bbox);

      if (i4_read_dlg_token( i, fmt, x, p, ap)!=TK_RPAREN)
        i4_expected(")",start_i,fmt);
      
      return bbox;
    } break;

  }
  return ret;
}

void i4_create_dialog(const i4_const_str &fmt,
                      i4_parent_window_class *parent,
                      i4_graphical_style_class *style,
                      ...)
{
  va_list ap;
  va_start(ap, style);


  i4_const_str::iterator i=fmt.begin();

  sw32 cx=0, cy=0;

  i4_read_object(parent, style, cx, cy, i, fmt, ap, 0);

  va_end(ap);

}