/****************************************************************************
    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 Jan 26 09:40:28 2004
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "tkgate.h"

#define DEBUG_NET 0

int ycIsKW(char*);

GNet *net_newCompatable(const char *name,GNet *rnet,GModuleDef *mdef)
{
  GNet *net;

  net = (GNet*) ob_malloc(sizeof(GNet),"GNet");

  if (mdef) {
    char buf[STRMAX];

    pickValidName(buf,name,"w",mdef->nets);
    net->signame = ob_strdup(buf);
    if (name) {
      net->show_name = 1;
    } else {
      net->show_name = 0;
    }
  } else {
    net->signame = 0;
    net->show_name = 0;
  }

  net->nbits = rnet ? rnet->nbits : 1;
  net->dtype = rnet ? rnet->dtype : 0;
  net->refs = 0;
  net->mark = 0;
  net->ionet = 0;
  net->mod = mdef;
  net->driver = 0;
  net->decoration = 0;
  net->next = 0;
 
  SHash_insert(mdef->nets,net->signame,net);

  if (mdef && XGate.circuit->es && mdef == XGate.circuit->es->env)
    DoTcl("tkg_netListInsert \"%s\" %d %d",net->signame,net->show_name,net->nbits);

#if DEBUG_NET
  printf("net_new() -> %x",net);
  if (net->signame)
    printf("  (%s)\n",net->signame);
  else
    printf("\n");
#endif

  return net;
}

GNet *net_new(const char *name,GModuleDef *mdef)
{
  return net_newCompatable(name,0,mdef);
}

void net_update(GNet *n)
{
  DoTcl("tkg_netListUpdate %s %d %d",n->signame,n->show_name,n->nbits);
}

void net_delete(GNet *net)
{
  ob_touch(net);
#if DEBUG_NET
  printf("GNet::Delete(%s) %x",net->signame ?: "",net);
#endif

  if (net->signame) {
    SHash_remove(net->mod->nets,net->signame);
    if (net->mod && XGate.circuit->es && net->mod == XGate.circuit->es->env)
      DoTcl("tkg_netListRemove %s",net->signame);
    ob_free(net->signame);
#if DEBUG_NET
    printf(" [%d]",r);
#endif
  }
#if DEBUG_NET
  printf("\n");
#endif
  ob_free(net);
}

/*
 * Increment the reference count of a net.
 */
void net_incref(GNet *net)
{
  ob_touch(net);
#if 0
  printf("net_incref(%s) [%d]\n",net->signame,net->refs+1);
#endif
  net->refs++;
}

/*
 * Decrement the reference count of a net.  If the reference
 * count goes to zero, the net is deleted.
 */
void net_decref(GNet *net)
{
  ob_touch(net);
#if 0
  printf("net_decref(%s) [%d]\n",net->signame,net->refs-1);
#endif
  if (--net->refs <= 0)
    net_delete(net);
}

/*
 * Select one of the nets 'a' or 'b'.  Typically used when joining two wires
 * to determine which net name will be retained and which will be deleted.
 */
GNet *net_pickOne(GNet *a,GNet *b,int decref_loser)
{
  GNet *r = 0;	/* Net to return */
  GNet *l = 0;	/* Losing net */

  if (!a) 
    r = b;
  else if (!b) 
    r = a;
  else if (a == b) {
    r = a;
    l = b;
  } else if (a->ionet) {
    r = a;
    l = b;
  } else if (b->ionet) {
    r = b;
    l = a;
  } else if (a->show_name) {
    r = a;
    l = b;
  } else if (b->show_name) {
    r = b;
    l = a;
  } else {
    r = a;
    l = b;
  }

  if (l && decref_loser) net_decref(l);

  return r;
}

/*
   Check to see if it is OK to connect wires from the
   specified nets.  Return 1 if OK, return 0 and display
   a status bar message if not OK. 
*/
int net_connectOK(GNet *n1,GNet *n2,int isMidWire)
{
  if (n1 == n2) {
    message(0,msgLookup("err.badconsame"));	/* Connection refused because wires are part of the same net. */
    return 0;
  }
  if (n1->ionet && n2->ionet) {
    message(0,msgLookup("err.badconptsp"));	/* Connection refused because both wires are module ports or supply. */
    return 0;
  }

  if (!isMidWire && n1->nbits != n2->nbits) {
    message(0,msgLookup("err.badconbitw"));	/* Connection refused because bit widths do not match. */
    return 0;
  }

  return 1;
}

/*
  Unselect all nets.
 */
void net_unselect(int drawp)
{
  if (XGate.circuit->nsel) {
    GNet *n = XGate.circuit->nsel;
    if (drawp) net_draw(n);
    XGate.circuit->nsel = 0;
    if (drawp) net_draw(n);
  }
}

/*
  Make 'n' the selected net.
 */
void net_select(GNet *n,int drawp)
{
  if (XGate.circuit->nsel == n) return;
  net_unselect(drawp);

  if (!n) return;

  ob_touch(XGate.circuit);

  if (drawp) net_draw(n);
  XGate.circuit->nsel = n;
  if (drawp) net_draw(n);
}



/*
   Redraw an entire net.
*/
void net_draw(GNet *net)
{
  wire_drawnet(net->driver);
}

void net_setSize(GNet *net,int nbits)
{
  if (net->nbits == nbits) return;

  ob_touch(net);
  net->nbits = nbits;

  if (net->mod && XGate.circuit->es && net->mod == XGate.circuit->es->env) {
    DoTcl("tkg_netListRemove %s",net->signame);
    DoTcl("tkg_netListInsert %s %d %d",net->signame,net->show_name,net->nbits);
  }
}

int listNets(GModuleDef *mdef)
{
  HashElem *nl;

  DoTcl("tkg_clearNetList");
  for (nl = Hash_first(mdef->nets);nl;nl = Hash_next(mdef->nets,nl)) {
    GNet *n = (GNet*) HashElem_obj(nl);
    DoTcl("tkg_netListAdd %s %d %d",n->signame,n->show_name,n->nbits);
  }
  DoTcl("tkg_netListEnd");

  return TCL_OK;
}

void net_editProps(GNet *n,int x,int y)
{
  char *sn = n->show_name ? "" :"@";
  int ioCode = 0;
  int wx = ctow_x(x-50);
  int wy = ctow_y(y-50);

  if (n->ionet) {
    ioCode = n->ionet->typeinfo->Code;
    if (ioCode != LOGICIN && ioCode != LOGICOUT && ioCode != LOGICTRI)
      ioCode = 0;	/* yappari yameta */
  }

  if (n->nbits == 1)
    DoTcl("tkg_editNet %d %d {%s%s} %d\n",wx,wy,n->signame,sn,ioCode);
  else
    DoTcl("tkg_editNet %d %d {%s[%d:0]%s} %d\n",wx,wy,n->signame,n->nbits-1,sn,ioCode);

  if (XGate.tcl->result && strcmp(XGate.tcl->result,"1") == 0) {
    GWire *w = wire_findClosest(n->driver,x,y);
    ob_touch(n);
    n->decoration = w->nidx;
  }
}

/*
   Set the signal name of a wire to s.  
*/
void net_rename(GNet *net,const char *s,int showName)
{
  char *oldName = ob_strdup(net->signame);
  char buf[STRMAX];

  if (net->signame) {
    DoTcl("tkg_netListRemove %s",net->signame);
    SHash_remove(net->mod->nets,net->signame);
    ob_free(net->signame);
  }
  ob_touch(net);
  net->signame = 0;
  net->show_name = showName;

  pickValidName(buf,s,"w",net->mod->nets);
  net->signame = ob_strdup(buf);

  SHash_insert(net->mod->nets,net->signame,net);

  if (net->mod && XGate.circuit->es && net->mod == XGate.circuit->es->env)
    DoTcl("tkg_netListInsert %s %d %d",net->signame,net->show_name,net->nbits);
  ob_free(oldName);
}

