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

#include <libraries/mathffp.h>
#include <intuition/intuitionbase.h>
#include <stdio.h>
#include <math.h>
#include <libraries/arpbase.h>
#include <libraries/iff.h>
#include <exec/memory.h>
#include "messages.h"
#include "chem_defs.h"
#include "chemest.h"

#define OK 0
#define NOK 1
#define RESX 640.0		       /* Breite des Bildes */
#define SCREENVERHAELTNIS 1.5	       /* fuer den Bildschirm individuell einstellen:
																	 Verhaeltnis Breite/Hoehe des Bildschirmes  */
double	  VERGRFAKTOR = 0.3;	       /* Umrechnungsfaktor pm -> Pixels   */

#undef PI
double	  PI = 3.14159265358979224;

int	  HEIGHT = 256; 	       /* Default, wird bei NTSC-Screen geaendert */
BOOL	  reflexion = FALSE;
int	  graphdriver, graphmode, 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*/

struct Atom *GAtom[106];
char	  dn[285];
char	  Dateiname[31] = NULL, IFFDateiname[31] = NULL;
char	  Pfadname[255] = NULL;
UBYTE	 *atomliste[105];


void	  Gadget_Auswertung(), atomedrehen(), PrintName(), Werte_loeschen(),
	  Load_Datafile(), Farben();
int	  atomezeichnen();

extern char ProgId[], Version[];

extern void Prepare();
extern UBYTE *AllocRemeber();
extern double atof();
extern struct Message *GetMsg();

struct Library *IFFBase = NULL;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct ArpBase *ArpBase;
struct Screen *FirstScreen;
struct Window *Window1, *Window2;
struct Window *OpenWindow();
struct IntuiMessage *message;
struct Message *GetMsg();
struct Gadget *GadgetPtr;
struct RastPort *RP;
struct Remember *RememberPtr = NULL;
long	  conwin;

USHORT	  code;
USHORT	  GadgetID;

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

int	  dateieinlesen()
 /* versucht, die Molekuel-Datei dn einzulesen */
{
  char	    dummy[4];
  int	    i, j, arp;
  FILE	   *datei = NULL, *fopen();

  strcpy(Dateiname, "");
  arp = arpreq(LOAD_MOL, Dateiname, Pfadname, Window1);
  if (arp)
  {
    Check_Pathname(Pfadname);
    strcpy(dn, Pfadname);
    strcat(dn, Dateiname);
    datei = fopen(dn, "r");
    if (datei)
    {
      SetPointer(Window1, wptr->Pointer, wptr->Height,
		 wptr->Width, wptr->XOffset, wptr->YOffset);
      Werte_loeschen();                /* alte Werte loeschen */
      fscanf(datei, "%d\n", &atomanz);
      for (i = 1; i <= atomanz; i++)
      {
	j = fgets(a[i].name, 4, datei);
	strcpy(dummy, a[i].name);
	dummy[strlen(dummy) - 1] = '\0';
	strcpy(a[i].name, dummy);
	fscanf(datei, "%d\n", &a[i].x);
	fscanf(datei, "%d\n", &a[i].y);
	fscanf(datei, "%d\n", &a[i].z);
	fscanf(datei, "%d\n", &a[i].r);
	alt[i] = a[i];		       /* Originale f. Editieren sichern */
      }
      fscanf(datei, "%lf\n", &zf);
      fscanf(datei, "%d\n", &drehx);
      fscanf(datei, "%d\n", &drehy);
      fscanf(datei, "%d\n", &drehz);
      fscanf(datei, "%lf\n", &l.x);
      fscanf(datei, "%lf\n", &l.y);
      fscanf(datei, "%lf\n", &l.z);
      fscanf(datei, "%d\n", &ref);
      fclose(datei);
      ClearPointer(Window1);
      if (zf == 0.0 || zf > 100)
      {
	MessageReq(Window1, WRONG_DATA, 8, 10, 12);
	return 0;
      }
      else
	return 1;
    }
    else
      return 0;
  }
  else
    return 0;
}

void	  dateispeichern()             /* speichert die Molekuel-Datei */
{
  int	    i, j, arp;
  FILE	   *datei = NULL, *fopen();

  arp = arpreq(SAVE_MOL, Dateiname, Pfadname, Window1);
  if (arp)
  {
    Check_Pathname(Pfadname);
    strcpy(dn, Pfadname);
    strcat(dn, Dateiname);
    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++)
      {
	j = 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);
    }
  }
}

void	  Bild_speichern()
{
  int	    i, arp, modus = 0;	      /* 0 = normale Grafik, kein HAM */
  char	    dummy[100];
  FILE	   *datei = NULL, *fopen();

  if (IFFBase)
  {
    i = strpos(Dateiname, ".cdm");
    if (i > 0)
    {
      strncpy(IFFDateiname, Dateiname, i);
      IFFDateiname[i] = '\0';
      strcat(IFFDateiname, ".iff");
    }
    arp = arpreq(SAVE_IFF, IFFDateiname, Pfadname, Window1);
    if (arp)
    {
      Check_Pathname(Pfadname);
      strcpy(dn, Pfadname);
      strcat(dn, IFFDateiname);
      if (MenuItem5_1.Flags & CHECKED)
	modus |= 1;		       /* Bit 1 auf 1 setzen = comress */
      else
	modus &= 126;		       /* Bit 1 auf 0 setzen = no compress */
      SetPointer(Window1, wptr->Pointer, wptr->Height,
		 wptr->Width, wptr->XOffset, wptr->YOffset);
      if (!(SaveBitMap(dn, &(FirstScreen->BitMap), Pal, modus)))
      {
	ClearPointer(Window1);
	sprintf(dummy, "%s: %d.", IFF_ERROR, IffError());
	MessageReq(Window1, dummy, 8, 10, 12);
      }
      ClearPointer(Window1);
    }
  }
  else
    MessageReq(Window1, NO_IFF, 8, 10, 12);
}

void	  Load_Datafile()
{
  int	    i, j;
  FILE	   *datei = NULL, *fopen();

#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);
    fscanf(datei, "%d\n", &geladene_Atome);
    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);
      }
      j = fgets(GAtom[i]->Kurzz, 4, datei);
      GAtom[i]->Kurzz[strlen(GAtom[i]->Kurzz) - 1] = '\0';
      j = fgets(GAtom[i]->Name, 21, datei);
      atomliste[i - 1] = (UBYTE *) GAtom[i]->Name;
      GAtom[i]->Name[strlen(GAtom[i]->Name) - 1] = '\0';
      fscanf(datei, "%d\n", &GAtom[i]->Farbe);
    }
    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	  Quit()
{
  Close_All(OK);
}

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

void	  bringeaufeinheitsvektor(v)
VEKTOR	 *v;

/* Der Vektor wird durch seine Laenge dividiert
     und erhaelt dadurch die Laenge 1		 */
{
  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	  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++)
      {
	farbe1 = 0.0;
	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	*/
  int	    altfarb;

  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));
  SetAPen(RP, altfarb);
}


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

  flag = TRUE;
  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;
  char	    dummy[80];
  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 = 1;
  schattenfarbe = 0;
  for (i = 1; i <= geladene_Atome; i++)
  {
    if (!strcmp(atom.name, GAtom[i]->Kurzz))
    {
      f = GAtom[i]->Farbe;
      i = geladene_Atome;
    }
  }
  if (f == 0)
  {
    sprintf(dummy, "%s %s", NO_ATOM, atom.name);
    MessageReq(Window1, dummy, 8, 10, 12);
    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 - 18);
}

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;
}

Menu_Auswertung(Menu_Nr)
USHORT	  Menu_Nr;
{
  USHORT    Menu, MenuItem, SubItem;
  struct MenuItem *MenuPunkt;

  Menu = MENUNUM(Menu_Nr);
  MenuItem = ITEMNUM(Menu_Nr);
  SubItem = SUBNUM(Menu_Nr);

  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:
	  Quit();
      }
      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 - 26, 280, 10);
	  Print(Window1, PRESS_MB, 13, 0, 20, HEIGHT - 18);
	  Logo();
	  for (n = 1; n <= atomanz; n++)
	  {
	    aktfarb = farbnr(&a[n]);
	    if (atomzeichnen(&a[n]))   /* solange FALSE, bis Maustaste gedr. */
	      break;
	  }
	  Block(Window1, 0, 20, HEIGHT - 26, 280, 10);
	  PrintName();
	  break;
      }
      break;
    case 4:			       /* Einstellungen */
      switch (MenuItem)
      {
	case 0:
	  MenuItem5_1.Flags ^= CHECKED;
	  break;
      }
      break;
  }
}

void	  main()                       /* Hauptprogramm            */
{
  Open_All();
  koordmx = round(RESX / 2.0);
  koordmy = round(((double) HEIGHT - 26.0) / 2.0);
  aspect = ((double) HEIGHT - 26.0) / RESX * SCREENVERHAELTNIS;

  vergr = VERGRFAKTOR;
  Werte_loeschen();                    /* sicherheitshalber loeschen, damit nicht irgend-
					  welcher Muell vorhanden ist */
  FOREVER
  {
    switch (Nachricht(Window1))
    {
      case CLOSEWINDOW:
	Quit();
      case MENUPICK:
	Menu_Auswertung(code);
	break;
    }
  }
}

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

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);
}

Open_All()
{
  void	   *OpenLibrary();
  struct Window *OpenWindow();
  struct Screen *OpenScreen();

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

  conwin = OpenRevWin("CON:160/88/320/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:iff.library")))
  {
    WrConWin(conwin, NO_IFF_WARNING);
    Delay(100L);
  }
  else
  {
    if (!(IFFBase = (struct IFFBase *)
	  OpenLibrary(IFFNAME, IFFVERSION)))
    {
      WrConWin(conwin, ERROR_NO_IFF);
      Delay(150L);
    }
  }

#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);
    }

  Load_Datafile();

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

  HEIGHT = IntuitionBase->FirstScreen->Height;
  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);
  RP = Window1->RPort;
}

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

Close_All(status)
{
  int	    i;

  if (RememberPtr)
    FreeRemember(RememberPtr, TRUE);
  if (Window1)
    ClearMenuStrip(Window1);
  if (Window1)
    CloseWindowSafely(Window1, NULL);
  if (FirstScreen)
    CloseScreen(FirstScreen);
  if (IFFBase)
    CloseLibrary(IFFBase);
  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);
}
