/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1992 by Metalworx
 *
 *	Name .....: control.c
 *	Created ..: Tue 21-Jan-92 21:42
 *	Revision .: 2
 *
 *	Date		Author		Comment
 *	=========	========	====================
 *	28-Jun-92       Mtwx            Anpassungen fuer die Einrichtung des 'Erzeugernamen-Gadgets'
 *	26-Apr-92	Mtwx		Anpassungen an diverse Mathe-Modelle
 *	21-Jan-92	Mtwx		Created this file!
 *
 * $Revision Header ********************************************************/
 #define REVISION 2


/***************************************************************************
* control.c : enthält alle zur Berechnung der Moleküle notwendigen	   *
*	      Routinen							   *
***************************************************************************/

/* ------------------------------- includes ----------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <intuition/intuitionbase.h>
#include <graphics/view.h>
#include <graphics/gfxbase.h>
#include <libraries/dosextens.h>
#include "mathlibs.h"
#include <libraries/arpbase.h>
#include <libraries/reqbase.h>
#include <clib/req_protos.h>
#include <req.h>
#include <exec/memory.h>
#include <time.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>

#include <mtwx_defs.h>

#include "messages.h"
#include "chem_defs.h"

/* ------------------------------- defines ------------------------------ */

/* ------------------------------- global references -------------------- */

BOOL	  reflexion = FALSE;
int	  ref;
int	  beanz;  /*Anzahl der Kalotten, die
					       das betrachtete Atom
					       berhren bzw. schneiden*/
int	  drehx = 0, drehy = 0, drehz = 0;
int	  atomanz;		       /*Anzahl der Atome    */
int	  geladene_Atome;	       /* Anzahl der Atome im Datenfile */
UBYTE	 *atomliste[105];
USHORT	  code;
USHORT	  GadgetID;
double	  mattheit, matthm1, abstand, zf;
struct Atom *GAtom[106];
VEKTOR	  l, b; 		       /*Lichtquelle, Beobachter*/
ATOMTYP   *a[MAXATOM],*alt[MAXATOM];   /* Atome */
T_MB	  mb;			       /*moegliche Beruehrungen*/
char CreaNameBuf[21];

struct IntuiMessage *message;
struct Gadget *GadgetPtr;

/* ------------------------------- external references ------------------ */

extern int HEIGHT,koordmx,koordmy,aktfarb;
extern long	 conwin;
extern UWORD Def_Pal[];
extern double aspect,vergr;
extern char	 Dateiname[];
extern struct ChemPrefs ChemPrefs;
extern struct ReqLib *ReqBase;
extern struct NewScreen FirstNewScreen;
extern struct NewWindow HauptFenster;
extern struct Menu Menu1;
extern struct Screen *FirstScreen;
extern struct Window *Window1;
extern struct Process *MyProcess;
extern APTR OldErrorWin;	  /* Windowstruktur vor dem Starten
				     des Tasks */

/* ------------------------------- prototypes --------------------------- */

/* Prototypes for functions defined in chemest.c */

#ifndef __NOPROTO
#ifndef __PROTO
#define __PROTO(a) a
#endif
#else
#ifndef __PROTO
#define __PROTO(a) ()
#endif
#endif

void Load_Datafile __PROTO((void));
void Farben __PROTO((void));
void set_screenmode __PROTO((int height));
void bringeaufeinheitsvektor __PROTO((register VEKTOR *v));
double cowinkelzwischen __PROTO((VEKTOR *v_v1, VEKTOR *v_v2));
void plot __PROTO((double x, double y, double hell1, double hell2));
void spiegle __PROTO((VEKTOR *v_v2, VEKTOR *v1));
void moeglicheberuehrungentesten __PROTO((int));
BOOL beruehrung __PROTO((double x, double y, double z));
int __regargs atomzeichnen __PROTO((ATOMTYP *v_m,int));
void atomskizze __PROTO((ATOMTYP *v_m));
void atomeordnen __PROTO((void));
void atomezentrieren __PROTO((void));
void atomedrehen __PROTO((char achse, double dx));
int farbnr __PROTO((ATOMTYP *v_atom));
void PrintName __PROTO((void));
void Werte_loeschen __PROTO((void));
int Nachricht __PROTO((struct Window *win));

/* ------------------------------- external references ------------------ */

extern void SimpleRequest __PROTO((char *,...));
extern short TwoGadRequest __PROTO((char *,...));

/* ------------------------------- routines ----------------------------- */

/*##########################################################################
# Menüfunktionen							   #
##########################################################################*/

void	  Load_Datafile()
{
  int	    i;
  char	   dummy[20];
  FILE *datei;
#ifdef GERMAN
  datei = fopen("chems_g.dat", "r");
#endif
#ifdef ENGLISH
  datei = fopen("chems_e.dat", "r");
#endif
  if (datei)
  {
    WrConWin(conwin, READING_DATA);
    fgets(dummy,19,datei); geladene_Atome=atoi(dummy);
    for (i = 1; i <= geladene_Atome; i++)
    {
      GAtom[i] = (struct Atom *) ArpAlloc(sizeof(struct Atom));
      if (GAtom[i] == NULL)
      {
	WrConWin(conwin, ERROR_NO_MEM);
	DisplayAlert(RECOVERY_ALERT,
#ifdef GERMAN
		     "\1\0\30 Kein Speicher fuer Daten-File\0c\1\0\50 Maustaste druecken.\0\0",
#endif
#ifdef ENGLISH
		     "\1\0\30 No memory for data file\0c\1\0\50 Click mouse button.\0\0",
#endif
		     70);
	Close_All(NOK);
      }
      fgets(GAtom[i]->Kurzz, 4, datei);
      GAtom[i]->Kurzz[strlen(GAtom[i]->Kurzz) - 1] = '\0';
      fgets(GAtom[i]->Name, 21, datei);
      atomliste[i - 1] = (UBYTE *) GAtom[i]->Name;
      GAtom[i]->Name[strlen(GAtom[i]->Name) - 1] = '\0';
      fgets(dummy,19,datei); GAtom[i]->Farbe=atoi(dummy);
    }
    GAtom[105] = (struct Atom *) ArpAlloc(sizeof(struct Atom));
    strcpy(GAtom[105]->Name, "??????????");
    fclose(datei);
    WrConWin(conwin, "ok\n");
  }
  else
  {
    WrConWin(conwin, ERROR_OPEN_DATA);
    Delay(150L);
    Close_All(NOK);
  }
}

void	  Farben()
{
  int	    i,rwert;
  struct ViewPort *cvp;

  cvp=&FirstScreen->ViewPort;
  rwert = palette_request(Window1, -1, -1, PALETTE_REQ, "DEFAULT", 4);
  if (rwert == 5)                      /* Default Farben! */
  {
    memcpy(ChemPrefs.Pal,Def_Pal,16*sizeof(UWORD));
    LoadRGB4(&FirstScreen->ViewPort, Def_Pal, 16);
  }
  else
  {
    for (i=0;i<16; i++)
      ChemPrefs.Pal[i]=(UWORD)GetRGB4(cvp->ColorMap, i);
  }
}

void set_screenmode(height) /* undokumentiert!!, ändert Screen in NTSC-
			       oder PAL-Screen, je nach 'height' */
int height;
{
  MyProcess->pr_WindowPtr=OldErrorWin;

  if (Window1)
    ClearMenuStrip(Window1);
  if (Window1)
    CloseWindowSafely(Window1, NULL);
  if (FirstScreen)
    CloseScreen(FirstScreen);
  HEIGHT=height;
  if((HEIGHT > 200) && (GfxBase->DisplayFlags & (UWORD)NTSC))
    HEIGHT=200;
  FirstNewScreen.Height = HEIGHT;
  if (!(FirstScreen = (struct Screen *)
	OpenScreen(&FirstNewScreen)))
  {
    WrConWin(conwin, NO_SCREEN);
    Delay(150L);
    Close_All(NOK);
  }
  LoadRGB4(&FirstScreen->ViewPort, ChemPrefs.Pal, 16);
  HauptFenster.Height = HEIGHT;
  HauptFenster.Screen = FirstScreen;
  if (!(Window1 = (struct Window *)
	OpenWindow(&HauptFenster)))
  {
    WrConWin(conwin, NO_MAIN_WIN);
    Delay(150L);
    Close_All(NOK);
  }
  SetMenuStrip(Window1, &Menu1);
  MyProcess = (struct Process *)FindTask((char *)0);
  OldErrorWin = MyProcess->pr_WindowPtr; /* alten Window-Pointer sichern */
  MyProcess->pr_WindowPtr=(APTR) Window1; /* Requests auf neuen Screen */
  koordmy = round(((double) HEIGHT - 26.0) / 2.0);
  if(HEIGHT<256)
    ChemPrefs.bv=2.0;
  else
    ChemPrefs.bv=1.5;
  aspect = ((double) HEIGHT - 26.0) / RESX * ChemPrefs.bv;
  ShowTitle(FirstScreen,ChemPrefs.Title);
}

/* --------- interne Programmroutinen -------------------------------*/

void	  bringeaufeinheitsvektor(v)
register VEKTOR   *v;

/* Der Vektor wird durch seine Laenge dividiert
     und erhaelt dadurch die Laenge 1		 */
{
  register double    r; 			/*Laenge*/

  r = sqrt((float)(v->x * v->x + v->y * v->y + v->z * v->z));
  if (r > 0.0)
  {
    v->x = v->x / r;
    v->y = v->y / r;
    v->z = v->z / r;
  }
}


double	  cowinkelzwischen(v_v1, v_v2)
VEKTOR	 *v_v1, *v_v2;

/* Cosinus des Winkels zwischen den Vektoren v1 und v2 */
{
  double    r_cowinkelzwischen;
  VEKTOR    v1, v2;

  v1 = *v_v1;
  v2 = *v_v2;
  bringeaufeinheitsvektor(&v1);
  bringeaufeinheitsvektor(&v2);
  r_cowinkelzwischen = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
  return r_cowinkelzwischen;
}


void	  plot(x, y, hell1, hell2)
double	  x, y, hell1, hell2;

/* Farbgebung nach Ordered-Dither-Verfahren
     in 17 Helligkeisstufen
  Uebergabe: 0 <= hell <= 16
     hell1: Farbe der Kalotte
     hell2: Spiegelung der Beleuchtung */
{
  typedef char T_FARBMATRIX[4][4];
  static T_FARBMATRIX farbmatrix =
  {
    {0, 8, 2, 10},
    {12, 4, 14, 6},
    {3, 11, 1, 9},
    {15, 7, 13, 5}};
  int	    kx, ky;

  kx = round(koordmx + x);
  ky = round(koordmy - y);
  if (hell2 > (double) farbmatrix[(kx & 3)][(ky & 3)])
    Pixel(Window1, ChemPrefs.Reflexionsfarbe, kx, ky)     /* Spiegelung */
      ;
  else if (hell1 > (double) farbmatrix[(kx & 3)][(ky & 3)])
    Pixel(Window1, aktfarb, kx, ky)    /* Farbe der Kalotte */
      ;
  else
    Pixel(Window1, ChemPrefs.Schattenfarbe, kx, ky);      /* Schatten */
}


void	  spiegle(v_v2, v1)
VEKTOR	 *v_v2, *v1;

/* v1 wird an der Ebene gespiegelt, die zu v2 normal steht */
{
  VEKTOR    v2;
  double    coa;		       /* 1/Cos des eingeschlossenen Winkels */

  v2 = *v_v2;
  bringeaufeinheitsvektor(v1);
  bringeaufeinheitsvektor(&v2);
  coa = -1.0 / (v1->x * v2.x + v1->y * v2.y + v1->z * v2.z);
  v1->x = v1->x * coa + 2.0 * v2.x;
  v1->y = v1->y * coa + 2.0 * v2.y;
  v1->z = v1->z * coa + 2.0 * v2.z;
}


void	  moeglicheberuehrungentesten(n)
/* laedt die Nummern aller Atome, die das betrachtete Atom
     schneiden oder berhren, ins Array mb */
int n;
{
  int	    i;
  double    dist;		       /* Abstand zwischen den Mittelpunkten des
					betrachteten und eines anderen Atomes	  */

  beanz = 0;
  if (n > 1)
  {
    for (i = 1; i <= n - 1; i++)
    {
      dist = (double) ((int) (a[n]->x - a[i]->x) * (int) (a[n]->x - a[i]->x))
	+ (double) ((int) (a[n]->y - a[i]->y) * (int) (a[n]->y - a[i]->y))
	+ (double) ((int) (a[n]->z - a[i]->z) * (int) (a[n]->z - a[i]->z));
      if (dist < 1e-5)
	dist = 1e-5;
      if (sqrt((float)dist) < (double) ((double) a[i]->r + a[n]->r))
      {
	beanz++;
	mb[beanz] = i;
      }
    }
  }
}


BOOL	 beruehrung(x, y, z)
double	  x, y, z;

/* testet, ob der Punkt (x,y,z), der auf der Oberflaeche
     der betrachteten Kalotte liegt, eventuell innerhalb einer
     anderen Kalotte liegt und deshalb unsichtbar bleiben muss:
     => Beruehrung := true    */
{
  BOOL	    r_beruehrung;
  int	    i;
  double    dist;		       /*Abstand der Mittelpunkte*/
  BOOL	    flag;		       /*wird gesetzt, wenn die
					       oben genannte Bedingung zutrifft */

  r_beruehrung = FALSE;
  if (beanz > 0)
  {
    i = 1;
    flag = FALSE;
    do
    {
      dist = (x - a[mb[i]]->x) * (x - a[mb[i]]->x)
	+ (y - a[mb[i]]->y) * (y - a[mb[i]]->y)
	+ (z - a[mb[i]]->z) * (z - a[mb[i]]->z);
      if (dist < 1e-5)
	dist = 1e-5;
      if (sqrt((float)dist) < (double) a[mb[i]]->r)
	flag = TRUE;
      i++;
    } while (!((i > beanz) || flag));
    if (flag)
      r_beruehrung = TRUE;
  }
  return r_beruehrung;
}


int	__regargs atomzeichnen(v_m,n)           /* zeichnet eine Kalotte */
ATOMTYP  *v_m;
int n;
{
  ATOMTYP   m;
  int	    xbild, zbild, zmbild, rbild, rzbild, mxbild, mzbild, lirand,
	    rerand;		       /* Anfang, Ende einer Pixelzeile */
  double    farbe1, farbe2, perspfaktor, vp, avp, r2double, rz2double,
	    radbild, r2bild, cowinkel; /* Cosinus		       */
  VEKTOR    o, vo, vl, vb, vs;	       /* vgl. Skizze!		 */

  m = *v_m;
  moeglicheberuehrungentesten(n);
  perspfaktor = abstand / (abstand + m.y);
  vp = vergr * perspfaktor;
  avp = aspect / vp;
  r2double = (double) ((int) (m.r) * (int) (m.r));
  radbild = m.r * vp;
  mxbild = round(m.x / avp);           /*Koordinaten des Mittelpunktes*/
  mzbild = round(m.z * vp);            /*der Kalotte in Pixels        */
  rbild = trunc(radbild);
  r2bild = radbild * radbild;
  for (zbild = rbild; zbild >= -rbild; zbild--)
  {
    vo.z = zbild / vp;
    o.z = vo.z + m.z;
    vl.z = l.z - o.z;
    rz2double = r2double - vo.z * vo.z;
    rzbild = trunc(sqrt((float)(r2bild - (double) (zbild * zbild))) / aspect);
    lirand = 20000;
    rerand = 20000;
    xbild = -rzbild;		       /* Feststellen der Raender des
					  sichtbaren Bereiches einer
					  Pixelzeile der Kalotte */
    do
    {
      vo.x = xbild * avp;
      vo.y = -sqrt((float)(rz2double - vo.x * vo.x));
      if (beruehrung(m.x + vo.x, m.y + vo.y, o.z))
	(xbild)++;
      else
	lirand = xbild;
    } while (!((lirand == xbild) || (xbild > rzbild)));
    if (lirand <= rzbild)
    {
      xbild = rzbild;
      do
      {
	vo.x = xbild * avp;
	vo.y = -sqrt((float)(rz2double - vo.x * vo.x));
	if (beruehrung(m.x + vo.x, m.y + vo.y, o.z))
	  (xbild)--;
	else
	  rerand = xbild;
      } while (!((rerand == xbild) || (xbild < -rzbild)));
      zmbild = zbild + mzbild;	       /* Berechnung der Farbe aller sichtbaren Punkte
		der Pixelzeile			      */
      for (xbild = lirand; xbild <= rerand; xbild++)
      {
	farbe2 = 0.0;
	vo.x = xbild * avp;
	vo.y = -sqrt((float)rz2double - vo.x * vo.x);
	o.x = vo.x + m.x;
	o.y = vo.y + m.y;
	vl.x = l.x - o.x;
	vl.y = l.y - o.y;
	cowinkel = cowinkelzwischen(&vl, &vo);
	farbe1 = 16.0 * cowinkel;      /* Reflexion */
	if (reflexion)
	  if (cowinkel > 0.0)
	  {
	    vs = vl;
	    spiegle(&vo, &vs);         /*vS enthlt jetzt den gespiegelten Lichtvektor*/
	    vb.x = b.x - o.x;
	    vb.y = b.y - o.y;
	    vb.z = b.z - o.z;
	    cowinkel = cowinkelzwischen(&vb, &vs);
	    cowinkel = cowinkel / mattheit - matthm1;
	    farbe2 = 16.0 * cowinkel;
	  }
	plot((double) xbild + mxbild, (double) zmbild, farbe1, farbe2);
	if (message = (struct IntuiMessage *) GetMsg(Window1->UserPort))
	{
	  int	    nklasse;

	  nklasse = message->Class;
	  code = message->Code;
	  GadgetPtr = (struct Gadget *) message->IAddress;
	  GadgetID = GadgetPtr->GadgetID;
	  ReplyMsg((struct Message*)message);
	  while (message = (struct IntuiMessage *) GetMsg(Window1->UserPort))
	    ReplyMsg((struct Message*)message);
	  return nklasse;
	}
      }
    }
  }
  return 0;
}


void	  atomskizze(v_m)
ATOMTYP  *v_m;

/* zeichnet den Umriss einer Kalotte */
{
  ATOMTYP   m;
  double    vp, radbild;	       /*Radius der Kalotte am Bild in Pixels	*/

  m = *v_m;
  vp = vergr * abstand / (abstand + m.y);
  radbild = vp * (double) m.r;
  Ellipse(Window1, aktfarb, koordmx + round(vp * m.x / aspect), koordmy - round(vp * m.z),
    (int) (round(radbild / aspect) - 2.0), (int) (round(radbild) - 2.0));
}


void	  atomeordnen()
/* ordnet die Atome von hinten nach vorne */
{
  int	    i;
  ATOMTYP   *zwi;
  BOOL	    flag;

  do
  {
    flag = FALSE;
    for (i = 1; i <= atomanz - 1; i++)
    {
      if ((a[i]->y < a[i + 1]->y))
      {
	zwi = a[i];
	a[i] = a[i + 1];
	a[i + 1] = zwi;
	flag = TRUE;
      }
    }
  } while (flag != FALSE);
}


void	  atomezentrieren()
/* zentriert die Atome im Koordinatensystem */
{
  int	    i, dx, dy, dz, minx, maxx, miny, maxy, minz, maxz;

  maxx = -30000;
  maxy = maxx;
  maxz = maxx;
  minx = 30000;
  miny = minx;
  minz = minx;
  for (i = 1; i <= atomanz; i++)
  {
    if (a[i]->x < minx)
      minx = a[i]->x;
    if (a[i]->x > maxx)
      maxx = a[i]->x;
    if (a[i]->y < miny)
      miny = a[i]->y;
    if (a[i]->y > maxy)
      maxy = a[i]->y;
    if (a[i]->z < minz)
      minz = a[i]->z;
    if (a[i]->z > maxz)
      maxz = a[i]->z;
  }
  dx = (int) round(((double) maxx + minx) / 2.0);
  dy = (int) round(((double) maxy + miny) / 2.0);
  dz = (int) round(((double) maxz + minz) / 2.0);
  for (i = 1; i <= atomanz; i++)
  {
    a[i]->x-=dx;
    a[i]->y-=dy;
    a[i]->z-=dz;
  }
}


void	  atomedrehen(achse, dx)
char	  achse;
double	  dx;

/* dreht alle Atome um den Winkel dx
     um die Koordinatenachse "Achse"    */
{
  int	    xalt, yalt, zalt, i;
  double    sx, cx;

  dx = dx / 180.0 * PI; 	       /*Umrechnung in rad*/
  sx = sin((float)dx);
  cx = cos((float)dx);
  for (i = 1; i <= atomanz; i++)
  {
    xalt = a[i]->x;
    yalt = a[i]->y;
    zalt = a[i]->z;
    switch (achse)
    {
      case 'x':
	a[i]->y = round(yalt * cx + zalt * sx);
	a[i]->z = round(-yalt * sx + zalt * cx);
	break;
      case 'y':
	a[i]->x = round(xalt * cx - zalt * sx);
	a[i]->z = round(xalt * sx + zalt * cx);
	break;
      case 'z':
	a[i]->x = round(xalt * cx + yalt * sx);
	a[i]->y = round(-xalt * sx + yalt * cx);
	break;
    }

  }
}

int	  farbnr(v_atom)
ATOMTYP  *v_atom;

 /* Zuteilung der Farbe einer Kalotte */
{
  register int i;
  int	    f;
  ATOMTYP   atom;

  f = 0;
  atom = *v_atom;
  for (i = 1; i <= geladene_Atome; i++)
  {
    if (!stricmp(atom.name, GAtom[i]->Kurzz))
    {
      f = GAtom[i]->Farbe;
      i = geladene_Atome;
    }
  }
  if (f == 0)
  {
    SimpleRequest("%s %s", NO_ATOM, atom.name);
    return (7);                        /* als default nehmen */
  }
  else
    return (f);
}

void	  PrintName()
{
  int	    i;
  char	    dummy[41];

  /* erzeugt von... */
  strcpy(dummy,CREANAMETEXT);
  strcat(dummy," ");
  strcat(dummy,CreaNameBuf);
  Shadow(Window1, dummy, 1, 13, 20, HEIGHT - 6);

  /* Molekülname */
  i = strpos(Dateiname, ".");
  if (i > 0)
  {
    strncpy(dummy, Dateiname, i);
    dummy[i] = '\0';
  }
  else
    strcpy(dummy, Dateiname);
  Shadow(Window1, dummy, 1, 13, -2, HEIGHT - 6);
}

void	  Werte_loeschen()
{
  register int i;

  for (i = 1; i <= atomanz; i++)
  {
    setmem(alt[i],sizeof(ATOMTYP),NULL);
    setmem(a[i],sizeof(ATOMTYP),NULL);
  }
  zf = 1.0;
  drehx = 0;
  drehy = 0;
  drehz = 0;
  l.x = 20000.0;
  l.y = -100000.0;
  l.z = 20000.0;
  ref = 0;
  strcpy(CreaNameBuf,"");
}

/*--------------------------- Unterroutinen ------------------------------*/

int Nachricht(win)
struct Window *win;
{
  int	    nklasse;

  FOREVER
  {
    if ((message = (struct IntuiMessage *) GetMsg(win->UserPort)) == NULL)
    {
      Wait(1L << win->UserPort->mp_SigBit);
      continue;
    }
    nklasse = message->Class;
    code = message->Code;
    GadgetPtr = (struct Gadget *) message->IAddress;
    GadgetID = GadgetPtr->GadgetID;
    ReplyMsg((struct Message *)message);
    while (message = (struct IntuiMessage *) GetMsg(win->UserPort))
      ReplyMsg((struct Message *)message);
    break;
  }
  return (nklasse);
}

