/* canvas-type text-box
 *   [CV-TEXT]
 *   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>
 *   velo: <scrolling velocitas in pixels, or missing = no scrolling>
 *   bgcolor: <background color of text-box: hex number (0xRRGGBB) or "transparent", (default: transparent)>
 *   default-text: <default text (according to vg4->font->totext()), or missing = empty>
 *   following text-header parameters:
 *   - text.border:         text-header "border"
 *   - text.font:           text-header "font"
 *   - text.fgcolor:        text-header "fgcolor"
 *   - text.bgcolor:        text-header "bgcolor"
 *   - text.bold:           text-header "bold"
 *   - text.linepitch:      text-header "linepitch"
 *   - text.orientation:    text-header "orientation"
 *   - text.v-orientation:  text-header "v-orientation"
 *   - text.nowrap:         text-header "nowrap"
 */

#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 {
  char *dfltext;                     /* default text */
  struct VG_Image *img;              /* text image */
  int bgcolor;                       /* background color of box */
  int ypos;                          /* y-start-position */
  int velo;                          /* scrolling velocitas, or 0 = no scrolling */
  int repw;                          /* waiting value before repeating */
  struct vgi_canvas_arrow arw_up;    /* arrow up */
  struct vgi_canvas_arrow arw_down;  /* arrow down */
  struct VG_Hash *hparam;            /* default parameters for text */
  struct VG_Hash *htags;             /* tags of text: key=<name>, value=<rectangle> */
};

void cvinit_text(struct vgi_canvas_inittype *, int);

static void * t_read_section(const struct VG_Canvas *, struct vgi_canvas_item *, const char *);
static struct VG_Rect * t_get_itag_rect(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_text_set(struct VG_Canvas *, const char *, const char *);


void
cvinit_text(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 = t_get_itag_rect;
  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->text_set = canvas_text_set;
} /* Ende cvinit_text */


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], bval[256], *pval;
  char *nptr;

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

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

  cvtype->hparam = vg4_hash_create_nolist();
  cvtype->htags = NULL;
  cvtype->bgcolor = VG_COLOR_TRANSPARENT;

  nptr = (char *)dtptr;
  while (canvas_get_lineval(nptr, "text", ename, sizeof(ename), NULL, 0, bval, sizeof(bval), &nptr)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, ename, pval);
  }

  if (canvas_get_lineval(dtptr, "velo", NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    cvtype->velo = atoi(pval);
  }

  if (cvtype->velo <= 0) {
    cvtype->velo = 0;
    cvitem->isselectable = VG_FALSE;
  }

  if (canvas_get_lineval(dtptr, "bgcolor", NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    if (strncmp(pval, "0x", 2) == 0) {
      errno = 0;
      cvtype->bgcolor = strtol(pval + 2, NULL, 16);
      if (errno != 0) { cvtype->bgcolor = VG_COLOR_TRANSPARENT; }
    }
  }

  if (canvas_get_lineval(dtptr, "default-text", NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    cvtype->dfltext = SML3_strdup(bval);
  }

  SML3_gumdest(&gm1);

  return cvtype;
} /* Ende t_read_section */


static struct VG_Rect *
t_get_itag_rect(struct vgi_canvas_item *cvitem, const char *itag)
{
  struct cv_type *cvtype;

  if (cvitem == NULL || itag == NULL || *itag == '\0') { return NULL; }

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

  return (struct VG_Rect *)vg4->hash->get(cvtype->htags, itag, NULL);
} /* Ende t_get_itag_rect */


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_up.img = &arw_dirs->up;
  cvtype->arw_down.img = &arw_dirs->down;

  /* set text */
  if (cvtype->img == NULL) {
    vg4->canvas->text_set(cvas, cvitem->name, cvtype->dfltext);
  }
} /* 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 updown;

  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 scroll to next/previous: 1=next-line, 2=page-down, 3=end, -1=previous-line, -2=page-up, -3=home */
  updown = 0;
  if (cvdata->hasfocus) {  /* has focus */
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_UP])) {
      updown = -1;
      cvtype->repw = VGI_CANVAS_REPWMAX;
    }
    if (VGI_CVKEY_PRESSED(cvdata->keys.k[CV_KEY_UP])) {
      if (cvtype->repw > 0) { cvtype->repw--; } else { updown = -1; }
    }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_PGUP])) { updown = -2; }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_HOME])) { updown = -3; }

    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_DOWN])) {
      updown = 1;
      cvtype->repw = VGI_CANVAS_REPWMAX;
    }
    if (VGI_CVKEY_PRESSED(cvdata->keys.k[CV_KEY_DOWN])) {
      if (cvtype->repw > 0) { cvtype->repw--; } else { updown = 1; }
    }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_PGDOWN])) { updown = 2; }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_END])) { updown = 3; }
  }

  /* draw */
  { struct VG_Image *imgp;
    struct VG_Position posb;
    struct VG_Rect rect;
    VG_BOOL do_scroll;
    int imgh;

    imgh = 0;
    do_scroll = VG_FALSE;
    if (cvdata->hasfocus && cvtype->velo > 0) {  /* activated and scrolling active */
      vg4->image->getsize(cvtype->img, NULL, NULL, &imgh);
      if (imgh > cvitem->rect.h) { do_scroll = VG_TRUE; }
    }

    if (do_scroll) {
      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;
      }

      /* scroll according to keypress up and down */
      if (updown < 0) {  /* up */
        if (updown == -3) {  /* home */
          cvtype->ypos = 0;
        } else if (updown == -2) {  /* page up */
          cvtype->ypos -= (cvitem->rect.h - 2);
        } else {  /* line up */
          cvtype->ypos -= cvtype->velo;
        }
        if (cvtype->ypos < 0) { cvtype->ypos = 0; }

      } else if (updown > 0) {  /* down */
        if (updown == 3) {  /* end */
          cvtype->ypos = imgh - cvitem->rect.h;
        } else if (updown == 2) {  /* page down */
          cvtype->ypos += (cvitem->rect.h - 2);
        } else {  /* line down */
          cvtype->ypos += cvtype->velo;
        }
        if (cvtype->ypos + cvitem->rect.h > imgh) { cvtype->ypos = imgh - cvitem->rect.h; }
      }

      /* scroll according to mouse position */
      if (cvdata->cvas->cve.mouse.show) {
        int xm, ym;
        if (vg4->input->mouse_position(&xm, &ym)) {
          const int maxcnt = CV_WAIT_TIME * 2;  /* milliseconds between scrolling (refer also to repw) */

          /* check for moving up-down */
          if (IS_IN_RECT(&cvtype->arw_up.rect, xm, ym, cvdata->xadd, cvdata->yadd)) {
            if (VGI_CVKEY_PRESSED(cvdata->keys.m_left)) {
              if (keyok) { cvtype->arw_up.scrollcount = CV_WAIT_TIME; }
              if ((cvtype->arw_up.scrollcount -= CV_WAIT_TIME) <= 0) {
                cvtype->arw_up.scrollcount += maxcnt;
                cvtype->ypos -= cvtype->velo;
                if (cvtype->ypos < 0) { cvtype->ypos = 0; }
              }
              keyok = VG_FALSE;
            }
          } else if (IS_IN_RECT(&cvtype->arw_down.rect, xm, ym, cvdata->xadd, cvdata->yadd)) {
            if (VGI_CVKEY_PRESSED(cvdata->keys.m_left)) {
              if (keyok) { cvtype->arw_down.scrollcount = CV_WAIT_TIME; }
              if ((cvtype->arw_down.scrollcount -= CV_WAIT_TIME) <= 0) {
                cvtype->arw_down.scrollcount += maxcnt;
                cvtype->ypos += cvtype->velo;
                if (cvtype->ypos + cvitem->rect.h > imgh) { cvtype->ypos = imgh - cvitem->rect.h; }
              }
              keyok = VG_FALSE;
            }
          }
        }
      }
    }

    /* copy relevant rectangle from text image */
    rect.x = 0;
    rect.w = cvitem->rect.w;
    rect.y = cvtype->ypos;
    rect.h = cvitem->rect.h;
    imgp = vg4_image_clone_nolist(cvtype->img, &rect, NULL);

    /* draw out */

    vg4->image->clear(cvitem->rimg);
    if (cvtype->bgcolor != VG_COLOR_TRANSPARENT) {
      vg4->image->fill(cvitem->rimg, cvtype->bgcolor);
    }
    posb.pos = VG_POS_UPPER_LEFT;
    posb.x = posb.y = 0;
    vg4->image->copy(cvitem->rimg, imgp, &posb, NULL);

    { int ispr;
      struct VG_Image *imgsprt;
      struct VG_ImagecopyAttr iattrsprt;
      for (ispr = 0; ispr < cvdata->cvas->cve.sprites.max; ispr++) {
        if (cvdata->cvas->cve.sprites.e[ispr].rimg == NULL) { continue; }
        if (strcmp(cvdata->cvas->cve.sprites.e[ispr].textbox, cvitem->name) != 0) { continue; }
        if (vg4->sprite->next(cvdata->cvas->cve.sprites.e[ispr].sprt, &imgsprt, &iattrsprt)) {
          if (imgsprt != NULL) {
            vg4->image->clear(cvdata->cvas->cve.sprites.e[ispr].rimg);
            vg4->image->copy(cvdata->cvas->cve.sprites.e[ispr].rimg, imgsprt, NULL, &iattrsprt);
            posb.pos = VG_POS_UPPER_LEFT;
            posb.x = cvdata->cvas->cve.sprites.e[ispr].rect.x;
            posb.y = cvdata->cvas->cve.sprites.e[ispr].rect.y - cvtype->ypos;
            vg4->image->copy(cvitem->rimg, cvdata->cvas->cve.sprites.e[ispr].rimg, &posb, NULL);
          }
        }
      }
    }

    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);
    vg4->image->destroy(imgp);
    if (cvdata->hasfocus && do_scroll) {
      canvas_draw_actborder(&cvitem->rect, cvdata->xadd, cvdata->yadd);
    }

    /* draw arrow-images */
    if (do_scroll) {
      /* upper image */
      if (cvtype->ypos > 0) {
        canvas_draw_arrow(cvdata->cvas, &cvtype->arw_up, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_FALSE, 1);
      } else {
        CLEAR_RECT(&cvtype->arw_up.rect);
      }

      /* lower image */
      if (cvtype->ypos + cvitem->rect.h < imgh) {
        canvas_draw_arrow(cvdata->cvas, &cvtype->arw_down, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_FALSE, 2);
      } else {
        CLEAR_RECT(&cvtype->arw_down.rect);
      }
    }
  }
} /* Ende t_draw */


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

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

  if (cvtype->dfltext != NULL) { free(cvtype->dfltext); }
  if (cvtype->img != NULL) { vg4->image->destroy(cvtype->img); }
  if (cvtype->hparam != NULL) { vg4->hash->destroy(cvtype->hparam); }
  if (cvtype->htags != NULL) { vg4->hash->destroy(cvtype->htags); }

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


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

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

  fprintf(outfp, "    type=text-box\n");
  namptr = vg4->image->getname(cvtype->img);
  fprintf(outfp, "    img=%s\n", namptr);
  fprintf(outfp, "    ypos=%d\n", cvtype->ypos);
  fprintf(outfp, "    velo=%d\n", cvtype->velo);
  fprintf(outfp, "    hparam=%s\n", (vg4->hash->is_empty(cvtype->hparam) ? "[unused]" : "[used]"));
  fprintf(outfp, "    htags=%s\n", (vg4->hash->is_empty(cvtype->htags) ? "[unused]" : "[used]"));
  { void *vpos;
    const char *key;
    vpos = NULL;
    for (key = vg4->hash->list(cvtype->hparam, &vpos); vpos != NULL; key = vg4->hash->list(cvtype->hparam, &vpos)) {
      fprintf(outfp, "     - %s=%s\n", key, (char *)vg4->hash->get(cvtype->hparam, key, NULL));
    }
  }
} /* Ende t_dump */


/* canvas_text_set:
 * set text of a text-box
 * @param cvas   canvas
 * @param iname  name of text-box
 * @param text   text, or NULL = set default-text
 */
static void
canvas_text_set(struct VG_Canvas *cvas, const char *iname, const char *text)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;

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

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

  /* create text image */
  { struct SML3_gummi gm1 = SML3_GUM_INITIALIZER;
    int gmpos = SML3_gumprintf(&gm1, 0, "[boxwidth=%d", cvitem->rect.w);

    { void *vpos;
      const char *key;
      char *valptr;
      vpos = NULL;
      for (key = vg4->hash->list(cvtype->hparam, &vpos); vpos != NULL; key = vg4->hash->list(cvtype->hparam, &vpos)) {
        valptr = (char *)vg4->hash->get(cvtype->hparam, key, NULL);
        if (valptr != NULL) { gmpos += SML3_gumprintf(&gm1, gmpos, " %s=%s", key, valptr); }
      }
    }

    gmpos += (int)SML3_gumcpy(&gm1, gmpos, "]");
    if (cvtype->img != NULL) {
      vg4->image->destroy(cvtype->img);
      cvtype->img = NULL;
    }

    if (text != NULL) {
      if (cvtype->htags != NULL) { vg4->hash->destroy(cvtype->htags); }
      { char afont[64];
        vg4->font->getdefault(afont, sizeof(afont));
        if (*cvas->cve.dflfont != '\0') { vg4->font->setdefault(cvas->cve.dflfont); }
        cvtype->img = vg4_font_totext_nolist(text, SML3_gumgetval(&gm1), cvas->cve.dirname, cvas->cve.hvar, &cvtype->htags);
        vg4->font->setdefault(afont);
      }
    }
    cvtype->ypos = 0;
    SML3_gumdest(&gm1);
  }

  /* correct text image according to vertical orientation */
  { int sprtyadd = 0;
    if (cvtype->img != NULL) {
      char *valptr = (char *)vg4->hash->get(cvtype->hparam, "v-orientation", NULL);
      if (valptr != NULL) {
        int iw, ih;
        vg4->image->getsize(cvtype->img, NULL, &iw, &ih);
        if (ih < cvitem->rect.h) {
          struct VG_Position posn;
          struct VG_Image *imgn = vg4_image_create_nolist(iw, cvitem->rect.h);
          if (strncmp(valptr, "center", 6) == 0) {
            posn.x = 0; posn.y = (cvitem->rect.h - ih) / 2; posn.pos = VG_POS_UPPER_LEFT;
          } else if (strncmp(valptr, "bottom", 6) == 0) {
            posn.x = 0; posn.y = cvitem->rect.h - ih; posn.pos = VG_POS_UPPER_LEFT;
          } else {
            posn.x = posn.y = 0; posn.pos = VG_POS_UPPER_LEFT;
          }
          sprtyadd = posn.y;
          vg4->image->copy(imgn, cvtype->img, &posn, NULL);
          vg4->image->destroy(cvtype->img);
          cvtype->img = imgn;

        } else {
          char *valptr2;
          if ((valptr2 = strchr(valptr, '+')) != NULL) { valptr = valptr2 + 1; }
          if (strncmp(valptr, "center", 6) == 0) {
            cvtype->ypos = (ih - cvitem->rect.h) / 2;
          } else if (strncmp(valptr, "bottom", 6) == 0) {
            cvtype->ypos = ih - cvitem->rect.h;
          }
        }
      }
    }

    /* correct sprites of this text-box */
    { int ielm;
      for (ielm = 0; ielm < cvas->cve.sprites.max; ielm++) {
        if (strcmp(cvitem->name, cvas->cve.sprites.e[ielm].textbox) == 0) {
          canvas_calc_rect(cvas, ielm, VG_TRUE);
          cvas->cve.sprites.e[ielm].rect.y += sprtyadd;
        }
      }
    }
  }
} /* Ende canvas_text_set */
