/*
 * Copyright (c) 1992, The Geometry Center
 *                     University of Minnesota 
 *                     1300 South Second Street
 *                     Minneapolis, MN  55454
 *
 * email address: software@geom.umn.edu
 *
 * This software is copyrighted as noted above.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is
 * preserved on all copies.
 *
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the authors, who may or may not act on them as they desire.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 *
 *     The National Science and Technology Research Center for
 *      Computation and Visualization of Geometric Structures
 */


#import <objc/objc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "link_edit_types.h"
#include "link_types.h"
#include "link_edit_global.h"
#include "link.h"
#import "EditView.h"
#import "DisplayView.h"
#import <appkit/SavePanel.h>
#import <appkit/OpenPanel.h>
#import "Link.h"
#include "fileops.h"
#include <math.h>


void saveMatrix(id linkObj, const char *filename)
{
  int **mat, i, numcross;
  FILE *sfile;
  /******MUST CHECK IF THIS IS POSSIBLE**********/
  [linkObj convertEditToMatrix];
  mat = [linkObj get_matrix];
  numcross = [linkObj get_numcross];
  sfile = fopen(filename, "w");
  if (sfile == NULL)
    {
      NXRunAlertPanel("Save error",
		      "Could not save to file %s",
		      "OK", NULL, NULL, filename);
      return;
    }
  fprintf(sfile, "%%L %s L%%\n", strrchr(filename, '/')+1);
  fprintf(sfile, "%%Nx %d Nx%%\n", numcross);
  fprintf(sfile, "%%M\n");
  for (i=0; i<numcross; i++)
    fprintf(sfile, "%c %d %c %d %c\n", (char) mat[i][0],
	    mat[i][1], (char) mat[i][2], mat[i][3],
	    (char) mat[i][4]);
  fprintf(sfile, "M%%\n");
  fclose(sfile);
}

void loadMatrix(id linkObj, const char *filename, int **rowptr,
		int ***matptr, int *numcrossptr)
{
  NXZone *zone = [linkObj zone];
  int **matrix, *row, numcross, i;
  FILE *file;
  char c, c1[2], c2[2], c3[2];
  BOOL done;

  file = fopen(filename, "r");
  if (*matptr != NULL)
    {
      NXZoneFree(zone, *rowptr);
      NXZoneFree(zone, *matptr);
    }

  /* Only care about Nx and M fields */
  done = NO;
  while(!done)
    {
      while (getc(file) != '%');
      c = getc(file);
      switch(c)
	{
	case 'N':
	  fscanf(file, "%*s %d %*s", &numcross);
	  row = (int *) NXZoneMalloc(zone, 5*numcross*sizeof(int));
	  matrix = (int **) NXZoneMalloc(zone, numcross*sizeof(int *));
	  for(i=0; i<numcross; i++)
	    matrix[i] = row + 5*i;
	  break;
	case 'M':
	  for (i=0; i<numcross; i++)
	    {
	      fscanf(file, "%s%d%s%d%s", c1,
		     &matrix[i][1], c2,
		     &matrix[i][3], c3);
	      matrix[i][0] = (int) c1[0];
	      matrix[i][2] = (int) c2[0];
	      matrix[i][4] = (int) c3[0];
	    }
	  done = YES;
	  break;
	default:
	  /* Read till rejoinding % */
	  while (getc(file) != '%');
	  c = getc(file);
	  break;
	}
    }
  *matptr = matrix;
  *rowptr = row;
  *numcrossptr = numcross;
  fclose(file);
}

/* Utility used for saveSB */
int calcDist(LinkPointList *pnt, LinkCrossingList *cross)
{
  int x = abs((int) pnt->dcx - (int) cross->dcx);
  int y = abs((int) pnt->dcy - (int) cross->dcy);
  return (x > y) ? x : y;
}

int saveSB(LinkStatus *gnrc, const char *name)
{
  char strng[160];
  LinkPointList *pnts[200];
  LinkCrossingList *crgs[200];
  FILE *fp = fopen (name, "w");
  int pnt_id = 0, lnk_id;
  LinkList *lnk;
  LinkPointList *pnt, *np;
  LinkCrossingList *cross;
  int numx = 0, i;

  if (fp == NULL)
    {
      NXRunAlertPanel("Save error",
		      "Could not save to file %s",
		      "OK", NULL, NULL, name);
      return -1;
      return(-1);
    }

  /* set link_id for each point */
  gnrc->num = 0;
  lnk = gnrc->link.next;
  while(lnk != NULL)
    {
      gnrc->num++;
      lnk_id = lnk->id;
      pnt = lnk->point.next;
      while(pnt != NULL) {
        pnt->link_id = lnk_id;
	pnt->linkptr = lnk;
        pnt->point_id = pnt_id++;
	cross = pnt->crossing.next;
	while(cross != NULL)
	  {
	    if(cross->z > 0.0)
	      {
		/* Record crossing & pnt in array */
		if (numx > 199)
		  {
		    LinkPrintMessage("Too damn many crossings to output to StringBean.");
		    return -1;
		  }
		pnts[numx] = pnt;
		crgs[numx] = cross;
		/* Label over crossing */
		cross->id = numx;
		/* Under crossing */
		cross->crossing->id = numx++;
	      }
	    cross = cross->next;
	  }
	pnt = pnt->next;
      }
     lnk = lnk->next;
   }

  /* Record global information */
  fprintf(fp, "%d %d %d %d\n", gnrc->num, pnt_id, numx, 0);

  /* Record the anchor of each component */
  for (lnk = gnrc->link.next; lnk != NULL; lnk = lnk->next)
    fprintf(fp, "%d ", lnk->point.next->point_id);
  fprintf(fp, "\n\n");

  /* Record edges */
  for (lnk = gnrc->link.next; lnk != NULL; lnk = lnk->next)
    {
      pnt = lnk->point.next;
      do
	{
	  fprintf(fp, "%d %3d %3d %d %d \n",
		  pnt->point_id,
		  (int) pnt->dcx,
		  (int) pnt->dcy,
		  (pnt->crossing.next) ?
  		     2*pnt->crossing.next->id + (pnt->crossing.next->z < 0) : -1,
		  (pnt->next) ?
		     pnt->next->point_id : lnk->point.next->point_id);
	  pnt = pnt->next;
	}
      while(pnt != NULL);
    }
  fprintf(fp, "\n");

  /* Record crossings */
  for (i=numx-1; i>=0; i--)
    {
      char lorr;
      pnt = pnts[i];
      cross = crgs[i];
      np = (pnt->next) ? pnt->next : pnt->linkptr->point.next;
      lorr = orient(pnt->x, pnt->y, np->x, np->y,
		    cross->partner->x, cross->partner->y);
      /* StringBean's idea of "right" v. "left" orientation is the opposite
       * from Linktool's.  So, we must do a little reversal (RIGHT is defined
       * as 0 in StringBean source).
       */
      lorr = (lorr == 'r') ? 1 : 0;
      fprintf(fp, "%3d %3d %3d %3d %d %d %d %d %d %d %d\n",
	      (int) cross->dcy - LINK_Y_HIT_RADIUS,
	      (int) cross->dcx - LINK_X_HIT_RADIUS,
	      (int) cross->dcy + LINK_Y_HIT_RADIUS,
	      (int) cross->dcx + LINK_X_HIT_RADIUS,
	      pnt->point_id,
	      cross->partner->point_id,
	      lorr,
	      calcDist(pnt, cross),
	      calcDist(cross->partner, cross),
	      (cross->next) ?
	      2*cross->next->id + (cross->next->z < 0) : -1,
	      (cross->crossing->next) ?
	      2*cross->crossing->next->id + (cross->crossing->next->z < 0) : -1);
    }
  
  fclose(fp);
  return 0;
}
			  

int LinkSaveAll(LinkStatus *status,const char *name)
/* returns 0 if successful */
{
  char strng[160];
  FILE *fp;

  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;

  printf("gnrc = %d\n", gnrc);

  fp = fopen(name,"w");
  if(fp == NULL) {
    sprintf(strng,"Cannot open file %s\n",name);
    LinkPrintMessage(strng);
    return(-1);
   }
  return LinkSaveAll2(status, (void *) fp, DEST_FILE);
}

int LinkSaveAll2(LinkStatus *status, void *fp, int dest)
{
  LinkList *lnk;
  LinkPointList *pnt;
  LinkCrossingList *crssng;
  int lnk_id,pnt_id,num_crssng;
  LinkStatus *gnrc = status;
  char *s;

  if (dest == DEST_FILE)
    fprintf((FILE *) fp,"LINK_PROJECTION\n");
  
  /* Save strand data */
  lnk = gnrc->link.next;
  lnk_id = 0;
  while(lnk != NULL) {
     s = "Strand";
     if (dest == DEST_FILE)
       fprintf((FILE *) fp," %s %d\n",s, lnk_id);
     else
       {
	 NXWriteType((NXTypedStream *) fp, "*", &s);
	 NXWriteType((NXTypedStream *) fp, "i", &lnk_id);
       }
     LinkPrintLink(gnrc,(void *) fp, dest, lnk);
     /* Set id's while we're at it...*/
     lnk->id = lnk_id;
     lnk_id++;
     lnk = lnk->next;
    }

  /* set link_id for each point */
  lnk = gnrc->link.next;
  while(lnk != NULL) {
     lnk_id = lnk->id;
     pnt = lnk->point.next;
     pnt_id = 0;
     while(pnt != NULL) {
        pnt->link_id = lnk_id;
        pnt->point_id = pnt_id;
        pnt_id++;
        pnt = pnt->next;
       }
     lnk = lnk->next;
    }

  /* Save crossing data */
  if (dest == DEST_FILE)
    {
      fprintf((FILE *) fp,"# crossing format: \n");
      fprintf((FILE *) fp,"#    UpLinkID EdgeID   DownLinkID EdgeID   UpParam DownParam\n");
    }
  /* Count number of over crossings */
  lnk = gnrc->link.next;
  num_crssng = 0;
  while(lnk != NULL) {
     pnt = lnk->point.next;
     while(pnt != NULL) {
        crssng = pnt->crossing.next;
        while(crssng != NULL) {
           if(crssng->z > 0.0) { /* Over crossing */
              num_crssng++;
             }
           crssng = crssng->next;
          }
        pnt = pnt->next;
       }
     lnk = lnk->next;
    }
  s = "Crossings";
  if (dest == DEST_FILE)
    fprintf((FILE *) fp," %s %d\n",s, num_crssng);
  else
    {
      NXWriteType((NXTypedStream *) fp, "*", &s);
      NXWriteType((NXTypedStream *) fp, "i", &num_crssng);
    }
  /* save only over crossings- under crossing implicit */
  lnk = gnrc->link.next;
  while(lnk != NULL) {
     pnt = lnk->point.next;
     while(pnt != NULL) {
        crssng = pnt->crossing.next;
        while(crssng != NULL) {
	  if(crssng->z > 0.0) { /* Over crossing */
	    if (dest == DEST_FILE)
	      fprintf((FILE *) fp,"   %d %d   %d %d   %.4lf  %.4lf\n",
		      lnk->id, pnt->point_id,
		      crssng->partner->link_id,crssng->partner->point_id,
		      crssng->param,crssng->crossing->param);
	    else NXWriteTypes((NXTypedStream *) fp, "iiiidd",
			      &lnk->id, &pnt->point_id,
			      &crssng->partner->link_id,&crssng->partner->point_id,
			      &crssng->param,&crssng->crossing->param);
	  }
	  crssng = crssng->next;
	}
        pnt = pnt->next;
      }
     lnk = lnk->next;
   }
  s = "End";
  if (dest == DEST_FILE)
    {
      fprintf((FILE *) fp," %s \n\n", s);
      fclose((FILE *) fp);
    }
//  else NXWriteType((NXTypedStream *) fp, "*", &s);
  return(0);
}

int LinkPrintLink(LinkStatus *gnrc, void *fp, int dest, LinkList *lnk)
/* Prints out coordinate data for strand */
/* Routine does not print initial Strand id, but does print End */
{
  LinkPointList *pnt;
  char *pointType;
  char *s, *t;
  s = "closed"; t = "data";
  /* Just to make sure, we're going to count the points on the strand */
  lnk->num = 0;
  pnt = lnk->point.next;
  while (pnt != NULL)
    {
      pnt = pnt->next;
      lnk->num++;
    }
  if (dest == DEST_FILE)
    {
      fprintf((FILE *) fp,"  %s %d\n",s, lnk->closed);
      fprintf((FILE *) fp,"  %s %d\n",t, lnk->num);
    }
  else
    {
      NXWriteType((NXTypedStream *) fp, "*", &s);
      NXWriteType((NXTypedStream *) fp, "i", &lnk->closed);
      NXWriteType((NXTypedStream *) fp, "*", &t);
      NXWriteType((NXTypedStream *) fp, "i", &lnk->num);
    }
  pnt = lnk->point.next;
  while(pnt != NULL) {
    if (pnt->isAnchor)
      {
	if (pnt->isVertex)
	  pointType = "anchorvertex";
	else pointType = "anchor";
      }
    else pointType = "vertex";
    if (dest == DEST_FILE)
      fprintf((FILE *) fp,"    %.4lf  %.4lf  %.4lf\t%s\n",pnt->x,pnt->y,pnt->z, pointType);
    else
      {
	NXWriteTypes((NXTypedStream *) fp, "ddd",
		      &pnt->x, &pnt->y, &pnt->z);
	NXWriteType((NXTypedStream *) fp, "*", &pointType);
      }
    pnt = pnt->next;
  }
  s = "End";
  if (dest == DEST_FILE)
    fprintf((FILE *) fp," %s\n\n", s);
  else NXWriteType((NXTypedStream *) fp, "*", &s);
  return(0);
}

int LinkGetFile(LinkStatus *status,const char *name, NXZone *zone)

/* returns 0 if successful */
{
  FILE *fp;
  LinkStatus *gnrc;

  gnrc = (LinkStatus *) status;

  if((fp = fopen(name,"r")) == NULL) {
     return(-1);
    }

  return LinkGetFile2(status, (void *) fp, DEST_FILE, zone);
}

int LinkGetFile2(LinkStatus *status, void *fp, int dest, NXZone *zone)
{
  char silly_strng[40];
  int ch,num_links,num_crssng;
  LinkList link,*lnk,*tmp_lnk;
  LinkPointList *pnt;
  int i;
  char *strng = (char *) silly_strng;
  LinkStatus *gnrc;
  gnrc = status;
  lnk = &link;
  lnk->next = NULL;
  num_links = 0;
  while(1) {
    if (dest == DEST_FILE)
      fscanf((FILE *) fp,"%s",strng);
    else NXReadType((NXTypedStream *) fp, "*", &strng);
    if(strcmp(strng,"Strand") == 0) {
      lnk->next = (LinkList *) NXZoneMalloc(zone, sizeof(LinkList));
      lnk = lnk->next; lnk->next = NULL;
      lnk->point.next = NULL;
      lnk->point.previous = NULL;
      lnk->num = 0; lnk->visible = LINK_YES;
      if (dest == DEST_FILE)
	fscanf((FILE *) fp,"%d",&(lnk->id));
      else NXReadType((NXTypedStream *) fp, "i", &(lnk->id));
      num_links++;
      if(LinkReadStrand(gnrc,fp,dest,lnk,zone) != 0) {
	if (dest == DEST_FILE)
	  fclose((FILE *) fp);
	return(-1);
      }
      continue;
    }
    if(strng[0] == '#') {	/* Read till end of line */
      ch = 'a';
      while(ch != '\n' && ch != '\r') {
	ch = fgetc(fp);
	if(ch == EOF) {
	  fclose((FILE *) fp);
	  return(-1);
	}
      }
    }
    if(strcmp(strng,"Crossings") == 0) {
      int up_lnk_id, dwn_lnk_id, up_pnt_id, dwn_pnt_id;
      LinkList *up_lnk,*dwn_lnk,**lnk_tbl;
      LinkPointList point,*up_pnt,*dwn_pnt;
      LinkCrossingList crossing;
      double up_param,dwn_param;
      double x0,y0,x1,y1;

      if (dest == DEST_FILE)
	fscanf(fp,"%d",&num_crssng);
      else NXReadType((NXTypedStream *) fp, "i", &num_crssng);

      /* make table of lnk pointers by id */
      if(num_links < 1) continue;
      lnk_tbl = (LinkList **) NXZoneMalloc(zone, num_links*sizeof(LinkList *));
      tmp_lnk = link.next;
      while(tmp_lnk != NULL) {
	lnk_tbl[tmp_lnk->id] = tmp_lnk; 
	tmp_lnk = tmp_lnk->next;
      }

      /* Read crossings */
      for(i=0;i<num_crssng;++i) {
	if (dest == DEST_FILE)
	  fscanf((FILE *) fp,"%d %d %d %d %lf %lf\n",&up_lnk_id,&up_pnt_id,
		 &dwn_lnk_id,&dwn_pnt_id,
		 &up_param,&dwn_param);
	else NXReadTypes((NXTypedStream *) fp, "iiiidd", &up_lnk_id,&up_pnt_id,
			 &dwn_lnk_id,&dwn_pnt_id,
			 &up_param,&dwn_param);
	up_lnk = lnk_tbl[up_lnk_id];
	dwn_lnk = lnk_tbl[dwn_lnk_id];

        /* find points corresponding to point id's */
        /* NOTE: point id's assumed correct from ReadStrand */
	up_pnt = up_lnk->point.next;
	while(up_pnt != NULL && up_pnt->point_id !=  up_pnt_id)
	  up_pnt = up_pnt->next;
	dwn_pnt = dwn_lnk->point.next;
	while(dwn_pnt != NULL && dwn_pnt->point_id !=  dwn_pnt_id)
	  dwn_pnt = dwn_pnt->next;

	if(up_pnt == NULL || dwn_pnt == NULL) {
	  if (dest == DEST_FILE)
	    fclose((FILE *) fp);
	  return(-1);
	}

	x0 = up_pnt->x; y0 = up_pnt->y;
	if(up_pnt->next != NULL) 
	  { x1 = up_pnt->next->x; y1 = up_pnt->next->y;}
	else if(up_lnk->closed) 
	  {x1=up_lnk->point.next->x; y1=up_lnk->point.next->y;}
	point.x = crossing.x = (1.0-up_param)*x0 + up_param*x1;
	point.y = crossing.y = (1.0-up_param)*y0 + up_param*y1;
	LinkComputePointDeviceCoords(gnrc,&point);
	crossing.dcx = point.dcx; crossing.dcy = point.dcy;

	LinkInsertCrossing(gnrc,up_lnk,up_pnt,dwn_lnk,dwn_pnt,
			   &crossing,up_param,dwn_param);
      }
      free(lnk_tbl);
      break;
    }
  }

  
  /* Compute crossings between new and old links */
  lnk = link.next;
  while(lnk != NULL) {
    pnt = lnk->point.next;
    while(pnt != NULL) {
      LinkComputeEdgeCrossings(gnrc,lnk,pnt);
      pnt = pnt->next;
    }
    lnk = lnk->next;
  }

  /* Find last link of existing links */
  /* Join the new links with the old */
  lnk = &(gnrc->link);
  while(lnk->next != NULL) lnk = lnk->next;
  lnk->next = link.next;

  return(0);
}

int LinkReadStrand(LinkStatus *gnrc, void *fp, int dest, LinkList *lnk, NXZone *zone)
/* Reads strand data into lnk. */
/* Reads AFTER initial strand id to End */
/* returns 0 if successful */
{
  char noAnchorIndicator = 0;
  int i,done,ch, numRead;
  char silly_strng[40];
  LinkPointList *pnt;
  double x,y,z;
  char silly_pointType[20];
  char c;
  char *strng = (char *) silly_strng;
  char *pointType = (char *) silly_pointType;
 
  done = 0;
  while(!done) {
    if (dest == DEST_FILE) {
      if(fscanf((FILE *) fp,"%s",strng) != 1)
	return(-1); }
    else NXReadType((NXTypedStream *) fp, "*", &strng);
    if(strcmp(strng,"closed") == 0) {
      if (dest == DEST_FILE)
	fscanf((FILE *) fp,"%d",&(lnk->closed));
      else NXReadType((NXTypedStream *) fp, "i", &(lnk->closed));
      continue;
    }
    
    if(strcmp(strng,"data") == 0) {
      if (dest == DEST_FILE)
	fscanf((FILE *) fp,"%d",&(lnk->num));
      else NXReadType((NXTypedStream *) fp, "i", &(lnk->num));
      pnt = &(lnk->point);
      for(i=0;i<lnk->num;++i) {
	if (dest == DEST_FILE)
	  {
	    if((numRead = fscanf((FILE *) fp,"%lf %lf %lf",&x,&y,&z)) != 3)
	      return(-1);
	  }
	else NXReadTypes((NXTypedStream *) fp, "ddd", &x, &y, &z);
	pnt->next = (LinkPointList *) NXZoneMalloc(zone, sizeof(LinkPointList));
	if(pnt->next == NULL) {
	  return(-1);
	}
	pnt->next->previous = pnt;
	pnt = pnt->next;
	pnt->next = NULL;
	pnt->crossing.next = NULL;
	pnt->point_id = i;
	pnt->x = x; pnt->y = y; pnt->z = z;
	/* See if there's a string argument before the next \n */
	if (dest == DEST_FILE)
	  {
	    while ((c = (unsigned char) fgetc(fp)) == ' ' || c == '\t');
	    if (c == '\n')	/* If there's no string argument */
	      {
		noAnchorIndicator = 1;
		pnt->isAnchor = 0; /* Assume it's a non-anchor vertex */
		pnt->isVertex = 1;
	      }
	    else
	      {
		noAnchorIndicator = 0;
		ungetc((int) c, (FILE *) fp);
	      }
	  }
	if (!noAnchorIndicator)	/* Here we can read in string */
	  {
	    if (dest == DEST_FILE)
	      {
		if (fscanf((FILE *) fp, "%s", pointType) != 1)
		  return -1;
	      }
	    else NXReadType((NXTypedStream *) fp, "*", &pointType);

	    if (strcmp(pointType,"anchor") == 0)
	      {
		pnt->isAnchor = 1;
		pnt->isVertex = 0;
	      }
	    else if (strcmp(pointType,"vertex") == 0)
	      {
		pnt->isAnchor = 0;
		pnt->isVertex = 1;
	      }
	    else
	      {
		pnt->isAnchor = 1;
		pnt->isVertex = 1;
	      }
	  }
	LinkComputePointDeviceCoords(gnrc,pnt);
      }
      continue;
    }

    if(strng[0] == '#') {	/* Read till end of line */
      ch = 'a';
      while(ch != '\n' && ch != '\r') {
	ch = fgetc((FILE *) fp);
	if(ch == EOF) {
	  return(-1);
	}
      }
    }
    
    if(strcmp(strng,"End") ==  0) {
      done = 1;
      continue;
    }
  }
  return(0);
}


/* Takes a full pathname, and turns it into a window title */

char *makeTitle(const char *filename)
{
  char *title = (char *) malloc (strlen(filename) + 3);
  char *lastSlash = strrchr(filename, '/');
  strcpy (title, lastSlash + 1);
  strcat (title, " - ");
  strncat (title, filename, lastSlash - filename);
  return title;
}
