/* canvas-type input
 *   [CV-INPUT]
 *   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>
 *   font: <fontname, (default: default font)>
 *   maxchar: <maximum number of input-characters (default: 128)>
 *   fgcolor: <text color (0xRRGGBB) for text, (default: white)>
 *   bgcolor: <background color (0xRRGGBB) or "transparent" for text, (default: transparent)>
 *   ch-acc: <allowed characters and -groups (%d,%a,%b,%p - see vg4->input->textbuffer()), (default: empty=all)>
 *   ch-rej: <not allowed characters and -groups (%d,%a,%b,%p - see vg4->input->textbuffer()), (default: empty=none)>
 *   text-dfl: <default text, (default: empty)>
 */

#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 maxchar;          /* maximum number of characters */
  char *text_buffer;    /* buffer for text */
  char *font;           /* font of text */
  char fgcolor[32];     /* color of text */
  char bgcolor[32];     /* background color of text */
  char *ch_acc;         /* allowed characters and -groups, or NULL */
  char *ch_rej;         /* not allowed characters and -groups, or NULL */
  struct {              /* cursor */
    struct VG_Rect rect;  /* rectangle */
    int blinkcount;       /* counter for blinking */
  } cursor;
};

void cvinit_input(struct vgi_canvas_inittype *, int);

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


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

  type_no = type;

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

  /* functions */
  vg4->canvas->input_set_text = canvas_input_set_text;
  vg4->canvas->input_get_text = canvas_input_get_text;
} /* Ende cvinit_input */


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

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

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

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

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

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

  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);
    vg4->misc->strcpy(cvtype->bgcolor, sizeof(cvtype->bgcolor), pval);
  }

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

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

  /* create text-buffer and set default text */
  if (cvtype->maxchar < 1) { cvtype->maxchar = 128; }
  if (cvtype->maxchar > 1024) { cvtype->maxchar = 1024; }
  cvtype->text_buffer = SML3_calloc(cvtype->maxchar + 1, sizeof(*cvtype->text_buffer));
  if (canvas_get_lineval(dtptr, "text-dfl", NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4->misc->strcpy(cvtype->text_buffer, cvtype->maxchar + 1, bval);
  }

  /* create cursor */
  { int w1, h1;
    char afont[64];
    vg4->font->getdefault(afont, sizeof(afont));
    if (*cvas->cve.dflfont != '\0') { vg4->font->setdefault(cvas->cve.dflfont); }
    vg4->font->maxsize(vg4->font->load(cvtype->font), &w1, &h1);
    vg4->font->setdefault(afont);
    cvtype->cursor.rect.x = 0;
    cvtype->cursor.rect.w = w1;
    cvtype->cursor.rect.y = 0;
    cvtype->cursor.rect.h = h1;
    cvtype->cursor.blinkcount = 0;
  }

  SML3_gumdest(&gm1);

  return cvtype;
} /* Ende t_read_section */


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

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

  /* draw */
  { struct VG_Image *imgp;
    struct VG_ImagecopyAttr iattr;
    struct VG_Position posb;

    imgp = NULL;
    VG_IMAGECOPY_ATTR_DEFAULT(&iattr);

    if (*cvtype->text_buffer != '\0') {  /* text available */
      struct SML3_gummi gm1 = SML3_GUM_INITIALIZER;
      int gmpos;
      gmpos = (int)SML3_gumcpy(&gm1, 0, "[");
      if (cvtype->font != NULL) {
        gmpos += SML3_gumprintf(&gm1, gmpos, " font=%s", cvtype->font);
      }
      if (*cvtype->fgcolor != '\0') {
        gmpos += SML3_gumprintf(&gm1, gmpos, " fgcolor=%s", cvtype->fgcolor);
      }
      if (*cvtype->bgcolor != '\0') {
        gmpos += SML3_gumprintf(&gm1, gmpos, " bgcolor=%s", cvtype->bgcolor);
      }
      gmpos += SML3_gumprintf(&gm1, gmpos, " nowrap=1 maxwidth=0");
      gmpos += (int)SML3_gumcpy(&gm1, gmpos, "]");
      { char afont[64];
        vg4->font->getdefault(afont, sizeof(afont));
        if (*cvdata->cvas->cve.dflfont != '\0') { vg4->font->setdefault(cvdata->cvas->cve.dflfont); }
        imgp = vg4_font_totext_nolist(cvtype->text_buffer, SML3_gumgetval(&gm1), cvdata->cvas->cve.dirname, NULL, NULL);
        vg4->font->setdefault(afont);
      }
      SML3_gumdest(&gm1);
    }

    if (cvdata->hasfocus) {  /* has focus */
      vg4->input->textbuffer(cvtype->text_buffer, cvtype->maxchar + 1, cvtype->ch_acc, cvtype->ch_rej);
      if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_RETURN])) { *selname = cvitem->name; }

    } else if (cvitem->isdisabled) {  /* disabled */
      iattr.pixel.pixelcolor = VG_PIXELCOLOR_GREY;
      iattr.pixel.brightness = iattr.pixel.brightness * 4 / 5;
    }

    /* draw out */
    cvtype->cursor.rect.x = 0;
    cvtype->cursor.rect.y = (cvitem->rect.h - cvtype->cursor.rect.h) / 2;
    vg4->image->clear(cvitem->rimg);
    if (imgp != NULL) {
      int w1, h1;
      vg4->image->getsize(imgp, NULL, &w1, &h1);
      if (w1 <= cvitem->rect.w - cvtype->cursor.rect.w) {
        posb.pos = VG_POS_UPPER_LEFT;
        posb.x = 0;
        posb.y = (cvitem->rect.h - h1) / 2;
        cvtype->cursor.rect.x = w1;
      } else {
        posb.pos = VG_POS_UPPER_RIGHT;
        posb.x = cvitem->rect.w - cvtype->cursor.rect.w;
        posb.y = (cvitem->rect.h - h1) / 2;
        cvtype->cursor.rect.x = posb.x;
      }
      vg4->image->copy(cvitem->rimg, imgp, &posb, &iattr);
      vg4->image->destroy(imgp);
    }
    if (cvdata->hasfocus) {  /* has focus */
      canvas_draw_actborder(&cvitem->rect, cvdata->xadd, cvdata->yadd);
    }

    if (cvdata->hasfocus) {  /* has focus */
      if (++cvtype->cursor.blinkcount == 7) { cvtype->cursor.blinkcount = -7; }
      if (cvtype->cursor.blinkcount >= 0) {
        int fgcolor = VG_COLOR_WHITE;
        if (*cvtype->fgcolor != '\0') {
          fgcolor = (int)strtol(cvtype->fgcolor, NULL, 16);
        }
        vg4->image->draw_rect(cvitem->rimg, &cvtype->cursor.rect, fgcolor, VG_TRUE);
      }
    }

    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);
  }
} /* 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->font != NULL) { free(cvtype->font); }
  if (cvtype->text_buffer != NULL) { free(cvtype->text_buffer); }
  if (cvtype->ch_acc != NULL) { free(cvtype->ch_acc); }
  if (cvtype->ch_rej != NULL) { free(cvtype->ch_rej); }

  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=textline-input\n");
  fprintf(outfp, "    maxchar=%d\n", cvtype->maxchar);
  namptr = cvtype->text_buffer;
  fprintf(outfp, "    text-buffer=\"%s\"\n", (namptr == NULL ? "" : namptr));
  namptr = cvtype->font;
  fprintf(outfp, "    font=%s\n", (namptr == NULL ? "" : namptr));
  fprintf(outfp, "    fgcolor=%s\n", cvtype->fgcolor);
  fprintf(outfp, "    bgcolor=%s\n", cvtype->bgcolor);
  namptr = cvtype->ch_acc;
  fprintf(outfp, "    ch_acc=\"%s\"\n", (namptr == NULL ? "" : namptr));
  namptr = cvtype->ch_rej;
  fprintf(outfp, "    ch_rej=\"%s\"\n", (namptr == NULL ? "" : namptr));
  fprintf(outfp, "    cursor.rect={%d+%d,%d+%d}\n",
          cvtype->cursor.rect.x,
          cvtype->cursor.rect.w,
          cvtype->cursor.rect.y,
          cvtype->cursor.rect.h);
} /* Ende t_dump */


/* canvas_input_set_text:
 * set text of a textline-input
 * @param cvas   canvas
 * @param iname  name of textline-input
 * @param text   text
 */
static void
canvas_input_set_text(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 = ""; }

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

  vg4->misc->strcpy(cvtype->text_buffer, cvtype->maxchar + 1, text);
} /* Ende canvas_input_set_text */


/* canvas_input_get_text:
 * get text of a textline-input
 * @param cvas   canvas
 * @param iname  name of textline-input
 * @return  text
 */
static const char *
canvas_input_get_text(struct VG_Canvas *cvas, const char *iname)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  const char *text;

  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 ""; }

  text = cvtype->text_buffer;

  return (text == NULL ? "" : text);
} /* Ende canvas_input_get_text */
