/*
** Verwaltung der Kunden
**
** Copyright (C) 1996 by Stefan Scherer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for non-commerical use and without fee is hereby granted,
** provided that the above copyright notice appear in all copies and that
** both that copyright notice and this permission notice appear in
** supporting documentation.  This software is provided "as is" without
** express or implied warranty.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include <exec/lists.h>
#include <exec/memory.h>
#include <libraries/locale.h>

#include <clib/alib_protos.h>
#include <proto/exec.h>
#include <proto/locale.h>

#include "Kunden.h"
#include "LocaleSupport.h"
#include "MiscSupport.h"


/* Dateiname für die Kundenliste */

#define FILENAME_CUSTOMERLIST  "Kundenliste.dat"
#define FILENAME_CATEGORY      "Kundenkategorien.dat"

/*
** Die Daten für jeden Kunden
*/

struct customerNode
  {
    struct Node Node;
    struct Customer cust;
  };


struct categoryNode
  {
    struct Node Node;
    char category[MAX_CUST_LEN];
  };


static struct List categoryList;
static struct categoryNode *aktCategoryNode;
static UWORD categoryCount;	/* Anzahl Kategorien in der Liste */

static struct List customerList;
static struct customerNode *aktCustomerNode;
static UWORD customerCount;	/* Anzahl der Kunden in der Liste */

static void *poolHeader = NULL;	/* Zeiger für Memory-Pool */


static UBYTE *Anreden[] =
{
  "Herrn",
  "Frau",
  "Firma",
  "",
  NULL
};

static char exceptions[256] =
"GmbH|KG|Co|eV|e.V.";		/* Default-Ausnahmen für Sortierung */


/*
** Status, ob Kundenliste verändert wurde (TRUE) oder nicht (FALSE)
*/

static BOOL status;


/* Prototypen privater Funktionen */

static int GetAnrede (STRPTR buffer);
static int CompareCustomers (struct customerNode *l, struct customerNode *r);
static int CompareCategories (struct categoryNode *l, struct categoryNode *r);
static UWORD GetNextCustomerNumber (void);

static BOOL NumberNotExists (UWORD);


/* Fehlercode und Fehlerstrings */

static int error;
static const STRPTR errormsg[] =
{
  "",				// ERR_CUST_OK
   "kein memorypool",		// ERR_CUST_NOPOOL
   "kein Speicher",		// ERR_CUST_NOMEM
   "Datei nicht gefunden",	// ERR_CUST_FILENOTFOUND
   "Liste ist leer",		// ERR_CUST_LISTEMPTY
   "Konnte File nicht erzeugen",	// ERR_CUST_CANTCREATEFILE
   "Eintrag nicht in Liste",	// ERR_CUST_NOITEM
   "Fehler beim speichern",	// ERR_CUST_SAVE
};


/*
** erstelle Kundenliste und lade sie nach
*/

int 
ConstructCustomer (void)
{
  NewList (&customerList);
  NewList (&categoryList);
  customerCount = 0;
  aktCustomerNode = NULL;
  categoryCount = 0;
  aktCategoryNode = NULL;

  status = FALSE;

  /* neuen Memory-Pool anfordern */
  if (!(poolHeader = LibCreatePool (MEMF_CLEAR, 4000, 4000)))
    return (error = ERR_CUST_NOPOOL);

  C_LoadCustomerList ();

  return (error = ERR_CUST_OK);
}


/* 
** Am Ende den Speicher freigeben etc. KEIN automatischer Save-Vorgang!!
*/

void 
DestructCustomer (void)
{
  if (poolHeader)
    {
      LibDeletePool (poolHeader);
      poolHeader = NULL;
    }
}


/*
** Setze die Sortier-Ausnahmen
*/

void 
C_SetExceptions (STRPTR except)
{
  strcpy (exceptions, except);	/* ändert den Status NICHT ! */
}


/*
** Lies die Sortier-Ausnahmen aus
*/

void 
C_GetExceptions (STRPTR except)
{
  strcpy (except, exceptions);
}


/*
** Lade eine Kundenliste ein und füge sie zur
** internen Kundenliste hinzu.
*/

int 
C_LoadCustomerList (void)
{
  STRPTR p, oldp;
  FILE *file;
  char buffer[4 * MAX_CUST_LEN];
  BOOL ok = TRUE;
  struct Customer cust;
  int result = ERR_CUST_OK;

  NewList (&customerList);
  customerCount = 0;
  aktCustomerNode = NULL;

  NewList (&categoryList);
  categoryCount = 0;
  aktCategoryNode = NULL;

  if (poolHeader)
    {
      LibDeletePool (poolHeader);
      poolHeader = NULL;
    }

  /* neuen Memory-Pool anfordern */
  if (!(poolHeader = LibCreatePool (MEMF_CLEAR, 4000, 4000)))
    return (error = ERR_CUST_NOPOOL);

  if (file = fopen (FILENAME_CUSTOMERLIST, "r"))
    {
      while (ok)
	{
	  if (!fgets (buffer, 2 * MAX_CUST_LEN - 1, file))
	    ok = FALSE;
	  if (ok)
	    {
	      if (p = strchr (buffer, '\t'))
		{
		  /* neue Kundenliste. Kategorie vorhanden */
		  *p++ = 0;
		  strcpy (cust.Adresse1, Anreden[GetAnrede (buffer)]);
		  oldp = p;

		  if (p = strchr (oldp, '\n'))
		    *p = 0;
		  strcpy (cust.Kategorie, oldp);	/* Kategorie */
		}
	      else
		{
		  /* alte Kundenliste. noch keine Kategorie vorhanden */
		  if (p = strchr (buffer, '\n'))
		    *p = 0;
		  strcpy (cust.Adresse1, Anreden[GetAnrede (buffer)]);
		  strcpy (cust.Kategorie, "Kunden");
		}
	    }

	  if (ok && !fgets (cust.Adresse2, MAX_CUST_LEN, file))
	    ok = FALSE;
	  if (ok && (p = strchr (cust.Adresse2, '\n')))
	    *p = 0;
	  if (ok && !fgets (cust.Adresse3, MAX_CUST_LEN, file))
	    ok = FALSE;
	  if (ok && (p = strchr (cust.Adresse3, '\n')))
	    *p = 0;
	  if (ok && !fgets (cust.Adresse4, MAX_CUST_LEN, file))
	    ok = FALSE;
	  if (ok && (p = strchr (cust.Adresse4, '\n')))
	    *p = 0;
	  if (ok && !fgets (cust.Adresse5, MAX_CUST_LEN, file))
	    ok = FALSE;
	  if (ok && (p = strchr (cust.Adresse5, '\n')))
	    *p = 0;

	  if (ok && !fgets (buffer, 38, file))
	    ok = FALSE;
	  cust.KundenNummer = atoi (buffer);

	  if (ok && !fgets (cust.Zeichen, MAX_CUST_LEN, file))
	    ok = FALSE;
	  if (ok && (p = strchr (cust.Zeichen, '\n')))
	    *p = 0;

	  cust.Telefon[0] = 0;
	  cust.Fax[0] = 0;
	  cust.Kommentar1[0] = 0;
	  cust.Kommentar2[0] = 0;

	  if (ok && !fgets (buffer, 4 * MAX_CUST_LEN, file))
	    ok = FALSE;

	  if (p = strchr (buffer, '\t'))
	    {
	      /* neue Kundenliste. Telefon,Fax,2*Kommentar vorhanden */
	      *p++ = 0;
	      strcpy (cust.Telefon, buffer);
	      oldp = p;

	      if (p = strchr (oldp, '\t'))
		{
		  *p++ = 0;
		  strcpy (cust.Fax, oldp);
		  oldp = p;

		  if (p = strchr (oldp, '\t'))
		    {
		      *p++ = 0;
		      strcpy (cust.Kommentar1, oldp);
		      oldp = p;

		      if (p = strchr (oldp, '\n'))
			*p = 0;
		      strcpy (cust.Kommentar2, oldp);
		    }
		}
	    }
	  else
	    {
	      /* alte Kundenliste. Nur Telefonnummer vorhanden */
	      if (p = strchr (buffer, '\n'))
		*p = 0;
	      strcpy (cust.Telefon, buffer);
	    }

	  if (ok && (result = C_AddCustomer (&cust)))
	    ok = FALSE;
	}
      fclose (file);
    }

  /* lade Kategorien ein */

  if (result == ERR_CUST_OK)
    {
      ok = TRUE;

      if (file = fopen (FILENAME_CATEGORY, "r"))
	{
	  while (ok)
	    {
	      /*
	      ** · Kategorie (usw.)
	      */
	      if (!fgets (buffer, MAX_CUST_LEN, file))
		ok = FALSE;
	      if (ok)
		{
		  if (p = strchr (buffer, '\n'))
		    *p = 0;

		  if (result = C_AddCategory (buffer))
		    ok = FALSE;
		}
	    }
	  fclose (file);
	}
      else
	result = ERR_CUST_FILENOTFOUND;
    }

  status = FALSE;		/* neue Liste, Status: Nicht verändert */

  return (error = result);
}


/*
** Speichern der Kundenliste im File
*/

int 
C_SaveCustomerList (void)
{
  int result = ERR_CUST_OK;
  FILE *file;
  BOOL ok = TRUE;

  struct customerNode *node;
  struct categoryNode *cnode;

  if (file = fopen (FILENAME_CUSTOMERLIST, "w"))
    {
      for (node = (struct customerNode *) customerList.lh_Head;
	   ok && node->Node.ln_Succ;
	   node = (struct customerNode *) node->Node.ln_Succ)
	{
	  if (fprintf (file, "%s\t%s\n%s\n%s\n%s\n%s\n", node->cust.Adresse1,
	               node->cust.Kategorie,
		       node->cust.Adresse2, node->cust.Adresse3,
		       node->cust.Adresse4, node->cust.Adresse5) < 0)
	    ok = FALSE;

	  if (ok && fprintf (file, "%d\n", node->cust.KundenNummer) < 0)
	    ok = FALSE;

	  if (ok && fprintf (file, "%s\n%s\t%s\t%s\t%s\n", node->cust.Zeichen,
			     node->cust.Telefon, node->cust.Fax,
			     node->cust.Kommentar1, node->cust.Kommentar2
	      ) < 0)
	    ok = FALSE;
	}
      if (!ok)
	result = ERR_CUST_SAVE;
      fclose (file);
    }
  else
    result = ERR_CUST_CANTCREATEFILE;

  if (result == ERR_CUST_OK)
    {
      if (file = fopen (FILENAME_CATEGORY, "w"))
	{
	  for (cnode = (struct categoryNode *) categoryList.lh_Head;
	       ok && cnode->Node.ln_Succ;
	       cnode = (struct categoryNode *) cnode->Node.ln_Succ)
	    {
	      /*
	      ** · Kategorie (usw.)
	      */

	      if (fprintf (file, "%s\n", cnode->category) < 0)
		ok = FALSE;
	    }
	  if (!ok)
	    result = ERR_CUST_SAVE;
	  fclose (file);
	}
      else
	result = ERR_CUST_CANTCREATEFILE;
    }

  status = FALSE;		/* gespeichert. Status: nicht verändert */

  return (error = result);
}


/*
** neuen Kunden in Liste einfügen
*/

int 
C_AddCustomer (struct Customer *cust)
{
  struct customerNode *newitem;

  if (!(newitem = LibAllocPooled (poolHeader, sizeof (struct customerNode))))
      return (error = ERR_CUST_NOMEM);

  status = TRUE;
  strcpy (newitem->cust.Kategorie, cust->Kategorie);
  strcpy (newitem->cust.Adresse2, cust->Adresse2);
  strcpy (newitem->cust.Adresse3, cust->Adresse3);
  strcpy (newitem->cust.Adresse4, cust->Adresse4);
  strcpy (newitem->cust.Adresse5, cust->Adresse5);
  strcpy (newitem->cust.Telefon, cust->Telefon);
  strcpy (newitem->cust.Fax, cust->Fax);
  strcpy (newitem->cust.Kommentar1, cust->Kommentar1);
  strcpy (newitem->cust.Kommentar2, cust->Kommentar2);
  strcpy (newitem->cust.Zeichen, cust->Zeichen);

  if ((cust->KundenNummer == 0xffff) || (cust->KundenNummer == 0))
    {
      newitem->cust.KundenNummer = GetNextCustomerNumber () + 1;
    }
  else
    {
      /* prüfe, ob Kundennummer bereits existiert */
      if (NumberNotExists (cust->KundenNummer))
	{
	  newitem->cust.KundenNummer = cust->KundenNummer;
	}
      else
	{
	  newitem->cust.KundenNummer = GetNextCustomerNumber () + 1;
	}
    }

  strcpy (newitem->cust.Adresse1, Anreden[GetAnrede (cust->Adresse1)]);

  InsertSorted (&customerList, (struct Node *) newitem,
		(struct Node *) aktCustomerNode, CompareCustomers);

  aktCustomerNode = newitem;
  customerCount++;

  return (error = ERR_CUST_OK);
}


/*
** um die Einträge sortieren zu können, wird eine Vergleichsfunktion
** benötigt. Diese vergleich zwei Kundeneinträge miteinander und
** gibt einen Wert zurück, der dem Vergleich entspricht.
**
** mögliche Rückgabewerte:
**
**    l < r      l = r      l > r
**     -1          0          1
*/

static int 
CompareCustomers (struct customerNode *l, struct customerNode *r)
{
  char lbuf[MAX_CUST_LEN + 1];
  char rbuf[MAX_CUST_LEN + 1];


  C_PrintCustLine ((struct MinCustomer *) &l->cust, lbuf);
  C_PrintCustLine ((struct MinCustomer *) &r->cust, rbuf);

  return (StrnCmp (Locale, lbuf, rbuf, -1, SC_COLLATE1));
}

static int 
CompareCategories (struct categoryNode *l, struct categoryNode *r)
{
  return (StrnCmp (Locale, l->category, r->category, -1, SC_COLLATE1));
}


/*
** aktuellen Kunden aus der Liste löschen
*/

int 
C_DelCustomer (void)
{
  struct customerNode *newakt;

  if (customerCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  status = TRUE;
  if (!aktCustomerNode->Node.ln_Succ->ln_Succ)
    {
      if (customerCount > 1)
	newakt = (struct customerNode *) aktCustomerNode->Node.ln_Pred;
      else
	newakt = NULL;

      RemTail (&customerList);
      LibFreePooled (poolHeader, aktCustomerNode,
                     sizeof (struct customerNode));
      customerCount--;
      aktCustomerNode = newakt;
    }
  else
    {
      if (customerCount > 1)
	newakt = (struct customerNode *) aktCustomerNode->Node.ln_Succ;
      else
	{
	  newakt = NULL;
	}

      Remove ((struct Node *) aktCustomerNode);
      customerCount--;
      aktCustomerNode = newakt;
    }

  return (error = ERR_CUST_OK);
}


/*
** Ändere den aktuellen Kundeneintrag
*/

int 
C_ChangeCustomer (struct Customer *cust)
{
  if (customerCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  status = TRUE;
  strcpy (aktCustomerNode->cust.Kategorie, cust->Kategorie);
  strcpy (aktCustomerNode->cust.Adresse2, cust->Adresse2);
  strcpy (aktCustomerNode->cust.Adresse3, cust->Adresse3);
  strcpy (aktCustomerNode->cust.Adresse4, cust->Adresse4);
  strcpy (aktCustomerNode->cust.Adresse5, cust->Adresse5);
  strcpy (aktCustomerNode->cust.Telefon, cust->Telefon);
  strcpy (aktCustomerNode->cust.Fax, cust->Fax);
  strcpy (aktCustomerNode->cust.Kommentar1, cust->Kommentar1);
  strcpy (aktCustomerNode->cust.Kommentar2, cust->Kommentar2);
  strcpy (aktCustomerNode->cust.Zeichen, cust->Zeichen);
  aktCustomerNode->cust.KundenNummer = cust->KundenNummer;

  strcpy (aktCustomerNode->cust.Adresse1, Anreden[GetAnrede (cust->Adresse1)]);

  Remove ((struct Node *) aktCustomerNode);
  InsertSorted (&customerList, (struct Node *) aktCustomerNode,
		NULL, CompareCustomers);

  return (error = ERR_CUST_OK);
}


/*
** Lese den aktuellen Kundeneintrag aus
*/

int 
C_GetCurrentCustomer (struct Customer *cust)
{
  if (customerCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  strcpy (cust->Kategorie, aktCustomerNode->cust.Kategorie);
  strcpy (cust->Adresse1, aktCustomerNode->cust.Adresse1);
  strcpy (cust->Adresse2, aktCustomerNode->cust.Adresse2);
  strcpy (cust->Adresse3, aktCustomerNode->cust.Adresse3);
  strcpy (cust->Adresse4, aktCustomerNode->cust.Adresse4);
  strcpy (cust->Adresse5, aktCustomerNode->cust.Adresse5);
  strcpy (cust->Telefon, aktCustomerNode->cust.Telefon);
  strcpy (cust->Fax, aktCustomerNode->cust.Fax);
  strcpy (cust->Kommentar1, aktCustomerNode->cust.Kommentar1);
  strcpy (cust->Kommentar2, aktCustomerNode->cust.Kommentar2);
  strcpy (cust->Zeichen, aktCustomerNode->cust.Zeichen);
  cust->KundenNummer = aktCustomerNode->cust.KundenNummer;

  return (error = ERR_CUST_OK);
}


/*
** Mache den num-ten Kunden zum aktuellen Kunden in der Liste
*/

int 
C_SetCurrentCustomerNumber (UWORD number)
{
  int i;
  struct Node *node;

  if (customerCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  if (number >= customerCount)
    return (error = ERR_CUST_NOITEM);

  for (i = 0, node = customerList.lh_Head; i < number;
       i++, node = node->ln_Succ);

  aktCustomerNode = (struct customerNode *) node;

  return (error = ERR_CUST_OK);
}



/*
** Im Unterschied dazu gibt es eine Funktion, mit der man einen Kunden
** mit der Kundennummer 'number' zum aktiven machen kann.
*/

int 
C_SetCurrentCustomer (UWORD custnumber)
{
  int i;
  struct customerNode *node;

  if (customerCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  for (i = 0, node = (struct customerNode *) customerList.lh_Head;
       node->Node.ln_Succ && node->cust.KundenNummer != custnumber;
       i++, node = (struct customerNode *) node->Node.ln_Succ);

  if (!node->Node.ln_Succ)
    return (error = ERR_CUST_NOITEM);

  aktCustomerNode = node;

  return (error = ERR_CUST_OK);
}


/*
**  ermittle einen bestimmten Kunden aus der Liste
**  nummer läuft von 0...count-1
*/

int 
C_GetCustomer (UWORD num, struct Customer *cust)
{
  struct customerNode *node;
  int i;

  if (customerCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  if (num >= customerCount)
    return (error = ERR_CUST_NOITEM);

  for (i = 0, node = (struct customerNode *) customerList.lh_Head;
       i < num && node->Node.ln_Succ;
       i++, node = (struct customerNode *) node->Node.ln_Succ);

  strcpy (cust->Kategorie, node->cust.Kategorie);
  strcpy (cust->Adresse1, node->cust.Adresse1);
  strcpy (cust->Adresse2, node->cust.Adresse2);
  strcpy (cust->Adresse3, node->cust.Adresse3);
  strcpy (cust->Adresse4, node->cust.Adresse4);
  strcpy (cust->Adresse5, node->cust.Adresse5);
  strcpy (cust->Telefon, node->cust.Telefon);
  strcpy (cust->Fax, node->cust.Fax);
  strcpy (cust->Kommentar1, node->cust.Kommentar1);
  strcpy (cust->Kommentar2, node->cust.Kommentar2);
  strcpy (cust->Zeichen, node->cust.Zeichen);
  cust->KundenNummer = node->cust.KundenNummer;

  return (error = ERR_CUST_OK);
}


/* bilde die umgewandelte Zeile für die Kundenliste.
** Der Nachname soll vorne erscheinen.
**
** Der String wird nach 'line' geschrieben.
**
** Der übergebene Kunde kann auch vom Typ 'struct Customer' sein, da
** diese Struktur identisch zu 'struct MinCustomer' ist.
*/

void 
C_PrintCustLine (struct MinCustomer *cust, STRPTR line)
{
  char *pos;

  if (pos = strrchr (cust->Adresse2, ' '))
    {
      int search = TRUE;
      char *apos, *npos;
      int alen;

      apos = exceptions;
      do
	{
	  if (npos = strchr (apos, '|'))
	    alen = npos++ - apos;
	  else
	    {
	      alen = strlen (apos);
	      npos = apos + alen;
	    }
	  /* alen = Länge der aktuellen Ausnahme
	  ** npos = Adresse des nächsten Wortes hinter '|' oder
	  ** auf dem Nullbyte
	  */

	  if (!strnicmp (pos + 1, apos, alen))
	    search = FALSE;
	  apos = npos;
	}
      while (search && *apos);

      if (search)		/* Ausnahme nicht gefunden */
	{
	  strcpy (line, pos + 1);
	  strcat (line, " ");
	  strncat (line, cust->Adresse2, pos - cust->Adresse2);
	}
      else
	strcpy (line, cust->Adresse2);
    }
  else
    strcpy (line, cust->Adresse2);
}


/*
** Kopiere nur den Nachnamen (falls möglich) in den String
*/

void 
C_PrintName (struct MinCustomer *cust, STRPTR line)
{
  char *pos;

  if (pos = strrchr (cust->Adresse2, ' '))
    {
      int search = TRUE;
      char *apos, *npos;
      int alen;

      apos = exceptions;
      do
	{
	  if (npos = strchr (apos, '|'))
	    alen = npos++ - apos;
	  else
	    {
	      alen = strlen (apos);
	      npos = apos + alen;
	    }
	  /* alen = Länge der aktuellen Ausnahme
	  ** npos = Adresse des nächsten Wortes hinter '|' oder
	  ** auf dem Nullbyte
	  */

	  if (!strnicmp (pos + 1, apos, alen))
	    search = FALSE;
	  apos = npos;
	}
      while (search && *apos);

      if (search)		/* Ausnahme nicht gefunden */
	{
	  strcpy (line, pos + 1);
	}
      else
	strcpy (line, cust->Adresse2);
    }
  else
    strcpy (line, cust->Adresse2);
}



/* lies die Nummer des aktuellen Eintrags in der Liste und der Kategorie.
** Berechne die Nummer (0...n) des Eintrags, der zur aktuellen Kategorie
** gehört.
*/

int 
C_GetCurrentCustomerNumberCat (void)
{
  struct customerNode *node;
  int i;

  if (aktCustomerNode)
    {
      for (i = 0, node = (struct customerNode *) customerList.lh_Head;
	   node->Node.ln_Succ && aktCustomerNode != node;
	   node = (struct customerNode *) node->Node.ln_Succ)
	{
	  if (!stricmp (aktCustomerNode->cust.Kategorie, node->cust.Kategorie))
	    i++;
	}
    }

  return (i);
}



/*
** ermittle die Nummer des aktuellen Kunden in der Liste
*/

int 
C_GetCurrentCustomerNumber (void)
{
  int i;
  struct Node *node;

  for (i = 0, node = customerList.lh_Head;
       ((struct customerNode *) node != aktCustomerNode) && node->ln_Succ;
       i++, node = node->ln_Succ);

  return (i);
}


/*
** ermittle die Anzahl der Kunden in der Liste
*/

int 
C_GetCustomerCount (void)
{
  return (customerCount);
}


/*
** gib die Nummer des ersten Kunden zurück, auf den die
** lückenhafte Beschreibung stimmt.
**
** Damit kann man einen Kunden anspringen, wenn man nur den Anfang des
** Namens eingegeben hat oder nur die Kundennummer weiß.
*/

int 
C_GetNearestCustomerNumber (struct Customer *cust)
{
  struct customerNode *node;
  BOOL searching = TRUE;
  int i;

  if (customerCount == 0)
    return (0);

  aktCustomerNode = (struct customerNode *) customerList.lh_Head;

  for (i = 0, node = (struct customerNode *) customerList.lh_Head;
       searching && node->Node.ln_Succ;
       node = (struct customerNode *) node->Node.ln_Succ)
    {
      if (CheckName (cust->Adresse2, node->cust.Adresse2))
	searching = FALSE;
      else
	i++;
    }

  return (i);
}


/*
** Auslesen des Status, ob die Liste verändert (TRUE) wurde oder nicht (FALSE).
*/

BOOL 
C_GetStatus (void)
{
  return (status);
}


/*
** Ermittle die gewünschte Anrede
**
** Durch das bisherige Format gibt es nur eine bestimmte Auswahl an
** Anreden (Herrn, Frau, Firma, "" )
*/

static int 
GetAnrede (STRPTR buffer)
{
  int anrede, i;
  BOOL searching;

  for (i = 0, searching = TRUE; Anreden[i] && searching;
       anrede = i, i++)
    searching = strcmp (buffer, Anreden[i]);

  if (searching)
    anrede = 3;			/* auf leeres Feld stellen */

  return (anrede);
}


static UWORD 
GetNextCustomerNumber (void)
{
  struct customerNode *node;
  UWORD num;

  if (IsListEmpty (&customerList))
    num = 0;
  else
    {
      num = 1;
      for (node = (struct customerNode *) customerList.lh_Head;
	   node->Node.ln_Succ;
	   node = (struct customerNode *) node->Node.ln_Succ)
	{
	  num = max (num, node->cust.KundenNummer);
	}
    }
  return (num);
}


/*
** Hole den Fehlerstring
*/

STRPTR 
C_GetErrorMsg (void)
{
  return (errormsg[error]);
}



/*
** Vergleiche den 'number'ten Kunden aus der Liste mit einem
** (lückenhaften) Pattern. Wenn der Pattern in den Kunden paßt,
** dann gib TRUE zurück, ansonsten gib FALSE zurück.
**
** Der Pattern kann wie folgt belegt sein: Zeichen, KundenNummer, Telefon,
** Anrede (=Adresse1) und Adreßpattern (= Adresse2)
*/

BOOL 
C_ComparePattern (UWORD number, struct Customer * cust)
{
  struct customerNode *node;
  BOOL result = TRUE;
  int i;
  STRPTR array[5];

  if (customerCount == 0)
    return (FALSE);

  if (number >= customerCount)
    return (FALSE);

  for (i = 0, node = (struct customerNode *) customerList.lh_Head;
       i < number && node->Node.ln_Succ;
       i++, node = (struct customerNode *) node->Node.ln_Succ);

  if (result && !CheckName (cust->Zeichen, node->cust.Zeichen))
    result = FALSE;

  if (result && !CheckName (cust->Adresse1, node->cust.Adresse1))
    result = FALSE;

  array[0] = node->cust.Adresse2;
  array[1] = node->cust.Adresse3;
  array[2] = node->cust.Adresse4;
  array[3] = node->cust.Adresse5;
  array[4] = NULL;
  if (result && !CheckNameArray (cust->Adresse2, array))
    result = FALSE;

  if (result && !CheckName (cust->Telefon, node->cust.Telefon))
    result = FALSE;

  if (result && !CheckName (cust->Fax, node->cust.Fax))
    result = FALSE;

  if (result && !CheckName (cust->Kommentar1, node->cust.Kommentar1))
    result = FALSE;

  if (result && !CheckName (cust->Kommentar2, node->cust.Kommentar2))
    result = FALSE;

  if (result && !CheckName (cust->Kategorie, node->cust.Kategorie))
    result = FALSE;

  if (cust->KundenNummer)
    if (cust->KundenNummer != node->cust.KundenNummer)
      result = FALSE;

  return (result);
}


/*
** neue Kategorie in Liste einfügen
*/

int 
C_AddCategory (STRPTR category)
{
  struct categoryNode *newitem;

  if (!(newitem = LibAllocPooled (poolHeader, sizeof (struct categoryNode))))
      return (error = ERR_CUST_NOMEM);

  status = TRUE;
  strcpy (newitem->category, category);

  InsertSorted (&categoryList, (struct Node *) newitem,
		(struct Node *) aktCategoryNode, CompareCategories);

  aktCategoryNode = newitem;
  categoryCount++;

  return (error = ERR_CUST_OK);
}

/*
** aktuelle Kategorie aus der Liste löschen
*/

int 
C_DelCategory (void)
{
  struct categoryNode *newakt;

  if (categoryCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  status = TRUE;
  if (!aktCategoryNode->Node.ln_Succ->ln_Succ)
    {
      if (categoryCount > 1)
	newakt = (struct categoryNode *) aktCategoryNode->Node.ln_Pred;
      else
	newakt = NULL;

      RemTail (&categoryList);
      LibFreePooled (poolHeader, aktCategoryNode,
                     sizeof (struct categoryNode));
      categoryCount--;
      aktCategoryNode = newakt;
    }
  else
    {
      if (categoryCount > 1)
	newakt = (struct categoryNode *) aktCategoryNode->Node.ln_Succ;
      else
	{
	  newakt = NULL;
	}

      Remove ((struct Node *) aktCategoryNode);
      categoryCount--;
      aktCategoryNode = newakt;
    }

  return (error = ERR_CUST_OK);
}


/*
** Ändere den aktuellen Kundeneintrag
*/

int 
C_ChangeCategory (STRPTR category)
{
  struct customerNode *node;

  if (categoryCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  status = TRUE;

  /* ändere alle Kunden mit der alten Kategorie auch mit.  */
  /* alte Kategorie: aktCategoryNode->category             */
  /* neue Kategorie: category                              */

  for (node = (struct customerNode *) customerList.lh_Head;
       node->Node.ln_Succ;
       node = (struct customerNode *) node->Node.ln_Succ)
    {
      if (strcmp (node->cust.Kategorie, aktCategoryNode->category) == 0)
	{
	  /* Kunde mit alter Kategorie gefunden -> ändern */
	  strcpy (node->cust.Kategorie, category);
	}
    }

  C_DelCategory ();
  C_AddCategory (category);

  return (error = ERR_CUST_OK);
}

/*
**  ermittle eine bestimmte Kategorie aus der Liste
**  nummer läuft von 0...count-1
*/

int 
C_GetCategory (UWORD num, STRPTR category)
{
  struct categoryNode *node;
  int i;

  if (categoryCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  if (num >= categoryCount)
    return (error = ERR_CUST_NOITEM);

  for (i = 0, node = (struct categoryNode *) categoryList.lh_Head;
       i < num && node->Node.ln_Succ;
       i++, node = (struct categoryNode *) node->Node.ln_Succ);

  strcpy (category, node->category);

  return (error = ERR_CUST_OK);
}

/*
** ermittle die Anzahl der Kategorien in der Liste
*/

int 
C_GetCategoryCount (void)
{
  return (categoryCount);
}

/*
** Mache den num-ten Kunden zum aktuellen Kunden in der Liste
*/

int 
C_SetCurrentCategory (UWORD number)
{
  int i;
  struct Node *node;

  if (categoryCount == 0)
    return (error = ERR_CUST_LISTEMPTY);

  if (number >= categoryCount)
    return (error = ERR_CUST_NOITEM);

  for (i = 0, node = categoryList.lh_Head; i < number;
       i++, node = node->ln_Succ);

  aktCategoryNode = (struct categoryNode *) node;

  return (error = ERR_CUST_OK);
}

int 
C_GetCurrentCategoryNumber (void)
{
  int i;
  struct Node *node;

  for (i = 0, node = categoryList.lh_Head;
       ((struct categoryNode *) node != aktCategoryNode) && node->ln_Succ;
       i++, node = node->ln_Succ);

  return (i);
}


/* prüfe, ob Kundennummer bereits vorkommt. */
/* TRUE, falls Nummer eindeutig */
/* FALSE, Nummer gibt es bereits */

static BOOL 
NumberNotExists (UWORD iNumber)
{
  int i;
  struct customerNode *node;
  BOOL searching = TRUE;


  if (IsListEmpty (&customerList))
    return (searching);

  for (i = 0, node = (struct customerNode *) customerList.lh_Head;
       node->Node.ln_Succ && searching;
       i++, node = (struct customerNode *) node->Node.ln_Succ)
    {
      if (iNumber == node->cust.KundenNummer)
	{
	  searching = FALSE;
	}
    }

  return (searching);
}
