/*
** Hilfsfunktionen allgemeiner Art
**
** 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>

#include <exec/types.h>
#include <dos/dos.h>
#include <libraries/locale.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/locale.h>

#define CATCOMP_ARRAY
#include "Locale.h"

#include "LocaleSupport.h"
#include "config.h"

/* Prototypen und privater Zeiger für Kopierfunktionen
** Auch die Hook-Strukturen werden bereitgestellt.
*/

static ULONG __saveds __asm getCharFunc (register __a0 struct Hook *hook,
					 register __a2 void *object,
					 register __a1 void *message);
static ULONG __saveds __asm putCharFunc (register __a0 struct Hook *hook,
					 register __a2 void *object,
					 register __a1 void *message);
static STRPTR CharPointer;
static struct Hook MyGetHook =
{
  {NULL}, (ULONG (*)())getCharFunc, NULL, 0};
static struct Hook MyPutHook =
{
  {NULL}, (ULONG (*)())putCharFunc, NULL, 0};

static void *Stream[14];	/* dataStream für FormatString() */

struct Library *LocaleBase = NULL;
struct Catalog *Catalog = NULL;	/* Zeiger auf den geöffneten Katalog */
struct Locale *Locale = NULL;	/* Zeiger auf die aktuelle Sprache */


/*
** Im Fehlerfall wird der Fehlercode hier gespeichert. Danach
** kann man mit GetErrorMsg() eine Fehlerbeschreibung anfordern.
*/

static int error;

static const STRPTR errormsg[] =
{
  "",						// ERR_LOC_OK
   "konnte locale.library nicht öffnen",	// ERR_LOC_NOLIB
   "konnte Locale nicht öffnen",		// ERR_LOC_NOLOCALE,
   "Locale muß deutsch sein! Bitte benutzen Sie 'deutsch'\n"
  "und 'Deutschland' als Localeeinstellung!"	// ERR_LOC_NOGERMAN 
};

/*
** Konstruktur-Funktion für Locale-Support.
*/

int 
ConstructLocale (void)
{
  if (!(LocaleBase = OpenLibrary ("locale.library", 38)))
    return (error = ERR_LOC_NOLIB);

  if (!(Locale = OpenLocale (NULL)))
    return (error = ERR_LOC_NOLOCALE);

#ifdef ONLY_GERMAN
/* 17.11.94: wieder entfernt, da Registrierung aus der Schweiz */
  if (strcmp (",", Locale->loc_MonDecimalPoint))
    return (error = ERR_LOC_NOGERMAN);

  if (strcmp (".", Locale->loc_MonGroupSeparator))
    return (error = ERR_LOC_NOGERMAN);
#endif

  if (Catalog = OpenCatalog (NULL, "MUI-Rechnung.catalog",
		     OC_BuiltInLanguage, "deutsch", OC_Version, 1, TAG_END))
    {
      /* localize all strings in our string array */
      ULONG num;

      for (num = 0;
           num < (sizeof (CatCompArray) / sizeof (struct CatCompArrayType));
           num++)
        {
	  CatCompArray[num].cca_Str =
	       GetCatalogStr (Catalog, num, CatCompArray[num].cca_Str);
        }
    }
  /* else use internal strings */

  return (error = ERR_LOC_OK);
}

/*
** Destruktor-Funktion für Locale-Support
*/

void 
DestructLocale (void)
{
  if (Locale)
    {
      CloseCatalog (Catalog);
      CloseLocale (Locale);
    }
  if (LocaleBase)
    {
      CloseLibrary (LocaleBase);
    }
}


/*
** Berechnung der heurigen Jahreszahl (z.B. 1994).
*/

ULONG 
GetThisYear (void)
{
  ULONG seconds, micros;
  struct DateStamp Date;
  char buffer[20];

  CurrentTime (&seconds, &micros);
  Date.ds_Days = seconds / 86400;
  Date.ds_Minute = seconds / 60 % 1440;
  Date.ds_Tick = seconds % 60;

  CharPointer = buffer;
  FormatDate (Locale, "%Y", &Date, &MyPutHook);
  return ((ULONG) atol (buffer));
}


/*
** Einfache Korrektur eines Datumstrings.
**
** Der übergebene String wird verändert, der Puffer muß
** mindestens 11 Zeichen lang sein!
*/

void 
CorrectDate (STRPTR date)
{
  ULONG seconds, micros;
  struct DateStamp Date;
  char dateformat[20];

  /* Locale DateFormat in eigenen Puffer umkopieren und das %y durch
  ** %Y ersetzen, damit im Datum immer 1993 anstatt 93 steht.
  */

  strcpy (dateformat, Locale->loc_ShortDateFormat);
  CharPointer = strchr (dateformat, 'y');
  if (CharPointer)
    *CharPointer = 'Y';
  CharPointer = date;
  if (ParseDate (Locale, &Date, dateformat, &MyGetHook))
    seconds = (Date.ds_Days * 24 + Date.ds_Minute) * 3600 + Date.ds_Tick;
  else
    CurrentTime (&seconds, &micros);

  Date.ds_Days = seconds / 86400;
  Date.ds_Minute = seconds / 60 % 1440;
  Date.ds_Tick = seconds % 60;
  CharPointer = date;
  FormatDate (Locale, dateformat, &Date, &MyPutHook);
}


/*
**  Formatiert einen Geldbetrag in einen Puffer mit der aktuellen Locale.
*/

void 
PrintMonetary (STRPTR buffer, double number)
{
  CharPointer = buffer;
  if (number < 0.0)
    {
      Stream[0] = (void *) (LONG) (((-(LONG) (fabs (number)*100 + .5)) / 100));
      Stream[1] = Locale->loc_MonDecimalPoint;
      Stream[2] = (void *) (((LONG) (fabs (number) * 100 + .5)) % 100);
      FormatString (Locale, "%lD%s%02ld", Stream, &MyPutHook);
    }
  else
    {
      Stream[0] = (void *) (LONG) ((((LONG) (number * 100 + .5)) / 100));
      Stream[1] = Locale->loc_MonDecimalPoint;
      Stream[2] = (void *) (((LONG) (number * 100 + .5)) % 100);
      FormatString (Locale, "%lD%s%02ld", Stream, &MyPutHook);
    }
}


/*
**  Formatiert einen Geldbetrag in einen Puffer mit der aktuellen Locale.
**  Zusätzlich wird noch die Währung mit ausgegeben.
*/

void 
PrintCurrency (STRPTR buffer, double number)
{
  CharPointer = buffer;
  if (number < 0.0)
    {
      Stream[0] = (void *) (LONG) (((-(LONG) (fabs (number)*100 + .5)) / 100));
      Stream[1] = Locale->loc_MonDecimalPoint;
      Stream[2] = (void *) (((LONG) (fabs (number) * 100 + .5)) % 100);
      Stream[3] = Locale->loc_MonCS;
      FormatString (Locale, "%lD%s%02ld %s", Stream, &MyPutHook);
    }
  else
    {
      Stream[0] = (void *) (LONG) ((((LONG) (number * 100 + .5)) / 100));
      Stream[1] = Locale->loc_MonDecimalPoint;
      Stream[2] = (void *) (((LONG) (number * 100 + .5)) % 100);
      Stream[3] = Locale->loc_MonCS;
      FormatString (Locale, "%lD%s%02ld %s", Stream, &MyPutHook);
    }
}


/*
**  Wertet einen Geldbetrag aus und wandelt diesen in eine double Zahl.
*/

double 
ScanMonetary (STRPTR string)
{
  double number;
  char buffer[20];
  char Ignore1, Point, Ignore2;
  char *From, *To;

  Ignore1 = *Locale->loc_MonGroupSeparator;
  Point = *Locale->loc_MonDecimalPoint;
  Ignore2 = *Locale->loc_MonFracGroupSeparator;


  if (string)
    {
      From = string;
      while (*From == ' ' || *From == '\t')
	From++;

      To = buffer;
      while (*From && *From != '\t' && *From != '\n')
	{
	  if (*From == Ignore1 || *From == Ignore2)
	    From++;
	  else if (*From == Point)
	    *To = '.', From++, To++;
	  else
	    *To++ = *From++;
	}
      *To = NULL;

      sscanf (buffer, "%lf", &number);

      if (number < 0.0)
	{
	  /* auf Pfennige runden */
	  number = -(double) ((LONG) (fabs (number) * 100 + .5)) / 100;
	}
      else
	{
	  /* auf Pfennige runden */
	  number = (double) ((LONG) (number * 100 + .5)) / 100;
	}
    }

  return (number);
}

double 
ScanMonetaryNew (STRPTR string, STRPTR group, STRPTR decimal, STRPTR fracgroup)
{
  double number;
  char buffer[20];
  char Ignore1, Point, Ignore2;
  char *From, *To;

  Ignore1 = *group;
  Point = *decimal;
  Ignore2 = *fracgroup;


  if (string)
    {
      From = string;
      while (*From == ' ' || *From == '\t')
	From++;

      To = buffer;
      while (*From && *From != '\t' && *From != '\n')
	{
	  if (*From == Ignore1 || *From == Ignore2)
	    From++;
	  else if (*From == Point)
	    *To = '.', From++, To++;
	  else
	    *To++ = *From++;
	}
      *To = NULL;

      sscanf (buffer, "%lf", &number);

      if (number < 0.0)
	{
	  /* auf Pfennige runden */
	  number = -(double) ((LONG) (fabs (number) * 100 + .5)) / 100;
	}
      else
	{
	  /* auf Pfennige runden */
	  number = (double) ((LONG) (number * 100 + .5)) / 100;
	}
    }

  return (number);
}

/*
** noch eine Funktion zum lesen einer Fließpunktzahl.
**
** Hierbei wird nur das letzte Zeichen, das keine Ziffer ist als Komma
** angesehen. Alle anderen Zeichen, die keine Ziffern sind, werden als
** Separationszeichen ignoriert.
**
** Dadurch können Beträge unterschiedlicher Währungen eingelesen werden.
*/

double 
ScanMonetaryMulti (STRPTR string)
{
  double number;
  char buffer[20];
  char *Point;
  char *From, *To;

  if (string)
    {
      From = string;
      while (*From == ' ' || *From == '\t')
	From++;

      /* suche das Kommazeichen */
      while (*From && *From != '\t' && *From != '\n')
	{
	  if (*From < '0' || *From > '9')
	    Point = From;
	  From++;
	}

      To = buffer;
      From = string;
      while (*From && From < Point && *From != '\t' && *From != '\n')
	{
	  if (*From < '0' || *From > '9')
	    From++;
	  else
	    *To++ = *From++;
	}
      *To++ = '.';
      while (*From && *From != '\t' && *From != '\n')
	{
	  if (*From < '0' || *From > '9')
	    From++;
	  else
	    *To++ = *From++;
	}
      *To = NULL;

      sscanf (buffer, "%lf", &number);

      if (number < 0.0)
	{
	  /* auf Pfennige runden */
	  number = -(double) ((LONG) (fabs (number) * 100 + .5)) / 100;
	}
      else
	{
	  /* auf Pfennige runden */
	  number = (double) ((LONG) (number * 100 + .5)) / 100;
	}
    }

  return (number);
}


/*
**  Hook getCharFunc - für Datumsauswertung
*/

static ULONG __saveds __asm 
getCharFunc (register __a0 struct Hook *hook,
	     register __a2 void *object,
	     register __a1 void *message)
{
  return ((ULONG) * CharPointer++);
}

static ULONG __saveds __asm 
putCharFunc (register __a0 struct Hook *hook,
	     register __a2 void *object,
	     register __a1 void *message)
{
  *CharPointer++ = (char) message;
  return (TRUE);	/* zwar nicht nötig, aber wegen den Prototypes */
}


/*
** Hole den Fehlerstring
*/

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


/* zeige einen Betrag mit oder ohne MwSt an
**
** Parameter:
**  buffer   = Zielpuffer für Betrag
**  betrag   = Nettobetrag
**  mwstsatz = MwSt (15 oder 7)
**  mode     = Darstellung in Netto(=0) oder Brutto (=1)
*/


void 
DisplayBetrag (STRPTR buffer, double betrag, double mwstsatz, UWORD mode)
{
  if (CO_GetIsNetto ())
    mode = 0;

  if (mode)
    {
      /* Brutto darstellen */
      double mwst;

      if (betrag < 0.0)
	{
	  mwst = -(double) ((LONG) (fabs (betrag) * mwstsatz + .5)) / 100;
	}
      else
	{
	  mwst = (double) ((LONG) (betrag * mwstsatz + .5)) / 100;
	}
      PrintCurrency (buffer, betrag + mwst);
    }
  else
    {
      /* Netto darstellen */
      PrintCurrency (buffer, betrag);
    }
}

void 
DisplayBetragOhneDM (STRPTR buffer, double betrag, double mwstsatz, UWORD mode)
{
  if (CO_GetIsNetto ())
    mode = 0;

  if (mode)
    {
      /* Brutto darstellen */
      double mwst;

      if (betrag < 0.0)
	{
	  mwst = -(double) ((LONG) (fabs (betrag) * mwstsatz + .5)) / 100;
	}
      else
	{
	  mwst = (double) ((LONG) (betrag * mwstsatz + .5)) / 100;
	}
      PrintMonetary (buffer, betrag + mwst);
    }
  else
    {
      /* Netto darstellen */
      PrintMonetary (buffer, betrag);
    }
}

/*
** rechne einen Bruttobetrag in einen Nettobetrag um. Hierbei ist
** Vorsicht beim Runden geboten, da sonst die Pfennige bereits auf "00"
** umschalten, bevor die Märker um eins aufgerundet werden.
**
** Parameter
**
** zahl - der Bruttobetrag
** mwst - die MwSt in Prozent
*/

double 
Brutto2Netto (double zahl, double mwst)
{
  if (CO_GetIsNetto ())
    {
      if (zahl < 0.0)
	{
	  zahl = -(double) ((LONG) (fabs (zahl) * 100 + .5)) / 100;
	}
      else
	{
	  zahl = (double) ((LONG) (zahl * 100 + .5)) / 100;
	}
      return (zahl);
    }
  else
    {
      if (zahl < 0.0)
	{
	  /* auf Pfennige runden */
	  zahl = -(double) ((LONG) (fabs (zahl) * 100 + .5)) / 100;
	  return (-(double) ((long) ((fabs (zahl) * 100 /
	          (100 + fabs (mwst))) * 100 + .5)) / 100);
	}
      else
	{
	  /* auf Pfennige runden */
	  zahl = (double) ((LONG) (zahl * 100 + .5)) / 100;
	  return ((double) ((long) ((zahl * 100 /
	          (100 + mwst)) * 100 + .5)) / 100);
	}
    }
}
