#include "layout.h"
#include "builder.h"
#include <stdio.h>

FormclassPtr NewFormclass(void) {
  FormclassPtr fc = (FormclassPtr)calloctrace(sizeof(Formclass), 1);
  return fc;
}

FieldPtr NewField(FormclassPtr form) {
  DisplayTypePtr display;
  InputTypePtr input;
  FieldPtr f = (FieldPtr)calloctrace(sizeof(Field), 1);
  
  if (!form->topfield) {
    form->fieldcount = 0;
    form->topfield = (FieldPtr *)malloctrace(sizeof(FieldPtr));
  }
  form->topfield =
    (FieldPtr *)
      realloctrace(form->topfield, ++form->fieldcount * sizeof(FieldPtr));
  form->topfield[form->fieldcount - 1] = f;

  f->detail = (FieldTypePtr)calloctrace(sizeof(FieldType), 1);
  return f;
}

void MakeFieldDetail(FieldPtr f, int inpordisp) {
  if (f->field_type == inpordisp) return;
  if (f->field_type == INPUT) FreeInputField(f->detail->inputptr);
  else if (f->field_type == DISPLAY) FreeDisplayField(f->detail->displayptr);
  f->field_type = inpordisp;
  if (inpordisp == INPUT) {
    f->detail->inputptr =
      (InputFieldPtr)calloctrace(sizeof(InputField), 1);
    f->detail->inputptr->inputsource =
      (InputTypePtr)calloctrace(sizeof(InputType), 1);
  } else {
    f->detail->displayptr =
      (DisplayFieldPtr)calloctrace(sizeof(DisplayField), 1);
    f->detail->displayptr->display =
      (DisplayTypePtr)calloctrace(sizeof(DisplayType), 1);
  }
}

void MakeField(Field *f, int type) {
  InputTypePtr input;
  DisplayTypePtr display;
  if (ISINPUT(type)) {
    MakeFieldDetail(f, INPUT);
    input = f->detail->inputptr->inputsource;
    f->detail->inputptr->input_type = type;
  } else {
    MakeFieldDetail(f, DISPLAY);
    display = f->detail->displayptr->display;
    f->detail->displayptr->display_type = type;
  }
  switch (type) {
  case SELECTIONINPUT:
    input->selection =
      (SelectionInputPtr)calloctrace(sizeof(SelectionInput), 1);
    break;
  case NUMERICINPUT:
    input->numeric =
      (NumericInputPtr)calloctrace(sizeof(NumericInput), 1);
    break;
  case TEXTINPUT:
    input->text =
      (TextInputPtr)calloctrace(sizeof(TextInput), 1);
    break;
  case BOOLEANINPUT:
    input->boolean =
      (BooleanInputPtr)calloctrace(sizeof(BooleanInput), 1);
    break;
  case TEXTDISPLAY:
    display->text =
      (TextDisplayPtr)calloctrace(sizeof(TextDisplay), 1);
    break;
  case LINEDISPLAY:
    display->line =
      (LineDisplayPtr)calloctrace(sizeof(LineDisplay), 1);
    break;
  case BOXDISPLAY:
    display->box =
      (BoxDisplayPtr)calloctrace(sizeof(BoxDisplay), 1);
    break;
  }
}

NodePtr NewNode(FormclassPtr form) {
  NodePtr n = (NodePtr)calloctrace(sizeof(Node), 1), *tra;
  int siz = 0;
  if (!form->topnode)
    form->topnode = (NodePtr *)calloctrace(sizeof(NodePtr), 1);
  for (tra = form->topnode; *tra; tra++) siz++;
  form->topnode =
    (NodePtr *)realloctrace(form->topnode, (++siz + 1) * sizeof(NodePtr));
  form->topnode[siz] = NULL;
  form->topnode[siz-1] = n;
  return n;
}

void NewAction(NodePtr node, char *name, int tonode) {
  ActionPtr action = (ActionPtr)calloctrace(sizeof(Action), 1), *tra;
  int siz = 0;
  action->label = name;
  action->nextnode = tonode;
  if (!node->actionlist)
    node->actionlist = (ActionPtr *)calloctrace(sizeof(ActionPtr), 1);
  for (tra = node->actionlist; *tra; tra++) siz++;
  node->actionlist =
    (ActionPtr *)
      realloctrace(node->actionlist, (++siz + 1) * sizeof(ActionPtr));
  node->actionlist[siz] = NULL;
  node->actionlist[siz-1] = action;
}

void SetTextAttributes(FieldPtr f, char *val) {
  if (strstr(val, "bold"))
    f->textattrs |= TextAttrBold;
  if (strstr(val, "italic") || strstr(val, "oblique"))
    f->textattrs |= TextAttrItalic;
}

void SetDefaultValue(FormclassPtr form, FieldPtr f, char *val) {
  Value v;
  InputTypePtr inp;
  char worked;
  worked = f->field_type == INPUT && EvalFormclass(val, form, &v);
  if (!worked) return;
  inp = f->detail->inputptr->inputsource;
  switch (f->detail->inputptr->input_type) {
  case SELECTIONINPUT:
    if (valconvtoIntList(&v)) {
      if (inp->selection->defaultvalues)
	freetrace(inp->selection->defaultvalues);
      inp->selection->defaultvalues = v.v.il;
    } else freeinternalvaluestorage(&v);
    break;
  case TEXTINPUT:
    if (valconvtoString(&v)) {
      if (inp->text->defaultvalue) freetrace(inp->text->defaultvalue);
      inp->text->defaultvalue = v.v.s;
    } else freeinternalvaluestorage(&v);
    break;
  case NUMERICINPUT:
    if (valconvtoInt(&v)) inp->numeric->defaultvalue = v.v.i;
    else freeinternalvaluestorage(&v);
    break;
  case BOOLEANINPUT:
    if (valconvtoBool(&v)) inp->boolean->defaultvalue = v.v.b;
    else freeinternalvaluestorage(&v);
    break;
  }
}

/*****************************************************************************
  How to free form structure: */

void FreeTextDisplay(TextDisplayPtr t) {
  if (!t) return;
  if (t->data) freetrace(t->data);
  freetrace(t);
}

void FreeBoxDisplay(BoxDisplayPtr b) {
  if (b) freetrace(b);
}

void FreeLineDisplay(LineDisplayPtr l) {
  if (l) freetrace(l);
}

void FreeTextInput(TextInputPtr t) {
  if (!t) return;
  if (t->calculate) freetrace(t->calculate);
  if (t->data) freetrace(t->data);
  if (t->defaultvalue) freetrace(t->defaultvalue);
  freetrace(t);
}

void FreeBooleanInput(BooleanInputPtr b) {
  if (!b) return;
  if (b->calculate) freetrace(b->calculate);
  freetrace(b);
}

void FreeNumericInput(NumericInputPtr n) {
  if (!n) return;
  if (n->calculate) freetrace(n->calculate);
  freetrace(n);
}

void FreeSelectionInput(SelectionInputPtr s) {
  char **choic;
  if (!s) return;
  for (choic = s->choices; choic && *choic; choic++)
    freetrace(*choic);
  if (s->choices) freetrace(s->choices);
  if (s->defaultvalues) freetrace(s->defaultvalues);
  if (s->data) freetrace(s->data);
  if (s->calculate) freetrace(s->calculate);
  freetrace(s);
}

void FreeInputField(InputFieldPtr inp) {
  if (!inp) return;
  switch (inp->input_type) {
  case SELECTIONINPUT:
    FreeSelectionInput(inp->inputsource->selection);
    break;
  case NUMERICINPUT:
    FreeNumericInput(inp->inputsource->numeric);
    break;
  case TEXTINPUT:
    FreeTextInput(inp->inputsource->text);
    break;
  case BOOLEANINPUT:
    FreeBooleanInput(inp->inputsource->boolean);
    break;
  default:
    break;
  }
  freetrace(inp->inputsource);
  if (inp->prompt) freetrace(inp->prompt);
  freetrace(inp);
}

void FreeDisplayField(DisplayFieldPtr disp) {
  if (!disp) return;
  switch (disp->display_type) {
  case TEXTDISPLAY:
    FreeTextDisplay(disp->display->text);
    break;
  case BOXDISPLAY:
    FreeBoxDisplay(disp->display->box);
    break;
  case LINEDISPLAY:
    FreeLineDisplay(disp->display->line);
    break;
  default:
    break;
  }
  freetrace(disp->display);
  freetrace(disp);
}
  
void FreeField(FieldPtr f) {
  if (!f) return;
  if (f->name) freetrace(f->name);
  if (f->up) freetrace(f->up);
  if (f->down) freetrace(f->down);
  if (f->right) freetrace(f->right);
  if (f->left) freetrace(f->left);
  if (f->helptext) freetrace(f->helptext);
  if (f->lockedif) freetrace(f->lockedif);
  if (f->hiddenif) freetrace(f->hiddenif);
  if (f->dependents) freetrace(f->dependents);
  if (f->field_type == INPUT) FreeInputField(f->detail->inputptr);
  else if (f->field_type == DISPLAY) FreeDisplayField(f->detail->displayptr);
  freetrace(f->detail);
  freetrace(f);
}

void FreeNode(NodePtr n) {
  ActionPtr *ac;
  if (!n) return;
  if (n->statusstring) freetrace(n->statusstring);
  if (n->defaultqueue) freetrace(n->defaultqueue);
  for (ac = n->actionlist; ac && *ac; ac++)
    freetrace(*ac);
  if (n->actionlist) freetrace(n->actionlist);
  freetrace(n);
}

void FreeFormclass(FormclassPtr form) {
  int i;
  FieldPtr *pf;
  NodePtr *nf;
  if (!form) return;
  if (form->formname) freetrace(form->formname);
  for (pf = form->topfield, i = 0; i < form->fieldcount; i++, pf++)
    FreeField(*pf);
  freetrace(form->topfield);
  for (nf = form->topnode; nf && *nf; nf++)
    FreeNode(*nf);
  if (form->topnode) freetrace(form->topnode);
  freetrace(form);
}

void FreeForminstance(ForminstancePtr forminst) {
  if (!forminst) return;
  FreeFormclass(forminst->class);
  if (forminst->queue) freetrace(forminst->queue);
  if (forminst->owner) freetrace(forminst->owner);
  freetrace(forminst);
}

