/****************************************************************************
    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.
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include "gsim.h"

static SState state;

void init_process()
{
  SState_init(&state,1);
}

/*
   Propogate the value on a net changed by event E
*/
void SNet_propogate(SNet *N,EvQueue *Q,SEvent *E)
{
  int i;

  for (i = 0;i < N->n_ports.num;i++) {
    SPort *P = N->n_ports.port[i];
    if (P->p_isDup || P->p_type->io == GIO_OUT || P->p_type->io == GIO_TRI)
      continue;
    (*P->p_gate->g_type->gi_processEvent)(P->p_gate,Q,E);
  }
}

/*
   Process a net event E
*/
void SNet_process(SNet *N,EvQueue *Q,SEvent *E)
{
  if (!SState_eq(&N->n_state,&E->evnet.state)) {
    SState_copy(&N->n_oldState,&N->n_state);
    SState_copy(&N->n_state,&E->evnet.state);
    if (N->n_watchNames) {
      HashElem *hE;

      for (hE = Hash_first(N->n_watchNames);hE;
	   hE = Hash_next(N->n_watchNames,hE)) {
	char *nname = SHashElem_key(hE);
	char buf[STRMAX],*p;

	p = buf;
	p += sprintf(p,"net %s ",nname);
	p += SState_getstr(&N->n_state,p);
	p += sprintf(p," @ %d",E->evnet.time); 
	sendMsg("%s",buf);
      }
    }
    if (N->n_breaks) {
      HashElem *hE;

      for (hE = Hash_first(N->n_breaks);hE;hE = Hash_next(N->n_breaks,hE)) {
	SBreakpoint *B = (SBreakpoint *)HashElem_obj(hE);
	int eq = SState_eq(&N->n_state,B->bp_value);
	int match = 0;

	switch (B->bp_op) {
	case BPO_EQUAL :
	  match = eq;
	  break;
	case BPO_NEQ :
	  match = !eq;
	  break;
	}

	if (match) {
	  Q->flags &= ~(EVF_RUN|EVF_POSCLOCK|EVF_NEGCLOCK);
	  sendMsg("break %d",B->bp_id);
	}
      }
    }
    SNet_propogate(N,Q,E);
  }
}

/*
   Process a port event E
*/
void SPort_process(SPort *P,EvQueue *Q,SEvent *E)
{
  SNet *N = P->p_net;
  int i;

  SState_copy(&P->p_state,&E->evport.state);		/* Copy event state to port */
  SState_reinit(&state,P->p_state.nbits);		/* Resize scratch state to correct bit length */
  SState_float(&state);					/* Initialize scratch state to floating */

  for (i = 0;i < N->n_ports.num;i++) {
    SPort *nP = N->n_ports.port[i];
    int port_type = SPort_effectiveType(nP);

    if (port_type != GIO_OUT && port_type != GIO_TRI && port_type != GIO_INOUT) {
      continue;
    }

    SState_wire(&state,&state,&nP->p_state);
  }

  E->evclass = EV_NET;					/* Transform event into net event */
  E->evnet.net = N;					/*   set net event occurs on */
  SState_copy(&E->evnet.state,&state);			/*   copy new state of net */
  SNet_process(N,Q,E);					/*   process the net event */
}
