/* this is an example of a synchronous tool.
   use it as a template for your tools that require no interactive
   participation by the user.

   suggested uses:
   data base manipulators, like this mirror
   data loaders for other file formats
   data savers for other file formats

   NOTE that this source code does not handle splines or flares
   see scale.c for how to access these

*/

/* some general includes. We tried to get all you would normally need
   change to fit your tool if needed.
*/

#include "exec/types.h"

/* these two statements allow more efficient allocmems, see
   SC readme for 6.0
   DON'T call pragmas/exec_pragmas.h
*/

#define __USE_SYSBASE
#include <proto/exec.h>

#include "stdio.h"
#include "string.h"
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/libraries.h>
#include <intuition/intuition.h>
#include <graphics/text.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include "exec/io.h"
#include "exec/memory.h"
#include "dos.h"
#include "libraries/dosextens.h"
#include "libraries/dos.h"
#include "libraries/filehandler.h"
#include "devices/inputevent.h"
#include "graphics/gfxmacros.h"

#include <graphics/gfx.h>
#include <hardware/blit.h>
#include <graphics/copper.h>
#include <graphics/view.h>
#include <graphics/rastport.h>
#include <graphics/gels.h>
#include <graphics/regions.h>
#include <graphics/clip.h>
#include <exec/exec.h>
#include <graphics/gfxbase.h>
#include <m68881.h>

#include "math.h"
#include "float.h"
#include "limits.h"
#include "proto/dos.h"
#include "stdlib.h"
#include "workbench/workbench.h"
#include "workbench/startup.h"
#include "proto/icon.h"
#include "time.h"


/* for Aladdin 4D run time libraries */
#include "alad_pragmas.h"
#include "alad_protos.h"

#include "defines.h"
#include "externs.h"

int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
void chkabort __ARGS((void)) { return; }  /* really */
void Chk_Abort __ARGS((void)) { return; } /* really */

static void getdefaults(int);
void mirrorlines(void);

struct Library *AladBase;
struct IntuitionBase *IntuitionBase;
struct SignalSemaphore *polysema;

/* these are for setting high<->low boundaries for selected polys
   if you need them. They are filled out in sethighlow
*/
int low[3],high[3],center[3];

int main(int,char **);
int getmirrordef(void);
static void sethighlow(void);

int main(argc,argv)
int argc;
char **argv;
{
 int t;

 /* open the libraries you need */
 IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",33);
 if(IntuitionBase == NULL) return(-1);
 AladBase = OpenLibrary("Aladdin4D:support/alad.library",0);
 if(AladBase == NULL) {
  CloseLibrary((struct Library *)IntuitionBase);
  return(-1);
 }
 /* find the semaphore */
 /* note that this is a synchronous tool. This means Aladdin
    has put itself to sleep when it runs
    so we really do not need the semaphore
    Aladdin has released it and will not wake up until we return
    we only find it to make sure aladdin is running
    although the function sethighlow shows how it
    is used, it really does not need to
 */
 Forbid();
 polysema = FindSemaphore("Aladdin4DSemaphore");
 Permit();
 /* if the semaphore doesn't exist, get out */
 if(!polysema) {
  CloseLibrary((struct Library *)IntuitionBase);
  CloseLibrary(AladBase);
  return(-1);
 }
 /* load the last values the user may have set */
 getdefaults(0);
 /* Part of what Aladdin has stored in the hardware key
    is the user request
    use readHSLdevice() to get it
    if it returns a 1, user clicked the tool with a LMB
    if it returns a 2, user clicked the tool with a RMB
 */
 t = readHSLdevice();
 if(t == 1) { /* LMB */
  /* if user used a left mouse button, just do the work */
  mirrorlines();
 } else if(t == 2) { /* RMB */
  /* if user used a right mouse button, open the defaults window
     and if he hits the perform gadget, do the work
  */
  if(getmirrordef()) mirrorlines();
  getdefaults(1);
 }
 /* finally cleanup and return */
 CloseLibrary((struct Library *)IntuitionBase);
 CloseLibrary(AladBase);
 return(0);
}

/* these are the variables the user can change */
long mirrorx = 1,mirrory,mirrorz,from,deforms;

/* this is the pointer to the current attach point */
int *atpoint;

static void getdefaults(int sav)
{
 /* establish a defaults file
    if tool run before, get them
    if first time, file won't exist, so return
 */
 LONG file;
 char *nam = "aladdin4ddef:{ALAD}_mir.def";
 double version;

 if(sav) {
  file=Open(nam,MODE_NEWFILE);
  if(file==0) { return; }
  version = 3.0; /* save the version number you want */
  Write(file,(BYTE *)&version,sizeof(version));
  Write(file,(BYTE *)&mirrorx,sizeof(mirrorx));
  Write(file,(BYTE *)&mirrory,sizeof(mirrory));
  Write(file,(BYTE *)&mirrorz,sizeof(mirrorz));
  Write(file,(BYTE *)&from,sizeof(from));
  Write(file,(BYTE *)&deforms,sizeof(deforms));
 } else {
  file=Open(nam,MODE_OLDFILE);
  if(file==0) { return; }
  Read(file,(BYTE *)&version,sizeof(version));
  if(version == 3.0) { /* allows you to change tool saves for future versions */
   Read(file,(BYTE *)&mirrorx,sizeof(mirrorx));
   Read(file,(BYTE *)&mirrory,sizeof(mirrory));
   Read(file,(BYTE *)&mirrorz,sizeof(mirrorz));
   Read(file,(BYTE *)&from,sizeof(from));
   Read(file,(BYTE *)&deforms,sizeof(deforms));
  }
 }
 Close(file);
}

/* this is the performance function.
   You would replace this with your own
*/
void mirrorlines()
{
 struct PolygonInfo *top,*po;
 struct PointInfo *pnt,*p;
 struct PolyBMpoint *pt;
 struct PathInfo *path;
 SHORT i;
 struct defpnt *dpt;

 /* the from variable is whether to use the center or atp */
 if(!from) {
  sethighlow();
  atpoint = center;
 } else {
  /* getatpoint returns a pointer to where the attach point is held */
  atpoint = getatpoint();
 }
 if(deforms) {
  top = firstpoly(); /* this is the first poly in the current space */
  while(top) {
   if(ISSELECTED(top)) { /* if the poly is selected */
    path = top->path; /* if the poly is assigned to a path */
    if(path && path->deform) { /* if path has deforms */
     deforms = askem("Mirror Deforms? (no undo)");
     top = NULL;
    } else {
     top = top->next;
    }
   } else {
    top = top->next;
   }
  }
 }
 top = firstpoly();  /* the first poly in the current space */
 while(top) {
  if(ISSELECTED(top)) { /* if the polygon is selected */
   pnt = top->firstpoint; /* the first point in the poly */
   while(pnt) {
    if(mirrorx) { pnt->x = atpoint[0] + (atpoint[0] - pnt->x); }
    if(mirrory) { pnt->y = atpoint[1] + (atpoint[1] - pnt->y); }
    if(mirrorz) { pnt->z = atpoint[2] + (atpoint[2] - pnt->z); }
    pnt = pnt->next;
   }
   /* if the poly is phong shaded or textured and has more than 4 points
      it uses a 4 point bounder, bmp.pnt
      which must be mirrored also
   */
   if((ISBMP(top)) && (top->points > 4)) { /* phong shaded or textured with >4 points */
    pt = top->bmp.pnt;
    for(i=0;i<4;i++) {
     if(mirrorx) pt->x[i] = atpoint[0] + (atpoint[0] - pt->x[i]);
     if(mirrory) pt->y[i] = atpoint[1] + (atpoint[1] - pt->y[i]);
     if(mirrorz) pt->z[i] = atpoint[2] + (atpoint[2] - pt->z[i]);
    }
   }
   path = top->path; /* not null if poly is assigned to a path */
   if(deforms && path && path->deform) { /* path->deform not null if path has deforms */
    p = top->firstpoint; /* first point in poly */
    while(p) {
     dpt = p->dpt; /* first deform location for point */
     while(dpt) {
      if(mirrorx) dpt->x = atpoint[0] + (atpoint[0] - dpt->x);
      if(mirrory) dpt->y = atpoint[1] + (atpoint[1] - dpt->y);
      if(mirrorz) dpt->z = atpoint[2] + (atpoint[2] - dpt->z);
      dpt = dpt->next;
     }
     p = p->next;
    }
   }
   if(ISMELD(top)) { /* for melds, only need to move the bounding polys */
    for(po = top->special; po; po = po->next) { /* the list of actual polys represented by the meld */
     if((po->points > 4)&&(ISBMP(po))) {
      pt = po->bmp.pnt;
      for(i=0;i<4;i++) {
       if(mirrorx) pt->x[i] = atpoint[0] + (atpoint[0] - pt->x[i]);
       if(mirrory) pt->y[i] = atpoint[1] + (atpoint[1] - pt->y[i]);
       if(mirrorz) pt->z[i] = atpoint[2] + (atpoint[2] - pt->z[i]);
      }
     }
    }
   }
  }
  top = top->next;
 }
}

static void sethighlow()
{
 struct PolygonInfo *top;
 struct PointInfo *pnt;

 /* the calling function should deselect paths or lights, etc.
    if they are not to participate. */
 low[0] = low[1] = low[2] = 1000000000;
 high[0] = high[1] = high[2] = -1000000000;
 ObtainSemaphore(polysema); /* grab the semaphore */
 top = firstpoly(); /* first poly in current space */
 while(top) {
  if(ISSELECTED(top)) { /* if it is selected */
   pnt = top->firstpoint; /* its first point */
   while(pnt) {
    if(pnt->x < low[0]) low[0] = pnt->x;
    if(pnt->x > high[0]) high[0] = pnt->x;
    if(pnt->y < low[1]) low[1] = pnt->y;
    if(pnt->y > high[1]) high[1] = pnt->y;
    if(pnt->z < low[2]) low[2] = pnt->z;
    if(pnt->z > high[2]) high[2] = pnt->z;
    pnt = pnt->next;
   }
  }
  top = top->next;
 }
 ReleaseSemaphore(polysema); /* let go of the semaphore */
 /* these are the center point of all selected polys */
 center[0] = (high[0]+low[0])>>1;
 center[1] = (high[1]+low[1])>>1;
 center[2] = (high[2]+low[2])>>1;
}
