/****************************************************************************
    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 Mon Dec  8 13:42:59 2003
****************************************************************************/
#include "tkgate.h"

#define SWITCH_OUT 0

static iconDimensions upswitch_iconDims[] = {
  {0, 0, 34, 26, 17, 13},
};

static iconDimensions dnswitch_iconDims[] = {
  {35, 0, 34, 26, 17, 13},
};
static int switch_iconBoldOffset = 27;

GCElement *Switch_Make(EditState **,GModuleDef *,int,int,int,int,const char *,int,const char**,int);
void Switch_Draw(GCElement *g,int md);
void Switch_EditProps(GCElement *g,int isLoadDialog);
void Switch_VerSave(FILE *f,GCElement *g);
void Switch_SimHitFunc(EditState*,GCElement*);
GCElement *Switch_Replicate(GModuleDef *M,GCElement *g,int x,int y,unsigned);

struct locate switch_out_loc[] = {
  {17,0,17,0,D_RIGHT},
  {0,-14,0,-14,D_UP},
  {-18,0,-18,0,D_LEFT},
  {0,13,0,13,D_DOWN}};

static char *psSwitch[] = {
  "%",
  "% A Switch",
  "/switchgate {",
  "  startnorotgate",
  "  % Outer Box",
  "  4 setlinewidth",
  "  -18 -12 moveto",
  "  15 -12 lineto",
  "  15 12 lineto",
  "  1 setlinewidth",
  "  -18 12 lineto",
  "  closepath",
  "  stroke",
  "  gsave      % Inner Box",
  "  -5 -9 moveto",
  "  -5 10 lineto",
  "  -16 10 lineto",
  "  -16 -9 lineto",
  "  closepath",
  "  stroke",
  "  0.8 setgray",
  "  -5 -9 moveto",
  "  -5 10 lineto",
  "  -16 10 lineto",
  "  -16 -9 lineto",
  "  closepath",
  "  fill",
  "  grestore",
  
  "  -8 0 moveto    % Knives",
  "  -8 7 lineto",
  "  -13 7 lineto",
  "  -13 0 lineto",
  "  stroke",
  
  "  -10.5 7  moveto    % Handle",
  "  -10.5 7.5 lineto",
  "  stroke",
  "  -10.5 8 1 0 360 arc",
  "  closepath",
  "  fill",
  
  "  -7 -1 moveto    % Hinges",
  "  -7 1 lineto",
  "  -9 -1 moveto",
  "  -9 1 lineto",
  "  -12 -1 moveto",
  "  -12 1 lineto",
  "  -14 -1 moveto",
  "  -14 1 lineto",
  "  stroke",
  "  -7 5 1 0 360 arc  % Clips",
  "  closepath fill",
  "  newpath",
  "  -9 5 1 0 360 arc",
  "  closepath fill",
  "  newpath",
  "  -12 5 1 0 360 arc",
  "  closepath fill",
  "  newpath",
  "  -14 5 1 0 360 arc",
  "  closepath fill",
  "  newpath",
  "  -7 -5 1 0 360 arc",
  "  closepath fill",
  "  newpath",
  "  -9 -5 1 0 360 arc",
  "  closepath fill",
  "  newpath",
  "  -12 -5 1 0 360 arc",
  "  closepath fill",
  "  newpath",
  "  -14 -5 1 0 360 arc",
  "  closepath fill",
  "  8 rfont",
  "  -3 4 moveto",
  "  (on) show",
  "  -3 -7 moveto",
  "  (off) show",
  "  grestore",
  "} bind def",
  0
};

GGateInfo gate_switch_info = {
  SWITCH,
  "Switch",
  "switch",0x0,
  "switchgate",psSwitch,

  {{"s",	{"gmswitch",0},		{"gmxswitch",0,0,100},	"gat_make switch"},
   {0}},

  upswitch_iconDims,

  1,{{"Z",OUT,1,1,switch_out_loc}},
  {{0,-17,CT},{17,0,LJ},{0,-17,CT},{17,0,LJ}},
  {1},

  {0},

  Switch_Make,
  Generic_Init,
  Generic_Delete,
  Generic_GetExtents,
  Generic_HitDistance,
  Switch_Draw,
  Generic_Move,
  Switch_Replicate,
  Err_AddInput,
  Err_AddOutput,
  Err_AddInOut,
  Err_ChangePin,
  Nop_SimStateFunc,
  Switch_SimHitFunc,
  Generic_PSWrite,
  Switch_EditProps,
  Switch_VerSave
};

GCElement *Switch_Make(EditState **es,GModuleDef *env,int GType,
			int x,int y,int r,const char *Name,int noWires,const char **options,int nOptions)
{
  GCElement *g = Generic_Make(es,env,GType,x,y,r,Name,noWires,options,nOptions);

  ob_touch(g);
  g->u.sw.perm_dipval = 0;
  g->u.sw.dipval = 0;

  return g;
}

void Switch_Draw(GCElement *g,int md)
{
  int C;
  int x,y;
  int idx = 0;

  if (g->u.sw.dipval) idx++;
  if (g->selected) idx += 4;

  C = g->typeinfo->icon[idx];
  x = g->xpos;
  y = g->ypos;

  Icon_draw(XGate.D,XGate.W,XGate.instGC,ctow_x(x),ctow_y(y),C);

  gate_drawWires(g,md);

  if (g->ename && g->show_name)
    gate_drawgatename(g,g->ename);
}

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

  ob_touch(ng);
  ng->u.sw.perm_dipval = g->u.sw.perm_dipval;
  ng->u.sw.dipval = g->u.sw.dipval;
  ng->u.sw.in_edit = 0;

  return ng;
}

void Switch_EditProps(GCElement *g,int isLoadDialog)
{
  Tcl_Interp *tcl = XGate.tcl;

  Generic_EditProps(g,isLoadDialog);
  if (isLoadDialog) {
    DoTcl("set edgat_switch %d",g->u.sw.perm_dipval);
  } else {
    const char *p;
    if ((p = Tcl_GetVar(tcl,"edgat_switch",TCL_GLOBAL_ONLY)))
      sscanf(p,"%d",&g->u.sw.perm_dipval);
    ob_touch(g);
    g->u.sw.dipval = g->u.sw.perm_dipval;
  }
}

void Switch_VerSave(FILE *f,GCElement *g)
{
  fprintf(f,"  //: %s %s (%s)"
	  ,g->typeinfo->vnames
	  ,g->ename
	  ,g->wires[0]->net->signame);
  VerilogBasicGateComment(f,g,0);
  fprintf(f," /st:%d",g->u.sw.dipval);
  fprintf(f,"\n");
}

void init_switch()
{
  Pixmap P;
  int upId,upBId,dnId,dnBId;
  GGateInfo *gi = &gate_switch_info;

  P = Pixmap_registerFromFile("switch","switch.b");
  
  upId = Icon_idimRegister(P,upswitch_iconDims,0);
  upBId = Icon_idimRegister(P,upswitch_iconDims,switch_iconBoldOffset);
  dnId = Icon_idimRegister(P,dnswitch_iconDims,0);
  dnBId = Icon_idimRegister(P,dnswitch_iconDims,switch_iconBoldOffset);

  gi->icon[0] = dnId;
  gi->icon[1] = upId;
  gi->icon[4] = dnBId;
  gi->icon[5] = upBId;

  RegisterGate(&gate_switch_info);
}

void Switch_SimHitFunc(EditState *es,GCElement *g)
{
  GSimSwitch *ss = SHash_find(es->smod->switches,g->ename);
  gate_draw(g,0);
  *ss->state = g->u.sw.dipval = !g->u.sw.dipval;
  gate_draw(g,0);

  DoTcl("tkg_simNetSet %s 1'b%d",ss->name,g->u.sw.dipval);
}
