/*
  Copyright (C) 1991-2002, The Numerical Algorithms Group Ltd.
  All rights reserved.
  Copyright (C) 2007-2010, Gabriel Dos Reis.
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

      - Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.

      - Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in
        the documentation and/or other materials provided with the
        distribution.

      - Neither the name of The Numerical Algorithms Group Ltd. nor the
        names of its contributors may be used to endorse or promote products
        derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "openaxiom-c-macros.h"
/***
  Contains all the code needed to parse input items,
  InputString
  SimpleBox
  RadioBox.
  ****/
#include "debug.h"
#include "halloc.h"
#include "sockio.h"
#include "parse.h"
#include "lex.h"
#include "hyper.h"

static void insert_item(InputItem * item);
static void add_box_to_rb_list(char * name , InputBox * box);
static int check_others(InputBox * list);

/* create an unmapped input window for getting strings * */
extern int make_input_file;

HyperLink *
make_input_window(InputItem * item)
{
  HyperLink *link;
  XSetWindowAttributes at;

  if (!make_input_file) {
    link = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink");
    if (link == NULL) {
      fprintf(stderr, "Ran out of memory allocating a hyper link!\n");
      exit(-1);
    }
    at.cursor = gActiveCursor;
    at.background_pixel = gInputBackgroundColor;
    at.border_pixel = gActiveColor;
    link->win = XCreateWindow(gXDisplay, gWindow->fDisplayedWindow, 0, 0, 100, 100, 0,
                              0, InputOutput, CopyFromParent,
                              CWCursor | CWBackPixel | CWBorderPixel, &at);
    XSelectInput(gXDisplay, link->win, ButtonPressMask);
    link->type = openaxiom_Inputstring_token;
    link->x = link->y = 0;
    /** This way when I click in an input window, I need only use reference
      to get a pointer to the item                             ***/
    link->reference.string = item;
    hash_insert(gLinkHashTable,(char *) link,(char *) &link->win);

    return link;
  }
  return 0;
}

/* create an unmapped input window for boxes */
HyperLink *
make_box_window(InputBox * box, int type)
{
  HyperLink *link = 0;
  XSetWindowAttributes at;

  if (!make_input_file) {
    link = (HyperLink *) halloc(sizeof(HyperLink), "Make_box_window");
    if (link == NULL) {
      fprintf(stderr, "Ran out of memory allocating a hyper link!\n");
      exit(-1);
    }
    at.cursor = gActiveCursor;
    at.background_pixel = gInputBackgroundColor;
    link->win = XCreateWindow(gXDisplay, gWindow->fDisplayedWindow,
                              0, 0, 100, 100, 0,
                              0, InputOutput, CopyFromParent,
                              CWCursor | CWBackPixel, &at);
    XSelectInput(gXDisplay, link->win, ButtonPressMask);
    link->type = type;
    link->x = link->y = 0;
    /** This way when I click in an input window, I need only use reference
      to get a pointer to the item                             ***/
    link->reference.box = box;
    hash_insert(gLinkHashTable, (char *)link,(char *) &link->win);
  }

  return link;
}

void
initialize_default(InputItem *item,char * buff)
{
  LineStruct *newline;
  LineStruct *curr_line;
  int size = item->size;
  int bp;

  item->curr_line = item->lines = alloc_inputline(size);
  curr_line = item->lines;
  item->num_lines = 1;
  curr_line->line_number = 1;
  /* while I still have lines to fill */
  for (bp = 0; *buff;) {
    if (*buff == '\n') {
      curr_line->len = bp;
      curr_line->buffer[bp] = 0;
      newline = alloc_inputline(size);
      newline->line_number = ++(item->num_lines);
      curr_line->next = newline;
      newline->prev = curr_line;
      curr_line = newline;
      bp = 0;
      buff++;
    }
    else if (bp == size) {
      curr_line->len = size + 1;
      curr_line->buffer[size] = '_';
      curr_line->buffer[size + 1] = 0;
      newline = alloc_inputline(size);
      newline->line_number = ++(item->num_lines);
      curr_line->next = newline;
      newline->prev = curr_line;
      bp = 0;
      curr_line = newline;
    }
    else {
      curr_line->buffer[bp++] = *buff++;
    }
  }
  curr_line->buff_pntr = curr_line->len = bp;
  item->curr_line = curr_line;
}



/* Parse the input string statement * */
void
parse_inputstring(void)
{
  TextNode *input_node = curr_node;
  char *name;
  InputItem *item;
  int size;
  char *default_value;

  gStringValueOk = 0;

  /* first get the name */
  input_node->type = token.type;
  get_expected_token(openaxiom_Lbrace_token);
  name = get_input_string();
  input_node->data.text = alloc_string(name);
  /* now get the width */
  get_expected_token(openaxiom_Lbrace_token);
  get_expected_token(openaxiom_Word_token);
  get_expected_token(openaxiom_Rbrace_token);
  size = atoi(token.id);
  if (size < 0) {
    fprintf(stderr, "Illegal size in Input string\n");
    longjmp(jmpbuf, 1);
  }

  /* get the default value */
  get_expected_token(openaxiom_Lbrace_token);
  default_value = get_input_string();

  /** now I need to malloc space for the input stuff **/
  item = (InputItem *) halloc(sizeof(InputItem), "InputItem");

  /* Now store all the string info */
  item->name = (char *)
    halloc((strlen(input_node->data.text) + 1) * (sizeof(char)),"parse_inputstring");
  strcpy(item->name, input_node->data.text);
  item->size = size;
  item->entered = 0;
  item->next = NULL;
  initialize_default(item, default_value);

  /** Now that I have all the structures made, lets make the window, and
    add the item to the list                                 ****/

  input_node->link = make_input_window(item);
  if (!make_input_file)
    item->win = input_node->link->win;      /* TTT */
  insert_item(item);
  gStringValueOk = 1;
  curr_node = input_node;
  return ;
}

void
parse_simplebox(void)
{
  InputBox *box;
  char *name;
  short int picked = 0;
  char *filename;
  TextNode *input_box = curr_node;

  gStringValueOk = 0;

  /* set the type and space fields  */
  input_box->type = openaxiom_SimpleBox_token;
  input_box->space = token.id[-1];

  /* IS it selected? */
  get_token();
  if (token.type == openaxiom_Lsquarebrace_token) {
    get_expected_token(openaxiom_Word_token);
    if (!is_number(token.id)) {
      fprintf(stderr,
              "parse_simple_box: Expected a value not %s\n", token.id);
      print_page_and_filename();
      jump();
    }
    else if (!strcmp(token.id, "1"))
      picked = 1;
    else if (!strcmp(token.id, "0"))
      picked = 0;
    else {
      fprintf(stderr, "parse_simple_box: Unexpected Value %s\n", token.id);
      print_page_and_filename();
      jump();
    }
    get_expected_token(openaxiom_Rsquarebrace_token);
    get_token();
  }

  if (token.type != openaxiom_Lbrace_token) {
    token_name(token.type);
    fprintf(stderr, "parse_inputbox was expecting a { not a %s\n", ebuffer);
    print_page_and_filename();
    jump();
  }

  name = get_input_string();
  if (gPageBeingParsed->box_hash && hash_find(gPageBeingParsed->box_hash, name)) {
    fprintf(stderr, "Input box name %s is not unique \n", name);
    print_page_and_filename();
    jump();
  }

  box = alloc_inputbox();
  box->name = alloc_string(name);
  input_box->data.text = alloc_string(name);
  box->picked = picked;

  /* Get the filename for the selected and unselected bitmaps */
  get_expected_token(openaxiom_Lbrace_token);
  filename = get_input_string();
  if (!make_input_file)
    box->selected = insert_image_struct(filename);
  get_expected_token(openaxiom_Lbrace_token);
  filename = get_input_string();
  if (!make_input_file) {
    box->unselected = insert_image_struct(filename);
    /* set the width and height for the maximaum of the two */
    input_box->height = max(box->selected->height, box->unselected->height);
    input_box->width = max(box->selected->width, box->unselected->width);
    /* Make the window and stuff */
    input_box->link = make_box_window(box, openaxiom_SimpleBox_token);
    box->win = input_box->link->win;

    /* Now add the box to the box_has table for this window */
    if (gPageBeingParsed->box_hash == NULL) {
      gPageBeingParsed->box_hash = (HashTable *) halloc(sizeof(HashTable),
                                                        "Box Hash");
      hash_init(
                gPageBeingParsed->box_hash, 
                BoxHashSize, 
                (EqualFunction) string_equal, 
                (HashcodeFunction) string_hash);
    }
    hash_insert(gPageBeingParsed->box_hash, (char *)box, box->name);
  }

  /* reset the curr_node and then return */
  curr_node = input_box;
  gStringValueOk = 1;
  return;
}
void
parse_radiobox(void)
{
  InputBox *box;
  char *name;
  char *group_name;
  short int picked = 0;
  TextNode *input_box = curr_node;

  gStringValueOk = 0;

  /* set the type and space fields  */
  input_box->type = openaxiom_Radiobox_token;
  input_box->space = token.id[-1];

  /* IS it selected? */
  get_token();
  if (token.type == openaxiom_Lsquarebrace_token) {
    get_expected_token(openaxiom_Word_token);
    if (!is_number(token.id)) {
      fprintf(stderr,
              "parse_simple_box: Expected a value not %s\n", token.id);
      print_page_and_filename();
      jump();
    }
    else if (!strcmp(token.id, "1"))
      picked = 1;
    else if (!strcmp(token.id, "0"))
      picked = 0;
    else {
      fprintf(stderr, "parse_simple_box: Unexpected Value %s\n", token.id);
      print_page_and_filename();
      jump();
    }
    get_expected_token(openaxiom_Rsquarebrace_token);
    get_token();
  }

  if (token.type != openaxiom_Lbrace_token) {
    token_name(token.type);
    fprintf(stderr, "parse_inputbox was expecting a { not a %s\n", ebuffer);
    print_page_and_filename();
    jump();
  }

  name = get_input_string();
  if (gPageBeingParsed->box_hash && hash_find(gPageBeingParsed->box_hash, name)) {
    fprintf(stderr, "Input box name %s is not unique \n", name);
    print_page_and_filename();
    jump();
  }

  box = alloc_inputbox();
  box->name = alloc_string(name);
  input_box->data.text = alloc_string(name);
  box->picked = picked;

  /* Now what I need to do is get the group name */
  get_token();
  if (token.type != openaxiom_Lbrace_token) {
    token_name(token.type);
    fprintf(stderr, "parse_inputbox was expecting a { not a %s\n", ebuffer);
    print_page_and_filename();
    jump();
  }
  group_name = get_input_string();

  /*
   * Now call a routine which searches the radio box list for the current
   * group name, and if found adds this box to it
   */
  add_box_to_rb_list(group_name, box);

  input_box->width = box->rbs->width;
  input_box->height = box->rbs->height;
  /* Make the window and stuff */
  input_box->link = make_box_window(box, openaxiom_Radiobox_token);
  if (!make_input_file)
    box->win = input_box->link->win;        /* TTT */


  /* Now add the box to the box_has table for this window */
  if (gPageBeingParsed->box_hash == NULL) {
    gPageBeingParsed->box_hash = (HashTable *) halloc(sizeof(HashTable),
                                                      "Box Hash");
    hash_init(
              gPageBeingParsed->box_hash, 
              BoxHashSize, 
              (EqualFunction) string_equal, 
              (HashcodeFunction) string_hash);
  }
  hash_insert(gPageBeingParsed->box_hash, (char *)box, box->name);

  /* reset the curr_node and then return */
  curr_node = input_box;
  gStringValueOk = 1;
  return;
}
static void
add_box_to_rb_list(char *name,InputBox *box)
{
  RadioBoxes *trace = gPageBeingParsed->radio_boxes;
  InputBox *list;
  /*int found = 0;*/

  while (trace != NULL && strcmp(trace->name, name))
    trace = trace->next;

  if (!trace) {
    fprintf(stderr, "Tried to add a radio box to a non-existent group %s\n",
            name);
    print_page_and_filename();
    jump();
  }

  /* now add the box to the list */
  list = trace->boxes;
  box->next = list;
  trace->boxes = box;

  if (box->picked && check_others(box->next)) {
    fprintf(stderr, "Only a single radio button can be picked\n");
    print_page_and_filename();
    box->picked = 0;
  }
  box->selected = trace->selected;
  box->unselected = trace->unselected;
  box->rbs = trace;

  return;
}
static int
check_others(InputBox *list)
{
  InputBox *trace = list;

  while (trace != NULL && !trace->picked)
    trace = trace->next;

  if (trace != NULL)
    return 1;
  else
    return 0;
}


/* inserts an item into the current input list */
static void
insert_item(InputItem *item)
{
  InputItem *trace = gPageBeingParsed->input_list;

  if (gPageBeingParsed->current_item == NULL) {
    gPageBeingParsed->current_item = item;
  }
  if (trace == NULL) {
    /** Insert at the front of the list **/
    gPageBeingParsed->input_list = item;
    return;
  }
  else {
    /** find the end of the list **/
    while (trace->next != NULL)
      trace = trace->next;
    trace->next = item;
    return;
  }
}

InputItem *save_item;

void
init_paste_item(InputItem *item)
{
  InputItem *trace = gPageBeingParsed->input_list;

  if (!item) {
    gPageBeingParsed->input_list = NULL;
    gPageBeingParsed->current_item = NULL;
    save_item = NULL;
  }
  else {
    save_item = item->next;
    trace->next = NULL;
  }
}
void
repaste_item(void)
{
  InputItem *trace;

  if (save_item) {
    for (trace = gPageBeingParsed->input_list; trace && trace->next != NULL;
         trace = trace->next);
    if (trace) {
      trace->next = save_item;
    }
    else {
      gWindow->page->input_list = save_item;
      gWindow->page->current_item = save_item;
    }
  }
  save_item = NULL;
}

InputItem *
current_item(void)
{
  InputItem *trace = gPageBeingParsed->input_list;

  if (trace) {
    for (; trace->next != NULL; trace = trace->next);
    return trace;
  }
  else
    return NULL;
}
int
already_there(char *name)
{
  RadioBoxes *trace = gPageBeingParsed->radio_boxes;

  while (trace && strcmp(trace->name, name))
    trace = trace->next;

  if (trace)
    return 1;
  else
    return 0;
}
void
parse_radioboxes(void)
{
  TextNode *return_node = curr_node;
  RadioBoxes *newrb;
  char *fname;

  /* I really don't need this node, it just sets up some parsing stuff */
  return_node->type = openaxiom_Noop_token;

  newrb = alloc_rbs();

  get_token();
  if (token.type != openaxiom_Lbrace_token) {
    token_name(token.type);
    fprintf(stderr, "\\radioboxes was expecting a name not %s\n", ebuffer);
    print_page_and_filename();
    jump();
  }

  newrb->name = alloc_string(get_input_string());

  /* quick search for the name in the current list */
  if (already_there(newrb->name)) {
    free(newrb->name);
    free(newrb);
    fprintf(stderr, "Tried to redefine radioboxes %s\n", newrb->name);
    print_page_and_filename();
    jump();
  }
  /* now I have to get the selected and unslected bitmaps */
  get_token();
  if (token.type != openaxiom_Lbrace_token) {
    token_name(token.type);
    fprintf(stderr, "\\radioboxes was expecting a name not %s\n", ebuffer);
    print_page_and_filename();
    jump();
  }
  fname = get_input_string();
  if (!make_input_file)
    newrb->selected = insert_image_struct(fname);

  get_token();
  if (token.type != openaxiom_Lbrace_token) {
    token_name(token.type);
    fprintf(stderr, "\\radioboxes was expecting a name not %s\n", ebuffer);
    print_page_and_filename();
    jump();
  }
  fname = get_input_string();
  if (!make_input_file) {
    newrb->unselected = insert_image_struct(fname);
    newrb->height = max(newrb->selected->height, newrb->unselected->height);
    newrb->width = max(newrb->selected->width, newrb->unselected->width);
    /* now add the thing to the current list of radio boxes */
  }
  newrb->next = gPageBeingParsed->radio_boxes;
  gPageBeingParsed->radio_boxes = newrb;

  curr_node = return_node;
  return;
}