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

void canvas_calc_rect(const struct VG_Canvas *, int, VG_BOOL);
VG_BOOL canvas_get_lineval(const char *, const char *, char *, size_t, char *, size_t, char *, size_t, char **);
VG_BOOL canvas_load_image(const struct VG_Canvas *, const char *, const char *, struct vgi_imgcnt *);
void canvas_draw_actborder(struct VG_Rect *, int, int);
struct VG_Position canvas_set_posi(struct VG_Rect *, struct VG_Image *, struct VG_ImagecopyAttrImage *, int, int);
void canvas_draw_arrow(const struct VG_Canvas *, struct vgi_canvas_arrow *, struct vgi_canvas_item *, int, int, int, VG_BOOL, int);


/* canvas_calc_rect:
 * set absolute destination rectangle of a box-tag or its sub-rectangle
 * @param cvas      canvas
 * @param ielm      element-index
 * @param issprite  whether element is a sprite-element or canvas-element
 */
void
canvas_calc_rect(const struct VG_Canvas *cvas, int ielm, VG_BOOL issprite)
{
  struct VG_Rect brect, *prect;
  const char *itag;
  struct VG_Rect limrect, *absrect;
  struct VG_Image **rimg;

  if (cvas == NULL || ielm < 0 || ielm >= cvas->cve.canvas.max) { return; }

  CLEAR_RECT(&brect);
  prect = NULL;

  if (issprite) {
    itag = cvas->cve.sprites.e[ielm].itag;
    limrect = cvas->cve.sprites.e[ielm].limrect;
    absrect = &cvas->cve.sprites.e[ielm].rect;
    rimg = &cvas->cve.sprites.e[ielm].rimg;
    if (*cvas->cve.sprites.e[ielm].textbox != '\0' && *itag != '\0') {
      prect = cvtypes_get_itag_rect(&cvinit, cvas, cvas->cve.sprites.e[ielm].textbox, itag);
    }
  } else {
    itag = cvas->cve.canvas.e[ielm].itag;
    limrect = cvas->cve.canvas.e[ielm].limrect;
    absrect = &cvas->cve.canvas.e[ielm].rect;
    rimg = &cvas->cve.canvas.e[ielm].rimg;
    if (*itag != '\0') {
      prect = vg4->hash->get(cvas->cve.main.htags, itag, NULL);
    }
  }
  *rimg = NULL;

  if (prect != NULL) {
    brect = *prect;
    if (!IS_RECT_EMPTY(&limrect)) {
      if (limrect.x + limrect.w > 0 && limrect.x < brect.x + brect.w
          && limrect.y + limrect.h > 0 && limrect.y < brect.y + brect.h
         ) {
        brect = limrect;
        if (brect.x < 0) { brect.w += brect.x; brect.x = 0; }
        if (brect.x + brect.w > prect->w) { brect.w = prect->w - brect.x; }
        if (brect.y < 0) { brect.h += brect.y; brect.y = 0; }
        if (brect.y + brect.h > prect->h) { brect.h = prect->h - brect.y; }
        brect.x += prect->x;
        brect.y += prect->y;
      } else {
        pwarn("Invalid limiting \"position-rect\" parameter for a canvas-item\n");
        brect = *prect;
      }
    }
  } else if (!IS_RECT_EMPTY(&limrect)) {
    brect = limrect;
  }

  if (!IS_RECT_EMPTY(&brect)) {
    *rimg = vg4_image_create_nolist(brect.w, brect.h);
  } else if (!issprite) {
    pwarn("No valid \"position-*\" parameter for a canvas-item found\n");
  }

  *absrect = brect;
} /* Ende canvas_calc_rect */


/* canvas_get_lineval:
 * search key from section-data, return value
 * @param dtptr         section-data
 * @param ename         for returning key-subname, if expected
 * @param ename_size    sizeof(ename)
 * @param bprefix       for returning prefix of value
 * @param bprefix_size  sizeof(bprefix)
 * @param bval          for returning value
 * @param bval_size     sizeof(bval)
 * @param dtnptr        pointer to next data in section-data
 * @return  whether key was found
 *
 * following possibilities:
 *  - <key>: <value>  (e.g.: name: mybutton)
 *      => ename must be NULL, as not expected
 *  - <key>: <bprefix>:<value>  (e.g.: img-dfl: img:myimage.bmp)
 *      => ename must be NULL, as not expected
 *  - <key>.<ename>: <value>  (e.g.: text.orientation: right)
 *  - <key>.<ename>: <bprefix>:<value>  (e.g.: element.myname: img:myimage.bmp)
 *
 * Example (when section-data was read into dtptr)
 *  - search <key>, e.g. velo
 *    if (canvas_get_lineval(dtptr, "velo", NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
 *  - search all <key>.<ename>, e.g. text.<ename>
 *    char *dtnptr = dtptr;
 *    while (canvas_get_lineval(dtnptr, "text", ename, sizeof(ename), NULL, 0, bval, sizeof(bval), &dtnptr)) {
 */
VG_BOOL
canvas_get_lineval(const char *dtptr, const char *key, char *ename, size_t ename_size, char *bprefix, size_t bprefix_size, char *bval, size_t bval_size, char **dtnptr)
{
  char *pt1, *pt2, *pt3, cmerk;

  if (dtnptr != NULL) { *dtnptr = (char *)dtptr; }
  if (dtptr == NULL || key == NULL || *key == '\0' || bval == NULL || bval_size == 0) { return VG_FALSE; }
  if (ename != NULL && ename_size == 0) { ename = NULL; }
  if (bprefix != NULL && bprefix_size == 0) { bprefix = NULL; }

  /* search for key */
  pt1 = NULL;
  while (pt1 == NULL) {
    if ((pt1 = strstr(dtptr, key)) == NULL) { return VG_FALSE; }
    for (pt2 = pt1 - 1; pt2 >= dtptr && (*pt2 == ' ' || *pt2 == '\t'); pt2--) {;}
    if (pt2 >= dtptr && *pt2 != '\n') { dtptr = pt1 + 1; pt1 = NULL; continue; }
    pt1 += strlen(key);
    if (ename != NULL) {  /* key must contain a name after period */
      if (*pt1 != '.') { dtptr = pt1; pt1 = NULL; continue; }
      pt1++;
      pt2 = pt1 + strcspn(pt1, VGI_WSPACE ":=");
      if (pt2 == pt1) { dtptr = pt1; pt1 = NULL; continue; }
      vg4->misc->strscpy(ename, ename_size, pt1, (size_t)(pt2 - pt1));
      pt1 = pt2;
    }
    pt1 += strspn(pt1, VGI_SPACE);
    if (*pt1 != ':' && *pt1 != '=') { dtptr = pt1; pt1 = NULL; continue; }  /* check for key-value separator */
    pt1++;
    pt1 += strspn(pt1, VGI_SPACE);
  }
  pt2 = pt1 + strcspn(pt1, "\n");

  /* pt1 = start value, pt2 = end value, now cut off spaces and comments */
  if (bprefix != NULL) {
    VG_BOOL scnp = VG_TRUE;
    pt3 = pt2;
    while (--pt3 >= pt1) {
      if (scnp) {
        if (strchr(VGI_SPACE, (int)*pt3) != NULL) { pt2 = pt3; } else { scnp = VG_FALSE; }
      }
      if (*pt3 == '#') { pt2 = pt3; scnp = VG_TRUE; }
    }
    cmerk = *pt2;
    *pt2 = '\0';
    *bprefix = '\0';
    /* search for prefix before value, separated by colon */
    if ((pt3 = strchr(pt1, ':')) != NULL) {
      *pt3 = '\0';
      vg4->misc->strcpy(bprefix, bprefix_size, pt1);
      *pt3 = ':';
      pt1 = pt3 + 1;
    }
  } else {
    cmerk = *pt2;
    *pt2 = '\0';
  }

  vg4->misc->strcpy(bval, bval_size, pt1);
  *pt2 = cmerk;

  if (dtnptr != NULL) { *dtnptr = pt2; }

  return VG_TRUE;
} /* Ende canvas_get_lineval */


/* canvas_load_image
 * load image according to prefix
 * @param cvas     canvas
 * @param bprefix  prefix, e.g. "img", "txt", "sprt"
 * @param bval     image-file
 * @param imgcnt   for filling with loaded image
 * @return  VG_TRUE = OK, VG_FALSE = not loaded
 */
VG_BOOL
canvas_load_image(const struct VG_Canvas *cvas, const char *bprefix, const char *bval, struct vgi_imgcnt *imgcnt)
{
  char rfile[VGI_PATHSIZE];

  if (bval == NULL || *bval == '\0' || imgcnt == NULL) { return VG_FALSE; }
  if (bprefix == NULL) { bprefix = ""; }

  imgcnt->type = VGI_IMGCNT_NONE;

  if (strcmp(bprefix, "img") == 0 || *bprefix == '\0') {
    /* img:<image-file> resp. <image-file> */
    struct VG_Image *imgp;
    if (cvas != NULL) {
      if (*bval != '/') {
        vg4->misc->strccat(rfile, sizeof(rfile), cvas->cve.dirname, "/", bval, NULL);
      } else {
        vg4->misc->strcpy(rfile, sizeof(rfile), bval + 1);
      }
      vg4_intern_fpath_with_var(rfile, sizeof(rfile), cvas->cve.hvar);
    } else {
      vg4->misc->strcpy(rfile, sizeof(rfile), bval);
    }
    imgp = vg4_image_load_nolist(rfile);
    if (imgp == NULL) { return VG_FALSE; }
    vg4_intern_free_imgcnt(imgcnt);
    imgcnt->type = VGI_IMGCNT_IMAGE;
    imgcnt->u.img = imgp;

  } else if (strcmp(bprefix, "txt") == 0) {
    /* txt:<text-file> */
    struct VG_Image *imgp;
    if (cvas != NULL) {
      if (*bval != '/') {
        vg4->misc->strccat(rfile, sizeof(rfile), cvas->cve.dirname, "/", bval, NULL);
      } else {
        vg4->misc->strcpy(rfile, sizeof(rfile), bval + 1);
      }
      vg4_intern_fpath_with_var(rfile, sizeof(rfile), cvas->cve.hvar);
      { char afont[64];
        vg4->font->getdefault(afont, sizeof(afont));
        if (*cvas->cve.dflfont != '\0') { vg4->font->setdefault(cvas->cve.dflfont); }
        imgp = vg4_font_loadtext_nolist(rfile, NULL, cvas->cve.hvar, NULL);
        vg4->font->setdefault(afont);
      }
    } else {
      vg4->misc->strcpy(rfile, sizeof(rfile), bval);
      imgp = vg4_font_loadtext_nolist(rfile, NULL, NULL, NULL);
    }
    if (imgp == NULL) { return VG_FALSE; }
    vg4_intern_free_imgcnt(imgcnt);
    imgcnt->type = VGI_IMGCNT_IMAGE;
    imgcnt->u.img = imgp;

  } else if (strcmp(bprefix, "sprt") == 0) {
    /* sprt:<sprite-file> */
    struct VG_Sprite *sprt;
    if (cvas != NULL) {
      if (*bval != '/') {
        vg4->misc->strccat(rfile, sizeof(rfile), cvas->cve.dirname, "/", bval, NULL);
      } else {
        vg4->misc->strcpy(rfile, sizeof(rfile), bval + 1);
      }
      vg4_intern_fpath_with_var(rfile, sizeof(rfile), cvas->cve.hvar);
    } else {
      vg4->misc->strcpy(rfile, sizeof(rfile), bval);
    }
    sprt = vg4_sprite_load_nolist(rfile);
    if (sprt == NULL) { return VG_FALSE; }
    vg4_intern_free_imgcnt(imgcnt);
    imgcnt->type = VGI_IMGCNT_SPRITE;
    imgcnt->u.sprt = sprt;

  } else {
    pwarn("prefix \"%s\" unknown\n", bprefix);
    return VG_FALSE;
  }

  return VG_TRUE;
} /* Ende canvas_load_image */


/* draw a border around activated item */
void
canvas_draw_actborder(struct VG_Rect *rectp, int xadd, int yadd)
{
  struct VG_Rect rect;

  if (rectp == NULL) { return; }

  rect = *rectp;
  rect.x = xadd + rect.x - 1;
  rect.w += 2;
  rect.y = yadd + rect.y - 1;
  rect.h += 2;
  vg4->window->draw_rect(&rect, VG_COLOR_RGB(128, 128, 128), VG_FALSE);
} /* Ende canvas_draw_actborder */


/* set copy-position from orientations */
struct VG_Position
canvas_set_posi(struct VG_Rect *rect, struct VG_Image *imgp, struct VG_ImagecopyAttrImage *iattr_image, int orientation, int v_orientation)
{
  struct VG_Position posb;
  int imgw, imgh;

  posb.pos = VG_POS_UPPER_LEFT;
  posb.x = posb.y = 0;

  if (rect == NULL || imgp == NULL) { return posb; }

  vg4->image->getsize(imgp, iattr_image, &imgw, &imgh);

  if (orientation == VG_ORI_LEFT) {
    posb.x = 0;
  } else if (orientation == VG_ORI_CENTER) {
    posb.x = (rect->w - imgw) / 2;
  } else if (orientation == VG_ORI_RIGHT) {
    posb.x = rect->w - imgw;
  } else {
    posb.x = (rect->w - imgw) / 2;
  }

  if (v_orientation == VG_ORI_LEFT) {  /* top */
    posb.y = 0;
  } else if (v_orientation == VG_ORI_CENTER) {
    posb.y = (rect->h - imgh) / 2;
  } else if (v_orientation == VG_ORI_RIGHT) {  /* bottom */
    posb.y = rect->h - imgh;
  } else {
    posb.y = (rect->h - imgh) / 2;
  }

  return posb;
} /* Ende canvas_set_posi */


/* draw arrow image */
void
canvas_draw_arrow(const struct VG_Canvas *cvas, struct vgi_canvas_arrow *arw, struct vgi_canvas_item *cvitem, int m_left, int xadd, int yadd, VG_BOOL doend, int updownleftright)
{
  const int distpix = 2;
  struct VG_ImagecopyAttr iattr;
  struct VG_Position posb;
  struct VG_Image *imgp;
  int w1, h1;

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

  if (doend) {
    imgp = NULL;
    if (arw->img->img_end != NULL) { vg4->image->getsize(arw->img->img_end, NULL, &w1, &h1); }
    if (imgp == NULL && arw->img->img_dfl != NULL) { vg4->image->getsize(arw->img->img_dfl, NULL, &w1, &h1); }
  } else {
    imgp = arw->img->img_dfl;
    if (imgp != NULL) { vg4->image->getsize(imgp, NULL, &w1, &h1); }
  }

  if (cvas->cve.mouse.show) {
    int xm, ym;
    if (vg4->input->mouse_position(&xm, &ym)) {
      if (IS_IN_RECT(&arw->rect, xm, ym, xadd, yadd)) {
        if (doend) {
          imgp = arw->img->img_end;
        } else {
          imgp = arw->img->img_act;
          if (VGI_CVKEY_PRESSED(m_left)) {
            imgp = arw->img->img_sel;
            if (imgp == NULL) { imgp = arw->img->img_act; }
          }
          if (imgp == NULL) { imgp = arw->img->img_dfl; }
        }
      }
    }
  }

  VG_IMAGECOPY_ATTR_DEFAULT(&iattr);
  if (imgp != NULL) { vg4->image->getsize(imgp, NULL, &w1, &h1); }

  if (updownleftright == 1) {  /* up */
    posb.pos = VG_POS_CENTERED;
    posb.x = xadd + cvitem->rect.x + cvitem->rect.w / 2;
    posb.y = yadd + cvitem->rect.y + h1 / 2 + distpix;

  } else if (updownleftright == 2) {  /* down */
    posb.pos = VG_POS_CENTERED;
    posb.x = xadd + cvitem->rect.x + cvitem->rect.w / 2;
    posb.y = yadd + cvitem->rect.y + cvitem->rect.h - h1 / 2 - distpix;

  } else if (updownleftright == 3) {  /* left */
    posb.pos = VG_POS_CENTERED;
    posb.x = xadd + cvitem->rect.x + w1 / 2 + distpix;
    posb.y = yadd + cvitem->rect.y + cvitem->rect.h / 2;

  } else if (updownleftright == 4) {  /* right */
    posb.pos = VG_POS_CENTERED;
    posb.x = xadd + cvitem->rect.x + cvitem->rect.w - w1 / 2 - distpix;
    posb.y = yadd + cvitem->rect.y + cvitem->rect.h / 2;

  } else {
    return;
  }

  if (imgp != NULL) { vg4->window->copy(imgp, &posb, &iattr); }

  /* set arrow-rectangle */
  arw->rect.x = posb.x - xadd - w1 / 2;
  arw->rect.w = w1;
  arw->rect.y = posb.y - yadd - h1 / 2;
  arw->rect.h = h1;
} /* Ende canvas_draw_arrow */
