/****************************************************************************
    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 Wed Jan  7 14:18:03 2004
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include "tkgate.h"

static char *psFrame[] = {
  "%",
  "% An frame gate",
  "%",
  "/psframe {",
  "  gsave",
  "  12 cfont",
  "  translate",
  "  pbox",
  "  [3 3] 0 setdash",
  "  stroke",
  "  [] 0 setdash",
  "  1 setgray",
  "  10 0 moveto dup stringwidth pop 10 add 0 rlineto stroke",
  "  0 setgray",
  "  15 4 pshow",
  "  grestore",
  "} bind def",
  0
};

GCElement *Frame_Make(EditState **es,GModuleDef *env,int GType,
		      int x,int y,int r,const char *Name,int noWire,const char**,int);
void Frame_Delete(GCElement *g,GModuleDef *env,int drawp);
void Frame_GetExtents(GCElement *g,int *minx,int *miny,int *maxx,int *maxy,int *bd);
int Frame_HitDistance(GCElement *g,int x,int y);
void Frame_Draw(GCElement *g,int md);
void Frame_PSWrite(FILE *f,GModLayout *L,GCElement *g);
void Frame_VerSave(FILE *f,GCElement *g);
void Frame_EditProps(GCElement *g,int isLoadDialog);
void Frame_SetProp(GCElement*,const char*,const void*);
GCElement *Frame_Replicate(GModuleDef *M,GCElement *g,int x,int y,unsigned);
void Frame_Move(GCElement *g,int x,int y);

GGateInfo gate_frame_info = {
  0,
  "Frame",
  "frame",0x0,
  "psframe",psFrame,

  {{"F",	{0,0},			{"gmframe",0,0},		"gat_make frame"},
   {0}},
  0,

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

  Frame_Make,
  Generic_Init,
  Frame_Delete,
  Frame_GetExtents,
  Frame_HitDistance,
  Frame_Draw,
  Frame_Move,
  Frame_Replicate,
  Err_AddInput,
  Err_AddOutput,
  Err_AddInOut,
  Err_ChangePin,
  Nop_SimStateFunc,
  Nop_SimHitFunc,
  Frame_PSWrite,
  Frame_EditProps,
  Frame_VerSave,
  Frame_SetProp
};

GCElement *Frame_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.frame.width = g->u.frame.height = 100;
  g->u.frame.text = ob_strdup("");

  return g;
}

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

  if (g->u.frame.text)
    ob_free(g->u.frame.text);
}

void Frame_Move(GCElement *g,int x,int y)
{
  ob_touch(g);

  if (g->top && g->left) {
    if (g->u.frame.height - y < MINSIZE)
      y = g->u.frame.height - MINSIZE;
    g->u.frame.height -= y;
    g->ypos += y;

    if (g->u.frame.width - x < MINSIZE)
      x = g->u.frame.width - MINSIZE;
    g->u.frame.width -= x;
    g->xpos += x;

  } else if (g->bottom && g->left) {
    if (g->u.frame.height + y < MINSIZE)
      y = MINSIZE - g->u.frame.height;
    g->u.frame.height += y;

    if (g->u.frame.width - x < MINSIZE)
      x = g->u.frame.width - MINSIZE;
    g->u.frame.width -= x;
    g->xpos += x;

  } else if (g->top && g->right) {
    if (g->u.frame.height - y < MINSIZE)
      y = g->u.frame.height - MINSIZE;
    g->u.frame.height -= y;
    g->ypos += y;

    if (g->u.frame.width + x < MINSIZE)
      x = MINSIZE - g->u.frame.width;
    g->u.frame.width += x;
  } else if (g->bottom && g->right) {
    if (g->u.frame.height + y < MINSIZE)
      y = MINSIZE - g->u.frame.height;
    g->u.frame.height += y;

    if (g->u.frame.width + x < MINSIZE)
      x = MINSIZE - g->u.frame.width;
    g->u.frame.width += x;
  } else {
    g->xpos += x;
    g->ypos += y;
  }
}


void Frame_GetExtents(GCElement *g,int *minx,int *miny,int *maxx,int *maxy,int *bd)
{
  *minx = g->xpos - 2;
  *miny = g->ypos - 2;
  *maxx = g->xpos + g->u.frame.width + 2;
  *maxy = g->ypos + g->u.frame.height + 2;

  if (bd) *bd = 20;
}

/*
  Frames can only be selected on edges or corners
 */
int Frame_HitDistance(GCElement *g,int x,int y)
{
  ob_touch(g);

  g->left = 0;
  g->right = 0;
  g->top = 0;
  g->bottom = 0;

  if ((x > g->xpos - 10) &&
      (x < g->xpos + g->u.frame.width + 10) &&
      (y > g->ypos - 10) &&
      (y < g->ypos + g->u.frame.height + 10)) {
    if (x < g->xpos + 10) {
      g->left = 1;
    }
    if (x > g->xpos + g->u.frame.width - 10) {
      g->right = 1;
    }
    if (y < g->ypos + 10) {
      g->top = 1;
    }
    if (y > g->ypos + g->u.frame.height - 10) {
      g->bottom = 1;
    }

    if (g->left || g->right || g->top || g->bottom)
      return GATERANGE-1;
  }
  return NOHIT; 			/* out of range */
}


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

  ng = Generic_Replicate(M,g,x,y,flags);

  ob_touch(ng);

  ng->u.frame.width = g->u.frame.width;
  ng->u.frame.height = g->u.frame.height;
  ng->u.frame.text = g->u.frame.text ? ob_strdup(g->u.frame.text) : 0;

  return ng;
}

void Frame_Draw(GCElement *g,int md)
{
  if (g->selected)
    XSetLineAttributes(XGate.D,XGate.frameGC,2,LineOnOffDash,CapButt,JoinMiter);

  if (!*g->u.frame.text) {
    ZDrawRectangle(XGate.D,XGate.W,XGate.frameGC,
		   ctow_x(g->xpos),ctow_y(g->ypos),
		   g->u.frame.width,g->u.frame.height);
  } else {
    int x = ctow_x(g->xpos);
    int y = ctow_y(g->ypos);
    int w;
    XPoint points[6];
    Tk_Font F;

    if (g->selected) {
      F = XGate.textbF;
      XSetFont(XGate.D,XGate.frameGC,XGate.textbXF);
    } else {
      F = XGate.textF;
      XSetFont(XGate.D,XGate.frameGC,XGate.textXF);
    }

    w = GKTextWidth(F,g->u.frame.text,strlen(g->u.frame.text));
    dce_DrawString(XGate.frameGC,g->xpos+15,g->ypos+4,LJ,g->u.frame.text);

    if (w+20 < g->u.frame.width) {
      points[0].x = x+w+20;
      points[0].y = y;
      points[1].x = x+g->u.frame.width;
      points[1].y = y;
      points[2].x = x+g->u.frame.width;
      points[2].y = y+g->u.frame.height;
      points[3].x = x;
      points[3].y = y+g->u.frame.height;
      points[4].x = x;
      points[4].y = y;
      points[5].x = x+10;
      points[5].y = y;
      ZDrawLines(XGate.D,XGate.W,XGate.frameGC,points,6,CoordModeOrigin);
    } else {
      points[0].x = x+g->u.frame.width;
      points[0].y = y+8;
      points[1].x = x+g->u.frame.width;
      points[1].y = y+g->u.frame.height;
      points[2].x = x;
      points[2].y = y+g->u.frame.height;
      points[3].x = x;
      points[3].y = y;
      points[4].x = x+10;
      points[4].y = y;
      ZDrawLines(XGate.D,XGate.W,XGate.frameGC,points,5,CoordModeOrigin);
    }


  }

  if (g->selected)
    XSetLineAttributes(XGate.D,XGate.frameGC,1,LineOnOffDash,CapButt,JoinMiter);
}

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

  fprintf(f,"(%s) %d %d %d %d psframe\n",filterParen(buf,g->u.frame.text),g->u.frame.width,g->u.frame.height,g->xpos,g->ypos);
}

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

  fprintf(f,"  //: frame %s",g->ename);
  VerilogBasicGateComment(f,g,0);

  fprintf(f," /wi:%d /ht:%d /tx:\"%s\"\n",g->u.frame.width,g->u.frame.height,quoteChars(buf,g->u.frame.text,"\"\\"));
}

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

  Generic_EditProps(g,isLoadDialog);
  if (isLoadDialog) {
    quoteChars(buf,g->u.frame.text,"\"[]\\");
    DoTcl("set edgat_frameLabel [iconv encode \"%s\"]",buf);
  } else {
    DoTcl("iconv decode $edgat_frameLabel");
    strcpy(buf,XGate.tcl->result);

    ob_touch(g);

    if (g->u.frame.text)
      ob_free(g->u.frame.text);
    g->u.frame.text = ob_strdup(buf);
  }
}

void Frame_SetProp(GCElement *g,const char *prop,const void *value)
{
  ob_touch(g);

  if (strcmp(prop,"/wi") == 0) {
    int n = *(int*)value;
    g->u.frame.width = n;
  } else if (strcmp(prop,"/ht") == 0) {
    int n = *(int*)value;
    g->u.frame.height = n;
  } else if (strcmp(prop,"/tx") == 0) {
    if (g->u.frame.text)
      ob_free(g->u.frame.text);
    g->u.frame.text = ob_strdup((char*)value);;
  }
}

void init_frame()
{
  RegisterGate(&gate_frame_info);
}
