/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    Last edit by hansen on Fri Sep  3 14:09:35 2004
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include "tkgate.h"

#define ENGLISH_LINESPACE	12	/* Line spacing for English text */
#define KANJI_LINESPACE		15	/* Line spacing for lines with kanji characters */

static char *psComment[] = {
  "%",
  "% An COMMENT gate",
  "%",
  "/pscomment {",
  "  0 startgate",
  "  12 cfont",
  "  newpath 0 0 moveto",
  "  { {} forall exch neg 0 exch rmoveto gsave show grestore } forall",
  "  grestore",
  "} bind def",
  0
};

GCElement *Comment_Make(EditState **es,GModuleDef *env,int GType,
		      int x,int y,int r,const char *Name,int noWire,const char**,int);
void Comment_Delete(GCElement *g,GModuleDef *env,int drawp);
void Comment_GetExtents(GCElement *g,int *minx,int *miny,int *maxx,int *maxy,int *bd);
int Comment_HitDistance(GCElement *g,int x,int y);
void Comment_Draw(GCElement *g,int md);
void Comment_PSWrite(FILE *f,GModLayout *L,GCElement *g);
void Comment_VerSave(FILE *f,GCElement *g);
void Comment_EditProps(GCElement *g,int isLoadDialog);
void Comment_SetProp(GCElement*,const char*,const void*);
GCElement *Comment_Replicate(GModuleDef *M,GCElement *g,int x,int y,unsigned);
void Comment_Init(GCElement*g);

GGateInfo gate_comment_info = {
  COMMENT,
  "COMMENT",
  "comment",0x0,
  "pscomment",psComment,

  {{"C",	{0,0},			{"gmcomment",0,0},		"gat_make comment"},
   {0}},
  0,

  0,{{0}},
  {{0,-12,CT},{12,0,LJ},{0,-12,CT},{12,0,LJ}},	/* Are these even used? */
  {1},

  {0},
  
  Comment_Make,
  Comment_Init,
  Comment_Delete,
  Comment_GetExtents,
  Comment_HitDistance,
  Comment_Draw,
  Generic_Move,
  Comment_Replicate,
  Err_AddInput,
  Err_AddOutput,
  Err_AddInOut,
  Err_ChangePin,
  Nop_SimStateFunc,
  Nop_SimHitFunc,
  Comment_PSWrite,
  Comment_EditProps,
  Comment_VerSave,
  Comment_SetProp
};

static int hasKanji(char *s)
{
  for (;*s;s++)
    if ((*s & 0x80)) return 1;

  return 0;
}

/*
 * Get the value of a built-in variable and put it in p.
 */
int Comment_getValue(char *p,const char *name)
{
  if (strcasecmp(name,"tkgate-version") == 0) {
    strcpy(p,TKGATE_VERSION);
  } else if (strcasecmp(name,"tkgate-homepage") == 0) {
    strcpy(p,TKGATE_HOMEPAGE);
  } else if (strcasecmp(name,"tkgate-mailcontact") == 0) {
    strcpy(p,TKGATE_MAILCONTACT);
  } else if (strcasecmp(name,"tkgate-copyright") == 0) {
    strcpy(p,TKGATE_COPYRIGHT);
  } else {
    *p = 0;
    return -1;
  }

  return 0;
}


/*
 * Expand any value strings in a comment line.
 *
 * Currently the only recognized tags are of the form:
 *
 * <value-of name="varname">
 *
 * where "varname" is the name of a variable.  The recognized variables are:
 *
 *     tkgate-version			TkGate version number.
 */
void Comment_expandLine(char *xline,const char *sline)
{
  const char *p;
  char *x = xline;
  char tag[STRMAX],name[STRMAX];

  for (p = sline;p && *p;) {
    int did_tag = 0;

    if (sscanf(p,"<%s",tag) == 1 && strcasecmp(tag,"value-of") == 0) {
      did_tag = 1;

      if (sscanf(p,"<%*s %[^=]=\"%[^\"]",tag,name) == 2) {
	if (strcasecmp(tag,"name") == 0) {
	  Comment_getValue(x,name);
	  x += strlen(x);
	}
      }

      p = strchr(p,'>');
      if (p) p++;
    }

    if (!did_tag) {
      *x++ = *p++;
    }
  }
  *x = 0;
}


static void Comment_updateSize(GCElement *g)
{
  ComLine *L;
  int blank_only = 1;
  int y; 
  
  if (!XGate.textF) return;

  ob_touch(g);

  y = 0;
  g->u.comment.width = 10; 
  g->u.comment.height = 0; 
  for (L = g->u.comment.first;L;L = L->next) {
    char *c;
    int w;
    char xline[STRMAX];

    Comment_expandLine(xline,L->text);

    w = GKTextWidth(XGate.textF,xline,strlen(xline));
    if (w > g->u.comment.width) g->u.comment.width = w;

#if TKGATE_JSUPPORT
    if (hasKanji(xline))
      y += KANJI_LINESPACE;
    else
      y += ENGLISH_LINESPACE;
#else
    y += ENGLISH_LINESPACE;
#endif

    for (c = xline;*c;c++)
      if (isprint(*c) && *c != ' ')
	blank_only = 0;
  }
  g->u.comment.height = y; 
  if (g->u.comment.height < 10) g->u.comment.height = 10;

  if (blank_only) {
    char *comment_emptymsg = msgLookup("comment.empty");
    int w;
#if TKGATE_JSUPPORT
#if USE_ICONV
    {
      char buf[STRMAX];
      DoTcl("iconv decode \"%s\"",quoteChars(buf,comment_emptymsg,"\"\\[]"));
      comment_emptymsg = XGate.tcl->result;
    }
#endif
#endif
    w = GKTextWidth(XGate.textF,comment_emptymsg,strlen(comment_emptymsg));
    g->u.comment.width = w;
    g->u.comment.height = ENGLISH_LINESPACE; 
  }
}

void Comment_Init(GCElement*g)
{
  Generic_Init(g);
  ob_touch(g);
  g->u.comment.width = g->u.comment.height = 0;
  g->u.comment.doLink = 0;
  g->u.comment.link = ob_strdup("");
}

void Comment_flushLines(GCElement *g)
{
  ComLine *L,*N;

  ob_touch(g);

  for (L = g->u.comment.first;L;L = N) {
    N = L->next;
    ob_free(L->text);
    ob_free(L);
  }
  g->u.comment.first = g->u.comment.last = 0;
}

void Comment_addLine(GCElement *g,const char *text)
{
  ComLine *L = (ComLine*) ob_malloc(sizeof(ComLine),"ComLine"); 

  L->text = ob_strdup(text);
  L->next = 0;

  ob_touch(g);
  if (g->u.comment.last) {
    ob_touch(g->u.comment.last);
    g->u.comment.last->next = L;
    g->u.comment.last = L;
  } else
    g->u.comment.first = g->u.comment.last = L;
}

GCElement *Comment_Make(EditState **es,GModuleDef *env,int GType,
		      int x,int y,int r,const char *Name,int noWire,const char **options,int nOptions)
{
  GCElement *g;

  if (!(g = Generic_Make(es,env,GType,x,y,r,Name,noWire,options,nOptions)))
    return NULL;

  ob_touch(g);
  g->u.comment.first = g->u.comment.last = 0;
  g->u.comment.doLink = 0;
  g->u.comment.link = ob_strdup("");

  if (es) {
    GGateInfo *gi = g->typeinfo;
    int ok;
    const char *temp;

    (*gi->EditProps)(g,1);
    DoTcl("tkg_editGate");
    if ((temp = Tcl_GetVar(XGate.tcl,"edgat_ok",TCL_GLOBAL_ONLY)) && sscanf(temp,"%d",&ok) == 1 && ok)
      (*gi->EditProps)(g,0);
    else {
      gate_delete(g,(*es)->env,1);
      return 0;
    }

  }

  return g;
}

void Comment_Delete(GCElement *g,GModuleDef *M,int drawp)
{
  if (M)     gate_remove(M,g);
  if (drawp) gate_draw(g,0);

  Comment_flushLines(g);
}

void Comment_GetExtents(GCElement *g,int *minx,int *miny,int *maxx,int *maxy,int *bd)
{
  if (!g->u.comment.width) Comment_updateSize(g);

  *minx = g->xpos;
  *miny = g->ypos;
  *maxx = g->xpos + g->u.comment.width;
  *maxy = g->ypos + g->u.comment.height;

  if (bd) *bd = 20;
}

int Comment_HitDistance(GCElement *g,int x,int y)
{
  int dy;

  if (x < g->xpos || x > g->xpos + g->u.comment.width)
    return GATERANGE+1;

  if (y < g->ypos || y > g->ypos + g->u.comment.height)
    return GATERANGE+1;

  dy = y-(g->ypos + g->u.comment.height/2);
  if (dy < 0) dy = -dy;

  if (dy < GATERANGE-1)
    return dy;

  return GATERANGE-1;
}

GCElement *Comment_Replicate(GModuleDef *M,GCElement *g,int x,int y,unsigned flags)
{
  GCElement *ng;
  ComLine *L;

  ng = Generic_Replicate(M,g,x,y,flags);
  ob_touch(ng);
  ng->u.comment.first = ng->u.comment.last = 0;
  for (L = g->u.comment.first;L;L = L->next) {
    Comment_addLine(ng,L->text);
  }

  ng->u.comment.doLink = g->u.comment.doLink;
  ng->u.comment.link = ob_strdup(g->u.comment.link);

  ng->u.comment.width = g->u.comment.width;
  ng->u.comment.height = g->u.comment.height;

  return ng;
}

void Comment_Draw(GCElement *g,int md)
{
  ComLine *L;
  int x,y; 
  GC gc;
  int blank_only = 1;

  x = g->xpos;
  y = g->ypos;

  if (!g->u.comment.width) Comment_updateSize(g);

  if (g->u.comment.doLink) {
    gc = XGate.hyperlinkGC;
#if TKGATE_JSUPPORT
    Tkg_changeColor(XGate.kanjiGC, GXxor, XGate.hyperlink_pixel);
#endif
  } else
    gc = XGate.commentGC;

  if (g->selected)
    XSetFont(XGate.D,gc,XGate.textbXF);
  else
    XSetFont(XGate.D,gc,XGate.textXF);


  for (L = g->u.comment.first;L;L = L->next) {
    const char *c;
    char xline[STRMAX];

    Comment_expandLine(xline,L->text);

#if TKGATE_JSUPPORT
    if (hasKanji(xline))
      y += KANJI_LINESPACE;
    else
      y += ENGLISH_LINESPACE;
#else
    y += ENGLISH_LINESPACE;
#endif
    dce_DrawString(gc,x,y,LJ,xline);

#if TKGATE_JSUPPORT
    if (hasKanji(xline))
      blank_only = 0;
#endif
    if (blank_only) {
      for (c = xline;*c;c++)
	if (isprint(*c) && *c != ' ')
	  blank_only = 0;
    }
  }

  if (blank_only) {
    char *comment_emptymsg = msgLookup("comment.empty");

    x = g->xpos;
#if TKGATE_JSUPPORT
#if USE_ICONV
    {
      char buf[STRMAX];
      DoTcl("iconv decode \"%s\"",quoteChars(buf,comment_emptymsg,"\"\\[]"));
      comment_emptymsg = XGate.tcl->result;
    }
#endif
    if (hasKanji(comment_emptymsg))
      y = g->ypos + KANJI_LINESPACE;
    else
      y = g->ypos + ENGLISH_LINESPACE;
#else
    y = g->ypos + ENGLISH_LINESPACE;
#endif
    dce_DrawString(gc,x,y,LJ,comment_emptymsg);
  }

#if TKGATE_JSUPPORT
  if (g->u.comment.doLink)
    Tkg_changeColor(XGate.kanjiGC, GXxor, XGate.comment_pixel);
#endif
}

void Comment_PSWrite(FILE *f,GModLayout *L,GCElement *g)
{
  ComLine *Li;
  char buf[STRMAX];

  fprintf(f,"[\n");
  for (Li = g->u.comment.first;Li;Li = Li->next) {
    int lspace;
    char xline[STRMAX];

    Comment_expandLine(xline,Li->text);

    if (hasKanji(xline))
      lspace = KANJI_LINESPACE;
    else
      lspace = ENGLISH_LINESPACE;
    fprintf(f,"   [%d (%s)]\n",lspace,filterParen(buf,xline));
  }
  fprintf(f,"] %d %d pscomment\n",g->xpos,g->ypos);
}

void Comment_VerSave(FILE *f,GCElement *g)
{
  char buf[STRMAX];
  ComLine *L;

  fprintf(f,"  //: comment %s /dolink:%d /link:\"%s\"",g->ename,g->u.comment.doLink,g->u.comment.link);

  VerilogBasicGateComment(f,g,0);
  fprintf(f,"\n");

  for (L = g->u.comment.first;L;L = L->next) {
    fprintf(f,"  //: /line:\"%s\"\n",quoteChars(buf,L->text,"\"\\"));
  }
  fprintf(f,"  //: /end\n");
}

void Comment_EditProps(GCElement *g,int isLoadDialog)
{
  char buf[STRMAX],pos[64];

  Generic_EditProps(g,isLoadDialog);

  if (isLoadDialog) {
    ComLine *L;
    int n = 0;

    for (L = g->u.comment.first;L;L = L->next) {
      sprintf(pos,"%d",n++);
      Tcl_SetVar2(XGate.tcl,"edgat_commentLines",pos,L->text,TCL_GLOBAL_ONLY);
    }

    sprintf(buf,"%d",n);
    Tcl_SetVar(XGate.tcl,"edgat_commentLen",buf,TCL_GLOBAL_ONLY);

    sprintf(buf,"%d",g->u.comment.doLink);
    Tcl_SetVar(XGate.tcl,"edgat_dolink",buf,TCL_GLOBAL_ONLY);
    Tcl_SetVar(XGate.tcl,"edgat_link",g->u.comment.link,TCL_GLOBAL_ONLY);
  } else {
    const char *p;
    int n = 0;
    int i;
    Tcl_Interp *tcl = XGate.tcl;

    Comment_flushLines(g);

    if ((p = Tcl_GetVar(tcl,"edgat_commentLen",TCL_GLOBAL_ONLY)))
      sscanf(p,"%d",&n);
    for (i = 0;i < n;i++) {
      sprintf(buf,"%d",i);
      p = Tcl_GetVar2(tcl,"edgat_commentLines",buf,TCL_GLOBAL_ONLY);
      if (p) Comment_addLine(g,p);
    }
    Comment_updateSize(g);

    ob_touch(g);
    p = Tcl_GetVar(tcl,"edgat_dolink",TCL_GLOBAL_ONLY);
    sscanf(p,"%d",&g->u.comment.doLink);
    p = Tcl_GetVar(tcl,"edgat_link",TCL_GLOBAL_ONLY);
    if (p) {
      ob_free(g->u.comment.link);
      g->u.comment.link = ob_strdup(p);
    }
  }
}

void Comment_SetProp(GCElement *g,const char *prop,const void *value)
{
  char *s = (char*)value;

  ob_touch(g);

  if (strcmp(prop,"/line") == 0) {
    Comment_addLine(g,s);
    Comment_updateSize(g);
  }
  if (strcmp(prop,"/dolink") == 0) {
    g->u.comment.doLink = *(int*)value;
  }
  if (strcmp(prop,"/link") == 0) {
    ob_free(g->u.comment.link);
    g->u.comment.link = ob_strdup(s);
  }
}

void init_comment()
{
  RegisterGate(&gate_comment_info);
}
