/****************************************************************************
    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.
****************************************************************************/
/*
    Functions that change the structure of a wire are defined here.  This
    include routines for cutting and attaching wires.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "tkgate.h"

#define DEBUG_CUT 0

GWireList *wire_link(GWireList *wl,GWire *w);

#define debugprint(m) {if (debugmode) printf(m);}

/* Wire snap cases */
#define HH 0x3
#define VV 0xC
#define HV 0x6
#define VH 0x9
#define HZ 0x1
#define ZH 0x2
#define VZ 0x4
#define ZV 0x8

struct wirenode *cutsegment();

extern int debugmode;

/*
 * return non-zero if this wire is a valid root wire.
 */
static int wire_root_ok(GWire *w)
{
  if (!w->gate) return 1;
  if (w->gate->typeinfo->Code != JOINT && w->gate->typeinfo->Code != TAP) return 1;
  if (w->gate->typeinfo->Code == TAP && w == w->gate->wires[TAP_TAP]) return 1;

  return 0;
}

/*
 * Compare w and *root_w to determine which is the better
 * root representative.  If w is better than *root_w, then
 * *root_w is set to w.
 */
static void wire_choose_root(GWire *w,GWire **root_w)
{
  if (!wire_root_ok(w)) return;		/* w must at least be a valid root */

  if (!*root_w) {			/* If no current root, then make w the root */
    *root_w = w;
    return;
  } 

  /*
   * Ideally, the root should be a driver of a segment.  If both w and
   * *root_w have the same driver status, then use the address to determine
   * the representative.
   */
  if ((w->driver == w && (*root_w)->driver == *root_w)
      || (w->driver != w && (*root_w)->driver != *root_w)) {
    if (w < *root_w) *root_w = w;
  } else if (w->driver == w && (*root_w)->driver != *root_w) {
    *root_w = w;
  }
}

/*
 * Recursively scan all wires in the tree starting at w and choose
 * a representative wire. The representative wire choosen for any
 * wire on a net is the same for all wires on the net.
 */
static void wire_sigroot_aux(GWire *w,GWire **root_w)
{
  int i;

  wire_choose_root(w,root_w);
  if (!w->gate) return;

  switch (w->gate->typeinfo->Code) {
  case JOINT :
    for (i=0;i<4;i++)
      if (w->gate->wires[i] && w->gate->wires[i] != w) {
	GWire *xw = w->gate->wires[i];
	wire_choose_root(xw,root_w);
	wire_sigroot_aux(wire_other(xw),root_w);
      }
    break;
  case TAP :
    if (w->gate->wires[TAP_OUT] == w) {
      GWire *xw = w->gate->wires[TAP_IN];
      wire_choose_root(xw,root_w);
      wire_sigroot_aux(wire_other(xw),root_w);
    }
    if (w->gate->wires[TAP_IN] == w) {
      GWire *xw = w->gate->wires[TAP_OUT];
      wire_choose_root(xw,root_w);
      wire_sigroot_aux(wire_other(xw),root_w);
    }
    break;
  }
}

/*
   Return the representative wire end with the signal name for w.
   The represent wire end must not be connected to a joint or
   the IN/OUT ports of a tap.
*/
GWire *wire_sigroot(GWire *w)
{
  GWire *root_w = 0;
  
  wire_sigroot_aux(w,&root_w);
  wire_sigroot_aux(wire_other(w),&root_w);

  if (!root_w) {
    logError(ERL_ERROR,"failed to find valid wire root.");
    return w;
  }

  return root_w;
}

static void setIoNet(GNet *net,GCElement *g)
{
  if (!g) return;
  if (g->typeinfo->Flags.IsIOnet) {
    ob_touch(net);			// JPH1215
    net->ionet = g;
  }
}

static void wire_replaceNetAux(GWire *w,GNet *net)
{
  struct wire *sw;
  int i;

  sw = w;
  w = wire_drivee(w);
  if (w == sw) {
#if 0
    /* This is no longer an error */
    logError(ERL_ERROR,"wire reverse needed.");
#endif
    join_wirereverse(w);
    sw = wire_driver(w);
    w  = wire_drivee(w);
  }

  wire_setNet(sw,net);
  if (net) setIoNet(net,sw->gate);
  wire_setNet(w,net);
  if (net) {
    ob_touch(sw);
    ob_touch(w);
    ob_touch(net);
    setIoNet(net,w->gate);
    sw->nidx = net->wnum++;
    w->nidx = net->wnum++;
  }

#if 0
  if (net)
    printf("   wire_replaceNetAux(%s) [0x%x]=%d [0x%x]=%d\n",
	   net->signame,sw,sw->nidx,w,w->nidx);
#endif

  if (w->gate)
    switch (w->gate->typeinfo->Code) {
    case JOINT :
      if (net) {
	ob_touch(net);
	ob_touch(w->gate);
	w->gate->u.joint.gidx = net->gnum++;
      }
      for (i=0;i<4;i++)
	if (w->gate->wires[i] && w->gate->wires[i] != w)
	  wire_replaceNetAux(w->gate->wires[i],net);
      break;
    case TAP :
      if (w->gate->wires[TAP_TAP] == w) break;
      if (net) {
	ob_touch(net);
	ob_touch(w->gate);
	w->gate->u.tap.gidx = net->gnum++;
      }
      if (w->gate->wires[TAP_IN] == w)
	wire_replaceNetAux(w->gate->wires[TAP_OUT],net);
      if (w->gate->wires[TAP_OUT] == w)
	wire_replaceNetAux(w->gate->wires[TAP_IN],net);
      break;
    }
}

GWire *wire_findIdx(GWire *w,int d)
{
  GWire *xw = 0;
  int i;

  if (w->nidx == d) return w;
  w = wire_drivee(w);
  if (w->nidx == d) return w;

  if (w->gate)
    switch (w->gate->typeinfo->Code) {
    case JOINT :
      for (i=0;i<4;i++)
	if (w->gate->wires[i] && w->gate->wires[i]->net && w->gate->wires[i] != w) {
	  xw = wire_findIdx(w,d);
	  if (xw) break;
	}
      break;
    case TAP :
      if (w->gate->wires[TAP_OUT] == w)
	xw = wire_findIdx(w->gate->wires[TAP_IN],d);
      if (w->gate->wires[TAP_IN] == w)
	xw = wire_findIdx(w->gate->wires[TAP_OUT],d);
      break;
    }

  return xw;
}

/*
   Chooses a unique net for a collection of connected wires,
   clears the 'net' field for all wires in the collection,
   deletes all but one of the 'net's, and returns a unique
   net.  A named net will take precidence over unnamed nets
   in selecting a net to return.
*/
GNet *wire_chooseUniqueNet(GWire *dw)
{
  GNet *net = 0;
  GWire *w;
  int i;

  w = wire_drivee(dw);

  net = net_pickOne(dw->net,w->net,0);
  net_incref(net);
  wire_setNet(dw,0);
  wire_setNet(w,0);

  if (w->gate)
    switch (w->gate->typeinfo->Code) {
    case JOINT :
      for (i=0;i<4;i++)
	if (w->gate->wires[i] && w->gate->wires[i]->net)
	  net = net_pickOne(net,wire_chooseUniqueNet(w->gate->wires[i]),1);
      break;
    case TAP :
      if (w != w->gate->wires[TAP_TAP]) {
	if (w->gate->wires[TAP_OUT]->net)
	  net = net_pickOne(net,wire_chooseUniqueNet(w->gate->wires[TAP_OUT]),1);
	if (w->gate->wires[TAP_IN]->net)
	  net = net_pickOne(net,wire_chooseUniqueNet(w->gate->wires[TAP_IN]),1);
      }
      break;
    }
  return net;
}

/*
   Replace the net on all connected components of w.  The old
   net is *not* deleted.
*/
void wire_replaceNet(GWire *w,GNet *net)
{
  w = wire_sigroot(w);
  wire_replaceNetAux(w,net);
}

/*
   Choose a final net for a wire.  If there is more than one net on
   a collection of wires choose one and delete the others.
*/
void wire_finalizeNet(GWire *w)
{
  GNet *net;
  GWire *old_decor_w = 0;

#if 0
  printf("wire_finalizeNet(0x%x (%s))\n",w,w->net->signame);
#endif

  w = wire_sigroot(w);
  net = wire_chooseUniqueNet(w);

  old_decor_w = wire_findIdx(w,net->decoration);	/* Get old decorated wire */

  ob_touch(net);
  net->wnum = 0;
  net->gnum = 0;
  net->ionet = 0;
  wire_replaceNetAux(w,net);
  net->driver = w;

  if (old_decor_w)					/* Reset decoration to updated index */
    net->decoration = old_decor_w->nidx;

  if (net->decoration < 0 || net->decoration >= net->wnum)
    net->decoration = 0;

  net->wnum = 10000;
  net->gnum = 10000;
  net_decref(net);
}

/*
  Find wire end closest to (x,y) on net rooted at w.
 */
GWire *wire_findClosest(GWire *w,int x,int y)
{
  GWire *ow = 0;
  GWire *xw = 0;
  GWire *bw;
  int i;

  if (!w) return 0;

  ow = wire_other(w);
  bw = w;
  if (wdist(ow,x,y) < wdist(bw,x,y))
    bw = ow;

  if (ow->gate) {
    GCElement *g = ow->gate;
    switch (g->typeinfo->Code) {
    case JOINT :
      for (i = 0;i < 4;i++) 
	if (g->wires[i] != ow) {
	  xw = wire_findClosest(g->wires[i],x,y);
	  if (xw && wdist(xw,x,y) < wdist(bw,x,y))
	    bw = xw;
	}
      break;
    case TAP :
      if (g->wires[TAP_IN] == ow) {
	xw = wire_findClosest(g->wires[TAP_OUT],x,y);
      } else if (g->wires[TAP_OUT] == ow) {
	xw = wire_findClosest(g->wires[TAP_IN],x,y);
      } else
	xw = 0;
      if (xw && wdist(xw,x,y) < wdist(bw,x,y))
	bw = xw;
      break;
    }
  } 

  return bw;
}


/*
   Low-level routine for connecting two wire nodes n1 and n2
*/
void wire_solder(GWireNode *n1,GWireNode *n2)
{
  int t;

  ob_touch(n1);
  ob_touch(n2);

  if (n1->out || n2->in) printf("Ack!\n");
  t = horizontal(n1,n1->in) | (horizontal(n2,n2->out) <<1) |
    (vertical(n1,n1->in)<<2) | (vertical(n2,n2->out)<<3);

  switch (t) {
  case HH :
  case VV :
    if ((n1->x != n2->x) || (n1->y != n2->y)) { /* Couldn't move a node */
      n1->out = n2;				  /* Don't remove bend */
      n2->in = n1;
      debugprint("*click* (case 1)\n");
    } else {					  /* General case */
      ob_touch(n1->in);
      ob_touch(n2->out);

      n1->in->out = n2->out;
      n2->out->in = n1->in;
      wirenode_free(n1);
      wirenode_free(n2);
      debugprint("*click* (case 2)\n");
    }
    break;
  case HV :
  case VH :
    ob_touch(n2->out);

    n1->out = n2->out;
    n2->out->in = n1;
    wirenode_free(n2);
    debugprint("*click* (case 3)\n");
    break;
  case ZH :
  case ZV :
    if (n1->in->in) {
      ob_touch(n1->in);

      n1->in->out = NULL;
      debugprint("*subclick* (case 4)\n");
      wire_solder(n1->in,n2);
      wirenode_free(n1);
    } else {
      ob_touch(n2->out);

      n1->out = n2->out;
      n2->out->in = n1;
      wirenode_free(n2);
      logError(ERL_ERROR,"Out of wire case 4 in wire_solder.");
    }
    break;
  case HZ :
  case VZ :
    if (n2->out->out) {
      ob_touch(n2->out);

      n2->out->in = NULL;
      debugprint("*subclick* (case 5)\n");
      wire_solder(n1,n2->out);
      wirenode_free(n2);
    } else {
      ob_touch(n2->out);

      n1->out = n2->out;
      n2->out->in = n1;
      logError(ERL_ERROR,"Out of wire case 5 in wire_solder.");
    }
    break;
  default :
    n1->out = n2;
    n2->in = n1;
    logError(ERL_FATAL,"Weird connection in wire_solder.");
  }
}

/*
   Connects two wires into a single wire.
*/
GWire *wire_connect(GModuleDef *env,GWire *w1,GWire *w2)
{
  GWire *nw;
  GWireNode *n1,*n2;

  if (w1->net->nbits != w2->net->nbits) return 0;

  if (w1->nodes->out)
    join_treereverse(w1);
  if (w2->nodes->in)
    join_treereverse(w2);

  n1 = w1->nodes;
  n2 = w2->nodes;

  ob_touch(w1->nodes);
  ob_touch(w2->nodes);

  w1->nodes->stype = FULL;
  w2->nodes->stype = FULL;

  if (!anchoredp(n1->in)) {
    wire_move(w1->nodes,w2->nodes->x - w1->nodes->x,
	      w2->nodes->y - w1->nodes->y,w1->nodes->stype);
  } else {
      wire_move(w2->nodes,w1->nodes->x - w2->nodes->x,
		w1->nodes->y - w2->nodes->y,w2->nodes->stype);
  }
  w1->nodes->end = NULL;
  w2->nodes->end = NULL;

  nw = wire_drivee(w2);
  ob_touch(nw);
  nw->driver = wire_driver(w1);
  nw = nw->driver;

  wire_solder(w1->nodes,w2->nodes);

  wire_finalizeNet(nw);

  ob_touch(env);
  env->wires = wire_unlink(env->wires,w1);
  env->wires = wire_unlink(env->wires,w2);

  wire_free(w1);
  wire_free(w2);

  return nw->driver;
}


/* Changes the driver of a node */
void wire_changedriver(GWireNode *n,GWire *w)
{
  if (n == NULL) return;

  if (n->end) {
    ob_touch(n->end);
    n->end->driver = w;
  } else
    wire_changedriver(n->out,w);
}

void setends(GWireNode *n)
{
  GWire *w;

  w = wirenode_drivee(n);
  ob_touch(w);
  w->driver = wirenode_driver(n);
  ob_touch(w->driver);
  w->driver->driver = w->driver;

  w->wtype = DRIVEE;
  w->driver->wtype = DRIVER;
}

/*
   Called when the wire clippers are used to cut a wire on a corner.
*/
GWireNode *wirenode_cutcorner(GWireNode *n,GModuleDef *env)
{
  GWireNode *cn;
  GWire *w = wirenode_driver(n);
  GNet *net = net_newCompatable(0,w->net,env);

  cn = wire_newnode();
  ob_touch(cn);
  cn->in = n->in;
  ob_touch(cn->in);
  cn->in->out = cn;
  cn->out = NULL;
  ob_touch(n);
  n->in = NULL;

  cn->end = wire_newend(env,w->net,0);
  ob_touch(cn->end);
  cn->end->nodes = cn;

  n->end = wire_newend(env,w->net,0);
  ob_touch(n->end);
  n->end->nodes = n;

  cn->x = n->x;
  cn->y = n->y;

  setends(n);
  setends(cn);

  wire_replaceNet(n->end,net);		/* Replace the 'net' for this wire */

  return cn;
}

GWireNode *wirenode_cutsegment(int x,int y,GWireNode *n,GModuleDef *env)
{
  GWireNode *cn1,*cn2;
  GWire *w = wirenode_driver(n);
  GNet *net = net_newCompatable(0,w->net,env);

  cn1 = wire_newnode();
  cn2 = wire_newnode();	
  
  ob_touch(cn1);
  ob_touch(cn2);
  if (n->x == n->out->x) {
    cn1->x = cn2->x = n->x;
    cn1->y = cn2->y = y;
  } else {
    cn1->x = cn2->x = x;
    cn1->y = cn2->y = n->y;
  }
  
  cn1->in = n;
  cn2->out = n->out;
  n->out = cn1;
  ob_touch(cn2->out);
  cn2->out->in = cn2;

#if DEBUG_CUT
  printf("@1: %d  %d\n",w->net->refs,net->refs);
#endif
  cn1->end = wire_newend(env,w->net,0);
  cn2->end = wire_newend(env,w->net,0);
#if DEBUG_CUT
  printf("@2: %d  %d\n",w->net->refs,net->refs);
#endif
  ob_touch(cn1->end);
  ob_touch(cn2->end);
  cn1->end->nodes = cn1;
  cn2->end->nodes = cn2;
  setends(cn1);
  setends(cn2);
#if DEBUG_CUT
  printf("@3: %d  %d\n",w->net->refs,net->refs);
#endif
  
  wire_replaceNet(cn2->end,net);		/* Replace the 'net' for this wire */
#if DEBUG_CUT
  printf("@4: %d  %d\n",w->net->refs,net->refs);
#endif

  return cn2;
}

/*
  Cut wire near node an at (x,y).  Assumes that the node has been marked with
  the type of cut that is to take place.
  */
void wire_cut(int x,int y,GWireNode *n,GModuleDef *env)
{
  GWireNode *cn;
  GWire *w,*nw;
  int nbits;
  GNet *na = 0, *nb = 0;

  w = wirenode_driver(n);
  nbits = w->net->nbits;

  switch (n->stype) {
  case FULL :
    if (n->in && n->out) {
      net_draw(w->net);

      cn = wirenode_cutcorner(n,env);

      na = wirenode_driver(n)->net;
      nb = wirenode_driver(cn)->net;

      if (!wire_shorten(n->end,env,0)) {
	nw = wirenode_drivee(n);
	net_setSize(nw->net,nbits);
	nw = wirenode_driver(n);
	net_setSize(nw->net,nbits);
	wire_finalizeNet(nw);
      }

      if (!wire_shorten(cn->end,env,0)) {
	nw = wirenode_driver(cn);
	net_setSize(nw->net,nbits);
	wire_finalizeNet(nw);
      }
    } else
      if (n->end->gate && (n->end->gate->typeinfo->Code == BLOCK)) {
	net_draw(w->net);
	na = w->net;
	w = n->end;
	block_deletewire(w);
	wire_nuke(w,0,env);
      }
    break;
  case VERTICAL :
  case HORIZONTAL :
    net_draw(w->net);

    cn = wirenode_cutsegment(x,y,n,env);

    na = wirenode_driver(n)->net;
    nb = wirenode_driver(cn)->net;


#if DEBUG_CUT
    printf("@5(n=%s): %d\n",n->out->end->net->signame,n->out->end->net->refs);
    printf("@5(cn=%s): %d\n",cn->end->net->signame,cn->end->net->refs);
#endif
    if (!wire_shorten(n->out->end,env,0)) {
      nw = wirenode_drivee(n);
      net_setSize(nw->net,nbits);
      nw = wirenode_driver(n);
      net_setSize(nw->net,nbits);
      wire_finalizeNet(nw);
    }
#if DEBUG_CUT
     else
      printf("ACK 1\n");
#endif

    if (!wire_shorten(cn->end,env,0)) {
      nw = wirenode_driver(cn);
      net_setSize(nw->net,nbits);
      wire_finalizeNet(nw);
    }
#if DEBUG_CUT
      else
      printf("ACK 2\n");
#endif
    break;
  }

  if (na && na->refs) net_draw(na);
  if (nb && nb->refs) net_draw(nb);

}

/*
 * Shortens a newly cut wire, totally deletes the wire if it is no longer
 * connected to anything other than a joint. Returns '1' if the wire was
 * deleted, '0' if the wire was retained.
 */
int wire_shorten(GWire *w,GModuleDef *env,int drawp)
{
  struct wirenode *n,*on;	/* node and "other" node on segment in question */

  if (wire_nuke(w,drawp,env))
    return 1;

  n = w->nodes;
  on = (n->in ? n->in : n->out);

  if ((abs(n->y - on->y) < 10) && (abs(n->x - on->x) < 10)) {
    if (on->in && on->out) {
      ob_touch(on);
      on->end = n->end;
      ob_touch(on->end);
      on->end->nodes = on;
      if (on->out == n)
	on->out = NULL;
      else
	on->in = NULL;
      wirenode_free(n);
    }
  } else {
    ob_touch(n);
    if (n->x == on->x) {
      n->y += 5*(n->y < on->y) - 5*(n->y > on->y);	/* Zero if n->y == on->y */
    }
    else {
      n->x += 5*(n->x < on->x) - 5*(n->x > on->x);
    }
  }
  return 0;
}

/*
 * Nukes a wire if it is not attached to anything.  Returns 1 if the wire
 * was nuked, 0 otherwise.
 */
int wire_nuke(GWire *w,int draw,GModuleDef *env)
{
  if (draw) wire_draw(w->driver->nodes);
  if (wire_trash(w->nodes,env,draw)) {
    ob_touch(env);
    env->wires = wire_unlink(env->wires,w);
    wire_free(w);
    return 1;
  }
  return 0;
}

int wire_trashin(GWireNode *n,GModuleDef *env,int drawp)
{
  int trashp;

  if (!n) return 0;

  if (!n->in) {
    if (n->end && n->end->gate) {
      switch (n->end->gate->typeinfo->Code) {
      case JOINT :
	return joint_dejoint(n,env,drawp);
      default :
	return 0;
      }
    } else {
      if (env) {
	ob_touch(env);
	env->wires = wire_unlink(env->wires,n->end);
      }
      wire_free(n->end);
      wirenode_free(n);
      return 1;
    }
  } else {
    if ((trashp = wire_trashin(n->in,env,drawp)))
      wirenode_free(n);
    return trashp;
  };
}

int wire_trashout(GWireNode *n,GModuleDef *env,int drawp)
{
  int trashp;
  
  if (!n) return 0;
  
  if (!n->out) {
    if (n->end && n->end->gate) {
      switch (n->end->gate->typeinfo->Code) {
      case JOINT :
	return joint_dejoint(n,env,drawp);
      default :
	return 0;
      } 
    } else {
      if (env) {
	ob_touch(env);
	env->wires = wire_unlink(env->wires,n->end);
      }
      wire_free(n->end);
      wirenode_free(n);
      return 1;
    }
  } else {
    if ((trashp = wire_trashout(n->out,env,drawp)))
      wirenode_free(n);
    return trashp;
  }
}

int wire_trash(GWireNode *n,GModuleDef *env,int drawp)
{
  if (n->in)
    return wire_trashin(n,env,drawp);
  else
    return wire_trashout(n,env,drawp);
}

GWireNode *wire_makecorner(GWireNode *n,int tx,int ty)
{
  GWireNode *nn;
  GWire *dn;

  if (!n->out)
    return NULL;
  
  if ((n->stype != HORIZONTAL) && (n->stype != VERTICAL))
    return NULL;
  
  dn = wirenode_driver(n);
  wire_draw(dn->nodes);
  nn = wire_newnode();
  ob_touch(nn);
  nn->out = wire_newnode();
  ob_touch(nn->out);
  nn->out->in = nn;
  
  if (n->x == n->out->x) {
    nn->x = nn->out->x = n->x;
    nn->y = nn->out->y = ty;
  } else {
    nn->y = nn->out->y = n->y;
    nn->x = nn->out->x = tx;
  }
  
  nn->out->out = n->out;
  ob_touch(n->out);
  n->out->in = nn->out;
  nn->in = n;
  n->out = nn;
  
  nn->stype = nn->out->stype = HORIZONTAL | VERTICAL;
  
  wire_draw(dn->nodes);
  
  return n->in ? nn : nn->out;
}

struct wire *wire_unattach(w,wl)
struct wire *w,*wl;
{
  if (wl) {
    if (w == wl)
      return w->next;
    else {
      ob_touch(wl);
      wl->next = wire_unattach(w,wl->next);
      return wl;
    }
  } else
    return NULL;
}

void wire_deletenode(GWireNode *n)
{
  if (n->out && !n->in) {
    ob_touch(n->end);
    ob_touch(n->out);
    n->end->nodes = n->out;
    n->out->end = n->end;
    n->out->in = NULL;
    n->out->stype = n->stype;
  } else
    if (n->in && !n->out) {
      ob_touch(n->end);
      ob_touch(n->in);
      n->end->nodes = n->in;
      n->in->end = n->end;
      n->in->out = NULL;
      n->in->stype = n->stype;
    } else {
      ob_touch(n->out);
      ob_touch(n->in);
      n->out->in = n->in;
      n->in->out = n->out;
      n->out->stype = n->stype;
    }
  wirenode_free(n);
}

void wire_deletezeronodes(GWireNode *n)
{
  if (n && n->out) {
    if (n->x == n->out->x && n->y == n->out->y) {
      struct wirenode *nn;

      nn = n->out;
      wire_deletenode(n);
      wire_deletezeronodes(nn);
    } else
      wire_deletezeronodes(n->out);
  }
}

void freenodelist(GWireNode *n)
{
  if (n) {
    freenodelist(n->out);
    ob_free(n);
  }
}

/*
  Evaluate quality of cutting segment between n1 and n2 at point (x,y).
  Lower values are better
 */
int cutscore(GWireNode *n1,GWireNode *n2,int x,int y)
{
  if (!n1 || !n2) return 1000;

  if (n1->x == n2->x) {
    if (n1->y < n2-> y) {
      if (y < n1->y || y > n2->y) return 1000;
    } else {
      if (y < n2->y || y > n1->y) return 1000;
    }
    return abs(x-n1->x);
  } else {
    if (n1->x < n2->x) {
      if (x < n1->x || x > n2->x) return 1000;
    } else {
      if (x < n2->x || x > n1->x) return 1000;
    }
    return abs(y-n1->y);
  }
}

void wire_dump(EditState *es)
{
  GWireList *l;
  GWire *lw;
  GWireNode *ln;

  printf("WIRES:\n");
  for (l = es->env->wires;l;l = l->cdr) {
    lw = l->car;
    if (!lw->nodes->out) continue;
    printf("  **  %s: ",lw->net->signame);
    for (ln = lw->nodes;ln;ln = ln->out)
      printf(" @%p:(%d %d)",ln,ln->x,ln->y);
    printf("\n");
  }
}


/*
 * Adds a new wire stub at position (x,y).  
 */
void wire_addstub(EditState *es,int x,int y)
{
  GWire *new_w1,*new_w2,*cut_w1,*cut_w2;
  GWireNode *n1 = 0;
  GWireNode *n2 = 0;
  int cs1,cs2;
  GWire *w;
  GWire *jw[4];					/* Attachment points for joint (0=right, 1=up, 2=left, 3=down) */

  jw[0] = jw[1] = jw[2] = jw[3] = 0;

  n1 = wire_hitall(x,y,es->env->wires);
  if (!n1) { return; }
  w = wirenode_driver(n1);

  if (distance(x,y,n1->x,n1->y) < MAXWIRERANGE) {	/* Attach to corner */
    if (!n1->in || !n1->out) {
      return;
    }

    x = n1->x;
    y = n1->y;

    net_draw(w->net);

    cut_w1 = wire_newend(es->env,w->net,1);
    cut_w2 = wire_newend(es->env,w->net,1);
    wire_newNetSegment(es->env,w->net,&new_w1,&new_w2);		/* The new wire to attach */

    ob_touch(cut_w1->nodes);
    cut_w1->nodes->in = n1->in;

    ob_touch(n1->in);
    n1->in->out = cut_w1->nodes;

    ob_touch(cut_w2->nodes);
    cut_w2->nodes->out = n1->out;

    ob_touch(n1->out);
    n1->out->in = cut_w2->nodes;

    ob_touch(new_w1->nodes);
    new_w1->nodes->x = x;
    new_w1->nodes->y = y;

    ob_touch(new_w2->nodes);
    new_w2->nodes->x = x;
    new_w2->nodes->y = y;

    cut_w1->nodes->x = x;
    cut_w1->nodes->y = y;
    cut_w2->nodes->x = x;
    cut_w2->nodes->y = y;
    
    if (n1->x > n1->in->x)
      jw[2] = cut_w1;
    else if (n1->x < n1->in->x)
      jw[0] = cut_w1;
    else if (n1->y > n1->in->y)
      jw[1] = cut_w1;
    else if (n1->y < n1->in->y)
      jw[3] = cut_w1;
    else {
      jw[0] = cut_w1;				/* Should never happen */
      logError(ERL_ERROR,"bogus w1 cut: (%d,%d) (%d,%d)\n",n1->x,n1->y,n1->in->x,n1->in->y);
    }

    if (n1->x > n1->out->x && !jw[2])
      jw[2] = cut_w2;
    else if (n1->x < n1->out->x && !jw[0])
      jw[0] = cut_w2;
    else if (n1->y > n1->out->y && !jw[1])
      jw[1] = cut_w2;
    else if (n1->y < n1->out->y && !jw[3])
      jw[3] = cut_w2;
    else {					/* Should never happen */
      logError(ERL_ERROR,"bogus w2 cut: (%d,%d) (%d,%d)\n",n1->x,n1->y,n1->out->x,n1->out->y);
      if (!jw[0])
	jw[0] = cut_w2;
      else
	jw[2] = cut_w2;
    }

    if (!jw[0]) {
      jw[0] = new_w1;
      new_w2->nodes->x += 12;
    } else if (!jw[1]) {
      jw[1] = new_w1;
      new_w2->nodes->y -= 12;
    } else if (!jw[2]) {
      jw[2] = new_w1;
      new_w2->nodes->x -= 12;
    } else if (!jw[3]) {
      jw[3] = new_w1;
      new_w2->nodes->y += 12;
    }

    ob_free(n1);
  } else {						/* Attach to segment */
    cs1 = cutscore(n1,n1->out,x,y);
    cs2 = cutscore(n1,n1->in,x,y);

    if (cs1 < cs2) {
      n2 = n1->out;
    } else {
      n1 = n1->in;
      n2 = n1->out;
    }
    if (!n2) return;

    net_draw(w->net);

    cut_w1 = wire_newend(es->env,w->net,1);
    cut_w2 = wire_newend(es->env,w->net,1);
    wire_newNetSegment(es->env,w->net,&new_w1,&new_w2);		/* The new wire to attach */

    ob_touch(n1);
    n1->out = cut_w1->nodes;

    ob_touch(cut_w1->nodes);
    cut_w1->nodes->in = n1;

    ob_touch(n2);
    n2->in = cut_w2->nodes;

    ob_touch(cut_w2->nodes);
    cut_w2->nodes->out = n2;

    ob_touch(new_w2->nodes);

    if (n1->x == n2->x) {
      x = n1->x;

      jw[0] = new_w1;

      new_w2->nodes->x = x + 12;
      new_w2->nodes->y = y;

      if (n1->y < n2->y) {
	jw[1] = cut_w1;
	jw[3] = cut_w2;
      } else {
	jw[1] = cut_w2;
	jw[3] = cut_w1;
      }
    } else if (n1->y == n2->y) {
      y = n1->y;

      jw[1] = new_w1;

      new_w2->nodes->x = x;
      new_w2->nodes->y = y - 12;

      if (n1->x < n2->x) {
	jw[2] = cut_w1;
	jw[0] = cut_w2;
      } else {
	jw[2] = cut_w2;
	jw[0] = cut_w1;
      }
    }

    ob_touch(new_w1->nodes);
    ob_touch(cut_w1->nodes);
    ob_touch(cut_w2->nodes);
    new_w1->nodes->x = x;
    new_w1->nodes->y = y;
    cut_w1->nodes->x = x;
    cut_w1->nodes->y = y;
    cut_w2->nodes->x = x;
    cut_w2->nodes->y = y;

  }

  if (jw[0]) { jw[0]->nodes->x += 2; jw[0]->orient = 0; }
  if (jw[1]) { jw[1]->nodes->y -= 2; jw[1]->orient = 1; }
  if (jw[2]) { jw[2]->nodes->x -= 2; jw[2]->orient = 2; }
  if (jw[3]) { jw[3]->nodes->y += 2; jw[3]->orient = 3; }
  joint_make(x,y,jw[0],jw[1],jw[2],jw[3],es);

  ob_touch(new_w2);
  new_w2->orient = (new_w1->orient + 2) % 4;

  setends(cut_w1->nodes);
  setends(cut_w2->nodes);
  setends(new_w1->nodes);

  wire_finalizeNet(new_w1);
  net_draw(w->net);
}

int wire_numOnPad(GWire *w)
{
  if (!w) 
    return 0;
  else
    return 1 + wire_numOnPad(w->next);
}
