/****************************************************************************
    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 Thu Feb 12 09:35:30 2004
****************************************************************************/
/*
* Error package for editor.
*/
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "tkgate.h"

#define ERRORHEADLINES 2

#define ERROR_DEBUG 1

/*
    Set the position of an error
*/
void SetErrorPosition(int x,int y)
{
  ob_touch(XGate.errl);
  XGate.ErrorMarkTimeout = 2;
  XGate.errl->ErrorXPos = x;
  XGate.errl->ErrorYPos = y;
}

void DrawErrorPositionMark()
{
  int x = XGate.errl->ErrorXPos;
  int y = XGate.errl->ErrorYPos;

  line(x-20,y-20,x+20,y+20);
  line(x-20,y+20,x+20,y-20);
}

void ClearErrorMark()
{
  if (XGate.ErrorMarkTimeout == 1) {
    DrawErrorPositionMark();
    XGate.ErrorMarkTimeout = 0;
  }
}

static GError *new_GError(const char *path,const char *msg)
{
  GError *E = (GError*) ob_malloc(sizeof(GError),"GError");
  char b[STRMAX];

  if (path) {
    GModuleDef *M = sim_findContainingMod(path);
    sprintf(b,"In %s: %s",M->Name,msg);
  } else
    strcpy(b, msg);

  E->Num = 0;
  E->path = ob_strdup(path);
  E->message = ob_strdup(b);
  E->g = 0;
  E->n = 0;
  E->next = E->last = 0;

  return E;
}

void Error_add(GError *E)
{

  if (!XGate.errl->curerr) {
    DoTcl("tkg_makeErrBox");
    ob_touch(XGate.errl);
    XGate.errl->ErrorCount = 0;
    XGate.errl->errlist = XGate.errl->curerr = E;
    ob_touch(E);
    ob_touch(XGate.errl->errlist);

    E->Num = XGate.errl->ErrorCount = 1;
    XGate.errl->errlist->last = NULL;
    XGate.errl->errlist->next = NULL;
  } else {
    ob_touch(E);
    ob_touch(XGate.errl);
    ob_touch(XGate.errl->curerr);

    E->Num = XGate.errl->ErrorCount++;
    XGate.errl->curerr->next = E;
    XGate.errl->curerr->next->last = XGate.errl->curerr;
    XGate.errl->curerr = XGate.errl->curerr->next;
    XGate.errl->curerr->next = NULL;
  }
  DoTcl("tkg_errBoxAdd \"%s\"",E->message);
}

void Error_gateReport(const char *path,GCElement *g,const char *msg)
{
  GError *E = new_GError(path,msg);

  ob_touch(E);
  ob_touch(XGate.errl);
  E->g = g;
  Error_add(E);
}

void Error_nodeReport(const char *path,GWireNode *n,const char *msg)
{
  GError *E = new_GError(path,msg);

  ob_touch(E);
  ob_touch(XGate.errl);
  E->n = n;
  Error_add(E);
}

void Error_genericReport(const char *path,const char *msg)
{
  GError *E = new_GError(path,msg);

  ob_touch(E);
  ob_touch(XGate.errl);
  Error_add(E);
}

void Error_close()
{
  GError *E;
  int i = 0;
  int N = 0;

  ob_touch(XGate.errl);
  XGate.errl->errors = (GError**) ob_malloc(sizeof(GError*)*XGate.errl->ErrorCount,"GError*[]");
  for (E = XGate.errl->errlist;E;E = E->next, i++) {
    XGate.errl->errors[i] = E;
    N++;
  }
}

void Error_purge()
{
  GError *E;

  ob_free(XGate.errl->errors);
  XGate.errl->errors = 0;
  ClearErrorMark();
  while (XGate.errl->errlist) {
    E = XGate.errl->errlist;
    XGate.errl->errlist = XGate.errl->errlist->next;
    ob_free(E->message);
    ob_free(E);
  }
  XGate.errl->curerr = XGate.errl->errlist = NULL;

  XGate.ErrorMarkTimeout = 0;
  FlagRedraw();
}

void Error_navigateToModule(EditState **es,const char *path)
{
  char buf[STRMAX],*T,*N;
  GModuleDef *M;

  while (*es) editstate_pop(es);

  M = XGate.circuit->root_mod;
  strcpy(buf,path);
  editstate_push(es,M,0);
  for (T = strtok(buf,".");T;T = N) {
    N = strtok(0,".");
    if (N) {
      GCElement *g = (GCElement*) SHash_find(M->gates,T);
      if (g && g->typeinfo->Code == BLOCK) {
	M = env_findModule(g->u.block.BlockFunction);
	editstate_push(es,M,g);
      }
    }
  }
  cpath_reshow();
}

EditState *Error_open(GError *E,EditState *es)
{
  int x,y;

  if (!E) return es;

  if (!E->n && !E->g) return es;

  if (!E->path) {
    message(0,msgLookup("err.noerr"));		/* "Can not locate error." */
    return es;
  }

  ob_touch(es);
  editstate_saveOrig(es);

  Error_navigateToModule(&es,E->path);

  ob_touch(XGate.circuit);
  XGate.circuit->select = XGate.circuit->last = 0;
  if (E->g) {
    ob_touch(E->g);
    XGate.circuit->select = E->g;
    E->g->selected = 1;
  }
  XGate.circuit->wsel = NULL;
  XGate.circuit->wnsel = NULL;
  
  if (E->n) {
    GWireNode *wn1,*wn2;

    wn1 = E->n;
    wn2 = wn1->out ? wn1->out : wn1->in;
    x = (wn1->x + wn2->x)/2;
    y = (wn1->y + wn2->y)/2;
  } else if (E->g) {
    x = E->g->xpos;
    y = E->g->ypos;
    if (E->g->typeinfo->Code == BLOCK) {
      x += E->g->u.block.gwidth/2;
      y += E->g->u.block.gheight/2 + 10;
    }
  } else {
    x = XGate.width/2;
    y = XGate.height/2;
  }

  XGate_setOrigin(XGate.width/2 - x,XGate.height/2 - y);
  ob_touch(es);
  editstate_saveOrig(es);

  editstate_setCurrent(es);
  SetErrorPosition(x,y);
  
  FlagRedraw();
  
  return es;
}


EditState *seterror(int n,EditState *es)
{
  GError *E;

  if (!XGate.errl->errors) {
    message(0,msgLookup("err.misserr"));
    return es;
  }

  E = XGate.errl->errors[n];
  return Error_open(E,es);
}

