/*
 * TransFig: Facility for Translating Fig code
 * Copyright (c) 1999 by T. Sato
 * Parts Copyright (c) 1989-2002 by Brian V. Smith
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and
 * documentation files (the "Software"), including without limitation the
 * rights to use, copy, modify, merge, publish and/or distribute copies of
 * the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that this copyright
 * notice remain intact.
 *
 */

/*
 * genmap.c: HTML imagemap driver for fig2dev
 *
 * T.Sato <VEF00200@nifty.ne.jp>
 *
 *
 * Description:
 *   Generate HTML imagemap from Fig file.
 *   If an object has comment like ``HREF="url" ALT="string"'',
 *   the object will become a link to the URL.
 *   Object with smaller depth will be listed before deeper ones.
 *   Figure comment will be used as the default link.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <math.h>
#include <ctype.h>
#include "bool.h"
#include "pi.h"

#include "fig2dev.h"
#include "object.h"	/* does #include <X11/xpm.h> */

#define TEXT_LENGTH  300

static int	border_margin = 0;
static char	url[TEXT_LENGTH], alt[TEXT_LENGTH];
static char	buf[2000];

struct hlink_item {
  char *url;
  char *alt;
  char *area;
  struct hlink_item* prev;
};
typedef struct hlink_item hlink;

/* The last link that was added. */
static hlink* last_link = 0;


#define fscale 15

#define XZOOM(x)   round(mag * ((x) - llx) / fscale + border_margin)
#define YZOOM(y)   round(mag * ((y) - lly) / fscale + border_margin)

#define MIN_DIST    5  /* ignore closer points when processing polyline */

#define ARC_STEP    (M_PI / 9.0)
#define ARC_EXPAND  1.02   /* make polygon slightly larger */


void
genmap_option(char opt, char *optarg)
{
  switch (opt) {
  case 'b':	/* border margin */
    sscanf(optarg,"%d",&border_margin);
    break;
  case 'G':
  case 'L':
    break;
  default:
    put_msg(Err_badarg, opt, "map");
    exit(1);
  }
}

static char *
is_link(F_comment *comment)
{
  char *cp1, *cp2;
  bool have_alt = false;
  int i;

  url[0] = '\0';
  alt[0] = '\0';

  while (comment != NULL) {
    cp1 = comment->comment;
    while (isspace(*cp1)) cp1++;  /* ignore leading spaces */

    if (strncasecmp(cp1, "HREF=", 5) == 0) {
      /* comment like "HREF=url ALT=string" */

      cp2 = cp1 + 5;
      while (isspace(*cp2)) cp2++;  /* spaces */
      i = 0;
      while (isgraph(*cp2)) url[i++] = *(cp2++);  /* URL */
      url[i] = '\0';
      while (isspace(*cp2)) cp2++;  /* spaces */
      if (*cp2 != '\0') {
	if (strncasecmp(cp2, "ALT=", 4) == 0) {
	  have_alt = true;
	  cp2 = cp2 + 4;
	  while (isspace(*cp2)) cp2++;	/* spaces */
	  i = 0;
	  if (*cp2 == '"') {
	    cp2++;
	    while (*cp2 != '\0' && *cp2 != '"') alt[i++] = *(cp2++);  /* text */
	  } else {
	    while (isgraph(*cp2)) alt[i++] = *(cp2++);	/* text */
	  }
	  alt[i] = '\0';
	} else {
	  fprintf(stderr, "fig2dev(map): unknown attribute: %s\n", cp2);
	}
      }
      if (!have_alt)
	fprintf(stderr, "fig2dev(map): ALT is required in HTML 3.2: %s\n", cp1);
      return cp1;
    }
    comment = comment->next;
  }
  return NULL;
}

static void
add_link(char *area)
{
    hlink* hl = (hlink*)malloc(sizeof(hlink));
    hl->url = (char *)malloc(strlen(url) + 1);
    strcpy(hl->url, url);
    hl->alt = (char *)malloc(strlen(alt) + 1);
    strcpy(hl->alt, alt);
    hl->area = (char *)malloc(strlen(area) + 1);
    strcpy(hl->area, area);
    hl->prev = last_link;
    last_link=hl;
}

void
genmap_start(F_compound *objects)
{
  char *basename;
  char *ref;
  int i;

  if (to != NULL) {
    basename = (char *)malloc(strlen(to) + 1);
    strcpy(basename, to);
    for (i = strlen(basename) - 1; 0 < i; i--) {
      if (basename[i] == '.') {
	basename[i] = '\0';
	break;
      }
    }
  } else {
    basename = "imagemap";
  }

  fprintf(tfp, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\">\n");
  fprintf(tfp, "<HTML>\n");
  fprintf(tfp, "<HEAD><TITLE>HTML imagemap generated by fig2dev</TITLE></HEAD>\n");
  fprintf(tfp, "<BODY>\n\n");
  fprintf(tfp, "<H1>HTML imagemap generated by fig2dev</H1>\n");
  fprintf(tfp, "<P>This file is generated by fig2dev.</P>\n");
  fprintf(tfp, "<P>You can copy the following lines into your HTML document.\n");
  fprintf(tfp, "You may need to edit the name of the image file in the first line.</P>\n");
  fprintf(tfp, "\n");
  fprintf(tfp, "<IMG SRC=\"%s.png\" USEMAP=\"#%s\">\n", basename, basename);
  fprintf(tfp, "<MAP NAME=\"%s\">\n", basename);

  ref = is_link(objects->comments);
  if (ref != NULL) {
    sprintf(buf, "<AREA COORDS=\"%d,%d,%d,%d\" %s>\n",
	    XZOOM(llx), YZOOM(lly), XZOOM(urx), YZOOM(ury), ref);
    add_link(buf);
  }
}

int
genmap_end(void)
{
  hlink* l;
  int len;
  char label[TEXT_LENGTH];

  for (l = last_link; l!= 0; l=l->prev) {
    fprintf(tfp, "%s", l->area);
  }
  fprintf(tfp, "</MAP>\n");

  fprintf(tfp, "\n");
  fprintf(tfp, "<h2>Alternative Text Links</h2>\n");
  fprintf(tfp, "<P>Because not all people can use imagemaps,\n");
  fprintf(tfp, "it is recommended to prepare an alternative way for the people\n");
  fprintf(tfp, "who can't use or don't want to use them.</P>\n");
  fprintf(tfp, "<P>Here are text the links extracted from the above imagemap.\n");
  fprintf(tfp, "If the ALT attribute (it is required in HTML 3.2) was not specified\n");
  fprintf(tfp, "in the imagemap, those URLs will be displayed\n");
  fprintf(tfp, "instead of labels specified by the ALT attributes.</P>\n");
  fprintf(tfp, "\n");
  fprintf(tfp, "<P ALIGN=\"CENTER\">\n");
  len = 0;
  for (l = last_link; l!= 0; l=l->prev) {
    if (l->alt[0] != '\0') strcpy(label, l->alt);
    else sprintf(label, "<TT>%s</TT>", l->url);
    fprintf(tfp, "%s<A HREF=%s>%s</A>\n",
	    (len == 0) ? "" : " | ", l->url, label);

    len = len + strlen(label) + 3;
    if (50 < len) {
      fprintf(tfp, "<BR>\n");
      len = 0;
    }
  }
  fprintf(tfp, "</P>\n");

  fprintf(tfp, "\n");
  fprintf(tfp, "</BODY>\n");
  fprintf(tfp, "</HTML>\n");

  /* all ok */
  return 0;
}

void
genmap_arc(F_arc *a)
{
  char *ref;
  int cx, cy, sx, sy, ex, ey;
  double r;
  double sa, ea;
  double alpha;

  ref = is_link(a->comments);
  if (ref != NULL) {
    cx = XZOOM(a->center.x);
    cy = YZOOM(a->center.y);
    sx = XZOOM(a->point[0].x);
    sy = YZOOM(a->point[0].y);
    ex = XZOOM(a->point[2].x);
    ey = YZOOM(a->point[2].y);

    if (a->direction == 0) {
      sa = atan2((double)(sy - cy), (double)(sx - cx));
      ea = atan2((double)(ey - cy), (double)(ex - cx));
    } else {
      ea = atan2((double)(sy - cy), (double)(sx - cx));
      sa = atan2((double)(ey - cy), (double)(ex - cx));
    }
    if (ea < sa) ea = ea + 2 * M_PI;

    r = sqrt((double)((sx - cx) * (sx - cx)
		      + (sy - cy) * (sy - cy))) * ARC_EXPAND + 1.0;

    sprintf(buf, "<AREA SHAPE=\"poly\" COORDS=\"");
    if (a->type == T_PIE_WEDGE_ARC)
      sprintf(&buf[strlen(buf)], "%d,%d,", cx, cy);
    for (alpha = sa; alpha < (ea - ARC_STEP / 4); alpha += ARC_STEP) {
      if (alpha != sa) sprintf(&buf[strlen(buf)], ",");
      sprintf(&buf[strlen(buf)], "%d,%d",
	      round(cx + r * cos(alpha)), round(cy + r * sin(alpha)));
    }
    sprintf(&buf[strlen(buf)], ",%d,%d",
	    round(cx + r * cos(ea)), round(cy + r * sin(ea)));
    sprintf(&buf[strlen(buf)], "\" %s>\n", ref);
    add_link(buf);
  }
}

void
genmap_ellipse(F_ellipse *e)
{
  char *ref;
  int x0, y0;
  double rx, ry;
  double angle, theta;

  ref = is_link(e->comments);
  if (ref != NULL) {
    x0 = XZOOM(e->center.x);
    y0 = YZOOM(e->center.y);
    rx = mag * e->radiuses.x / fscale;
    ry = mag * e->radiuses.y / fscale;
    angle = - e->angle;
    if (e->radiuses.x == e->radiuses.y) {
      sprintf(buf, "<AREA SHAPE=\"circle\" COORDS=\"%d,%d,%d\" %s>\n",
	      x0, y0, round(rx) + 1, ref);
      add_link(buf);
    } else {
      rx = rx * ARC_EXPAND + 1.0;
      ry = ry * ARC_EXPAND + 1.0;
      sprintf(buf, "<AREA SHAPE=\"poly\" COORDS=\"");
      for (theta = 0.0; theta < 2.0 * M_PI; theta += ARC_STEP) {
	if (theta != 0.0) sprintf(&buf[strlen(buf)], ",");
	sprintf(&buf[strlen(buf)], "%d,%d",
		round(x0 + cos(angle) * rx * cos(theta)
		      - sin(angle) * ry * sin(theta)),
		round(y0 + sin(angle) * rx * cos(theta)
		      + cos(angle) * ry * sin(theta)));
      }
      sprintf(&buf[strlen(buf)], "\" %s>\n", ref);
      add_link(buf);
    }
  }
}

void
genmap_line(F_line *l)
{
  char *ref;
  int xmin, xmax, ymin, ymax;
  int x, y, last_x, last_y;
  F_point *p;

  ref = is_link(l->comments);
  if (ref != NULL) {
    switch (l->type) {
    case T_BOX:
    case T_ARC_BOX:
    case T_PIC_BOX:
      xmin = xmax = l->points->x;
      ymin = ymax = l->points->y;
      for (p = l->points->next; p != NULL; p = p->next) {
	if (p->x < xmin) xmin = p->x;
	if (xmax < p->x) xmax = p->x;
	if (p->y < ymin) ymin = p->y;
	if (ymax < p->y) ymax = p->y;
      }
      sprintf(buf, "<AREA COORDS=\"%d,%d,%d,%d\" %s>\n",
	      XZOOM(xmin), YZOOM(ymin), XZOOM(xmax), YZOOM(ymax), ref);
      add_link(buf);
      break;
    case T_POLYLINE:
    case T_POLYGON:
      sprintf(buf, "<AREA SHAPE=\"poly\" COORDS=\"");
      for (p = l->points; (l->type==T_POLYLINE? p: p->next) != NULL; p = p->next) {
	x = XZOOM(p->x);
	y = YZOOM(p->y);
	if (p == l->points
	    || MIN_DIST < abs(last_x - x) || MIN_DIST < abs(last_y - y)) {
	  if (sizeof(buf) < strlen(buf) + 100) {
	    fprintf(stderr, "fig2dev(map): too complex POLYLINE... ignored\n");
	    return;
	  } else {
	    if (p != l->points) sprintf(&buf[strlen(buf)], ",");
	    sprintf(&buf[strlen(buf)], "%d,%d", x, y);
	    last_x = x;
	    last_y = y;
	  }
	}
      }
      sprintf(&buf[strlen(buf)], "\" %s>\n", ref);
      add_link(buf);
      break;
    }
  }
}

void
genmap_text(F_text *t)
{
  char *ref;

  ref = is_link(t->comments);
  if (ref != NULL) {
    fprintf(stderr, "fig2dev(map): TEXT can't be used as link region\n");
  }
}

struct driver dev_map = {
	genmap_option,
	genmap_start,
	gendev_nogrid,
	genmap_arc,
	genmap_ellipse,
	genmap_line,
	gendev_null,
	genmap_text,
	genmap_end,
	INCLUDE_TEXT
};

