/***************************************************************************
  chemest.c : Hauptprogramm von Chemesthetics
***************************************************************************/

#include <stdio.h>
#include <math.h>
#include <libraries/mathffp.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <libraries/dosextens.h>
#include <libraries/arpbase.h>
#include <libraries/reqbase.h>
#include <clib/req_protos.h>
#include <req.h>  /* enthält #pragmas für req.library */
#include <exec/memory.h>
#include <time.h>
#include <proto/mathffp.h>
#include "messages.h"
#include "chem_defs.h"
#include "chemest.h"
#define OK 0
#define NOK 1
#define RESX 640.0		       /* Breite des Bildes */
double	  VERGRFAKTOR = 0.3;	       /* Umrechnungsfaktor pm -> Pixels   */

int	  HEIGHT = 256; 	       /* Default, wird bei NTSC-Screen geaendert */
BOOL	  reflexion = FALSE, reqlib_used=FALSE;
int	  koordmx, koordmy, n, ref;
int	  reflexionsfarbe, schattenfarbe, aktfarb, beanz;	/*Anzahl der Kalotten, die
					       das betrachtete Atom
					       berhren bzw. schneiden*/

double	  mattheit, matthm1, abstand, aspect, vergr, zf;
int	  drehx = 0, drehy = 0, drehz = 0;

VEKTOR	  l, b; 		       /*Lichtquelle, Beobachter*/

int	  atomanz;		       /*Anzahl der Atome    */
int	  geladene_Atome;	       /* Anzahl der Atome im Datenfile */

T_A	  a, alt;		       /*Atome		    */

T_MB	  mb;			       /*moegliche Beruehrungen*/

long	  conwin;
char	  dn[DSIZE+FCHARS+2];
char	  Dateiname[FCHARS+1], IFFDateiname[FCHARS+1];
char	  Pfadname[DSIZE+1],Titel[80];
UBYTE	 *atomliste[105];
USHORT	  code;
USHORT	  GadgetID;

struct Atom *GAtom[106];
struct ChemPrefs ChemPrefs= { 1.5,TRUE,TRUE,FALSE };
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct ArpBase *ArpBase;
struct ReqLib *ReqBase;
struct Screen *FirstScreen;
struct Window *Window1, *Window2;
struct IntuiMessage *message;
struct Gadget *GadgetPtr;
struct Remember *RememberPtr = NULL;
struct Process *MyProcess;
struct ReqFileRequester FileStr; /* kommt aus reqbase.h */
FILE *datei;
APTR OldErrorWin;	      /* Windowstruktur vor dem Starten des Tasks */

extern char ProgId[], Version[];

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

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

int dateieinlesen __PROTO((void));
void dateispeichern __PROTO((void));
void Bild_speichern __PROTO((void));
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((void));
BOOL beruehrung __PROTO((double x, double y, double z));
int __regargs atomzeichnen __PROTO((ATOMTYP *v_m));
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));
void Menu_Auswertung __PROTO((USHORT Menu_Nr));
void main __PROTO((void));
int Nachricht __PROTO((struct Window *win));
void Open_All __PROTO((void));
void Close_All __PROTO((BOOL status));

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

extern void Prepare(),PrepareLogo(),FreeLogo(),PrepareEGadgets(),
	    FreeEGadgets(),floh(),StatRequest(),license(),SimpleRequest(),
	    SysInfo();
extern long save();
extern short TwoGadRequest();
extern UBYTE *AllocRemeber();
extern double bildverh();
extern struct Message *GetMsg();
extern struct Task *FindTask();
extern struct Window *OpenWindow();

/* --------- Menuefunktionen ----------------------------------------*/

int	  dateieinlesen()
 /* versucht, die Molekuel-Datei dn einzulesen */
{
  char	    dummy[20];
  int	    i;
  FILE	   *datei;

  strcpy(Titel,LOAD_MOL);
  if(FileRequester(&FileStr))
  {
    datei = fopen(dn, "r");
    if (datei)
    {
      SetPointer(Window1, wptr->Pointer, wptr->Height,
		 wptr->Width, wptr->XOffset, wptr->YOffset);
      Werte_loeschen();                /* alte Werte loeschen */
      fgets(dummy,4,datei);
      atomanz=atoi(dummy);
      if(atomanz==0)
      {
	SimpleRequest(WRONG_DATA);
	return 0;
      }
      for (i = 1; i <= atomanz; i++)
      {
	fgets(a[i].name, 4, datei);
	a[i].name[strlen(a[i].name)-1]='\0';  /* \n loeschen */
	fgets(dummy,7,datei); a[i].x=atoi(dummy);
	fgets(dummy,7,datei); a[i].y=atoi(dummy);
	fgets(dummy,7,datei); a[i].z=atoi(dummy);
	fgets(dummy,7,datei); a[i].r=atoi(dummy);
	alt[i] = a[i];		       /* Originale f. Editieren sichern */
      }
      fgets(dummy,19,datei); zf=atof(dummy);
      fgets(dummy,19,datei); drehx=atoi(dummy);
      fgets(dummy,19,datei); drehy=atoi(dummy);
      fgets(dummy,19,datei); drehz=atoi(dummy);
      fgets(dummy,19,datei); l.x=atof(dummy);
      fgets(dummy,19,datei); l.y=atof(dummy);
      fgets(dummy,19,datei); l.z=atof(dummy);
      fgets(dummy,19,datei); ref=atoi(dummy);
      fclose(datei);
      ClearPointer(Window1);
      if (zf == 0.0 || zf > 100)
      {
	SimpleRequest(WRONG_DATA);
	return 0;
      }
      else
	return 1;
    }
    else
      return 0;
  }
  else
    return 0;
  reqlib_used=TRUE; /* wirklich benutzt! */
}

void	  dateispeichern()             /* speichert die Molekuel-Datei */
{
  int	    i;

  strcpy(Titel,SAVE_MOL);
  if(FileRequester(&FileStr))
  {
    datei = fopen(dn, "w");
    if (datei)
    {
      SetPointer(Window1, wptr->Pointer, wptr->Height,
		 wptr->Width, wptr->XOffset, wptr->YOffset);
      fprintf(datei, "%d\n", atomanz);
      for (i = 1; i <= atomanz; i++)
      {
	fprintf(datei, "%s\n", alt[i].name);
	fprintf(datei, "%d\n", alt[i].x);
	fprintf(datei, "%d\n", alt[i].y);
	fprintf(datei, "%d\n", alt[i].z);
	fprintf(datei, "%d\n", alt[i].r);
      }
      fprintf(datei, "%lf\n", zf);
      fprintf(datei, "%d\n", drehx);
      fprintf(datei, "%d\n", drehy);
      fprintf(datei, "%d\n", drehz);
      fprintf(datei, "%lf\n", l.x);
      fprintf(datei, "%lf\n", l.y);
      fprintf(datei, "%lf\n", l.z);
      fprintf(datei, "%d\n", ref);
      fclose(datei);
      ClearPointer(Window1);
    }
  }
  reqlib_used=TRUE; /* wirklich benutzt! */
}

void	  Bild_speichern()
{
  int	    i;

  i = strpos(Dateiname, ".cdm");
  if (i > 0)
  {
    strncpy(IFFDateiname, Dateiname, i);
    IFFDateiname[i] = '\0';
    strcat(IFFDateiname, ".iff");
  }
  strcpy(Titel,SAVE_IFF);
  strcpy(Dateiname,IFFDateiname);
  if(FileRequester(&FileStr))
  {
    SetPointer(Window1, wptr->Pointer, wptr->Height,
	       wptr->Width, wptr->XOffset, wptr->YOffset);
    if(!(save(dn,FirstScreen,Window1->Width,HEIGHT)))
    {
      ClearPointer(Window1);
      SimpleRequest("%s", IFF_ERROR);
    }
    ClearPointer(Window1);
  }
  reqlib_used=TRUE;
}

void	  Load_Datafile()
{
  int	    i;
  char	   dummy[20];

#ifdef GERMAN
  datei = fopen("chems_g.dat", "r");
#endif
#ifdef ENGLISH
  datei = fopen("chems_e.dat", "r");
#endif
  if (datei)
  {
    SetPointer(Window1, wptr->Pointer, wptr->Height,
	       wptr->Width, wptr->XOffset, wptr->YOffset);
    WrConWin(conwin, READING_DATA);
    fgets(dummy,19,datei); geladene_Atome=atoi(dummy);
    for (i = 1; i <= geladene_Atome; i++)
    {
      GAtom[i] = (struct Atom *) AllocRemember(&RememberPtr,
					sizeof(struct Atom), MEMF_CLEAR);
      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 *) AllocRemember(&RememberPtr,
					sizeof(struct Atom), MEMF_CLEAR);
    strcpy(GAtom[105]->Name, "??????????");
    fclose(datei);
    WrConWin(conwin, "ok\n");
    ClearPointer(Window1);
  }
  else
  {
    WrConWin(conwin, ERROR_OPEN_DATA);
    Delay(150L);
    Close_All(NOK);
  }
}

void	  Farben()
{
  int	    rwert;

  rwert = palette_request(Window1, -1, -1, PALETTE_REQ, "DEFAULT", 4);
  if (rwert == 5)                      /* Default Farben! */
    LoadRGB4(&FirstScreen->ViewPort, &Def_Pal[0], 16);
}

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, &Pal[0], 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(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, reflexionsfarbe, kx, ky)     /* Spiegelung */
      ;
  else if (hell1 > (double) farbmatrix[(kx & 3)][(ky & 3)])
    Pixel(Window1, aktfarb, kx, ky)    /* Farbe der Kalotte */
      ;
  else
    Pixel(Window1, 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()
/* laedt die Nummern aller Atome, die das betrachtete Atom
     schneiden oder berhren, ins Array mb */
{
  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(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(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)
ATOMTYP  *v_m;

/* zeichnet eine Kalotte */
{
  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();
  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(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(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(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(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(message);
	  while (message = (struct IntuiMessage *) GetMsg(Window1->UserPort))
	    ReplyMsg(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 = a[i].x - dx;
    a[i].y = a[i].y - dy;
    a[i].z = 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(dx);
  cx = cos(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;
  /* in diese Tabelle gewuenschte Farben je nach Graphikkarte
     und persoenlichem Geschmack eintragen (nicht Schwarz!)
     und Tabelle nach Bedarf erweitern */

  reflexionsfarbe = 2;
  schattenfarbe = 1;
  for (i = 1; i <= geladene_Atome; i++)
  {
    if (!strcmp(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[31];

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

void	  Werte_loeschen()
{
  register int i;

  for (i = 1; i <= MAXATOM; i++)
  {
    strcpy(alt[i].name, "");
    alt[i].x = 0;
    alt[i].y = 0;
    alt[i].z = 0;
    alt[i].r = 0;
    strcpy(a[i].name, "");
    a[i].x = 0;
    a[i].y = 0;
    a[i].z = 0;
    a[i].r = 0;
  }
  zf = 0.0;
  drehx = 0;
  drehy = 0;
  drehz = 0;
  l.x = 0.0;
  l.y = 0.0;
  l.z = 0.0;
  ref = 0;
}

void Menu_Auswertung(Menu_Nr)
USHORT	  Menu_Nr;
{
  USHORT    Menu, MenuItem;
  long stime,etime;

  Menu = MENUNUM(Menu_Nr);
  MenuItem = ITEMNUM(Menu_Nr);
/*  SubItem = SUBNUM(Menu_Nr); */  /* fuer spaeteren Gebrauch */

  switch (Menu)
  {
    case 0:			       /* Copyright, Credits */
      switch (MenuItem)
      {
	case 3:
	  Credits();
	  break;
      }
      break;
    case 1:			       /* Projekt */
      switch (MenuItem)
      {
	case 0:
	  if (dateieinlesen())
	    Prepare();
	  break;
	case 1:
	  dateispeichern();
	  break;
	case 2:
	  Bild_speichern();
	  break;
	case 3:
	  SysInfo();
	  break;
	case 4:
	  if(TwoGadRequest(CONFIRM_QUIT))
	    Close_All(OK);
	  break;
      }
      break;
    case 2:			       /* Edit */
      switch (MenuItem)
      {
	case 0:
	  if (Eingabe())
	    Prepare();                 /* bei OK */
	  break;
	case 1:
	  Farben();
	  break;
      }
      break;
    case 3:			       /* Zeichnen */
      switch (MenuItem)
      {
	case 0:
	  Cls(Window1, 0);
	  Logo();
	  for (n = 1; n <= atomanz; n++)
	  {
	    aktfarb = farbnr(&a[n]);
	    atomskizze(&a[n]);
	  }
	  PrintName();
	  break;
	case 1:
	  Block(Window1, 0, 20, HEIGHT - 12, 280, 11);
	  Print(Window1, PRESS_MB, 1, 0, 20, HEIGHT - 6);
	  Logo();
	  stime=time(&stime);
	  for (n = 1; n <= atomanz; n++)
	  {
	    aktfarb = farbnr(&a[n]);
	    if (atomzeichnen(&a[n]))   /* solange FALSE, bis Maustaste gedr. */
	      break;
	  }
	  etime=time(&etime);
	  Block(Window1, 0, 20, HEIGHT - 12, 280, 11);
	  PrintName();
	  StatRequest(Window1,etime-stime,n-1);
	  break;
      }
      break;
    case 4:			       /* Einstellungen */
      switch (MenuItem)
      {
	case 0:
	  prefs(&ChemPrefs);
	  if(ChemPrefs.bv<1.0)
	    ChemPrefs.bv=1.0;
	  aspect = ((double) HEIGHT - 26.0) / RESX * ChemPrefs.bv;
	  break;
      }
      break;
  }
}

void	  main()                       /* Hauptprogramm            */
{
  Open_All();
  koordmx = round(RESX / 2.0);
  koordmy = round(((double) HEIGHT - 26.0) / 2.0);
  if(HEIGHT<256)
    ChemPrefs.bv=2.0;
  aspect = ((double) HEIGHT - 26.0) / RESX * ChemPrefs.bv;

  vergr = VERGRFAKTOR;
  Werte_loeschen();                    /* sicherheitshalber loeschen, damit nicht irgend-
					  welcher Muell vorhanden ist */
  FOREVER
  {
    switch (Nachricht(Window1))
    {
      case MENUPICK:
	Menu_Auswertung(code);
	break;
      case VANILLAKEY:	  /* alle diese Features sind nicht dokumentiert,
			     da sie nur für mich interessant sind! */
	switch(code)
	{
	  case 'l':
	  case 'L':
	    license();
	    break;
	  case 'N':
	    set_screenmode(200);
	    break;
	  case 'P':
	    set_screenmode(256);
	    break;
	 }
	 break;
    }
  }
}

/*--------------------------- 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(message);
    while (message = (struct IntuiMessage *) GetMsg(win->UserPort))
      ReplyMsg(message);
    break;
  }
  return (nklasse);
}

void Open_All()
{
  if (!(IntuitionBase = (struct IntuitionBase *)
	OpenLibrary("intuition.library", 0L)))
  {
    printf(NO_INTUI);
    Close_All(NOK);
  }

  conwin = OpenRevWin("CON:120/88/400/80/Chemesthetics");
  if (conwin == -1)
  {
    printf(NO_CONSOLE);
    Close_All(NOK);
  }

  if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0L)))
  {
    WrConWin(conwin, NO_GFX);
    Delay(150L);
    Close_All(NOK);
  }

  if (!(TestFile("libs:arp.library")))
  {
    WrConWin(conwin, NO_ARP);
    Delay(150L);
    Close_All(NOK);
  }

  if (!(ArpBase = (struct ArpBase *) OpenLibrary(ArpName, ArpVersion)))
  {
    WrConWin(conwin, ERROR_NO_ARP);
    Delay(150L);
    Close_All(NOK);
  }

  if (!(TestFile("libs:req.library")))
  {
    WrConWin(conwin, NO_REQ_LIB);
    Delay(150L);
    Close_All(NOK);
  }
  else
  {
    if (!(ReqBase = (struct ReqLib *) OpenLibrary("req.library",0L)))
    {
      WrConWin(conwin, ERROR_NO_REQ);
      Delay(150L);
      Close_All(NOK);
    }
    else
    {
      dn[0]=0;
      FileStr.Title= Titel;
      FileStr.PathName = dn;
      FileStr.Dir = Pfadname;
      FileStr.File = Dateiname;
      FileStr.Flags = FRQCACHINGM|FRQCACHEPURGEM;
      FileStr.dirnamescolor = 8;
      FileStr.devicenamescolor = 4;
      strcpy(Pfadname,"Examples");
      strcpy(Dateiname,"");
    }
  }

#ifdef GERMAN
  if (!(TestFile("chems_g.dat")))
#endif
#ifdef ENGLISH
  if (!(TestFile("chems_e.dat")))
#endif
  {
      WrConWin(conwin, NO_DATA);
      Delay(100L);
      Close_All(NOK);
  }

  PrepareSanduhr();
  Load_Datafile();
  sprintf(Ver, "%s V%s", ProgId, Version);

  if(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, &Pal[0], 16);

  if (Metalworx(FirstScreen))
    WrConWin(conwin, NO_METAL_WIN);

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

  license();
  ShowTitle(FirstScreen,ChemPrefs.Title);
  PrepareLogo();
  PrepareEGadgets();
}

/****************************************
* Funktion: Alles geoeffnete schliessen *
****************************************/

void Close_All(status)
BOOL status;
{
  MyProcess->pr_WindowPtr=OldErrorWin;	/* alten Window-Pointer wieder
					   herstellen */
  if(reqlib_used)
    PurgeFiles(&FileStr);           /* Speicher f. FileListe freigeben */
  FreeEGadgets();
  FreeLogo();
  if (RememberPtr)
    FreeRemember(RememberPtr, TRUE);
  if (Window1)
    ClearMenuStrip(Window1);
  if (Window1)
    CloseWindowSafely(Window1, NULL);
  if (FirstScreen)
    CloseScreen(FirstScreen);
  FreeSanduhr();
  if (ReqBase)
    CloseLibrary(ReqBase);
  if (ArpBase)
    CloseLibrary(ArpBase);
  if (GfxBase)
    CloseLibrary(GfxBase);
  if (conwin != -1)
  {
    WrConWin(conwin, "Thanx! Have a nice day!");
    Delay(100L);
    close(conwin);
  }
  if (IntuitionBase)
    CloseLibrary(IntuitionBase);
  if (status == OK)
    exit(0);
  else
    exit(1);
}
