/*
 * 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
 */

/********THIS FILE'S A-OTAY********/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#import <objc/objc.h>
#include "link_types.h"
#include "link_edit_types.h"
#include "link_edit_global.h"
#import "EditView.h"
#include "link.h"
#import "Link.h"
#import <appkit/Panel.h>

LinkList *LinkCreateNewLink(LinkStatus *status)

{
  LinkStatus *gnrc;
  LinkList *lnk;

  gnrc = (LinkStatus *) status;
  lnk = &(gnrc->link);
  while(lnk->next != NULL) lnk = lnk->next;
  lnk->next = (LinkList *) malloc(sizeof(LinkList));
  if((lnk = lnk->next) == NULL) {
     fprintf(stderr,"Warning:  unable to allocate new link.\n");
     return(NULL);
    }

  lnk->next = NULL;
  lnk->point.next = NULL;
  lnk->point.previous = NULL;
  lnk->visible = LINK_YES;
  lnk->closed = LINK_NO;
  lnk->num = 0;
  lnk->visited = 0;
  gnrc->current_link = lnk;
  gnrc->num++;
  return(lnk);
}

void LinkModeNone(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_NO_CHOICE;
}

void LinkModeAddPoint(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_ADD_POINT;
  LinkPrintMessage("Click to add point.");
}

void LinkModeAddAnchor(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_ADD_ANCHOR;
  LinkPrintMessage("Click to make anchor.");
}

void LinkModeDeletePoint(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_DELETE_POINT;
  LinkPrintMessage("Click to delete point.");
}

void LinkModeMovePoint(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_MOVE_POINT;
  LinkPrintMessage("Click and drag to move point.");
}

void LinkModeSelectLink(LinkStatus *status)
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_SELECT_LINK;
  LinkPrintMessage("Click to select strand.");
}

void LinkModeHideLink(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_HIDE_LINK;
  LinkPrintMessage( "Select strand to hide.");
}

void LinkModeTranslateLink(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_TRANSLATE_LINK;
  LinkPrintMessage("Click and drag to translate strand.");
}

void LinkModeRotateLink(LinkStatus *status)

 
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_ROTATE_LINK;
  LinkPrintMessage("Sorry, rotating is not yet implemented.");
}

void LinkModeScaleLink(LinkStatus *status)

 
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_SCALE_LINK;
  LinkPrintMessage("Sorry, scaling is not yet implemented.");
}

void LinkModeShiftView(LinkStatus *status)

 
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_SHIFT_VIEW;
  LinkPrintMessage("Click and drag to shift view.");
}

void LinkModeFlipCrossing(LinkStatus *status)

 
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_FLIP_CROSSING;
  LinkPrintMessage("Click to flip crossing.");
}

void LinkModeZoomView(LinkStatus *status)

 
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_ZOOM_VIEW;
  LinkPrintMessage("Click and drag to zoom view.");
}

void LinkModeJoinLinks(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_JOIN_SELECT_HEAD;
  LinkPrintMessage("Select end of first strand.");
}

void LinkModeReverseArrows(LinkStatus *status)

  
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_REVERSE_ARROWS;
  LinkPrintMessage("Select strand to reverse.");
}

void LinkModeCloseStrand(LinkStatus *status)
  
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_CLOSE_LINK;
  LinkPrintMessage("Select strand to close.");
}

void LinkModeOpenStrand(LinkStatus *status)
  
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_OPEN_LINK;
  LinkPrintMessage("Select segment to cut.");
}

void LinkModeDeleteStrand(LinkStatus *status)
  
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->choice[0] = LINK_DELETE_LINK;
  LinkPrintMessage("Select strand to delete.");
}


void LinkShowXRuler(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->xruler = LINK_SHOW;
}


void LinkShowYRuler(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->yruler = LINK_SHOW;
}


void LinkHideXRuler(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->xruler = LINK_HIDE;
}


void LinkHideYRuler(LinkStatus *status)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->yruler = LINK_HIDE;
}

void LinkPublicReDraw(LinkStatus *status)
 
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  [globalEditView clearScreen];
  [globalEditView ReDrawLinkTopWindow:gnrc];
}

void LinkPublicClear(LinkStatus *status)
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  LinkClearData(gnrc);
  [globalEditView clearScreen];
  [globalEditView ReDrawLinkTopWindow:gnrc];
}

void LinkOpenCurrentLink(LinkStatus *status)
{
  LinkStatus *gnrc;
  LinkList *lnk;
  LinkPointList *pnt;

  gnrc = (LinkStatus *) status;
  lnk = gnrc->current_link;
  if(lnk == NULL) {
     LinkPrintMessage("No currently selected link.");
     return;
    }
  if(!lnk->closed) {
     LinkPrintMessage("Link is already open.");
     return;
    }
  lnk->closed = 0;

  /* find last point */
  pnt = &(lnk->point);
  if(pnt->next == NULL) {
     LinkPrintMessage("Link is empty!");
     return;
    }
  while(pnt->next != NULL) pnt = pnt->next;

  LinkFreeEdgeCrossings(gnrc,lnk,pnt);
/*  XClearWindow(dpy,gnrc->TopWindow);*/
  [globalEditView clearScreen];
  [globalEditView ReDrawLinkTopWindow :gnrc];

}

/* This counts the number of points in lnk and updates the lnk->num field */
void UpdateNumPoints(LinkList *lnk)
{
  LinkPointList *pnt = lnk->point.next;
  lnk->num = 0;
  while (pnt != NULL)
    {
      lnk->num++;
      pnt = pnt->next;
    }
}

#define FREEPNT 1
#define FREEPNTNEXT 2

void LinkCutCurrentLink(LinkStatus *status, LinkList *lnk, LinkPointList *pnt)
{
  LinkStatus *gnrc;
  int toFree = 0;
  gnrc = (LinkStatus *) status;
  if (gnrc == NULL || lnk == NULL || pnt == NULL)
    {
      LinkPrintMessage( "Pretty bad parameter passing!");
      return;
    }
  /* pnt = start of selected edge */
  LinkFreeEdgeCrossings(gnrc, lnk, pnt);     /* Get rid of edge crossings */
  /* cases: first or last edge of link; link open or closed */
  /* Case 1: link closed; still one strand left */
  if (lnk->closed)
    {
      LinkPointList *tmp_pnt;
      /* Traverse until end */
      for (tmp_pnt = lnk->point.next; tmp_pnt->next != NULL; tmp_pnt = tmp_pnt->next);
      tmp_pnt->next = lnk->point.next;       /* Connect old end to old beginning */
      tmp_pnt->next->previous = tmp_pnt;
      lnk->point.next = pnt->next;           /* pnt->next is now first point */
      pnt->next->previous = &lnk->point;
      pnt->next = NULL;                      /* And, we seal it off */
      lnk->closed = 0;
    }
  else
    {
      if (pnt == lnk->point.next)            /* If pnt is the first point... */
	{
	  lnk->point.next = pnt->next;       /* Update first point */
	  toFree |= FREEPNT;                 /* Get rid of initial point */
	  lnk->num--;                        /* Update lnk->num */
	}
      if (pnt->next->next == NULL)           /* If pnt->next is the last point... */
	{
	  toFree |= FREEPNTNEXT;             /* Get rid of final point */
	  lnk->num--;
	}
      if (toFree == 0)                       /* If one of the other cases not met...*/
	{
	  LinkList *newlnk = LinkCreateNewLink(gnrc);
	  newlnk->point.next = lnk->point.next;  /* First point of new = first of old */
	  lnk->point.next->previous = &newlnk->point;
	  lnk->point.next = pnt->next;           /* Update first point of old */
	  pnt->next->previous = &lnk->point;
	  pnt->next = NULL;                      /* End pointlist of new */
	  /* Update number of points field */
	  UpdateNumPoints(lnk);
	  UpdateNumPoints(newlnk);
	}
    }
  if (lnk->num == 0)
    {
      gnrc->current_link = lnk;
      LinkDeleteCurrentSelection((VOID *) gnrc);
      return;
    }

  if (toFree & FREEPNTNEXT)
    {
      free(pnt->next);
      pnt->next = NULL;
    }
  if (toFree & FREEPNT)
    free(pnt);
}

int LinkCloseCurrentLink(LinkStatus *status)
{
  LinkStatus *gnrc;
  LinkList *lnk;
  LinkPointList *pnt;

  gnrc = (LinkStatus *) status;
  lnk = gnrc->current_link;
  if(lnk == NULL) {
     LinkPrintMessage("No currently selected strand.");
     return 0;
    }
  if(lnk->closed) {
     LinkPrintMessage("Strand is already closed.");
     return 0;
    }
  if(lnk->num < 3) {
/*    [globalEditView display];
    [[currentLinkObj get_window] flushWindow];
    NXRunAlertPanel("Alert", "Too few points to close strand!", NULL, NULL, NULL); */
     LinkPrintMessage("Too few points to close strand!");
     return 0;
    }
  /* find last point */
  pnt = &(lnk->point);
  if(pnt->next == NULL) {
     LinkPrintMessage("Link is empty!");
     return 0;
    }
  lnk->closed = 1;
  while(pnt->next != NULL) pnt = pnt->next;
  LinkComputeEdgeCrossings(gnrc,lnk,pnt);
  [globalEditView clearScreen];
  [globalEditView ReDrawLinkTopWindow:gnrc];
  return 1;
}

void LinkPublicPrintMessage(LinkStatus *status,char *mssg)

{
  LinkStatus *gnrc;

  gnrc = (LinkStatus *) status;
  LinkPrintMessage(mssg);
}

int LinkPublicUrgentWindowDialogue(LinkStatus *status,char *prmpt,char *rtrn)

{
  LinkStatus *gnrc;
  int num;

  gnrc = (LinkStatus *) status;
/*  num = LinkUrgentWindowDialogue(gnrc,prmpt,rtrn);*/
  return(num);
}

void LinkShowAll(LinkStatus *status)

{
  LinkStatus *gnrc;
  LinkList *lnk;

  gnrc = (LinkStatus *) status;
  lnk = gnrc->link.next;
  while(lnk != NULL) {
     lnk->visible = LINK_SHOW;
     lnk = lnk->next;
    }
/*  XClearWindow(dpy,gnrc->TopWindow);*/
  [globalEditView clearScreen];
  [globalEditView ReDrawLinkTopWindow:gnrc];
}

void LinkDeleteCurrentSelection(LinkStatus *status)

{
  LinkStatus *gnrc;

  gnrc = (LinkStatus *) status;
  LinkDeleteLink(gnrc,gnrc->current_link);
}

void LinkPublicDeleteAll(LinkStatus *status)

{
  LinkStatus *gnrc;

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

void LinkSetAxes(LinkStatus *status,int set)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->axes = set;
}

void LinkSetArrows(LinkStatus *status,int set)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->arrows = set;
}

void LinkSetVertices(LinkStatus *status,int set)
{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->vertices = set;
}

void LinkSetRulers(LinkStatus *status,int set)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->xruler = set;
  gnrc->yruler = set;
}

void LinkSetAnchors(LinkStatus *status,int set)

{
  LinkStatus *gnrc;
  gnrc = (LinkStatus *) status;
  gnrc->anchors = set;
}


int LinkSaveThreeD(LinkStatus *status,char *name)

/* returns 0 if successful */
{
  char strng[160];
  FILE *fp;
  LinkList *lnk;
  LinkPointList *pnt;
  LinkCrossingList *crssng;
  int num_links,num_points;

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

  fp = fopen(name,"w");
  if(fp == NULL) {
    sprintf(strng,"Cannot open file %s\n",name);
    LinkPrintMessage(strng);
    return(-1);
   }
  fprintf(fp,"LINK\n");
  /* Count Number of links */
  num_links = 0;
  lnk = gnrc->link.next;
  while(lnk != NULL){
     if(lnk->point.next != NULL) { /* Print only nonempty links */
        num_links++;
       }
     lnk = lnk->next;
    }
   fprintf(fp,"%d\n",num_links);

  /* Count and print number of points for each strand */
  lnk = gnrc->link.next;
  while(lnk != NULL){
     if((pnt = lnk->point.next) != NULL) {  /* Non-empty links only */
        num_points=0;
        while(pnt != NULL) {
           num_points++;
           crssng = pnt->crossing.next;
           while(crssng != NULL) {
              num_points++;
              crssng = crssng->next;
             }
           pnt = pnt->next;
          }
        fprintf(fp,"%d\n",num_points);
       }
     lnk = lnk->next;
    }

  /* Print out strand data */
  lnk = gnrc->link.next;
  while(lnk != NULL){
     if(lnk->point.next != NULL) { /* Only non-empty strands */
        LinkPrintStrand3D(gnrc,fp,lnk);
        fprintf(fp,"\n");
       }
     lnk = lnk->next;
    }
  fclose(fp);
  return(0);
}

void LinkCenterView(LinkStatus *status)
 
{
  LinkStatus *gnrc;
  LinkList *lnk;
  LinkPointList *pnt;
  float dcxmin,dcxmax,dcymin,dcymax;
  float dx,dy;
  double xfactor,yfactor;
  int areAnyVisible = 0;

  gnrc = (LinkStatus *) status;

  /* Be sure there are visible links */
  lnk = gnrc->link.next;
  while (lnk != NULL)
    {
      if (lnk->visible) areAnyVisible = 1;
      lnk = lnk->next;
    }
  if (!areAnyVisible) return;

  /* Compute dc bounding box */
  lnk = gnrc->link.next;
  /* find first lnk with non empty point list */
  while(lnk != NULL && lnk->point.next == NULL) lnk = lnk->next;
  if(lnk == NULL) return;

  pnt = lnk->point.next;
/*  dcxmin = pnt->dcx - 1;  dcxmax = pnt->dcx + 1;
  dcymin = pnt->dcy - 1;  dcymax = pnt->dcy + 1;*/
  dcxmin = pnt->dcx;  dcxmax = pnt->dcx;
  dcymin = pnt->dcy;  dcymax = pnt->dcy;

  
  while(lnk != NULL) {
     if(!lnk->visible) { lnk = lnk->next; continue;}
     pnt = lnk->point.next;
     while(pnt != NULL) {
         if(pnt->dcx < dcxmin) dcxmin = pnt->dcx;
         else if(pnt->dcx > dcxmax) dcxmax = pnt->dcx;
         if(pnt->dcy < dcymin) dcymin = pnt->dcy;
         else if(pnt->dcy > dcymax) dcymax = pnt->dcy;
         pnt = pnt->next;
        }
     lnk = lnk->next;
    }

  if (dcxmax == dcxmin || dcymax == dcymin) return;

  /* Put center of bounding box in center of screen */

  dx = gnrc->width/2  - (dcxmax + dcxmin)/2;
  dy = gnrc->height/2 - (dcymax + dcymin)/2;
  gnrc->origin.dcx += dx; gnrc->origin.dcy += dy;

  /* Adjust scale so bbox takes up at most 1/2 screen in x or y direction */
  /* Do not change aspect ratio */

  xfactor = 0.5 * (double) (gnrc->width) / (double) (dcxmax-dcxmin);
  yfactor = 0.5 * (double) (gnrc->height) / (double) (dcymax-dcymin);
  if(xfactor < yfactor) {
     gnrc->xscale *= xfactor; gnrc->yscale *= xfactor;
    }
  else {
     gnrc->xscale *= yfactor; gnrc->yscale *= yfactor;
    }
  LinkComputeDeviceCoords(gnrc);
}


