/****************************************************************************
    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 19 08:35:03 2004
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include "tkgate.h"

#define MODINT_DEBUG 0

#define MODINT_BLOCKSPACE	50


void block_cutoffwire(GWire *w,EditState *es);		/* Must match definition in block.c */

static int blknam_compare(const void *pA,const void *pB)
{
  GCElement **A = (GCElement**) pA;
  GCElement **B = (GCElement**) pB;

  return strcmp((*A)->u.block.BlockFunction,(*B)->u.block.BlockFunction);
}

GCElement *modint_find(const char *name)
{
  GModuleDef *M = XGate.circuit->mid_mod;
  HashElem *E;

  for (E = Hash_first(M->gates);E;E = Hash_next(M->gates,E)) {
    GCElement *g = (GCElement*) HashElem_obj(E);
    if (strcmp(g->u.block.BlockFunction,name) == 0)
      return g;
  }
  return 0;
}

void modint_reset(EditState *es,GCElement *g)
{
  int i;

  if (!g || g->typeinfo->Code != BLOCK)
    return;

  for (i = 0;i < g->typeinfo->NumPads;i++) {
    GWire *w,*n_w;

    for (w = g->wires[i];w; w = n_w) {
      n_w = w->next;
      block_cutoffwire(w,es);
    }
  }

  gate_draw(g,GD_NOWIRE);
  ob_touch(g);
  g->u.block.gwidth = MINSIZE;
  g->u.block.gheight = MINSIZE;
  gate_draw(g,GD_NOWIRE);
}

void modint_deleteInterface(GModuleDef *M)
{
  GCElement *d = M->blockdescript;

  if (d) gate_delete(d,XGate.circuit->mid_mod,0);

  if (XGate.circuit->es->isInterface) {
    modint_arrange(XGate.circuit->es);
    FlagRedraw();
  }
}

void modint_renameInterface(GModuleDef *M)
{
  GCElement *g = M->blockdescript;

  if (g) {
    if (g->u.block.BlockFunction)
      ob_free(g->u.block.BlockFunction);
    ob_touch(g);
    g->u.block.BlockFunction = ob_strdup(M->Name);
  }

  if (XGate.circuit->es->isInterface) {
    modint_arrange(XGate.circuit->es);
    FlagRedraw();
  }
}

void modint_setInterface(GModuleDef *M,GCElement *g)
{
  GCElement *d = M->blockdescript;
  GCElement *r = 0;

  if (d)
    gate_delete(d,XGate.circuit->mid_mod,0);

  if (g) {
    r = (*g->typeinfo->ReplicateGate)(XGate.circuit->mid_mod,g,0,0,0);
    ob_touch(r);
    r->show_name = 0;
  } else {
    GGateInfo *bgi = gtype_get(BLOCK);
    const char *args[2];
    args[0] = "-func";
    args[1] = M->Name;
    r = (*bgi->MakeFunction)(0,XGate.circuit->mid_mod,BLOCK,50,50,0,0,0,args,2);
    
  }
  ob_touch(M);
  M->blockdescript = r;
#if MODINT_DEBUG
  printf("modint_setInterface(%s) -> %p [n=%d]\n",
	 M->Name,r,Hash_numElems(XGate.circuit->mid_mod->gates));
#endif
}

void modint_arrange(EditState*es)
{
  GModuleDef *M = XGate.circuit->mid_mod;
  HashElem *E;
  GCElement **blist;
  int N = Hash_numElems(M->gates);
  int i;
  int x,y,maxh;
  int isFirst;  

  if (N < 1) return;			/* No defined blocks */

  ob_touch(XGate.circuit);
  XGate.circuit->no_set_modify = 1;
  blist = (GCElement**) ob_calloc(N,sizeof(GCElement*),"GCElement*[]");

  for (i = 0,E = Hash_first(M->gates);E;i++, E = Hash_next(M->gates,E))
    blist[i] = (GCElement*) HashElem_obj(E);

  qsort(blist,N,sizeof(GCElement*),blknam_compare);

  x = MODINT_BLOCKSPACE;
  y = MODINT_BLOCKSPACE;
  maxh = 0;
  isFirst = 1;
  for (i = 0;i < N;i++) {
    GCElement *g = blist[i];

    ob_touch(g);
    g->top = g->bottom = g->right = g->left = 0;

    if (!isFirst && (x+g->u.block.gwidth + MODINT_BLOCKSPACE) > XGate.width) { 
      x = MODINT_BLOCKSPACE;
      y += MODINT_BLOCKSPACE + maxh;
    }

    gate_moveTo(g,x,y);

#if MODINT_DEBUG
    printf("  place %s::%s @ (%d, %d)\n",g->u.block.BlockFunction,g->ename,g->xpos,g->ypos);
#endif


    x += g->u.block.gwidth + MODINT_BLOCKSPACE;
    if (g->u.block.gheight > maxh)
      maxh = g->u.block.gheight;
    isFirst = 0;
  }

  ob_touch(XGate.circuit);
  XGate.circuit->no_set_modify = 0;
}

void modint_update(EditState *es)
{
#if MODINT_DEBUG
  printf("modint_update\n");
#endif
}

void modint_edit(EditState **es,GModuleDef *M)
{
  ob_touch(*es);
  editstate_push(es,XGate.circuit->mid_mod,0);
  (*es)->isInterface = 1;
  editstate_setCurrent(*es);

  modint_arrange(*es);

  FlagRedraw();
}

