/* canvas-type selection-chain
 *   [CV-CHAIN]
 *   name: <arbitrary unique name>
 *   position-itag: (position): <box-tag of an element in the main text>
 *   position-rect: (position): <rectangle (<x>+<w>,<y>+<h>) within the box-tag or the main text>
 *   disable: <whether is disabled: 1 = yes, 0 = no, or missing = enabled>
 *   orientation: <left|center|right>
 *   v-orientation: <top|center|bottom>
 *   element.<name>: (repeated for each image to insert): [img:]<image file> or txt:<text file> or sprt:<sprite file>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "canvas.h"

static int type_no = 0;

struct cv_type {
  int active;                         /* activated chain-item (1-max), or 0 = none */
  struct vgi_canvas_arrow arw_right;  /* arrow right */
  struct vgi_canvas_arrow arw_left;   /* arrow left */
  int orientation;                    /* one of VG_ORIENTATIONS */
  int v_orientation;                  /* one of VG_ORIENTATIONS */
  int max;                            /* number of chain-items */
  struct {                            /* chain-items */
    struct vgi_imgcnt img;              /* chain-item image */
    char *name;                         /* name of chain-item */
    VG_BOOL isnative;                   /* whether image is explicitly enlisted in canvas-file */
    VG_BOOL isdisabled;                 /* whether is disabled */
  } *e;
};

void cvinit_chain(struct vgi_canvas_inittype *, int);

static void * t_read_section(const struct VG_Canvas *, struct vgi_canvas_item *, const char *);
static void t_pre_exec(struct VG_Canvas *, struct vgi_canvas_item *);
static void t_draw(struct vgi_canvas_data *, struct vgi_canvas_item *, const char **);
static void t_destroy(struct vgi_canvas_item *);
static void t_dump(FILE *, struct vgi_canvas_item *);

static void canvas_chain_clear(struct VG_Canvas *, const char *);
static VG_BOOL canvas_chain_add(struct VG_Canvas *, const char *, const char *, const char *, const char *);
static VG_BOOL canvas_chain_disable(struct VG_Canvas *, const char *, const char *, VG_BOOL);
static void canvas_chain_set_activated(struct VG_Canvas *, const char *, const char *);
static const char * canvas_chain_get_activated(struct VG_Canvas *, const char *);


void
cvinit_chain(struct vgi_canvas_inittype *cvinittype, int type)
{
  if (cvinittype == NULL) { return; }

  type_no = type;

  /* cvinit */
  cvinittype->mouse_opaque = VG_FALSE;
  cvinittype->uses_cursor = VG_TRUE;
  cvinittype->read_section = t_read_section;
  cvinittype->get_itag_rect = NULL;
  cvinittype->warp_mouse = NULL;
  cvinittype->pre_exec = t_pre_exec;
  cvinittype->draw = t_draw;
  cvinittype->destroy = t_destroy;
  cvinittype->dump = t_dump;

  /* functions */
  vg4->canvas->chain_clear = canvas_chain_clear;
  vg4->canvas->chain_add = canvas_chain_add;
  vg4->canvas->chain_disable = canvas_chain_disable;
  vg4->canvas->chain_set_activated = canvas_chain_set_activated;
  vg4->canvas->chain_get_activated = canvas_chain_get_activated;
} /* Ende cvinit_chain */


static void *
t_read_section(const struct VG_Canvas *cvas, struct vgi_canvas_item *cvitem, const char *dtptr)
{
  struct SML3_gummi gm1 = SML3_GUM_INITIALIZER;
  struct cv_type *cvtype;
  char ename[64], bprefix[32], bval[256], *pval;
  char *nptr;

  if (cvas == NULL || cvitem == NULL || dtptr == NULL) { return NULL; }

  cvtype = SML3_calloc(1, sizeof(*cvtype));

  nptr = (char *)dtptr;
  while (canvas_get_lineval(nptr, "element", ename, sizeof(ename), bprefix, sizeof(bprefix), bval, sizeof(bval), &nptr)) {
    int telm = cvtype->max++;

    if (telm == 0) {
      cvtype->e = SML3_malloc(sizeof(*cvtype->e));
    } else {
      cvtype->e = SML3_realloc(cvtype->e, (telm + 1) * sizeof(*cvtype->e));
    }

    memset(&cvtype->e[telm], 0, sizeof(*cvtype->e));
    canvas_load_image(cvas, bprefix, bval, &cvtype->e[telm].img);
    if (cvtype->e[telm].img.type == VGI_IMGCNT_NONE) {  /* remove new element */
      if (--cvtype->max == 0) {
        free(cvtype->e);
        cvtype->e = NULL;
      }
    } else {
      cvtype->e[telm].name = SML3_strdup(ename);
      cvtype->e[telm].isnative = VG_TRUE;
    }
  }

  if (canvas_get_lineval(dtptr, "orientation", NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    if (strcmp(pval, "left") == 0) {
      cvtype->orientation = VG_ORI_LEFT;
    } else if (strcmp(pval, "center") == 0) {
      cvtype->orientation = VG_ORI_CENTER;
    } else if (strcmp(pval, "right") == 0) {
      cvtype->orientation = VG_ORI_RIGHT;
    } else {
      cvtype->orientation = VG_ORI_CENTER;
    }
  }

  if (canvas_get_lineval(dtptr, "v-orientation", NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    if (strcmp(pval, "top") == 0) {
      cvtype->v_orientation = VG_ORI_LEFT;
    } else if (strcmp(pval, "center") == 0) {
      cvtype->v_orientation = VG_ORI_CENTER;
    } else if (strcmp(pval, "bottom") == 0) {
      cvtype->v_orientation = VG_ORI_RIGHT;
    } else {
      cvtype->v_orientation = VG_ORI_CENTER;
    }
  }

  SML3_gumdest(&gm1);

  return cvtype;
} /* Ende t_read_section */


static void
t_pre_exec(struct VG_Canvas *cvas, struct vgi_canvas_item *cvitem)
{
  struct cv_type *cvtype;
  struct vgi_canvas_dirs *arw_dirs;

  if (cvas == NULL || cvitem == NULL) { return; }

  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  /* set arrow-images */
  if (cvas->cve.arrow.user_set) {
    arw_dirs = &cvas->cve.arrow.user;
  } else if (cvas->cve.ishigh) {
    arw_dirs = &cvas->cve.arrow.high;
  } else {
    arw_dirs = &cvas->cve.arrow.low;
  }
  cvtype->arw_left.img = &arw_dirs->left;
  cvtype->arw_right.img = &arw_dirs->right;
} /* Ende t_pre_exec */


static void
t_draw(struct vgi_canvas_data *cvdata, struct vgi_canvas_item *cvitem, const char **selname)
{
  struct cv_type *cvtype;
  int leftright;

  if (cvdata == NULL || cvdata->cvas == NULL || cvitem == NULL || selname == NULL) { return; }
  if (cvitem->rimg == NULL) { return; }
  if (cvitem->isdisabled) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  /* check if shall moving left (-1) or right (1) */
  leftright = 0;
  if (cvdata->hasfocus) {  /* has focus */
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_LEFT]) || VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_DOWN])) {
      leftright = -1;
    }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_RIGHT]) || VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_UP])) {
      leftright = 1;
    }
  }

  /* draw */
  { struct VG_Image *imgp;
    struct VG_Position posb;
    int *active, oldact;

    active = &cvtype->active;
    if (*active < 1 || *active > cvtype->max) {
      for (*active = 1; *active <= cvtype->max;) {
        if (!cvtype->e[*active - 1].isdisabled) { break; }
        (*active)++;
      }
      if (*active > cvtype->max) { *active = 0; }
    }

    if (cvdata->hasfocus) {  /* has focus */
      VG_BOOL keyok;

      if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_RETURN]) || VGI_CVKEY_NEWPRESSED(cvdata->keys.m_left)) {
        keyok = VG_TRUE;
      } else {
        keyok = VG_FALSE;
      }

      /* activate chain-item according to keypress left and right */
      if (leftright < 0) {  /* left */
        oldact = *active;
        if (*active > 1) { (*active)--; }
        for (; *active > 0;) {
          if (!cvtype->e[*active - 1].isdisabled) { break; }
          (*active)--;
        }
        if (*active < 1) { *active = oldact; }
      } else if (leftright > 0) {  /* right */
        oldact = *active;
        if (*active < cvtype->max) { (*active)++; }
        for (; *active <= cvtype->max;) {
          if (!cvtype->e[*active - 1].isdisabled) { break; }
          (*active)++;
        }
        if (*active > cvtype->max) { *active = oldact; }
      }

      /* activate chain-item according to mouse */
      if (cvdata->cvas->cve.mouse.show) {
        int xm, ym;
        if (vg4->input->mouse_position(&xm, &ym)) {
          /* check for moving left-right */
          if (IS_IN_RECT(&cvtype->arw_left.rect, xm, ym, cvdata->xadd, cvdata->yadd)) {
            if (VGI_CVKEY_NEWPRESSED(cvdata->keys.m_left)) {
              oldact = *active;
              if (*active > 1) { (*active)--; }
              for (; *active > 0;) {
                if (!cvtype->e[*active - 1].isdisabled) { break; }
                (*active)--;
              }
              if (*active < 1) { *active = oldact; } else { keyok = VG_FALSE; }
            }
          } else if (IS_IN_RECT(&cvtype->arw_right.rect, xm, ym, cvdata->xadd, cvdata->yadd)) {
            if (VGI_CVKEY_NEWPRESSED(cvdata->keys.m_left)) {
              oldact = *active;
              if (*active < cvtype->max) { (*active)++; }
              for (; *active <= cvtype->max;) {
                if (!cvtype->e[*active - 1].isdisabled) { break; }
                (*active)++;
              }
              if (*active > cvtype->max) { *active = oldact; } else { keyok = VG_FALSE; }
            }
          }
        }
      }

      if (keyok) { *selname = cvitem->name; }
    }

    /* draw out */
    if (*active > 0) {
      struct VG_ImagecopyAttr iattr;
      imgp = vg4_intern_get_img_from_imgcnt(&cvtype->e[*active - 1].img, &iattr);
      vg4->image->clear(cvitem->rimg);
      posb = canvas_set_posi(&cvitem->rect, imgp, NULL, cvtype->orientation, cvtype->v_orientation);
      vg4->image->copy(cvitem->rimg, imgp, &posb, &iattr);
      posb.pos = VG_POS_UPPER_LEFT;
      posb.x = cvdata->xadd + cvitem->rect.x;
      posb.y = cvdata->yadd + cvitem->rect.y;
      vg4->window->copy(cvitem->rimg, &posb, NULL);
      if (cvdata->hasfocus) {  /* has focus */
        canvas_draw_actborder(&cvitem->rect, cvdata->xadd, cvdata->yadd);
      }
    }

    /* draw arrow-images */
    if (cvdata->hasfocus && cvtype->max > 0) {
      /* left image */
      if (cvtype->max == 1) {
        CLEAR_RECT(&cvtype->arw_left.rect);
      } else {
        for (oldact = *active - 1; oldact >= 1; oldact--) {
          if (!cvtype->e[oldact - 1].isdisabled) { break; }
        }
        if (oldact >= 1) {
          canvas_draw_arrow(cvdata->cvas, &cvtype->arw_left, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_FALSE, 3);
        } else {
          canvas_draw_arrow(cvdata->cvas, &cvtype->arw_left, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_TRUE, 3);
        }
      }

      /* right image */
      if (cvtype->max == 1) {
        CLEAR_RECT(&cvtype->arw_right.rect);
      } else {
        for (oldact = *active + 1; oldact <= cvtype->max; oldact++) {
          if (!cvtype->e[oldact - 1].isdisabled) { break; }
        }
        if (oldact <= cvtype->max) {
          canvas_draw_arrow(cvdata->cvas, &cvtype->arw_right, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_FALSE, 4);
        } else {
          canvas_draw_arrow(cvdata->cvas, &cvtype->arw_right, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_TRUE, 4);
        }
      }
    }
  }
} /* Ende t_draw */


static void
t_destroy(struct vgi_canvas_item *cvitem)
{
  struct cv_type *cvtype;
  int uelm;

  if (cvitem == NULL) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  for (uelm = 0; uelm < cvtype->max; uelm++) {
    vg4_intern_free_imgcnt(&cvtype->e[uelm].img);
    if (cvtype->e[uelm].name != NULL) { free(cvtype->e[uelm].name); }
  }
  if (cvtype->max > 0) { free(cvtype->e); }

  free(cvtype);
} /* Ende t_destroy */


static void
t_dump(FILE *outfp, struct vgi_canvas_item *cvitem)
{
  struct cv_type *cvtype;
  const char *namptr;
  int uelm;

  if (outfp == NULL || cvitem == NULL) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  fprintf(outfp, "    type=selection-chain\n");
  fprintf(outfp, "    active=%d\n", cvtype->active);
  fprintf(outfp, "    chain-items=%d:\n", cvtype->max);
  for (uelm = 0; uelm < cvtype->max; uelm++) {
    fprintf(outfp, "    - [%d]\n", uelm + 1);
    fprintf(outfp, "      ename=%s\n", cvtype->e[uelm].name);
    namptr = vg4_intern_get_name_from_imgcnt(&cvtype->e[uelm].img);
    fprintf(outfp, "      img=%s\n", namptr);
    fprintf(outfp, "      is-native=%s\n", (cvtype->e[uelm].isnative ? "yes" : "no"));
    fprintf(outfp, "      is-disabled=%s\n", (cvtype->e[uelm].isdisabled ? "yes" : "no"));
  }
} /* Ende t_dump */


/* canvas_chain_clear:
 * clear chain
 * @param cvas   canvas
 * @param iname  name of chain
 */
static void
canvas_chain_clear(struct VG_Canvas *cvas, const char *iname)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  int uelm, ustart;
  
  if (cvas == NULL || iname == NULL || *iname == '\0') { return; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  ustart = 0;
  for (uelm = 0; uelm < cvtype->max; uelm++) {
    if (cvtype->e[uelm].isnative) {
      cvtype->e[uelm].isdisabled = VG_FALSE;
      ustart++;
    } else {
      vg4_intern_free_imgcnt(&cvtype->e[uelm].img);
      if (cvtype->e[uelm].name != NULL) {
        free(cvtype->e[uelm].name);
        cvtype->e[uelm].name = NULL;
      }
    }
  }

  if (ustart == 0) {
    free(cvtype->e);
    cvtype->e = NULL;
  }
  cvtype->max = ustart;
  cvtype->active = 0;
} /* Ende canvas_chain_clear */


/* canvas_chain_add:
 * add chain-item to chain
 * @param cvas        canvas
 * @param iname       name of chain
 * @param fileprefix  specifying the file to load:
 *                     - "img" for an image
 *                     - "txt" for a text
 *                     - "sprt" for a sprite
 * @param filename    file to load
 * @param ename       name of chain-item
 * @return  VG_TRUE = added or VG_FALSE = not added
 */
static VG_BOOL
canvas_chain_add(struct VG_Canvas *cvas, const char *iname, const char *fileprefix, const char *filename, const char *ename)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  int uelm;
  VG_BOOL retw;

  if (cvas == NULL || iname == NULL || *iname == '\0'
      || fileprefix == NULL || *fileprefix == '\0'
      || filename == NULL || *filename == '\0'
      || ename == NULL || *ename == '\0') { return VG_FALSE; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return VG_FALSE; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return VG_FALSE; }

  retw = VG_FALSE;

  uelm = cvtype->max++;
  if (uelm == 0) {
    cvtype->e = SML3_malloc(sizeof(*cvtype->e));
  } else {
    cvtype->e = SML3_realloc(cvtype->e, (uelm + 1) * sizeof(*cvtype->e));
  }
  memset(&cvtype->e[uelm], 0, sizeof(*cvtype->e));

  /* load file */
  if (canvas_load_image(cvas, fileprefix, filename, &cvtype->e[uelm].img)) {
    cvtype->e[uelm].name = SML3_strdup(ename);
    cvtype->e[uelm].isnative = VG_FALSE;
    retw = VG_TRUE;
  } else {
    if (--cvtype->max == 0) {
      free(cvtype->e);
      cvtype->e = NULL;
    }
  }

  return retw;
} /* Ende canvas_chain_add */


/* canvas_chain_disable:
 * disable or enable chain-item
 * @param cvas     canvas
 * @param iname    name of chain
 * @param ename    name of chain-item to disable/enable
 * @param disable  whether to disable
 * @return  whether it was previous disabled
 */
static VG_BOOL
canvas_chain_disable(struct VG_Canvas *cvas, const char *iname, const char *ename, VG_BOOL disable)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  int uelm;
  VG_BOOL wasdisabled;

  if (cvas == NULL || iname == NULL || *iname == '\0' || ename == NULL || *ename == '\0') { return VG_TRUE; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return VG_TRUE; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return VG_TRUE; }

  wasdisabled = VG_TRUE;

  for (uelm = 0; uelm < cvtype->max; uelm++) {
    if (strcmp(ename, cvtype->e[uelm].name) == 0) {
      wasdisabled = cvtype->e[uelm].isdisabled;
      cvtype->e[uelm].isdisabled = disable;
      break;
    }
  }

  return wasdisabled;
} /* Ende canvas_chain_disable */


/* canvas_chain_set_activated:
 * activate chain-item of a chain
 * @param cvas    canvas
 * @param iname   name of chain
 * @param ename   name of chain-item to activate
 */
static void
canvas_chain_set_activated(struct VG_Canvas *cvas, const char *iname, const char *ename)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  int uelm;

  if (cvas == NULL || iname == NULL || *iname == '\0' || ename == NULL || *ename == '\0') { return; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  for (uelm = 0; uelm < cvtype->max; uelm++) {
    if (strcmp(ename, cvtype->e[uelm].name) == 0) {
      cvtype->active = uelm + 1;
      break;
    }
  }
  if (uelm == cvtype->max) {
    cvtype->active = 0;
  }
} /* Ende canvas_chain_set_activated */


/* canvas_chain_get_activated:
 * get activated chain-item of a chain
 * @param cvas     canvas
 * @param iname    name of chain
 * @return   name of activated chain-item, or NULL = none
 */
static const char *
canvas_chain_get_activated(struct VG_Canvas *cvas, const char *iname)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  const char *ename;
  int item_no;

  if (cvas == NULL || iname == NULL || *iname == '\0') { return NULL; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return NULL; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return NULL; }

  ename = NULL;
  item_no = cvtype->active;
  if (item_no > 0) { ename = cvtype->e[item_no - 1].name; }

  return ename;
} /* Ende canvas_chain_get_activated */
