(*------------------------------------------

  :Module.      TranslationOutput.mod
  :Author.      Albert Weinert  [awn]
  :Address.     Krähenweg 21 , D-5000 Köln 30
  :EMail.       Usenet_> aweinert@darkness.gun.de
  :EMail.       Z-Netz_> A.WEINERT@DARKNESS.ZER
  :Phone.       0221 / 580 29 84
  :Revision.    R.1
  :Date.        09-Jul-1993
  :Copyright.   Albert Weinert
  :Language.    Oberon-2
  :Translator.  Amiga Oberon 3.00d
  :Contents.    <Kurzbeschreibung>
  :Imports.     <Importiertes, Name/ShortCut des Autors>
  :Remarks.     <Was Du willst, evtl. Usage>
  :Bugs.        <Bekannte Fehler>
  :Usage.       <Angaben zur Anwendung>
  :History.     .0     [awn] 23-May-1993 : Erstellt
  :History.     .1     [awn] 09-Jul-1993 : Falsche Node übergabe wenn Desc String zu lang.

--------------------------------------------*)
MODULE TranslationOutput;

IMPORT  cd:=C2OData,
        cl:=KitCatLocale,
        ll:=LinkedLists,
        ms:=MoreStrings,
        BT:=BasicTypes,
        Strings,
        sc:=StringConvert,
        SYSTEM,
        Dos;
CONST  s1 = 0;
       c = 1;
       d = 2;
       x = 3;
       u = 4;
       b = 5;
       ld = 6;
       lx = 7;
       lu = 8;
       lc = 9;
       D = 10;
       U = 11;
       X = 12;
       lD= 13;
       lU= 14;
       lX= 15;
       max = 16;

TYPE format = ARRAY max OF INTEGER;
(*       format = RECORD
                s : INTEGER;
                c : INTEGER;
                d : INTEGER;
                x : INTEGER;
                u : INTEGER;
                b : INTEGER;
                ld : INTEGER;
                lx : INTEGER;
                lu : INTEGER;
                lc : INTEGER;
                D : INTEGER;
                U : INTEGER;
                X : INTEGER;
                lD: INTEGER;
                lU: INTEGER;
                lX: INTEGER;
              END; *)

       charb = STRUCT;
                char : CHAR;
                found: BOOLEAN;
                end : BOOLEAN;
               END;

       chary = ARRAY 13 OF charb;


VAR writeok : BOOLEAN;

  PROCEDURE PrintError(errnum : LONGINT; p1,p2,p3,p4:SYSTEM.ADDRESS);
    BEGIN
      Dos.PrintF(cl.GetString(cl.msgWarning)^);
      IF errnum # -1 THEN
        Dos.PrintF(cl.GetString(errnum)^,p1,p2,p3,p4);
      ELSE
        Dos.PrintF(cl.GetString(cd.error)^,p1,p2,p3,p4);
      END;
      Dos.PrintF("\n");
    END PrintError;


  PROCEDURE CheckTranslation*(onlycd,melt:BOOLEAN):BOOLEAN;
  (*------------------------------------------
    :Input.
    :Output.    TRUE bei Erfolg
    :Semantic.  Überprüft die aktuelle Translation Liste mit der DescriptionListe
    :Note.
    :Update.    23-May-1993 [awn] - erstellt.
    :Update.    09-Jul-1993 [awn] - Dupecheck eingebaut
  --------------------------------------------*)

    (* $ClearVars+ *)
    VAR str1: BT.DynString;
        str2: BT.DynString;
        dum : BT.DynString;
        i : INTEGER;
        f1,f2 : format;
        sn,dn,tn:ll.Node;
        ok, error : BOOLEAN;
        dlen, tlen : LONGINT;

      PROCEDURE MeltCdAndCt():BOOLEAN;
      (*------------------------------------------
        :Input.
        :Output.
        :Semantic.  Verschmelzt die Description und die Translation
        :Note.
        :Update.    27-May-1993 [awn] - erstellt.
        :Update.    29-May-1993 [awn] - -Es wurden die Falschen Text zu geordnet
      --------------------------------------------*)

      (* $ClearVars+ *)
      VAR dn, tn: ll.Node;
          ok,found : BOOLEAN;
      BEGIN
        dn:=cd.CDList.head;
        tn:=cd.CTList.head;
        ok:=TRUE;
        WHILE (dn # NIL) & ok DO
          tn:=cd.CTList.head;
          found:=FALSE;
          WHILE (tn # NIL) & ok & ~found DO
            IF tn(cd.CTNode).idName^ = dn(cd.CDNode).idName^ THEN
              found:=TRUE;
              tn(cd.CTNode).idNumber:=dn(cd.CDNode).idNumber;
              dn(cd.CDNode).tnode:=tn(cd.CTNode);
              tn(cd.CTNode).lengthbytes:=dn(cd.CDNode).lengthbytes;
              tn(cd.CTNode).dnode:=dn(cd.CDNode);
              INCL(dn(cd.CDNode).flags,cd.set);
              INCL(tn(cd.CTNode).flags,cd.set);
            END;
            tn:=tn.next;
          END;
          dn:=dn.next;
        END;
        RETURN ok;
      END MeltCdAndCt;

      PROCEDURE CountFormat(s:BT.DynString;VAR f:format):BOOLEAN;
      (*------------------------------------------
        :Input.     s=String der "bezählt" werden soll
        :Output.    f=Enthält die anzahl der entsprechenden Format
        :Semantic.
        :Note.
        :Update.    29-May-1993 [awn] - erstellt.
      --------------------------------------------*)
        VAR i,j : LONGINT;
            found,found2 : BOOLEAN;
            chars : chary;
        BEGIN
          f:=format(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          ok:=TRUE;
          i:=0;
          WHILE (s[i] # "\o") & ok DO
            IF s[i] = "%" THEN
              found:=TRUE;
              chars:=chary("b",FALSE,TRUE,
                           "s",FALSE,TRUE,
                           ".",FALSE,FALSE,
                           "-",FALSE,FALSE,
                           "$",FALSE,FALSE,
                           "d",FALSE,TRUE,
                           "c",FALSE,TRUE,
                           "u",FALSE,TRUE,
                           "x",FALSE,TRUE,
                           "l",FALSE,FALSE,
                           "U",FALSE,TRUE,
                           "D",FALSE,TRUE,
                           "X",FALSE,TRUE);

              WHILE found & (s[i]#"\o") DO
                INC(i);
                j:=0;
                found2:=FALSE;
                WHILE (j < LEN(chars)) & ~found2 & found DO
                  IF s[i] = chars[j].char THEN
                    IF chars[j].found THEN
                      found2:=TRUE;
                      found:=FALSE;
                    ELSE;
                      chars[j].found:=TRUE;
                      found:=~chars[j].end;
                      found2:=TRUE;
                    END;
                  END;
                  INC(j);
                END (* WHILE (chars... *);
                IF ~found2 THEN
                  IF ~((s[i]>="0") & (s[i]<="9")) THEN
                    found:=FALSE
                  END;
                END;

              END; (* WHILE found DO *)

              CASE s[i-1] OF
                | "l" :
                    CASE s[i] OF
                      | "c" : INC(f[lc]);
                      | "d" : INC(f[ld]);
                      | "u" : INC(f[lu]);
                      | "x" : INC(f[lx]);
                      | "D" : INC(f[lD]);
                      | "U" : INC(f[lU]);
                      | "X" : INC(f[lX]);
                    ELSE
                     error:=TRUE;
                     PrintError(cl.errUnknownFormat,
                                cd.para1,
                                cd.para2,
                                cd.para3,NIL);
                    END;

              ELSE
                CASE s[i] OF
                  | "c" : INC(f[c]);
                  | "d" : INC(f[d]);
                  | "u" : INC(f[u]);
                  | "x" : INC(f[x]);
                  | "D" : INC(f[D]);
                  | "U" : INC(f[U]);
                  | "X" : INC(f[X]);
                  | "s" : INC(f[s1]);
                  | "b" : INC(f[b]);
                ELSE
                  ok:=FALSE;
                  PrintError(cl.errUnknownFormat,
                             cd.para1,
                             cd.para2,
                             cd.para3,NIL);
                END;
              END;

            END;
            INC(i);
          END;

          RETURN ok;
        END CountFormat;
    BEGIN
      ok:=TRUE;
      error:=FALSE;
      IF ~ onlycd OR melt THEN
        IF cd.CTList.isEmpty() THEN RETURN TRUE END;
        ok:=MeltCdAndCt();
      END;
      IF ~onlycd THEN
        IF ok THEN
          tn:=cd.CTList.head;
          WHILE (tn # NIL) & ok DO
            IF ~(cd.set IN tn(cd.CTNode).flags) THEN
              error:=TRUE;
              PrintError(cl.errCTnotinCD,
                         SYSTEM.ADR(tn(cd.CTNode).idName^),
                         tn(cd.CTNode).line,
                         NIL,NIL);
            END;
            tn:=tn.next;
          END;
        END;
        IF ok THEN
          dn:=cd.CDList.head;
          WHILE (dn # NIL) & ok DO
            IF ~(cd.set IN dn(cd.CDNode).flags) THEN
            error:=TRUE;
            PrintError(cl.errCDnotinCT,
                       SYSTEM.ADR(dn(cd.CDNode).idName^),
                       dn(cd.CDNode).line,
                       NIL,NIL);
            END;
            dn:=dn.next;
          END;
        END;
      END;
      ok:=~error;
      IF ok THEN
        dn:=cd.CDList.head;
        WHILE (dn#NIL) & ok DO
          IF ~onlycd THEN
            ok:=sc.Convert(dn(cd.CDNode).tnode.fullstr^,FALSE,FALSE,dn(cd.CDNode).tnode.converted);
            IF ~ok THEN
              PrintError( cd.error, SYSTEM.ADR( dn(cd.CDNode).tnode.fullstr^ ), NIL, NIL, NIL );
              cd.error := NIL;
            END;
          END;
          IF ok THEN
            ok:=sc.Convert(dn(cd.CDNode).fullstr^,FALSE,FALSE,dn(cd.CDNode).converted);
            IF ~ok THEN
              PrintError( cd.error, SYSTEM.ADR( dn(cd.CDNode).fullstr^ ), NIL, NIL, NIL );
              cd.error := NIL;
            END;
          END;
          (* Zähle und Prüfe Formatzeichen in der Beschreibung *)
          IF ok THEN
            cd.para1:=SYSTEM.ADR(cd.CDList.filename^);
            cd.para2:=SYSTEM.ADR(dn(cd.CDNode).idName^);
            cd.para3:=dn(cd.CDNode).line;
            ok:=CountFormat(dn(cd.CDNode).converted,f1);
          END;

          IF ~onlycd THEN
            (* Zähle und Prüfe die Formatzeich in der Übersetzung *)
            IF ok THEN
              cd.para1:=SYSTEM.ADR(cd.CTList.filename^);
              cd.para2:=SYSTEM.ADR(dn(cd.CDNode).tnode.idName^);
              cd.para3:=dn(cd.CDNode).tnode.line;
              ok:=CountFormat(dn(cd.CDNode).tnode.converted,f2);
            END;
          END;

          IF ~onlycd THEN
            (* Vergleich Formatzeichen in Beschreibung und Übersetzung *)
            IF ok THEN
              FOR i:=0 TO max-1 DO
                IF f1[i] # f2[i] THEN
                  error:=TRUE;
                  PrintError(cl.errWrongFormat,
                             SYSTEM.ADR(cd.CTList.filename^),
                             SYSTEM.ADR(dn(cd.CDNode).tnode.idName^),
                             dn(cd.CDNode).tnode.line,NIL);
                  i:=max-1;
                END;
              END;
            END;
          END;
          IF ok THEN
            dlen:=Strings.Length(dn(cd.CDNode).converted^);
            IF ~onlycd THEN
              tlen:=Strings.Length(dn(cd.CDNode).tnode.converted^);
            END;
  
            (* Überprüfe die minimale Länge der Beschreibung und Übersetzung *)
            IF dn(cd.CDNode).minLen # -1 THEN
              IF dlen < (dn(cd.CDNode).minLen) THEN
                error:=TRUE;
                PrintError(cl.errStringToShort,
                           SYSTEM.ADR(dn(cd.CDNode).idName^),
                           SYSTEM.ADR(cd.CDList.filename^),
                           tn(cd.CTNode).line,NIL);
              END;
              IF ~onlycd THEN
                IF tlen < (dn(cd.CDNode).minLen) THEN
                  error:=TRUE;
                  PrintError(cl.errStringToShort,
                             SYSTEM.ADR(dn(cd.CDNode).tnode.idName^),
                             SYSTEM.ADR(cd.CTList.filename^),
                             dn(cd.CDNode).tnode.line,NIL);
                END;
              END;
            END;
  
            (* Überprüf die Maximale Länger der Beschreibung und Übersetzung *)
            IF dn(cd.CDNode).maxLen # -1 THEN
              IF dlen > (dn(cd.CDNode).maxLen) THEN
                error:=TRUE;
                PrintError(cl.errStringToLong,
                           SYSTEM.ADR(dn(cd.CDNode).idName^),
                           SYSTEM.ADR(cd.CDList.filename^),
                           dn(cd.CDNode).line,NIL);
              END;
              IF ~onlycd THEN
                IF tlen > (dn(cd.CDNode).maxLen) THEN
                  PrintError(cl.errStringToLong,
                             SYSTEM.ADR(dn(cd.CDNode).tnode.idName^),
                             SYSTEM.ADR(cd.CTList.filename^),
                             dn(cd.CDNode).tnode.line,NIL);
                END;
              END;
            END;
          END;
          dn:=dn.next;
        END;
      END;

      IF ~onlycd THEN
        IF (cd.CTList.language = "") THEN
          cd.error:=cl.errNoLanguage;
          cd.para1:=SYSTEM.ADR(cd.CTList.filename^);
          ok:=FALSE;
        END;
        IF (cd.CTList.version = "") THEN
          cd.error:=cl.errNoVersion;
          cd.para1:=SYSTEM.ADR(cd.CTList.filename^);
          ok:=FALSE;
        END;
      END;

      (* Dupecheck *)
      dn:=cd.CDList.head;
      WHILE dn # NIL DO
        IF ~ (cd.dupe IN dn(cd.Node).flags) THEN
          tn:=dn.next;
          WHILE tn # NIL DO
            IF tn(cd.CDNode).idName^ = dn(cd.CDNode).idName^ THEN
              INCL(tn(cd.Node).flags,cd.dupe);
              error:=TRUE;
              PrintError(cl.errDupeInDesc,
                         SYSTEM.ADR(dn(cd.CDNode).idName^),
                         SYSTEM.ADR(cd.CDList.filename^),
                         dn(cd.Node).line,
                         tn(cd.Node).line);
            END (* END IF tn(cd.Node)... *);
            tn:=tn.next;
          END (* WHILE tn # NIL DO *);
        END (* IF ~ (cd.dupe IN) *);
        dn:=dn.next;
      END (* WHILE dn # NIL DO *);


      IF error THEN RETURN FALSE END;
      RETURN ok;
    END CheckTranslation;

  PROCEDURE Create*(name:ARRAY OF CHAR):BOOLEAN;
  (*------------------------------------------
    :Input.     name = name der Datei
    :Output.    TRUE bei Erfolg
    :Semantic.  Erstellte eine Translation Datei.
    :Note.
    :Update.    23-May-1993 [awn] - erstellt.
  --------------------------------------------*)
    VAR fh : Dos.FileHandlePtr;
        dn : ll.Node;
        sn : ll.Node;
        sl : ll.List;
    BEGIN
      fh:=Dos.Open(name,Dos.newFile);
      writeok:=fh # NIL;
      IF writeok THEN
        IF ~cd.CTList.comlist.isEmpty() THEN
          sn:=cd.CTList.comlist.head;
          WHILE (sn # NIL) & writeok DO
            writeok:=Dos.FPrintf(fh,"%s\n",SYSTEM.ADR(sn(cd.SNode).string^))#-1;
            sn:=sn.next;
            END;
          IF writeok THEN
            writeok:=Dos.FPrintf(fh,";;\n")#-1;
          END;
        ELSE
          writeok:=Dos.FPrintf(fh,"## version $VER: XX.catalog XX.XX (XX.XX.XX)\n"
                                  "## codeset 0\n"
                                  "## language xx\n;;\n")#-1;
        END;
        dn:=cd.CDList.head;
        WHILE (dn # NIL) & writeok DO
          WITH dn : cd.CDNode DO;
            sn:=NIL;
            sl:=dn(cd.CDNode).comments;
            IF sl # NIL THEN
              sn:=sl.head;
            END;
            WHILE (sn # NIL) AND writeok DO
              IF sn(cd.SNode).string # NIL THEN
                writeok:=Dos.FPrintf(fh,"%s\n",SYSTEM.ADR(sn(cd.SNode).string^))#-1;
              END;
              sn:=sn.next;
            END;
            IF writeok THEN
              writeok:=Dos.FPrintf(fh,"%s\n",SYSTEM.ADR(dn.idName^))#-1;
            END;
            IF writeok THEN
              IF dn.tnode = NIL THEN
                writeok:=Dos.FPrintf(fh,"\n")#-1;
              ELSE;
                sn:=dn.tnode(cd.CTNode).strings.head;
                WHILE (sn # NIL) & writeok DO
                  writeok:=Dos.FPrintf(fh,"%s",SYSTEM.ADR(sn(cd.SNode).string^))#-1;
                  sn:=sn.next;
                  IF writeok THEN
                    IF sn # NIL THEN
                      writeok:=Dos.FPrintf(fh,"\\\n")#-1;
                    ELSE
                      writeok:=Dos.FPrintf(fh,"\n")#-1;
                    END;
                  END;
                END;
              END;
              sn:=dn.strings.head;
              WHILE (sn # NIL) & writeok DO
                writeok:=Dos.FPrintf(fh,";; %s;\n",SYSTEM.ADR(sn(cd.SNode).string^))#-1;
                sn:=sn.next;
                IF writeok THEN
                  IF sn = NIL THEN
                    writeok:=Dos.FPrintf(fh,";;\n")#-1;
                  END;
                END;
              END;
            END;
          END;
          dn:=dn.next;
        END;
        Dos.OldClose(fh);
      END;
      RETURN writeok;
    END Create;

END TranslationOutput.
