(*
    BSD 2-Clause License

    Copyright (c) 2018-2022, Anton Krotov
    All rights reserved.
*)

MODULE HOST;

IMPORT SYSTEM, K := KOSAPI, API;


CONST

    slash* = "/";
    eol* = 0DX + 0AX;

    bit_depth* = API.BIT_DEPTH;
    maxint* = ROR(-2, 1);
    minint* = ROR(1, 1);

    MAX_PARAM = 1024;


TYPE

    DAYS = ARRAY 12, 31, 2 OF INTEGER;

    FNAME = ARRAY 520 OF CHAR;

    FS = POINTER TO rFS;

    rFS = RECORD
        subfunc, pos, hpos, bytes, buffer: INTEGER;
        name: FNAME
    END;

    FD = POINTER TO rFD;

    rFD = RECORD
        attr: INTEGER;
        ntyp: CHAR;
        reserved: ARRAY 3 OF CHAR;
        time_create, date_create,
        time_access, date_access,
        time_modif,  date_modif,
        size, hsize: INTEGER;
        name: FNAME
    END;


VAR


    Console: BOOLEAN;

    days: DAYS;

    Params: ARRAY MAX_PARAM, 2 OF INTEGER;
    argc*: INTEGER;

    maxreal*, inf*: REAL;


PROCEDURE [stdcall, "Console.obj", "con_init"] con_init (wnd_width, wnd_height, scr_width, scr_height, title: INTEGER);

PROCEDURE [stdcall, "Console.obj", "con_exit"] con_exit (bCloseWindow: BOOLEAN);

PROCEDURE [stdcall, "Console.obj", "con_write_string"] con_write_string (string, length: INTEGER);


PROCEDURE ExitProcess* (p1: INTEGER);
BEGIN
    IF Console THEN
        con_exit(FALSE)
    END;
    K.sysfunc1(-1)
END ExitProcess;


PROCEDURE OutChar* (c: CHAR);
BEGIN
    IF Console THEN
        con_write_string(SYSTEM.ADR(c), 1)
    ELSE
        K.sysfunc3(63, 1, ORD(c))
    END
END OutChar;


PROCEDURE GetFileInfo (FName: ARRAY OF CHAR; VAR Info: rFD): BOOLEAN;
VAR
    res2: INTEGER;
    fs:   rFS;

BEGIN
    fs.subfunc := 5;
    fs.pos := 0;
    fs.hpos := 0;
    fs.bytes := 0;
    fs.buffer := SYSTEM.ADR(Info);
    COPY(FName, fs.name)
    RETURN K.sysfunc22(70, SYSTEM.ADR(fs), res2) = 0
END GetFileInfo;


PROCEDURE Exists (FName: ARRAY OF CHAR): BOOLEAN;
VAR
    fd: rFD;

BEGIN
    RETURN GetFileInfo(FName, fd) & ~(4 IN BITS(fd.attr))
END Exists;


PROCEDURE Close (VAR F: FS);
BEGIN
    IF F # NIL THEN
        DISPOSE(F)
    END
END Close;


PROCEDURE Open (FName: ARRAY OF CHAR): FS;
VAR
    F: FS;

BEGIN
    IF Exists(FName) THEN
        NEW(F);
        IF F # NIL THEN
            F.subfunc := 0;
            F.pos := 0;
            F.hpos := 0;
            F.bytes := 0;
            F.buffer := 0;
            COPY(FName, F.name)
        END
    ELSE
        F := NIL
    END

    RETURN F
END Open;


PROCEDURE Read (F: FS; Buffer, Count: INTEGER): INTEGER;
VAR
    res, res2: INTEGER;

BEGIN
    IF F # NIL THEN
        F.subfunc := 0;
        F.bytes := Count;
        F.buffer := Buffer;
        res := K.sysfunc22(70, SYSTEM.ADR(F^), res2);
        IF res2 > 0 THEN
            F.pos := F.pos + res2
        END
    ELSE
        res2 := 0
    END

    RETURN res2
END Read;


PROCEDURE Write (F: FS; Buffer, Count: INTEGER): INTEGER;
VAR
    res, res2: INTEGER;

BEGIN
    IF F # NIL THEN
        F.subfunc := 3;
        F.bytes := Count;
        F.buffer := Buffer;
        res := K.sysfunc22(70, SYSTEM.ADR(F^), res2);
        IF res2 > 0 THEN
            F.pos := F.pos + res2
        END
    ELSE
        res2 := 0
    END

    RETURN res2
END Write;


PROCEDURE Create (FName: ARRAY OF CHAR): FS;
VAR
    F:    FS;
    res2: INTEGER;

BEGIN
    NEW(F);
    IF F # NIL THEN
        F.subfunc := 2;
        F.pos := 0;
        F.hpos := 0;
        F.bytes := 0;
        F.buffer := 0;
        COPY(FName, F.name);
        IF K.sysfunc22(70, SYSTEM.ADR(F^), res2) # 0 THEN
            DISPOSE(F)
        END
    END

    RETURN F
END Create;


PROCEDURE FileRead* (F: INTEGER; VAR Buffer: ARRAY OF CHAR; bytes: INTEGER): INTEGER;
VAR
    n: INTEGER;
    fs: FS;

BEGIN
    SYSTEM.GET(SYSTEM.ADR(F), fs);
    n := Read(fs, SYSTEM.ADR(Buffer[0]), bytes);
    IF n = 0 THEN
        n := -1
    END

    RETURN n
END FileRead;


PROCEDURE FileWrite* (F: INTEGER; Buffer: ARRAY OF BYTE; bytes: INTEGER): INTEGER;
VAR
    n: INTEGER;
    fs: FS;

BEGIN
    SYSTEM.GET(SYSTEM.ADR(F), fs);
    n := Write(fs, SYSTEM.ADR(Buffer[0]), bytes);
    IF n = 0 THEN
        n := -1
    END

    RETURN n
END FileWrite;


PROCEDURE FileCreate* (FName: ARRAY OF CHAR): INTEGER;
VAR
    fs: FS;
    res: INTEGER;

BEGIN
    fs := Create(FName);
    SYSTEM.GET(SYSTEM.ADR(fs), res)
    RETURN res
END FileCreate;


PROCEDURE FileClose* (F: INTEGER);
VAR
    fs: FS;

BEGIN
    SYSTEM.GET(SYSTEM.ADR(F), fs);
    Close(fs)
END FileClose;


PROCEDURE FileOpen* (FName: ARRAY OF CHAR): INTEGER;
VAR
    fs: FS;
    res: INTEGER;

BEGIN
    fs := Open(FName);
    SYSTEM.GET(SYSTEM.ADR(fs), res)
    RETURN res
END FileOpen;


PROCEDURE chmod* (FName: ARRAY OF CHAR);
END chmod;


PROCEDURE GetTickCount* (): INTEGER;
    RETURN K.sysfunc2(26, 9)
END GetTickCount;


PROCEDURE AppAdr (): INTEGER;
VAR
    buf: ARRAY 1024 OF CHAR;
    a: INTEGER;

BEGIN
    a := K.sysfunc3(9, SYSTEM.ADR(buf), -1);
    SYSTEM.GET(SYSTEM.ADR(buf) + 22, a)
    RETURN a
END AppAdr;


PROCEDURE GetCommandLine (): INTEGER;
VAR
    param: INTEGER;

BEGIN
    SYSTEM.GET(28 + AppAdr(), param)
    RETURN param
END GetCommandLine;


PROCEDURE GetName (): INTEGER;
VAR
    name: INTEGER;

BEGIN
    SYSTEM.GET(32 + AppAdr(), name)
    RETURN name
END GetName;


PROCEDURE GetChar (adr: INTEGER): CHAR;
VAR
    res: CHAR;

BEGIN
    SYSTEM.GET(adr, res)
    RETURN res
END GetChar;


PROCEDURE ParamParse;
VAR
    p, count, name, cond: INTEGER;
    c: CHAR;


    PROCEDURE ChangeCond (A, B, C: INTEGER; c: CHAR; VAR cond: INTEGER);
    BEGIN
        IF (c <= 20X) & (c # 0X) THEN
            cond := A
        ELSIF c = 22X THEN
            cond := B
        ELSIF c = 0X THEN
            cond := 6
        ELSE
            cond := C
        END
    END ChangeCond;


BEGIN
    p := GetCommandLine();
    name := GetName();
    Params[0, 0] := name;
    WHILE GetChar(name) # 0X DO
        INC(name)
    END;
    Params[0, 1] := name - 1;
    cond := 0;
    count := 1;
    WHILE (argc < MAX_PARAM) & (cond # 6) DO
        c := GetChar(p);
        CASE cond OF
        |0: ChangeCond(0, 4, 1, c, cond); IF cond = 1 THEN Params[count, 0] := p END
        |1: ChangeCond(0, 3, 1, c, cond); IF cond IN {0, 6} THEN Params[count, 1] := p - 1; INC(count) END
        |3: ChangeCond(3, 1, 3, c, cond); IF cond = 6 THEN Params[count, 1] := p - 1; INC(count) END
        |4: ChangeCond(5, 0, 5, c, cond); IF cond = 5 THEN Params[count, 0] := p END
        |5: ChangeCond(5, 1, 5, c, cond); IF cond = 6 THEN Params[count, 1] := p - 1; INC(count) END
        |6:
        END;
        INC(p)
    END;
    argc := count
END ParamParse;


PROCEDURE GetArg* (n: INTEGER; VAR s: ARRAY OF CHAR);
VAR
    i, j, len: INTEGER;
    c: CHAR;

BEGIN
    j := 0;
    IF n < argc THEN
        len := LEN(s) - 1;
        i := Params[n, 0];
        WHILE (j < len) & (i <= Params[n, 1]) DO
            c := GetChar(i);
            IF c # 22X THEN
                s[j] := c;
                INC(j)
            END;
            INC(i)
        END
    END;
    s[j] := 0X
END GetArg;


PROCEDURE GetCurrentDirectory* (VAR path: ARRAY OF CHAR);
VAR
    n: INTEGER;

BEGIN
    n := K.sysfunc4(30, 2, SYSTEM.ADR(path[0]), LEN(path) - 2);
    path[n - 1] := slash;
    path[n] := 0X
END GetCurrentDirectory;


PROCEDURE isRelative* (path: ARRAY OF CHAR): BOOLEAN;
    RETURN path[0] # slash
END isRelative;


PROCEDURE UnixTime* (): INTEGER;
VAR
    date, time, year, month, day, hour, min, sec: INTEGER;

BEGIN
    date  := K.sysfunc1(29);
    time  := K.sysfunc1(3);

    year  := date MOD 16;
    date  := date DIV 16;
    year  := (date MOD 16) * 10 + year;
    date  := date DIV 16;

    month := date MOD 16;
    date  := date DIV 16;
    month := (date MOD 16) * 10 + month;
    date  := date DIV 16;

    day   := date MOD 16;
    date  := date DIV 16;
    day   := (date MOD 16) * 10 + day;
    date  := date DIV 16;

    hour  := time MOD 16;
    time  := time DIV 16;
    hour  := (time MOD 16) * 10 + hour;
    time  := time DIV 16;

    min   := time MOD 16;
    time  := time DIV 16;
    min   := (time MOD 16) * 10 + min;
    time  := time DIV 16;

    sec   := time MOD 16;
    time  := time DIV 16;
    sec   := (time MOD 16) * 10 + sec;
    time  := time DIV 16;

    INC(year, 2000)

    RETURN ((year - 1970) * 365 + days[month - 1, day - 1, ORD(year DIV 4 = 0)] + (year - 1969) DIV 4) * 86400 + hour * 3600 + min * 60 + sec
END UnixTime;


PROCEDURE splitf* (x: REAL; VAR a, b: INTEGER): INTEGER;
BEGIN
    SYSTEM.GET32(SYSTEM.ADR(x), a);
    SYSTEM.GET32(SYSTEM.ADR(x) + 4, b)
    RETURN a
END splitf;


PROCEDURE d2s* (x: REAL): INTEGER;
VAR
    h, l, s, e: INTEGER;

BEGIN
    e := splitf(x, l, h);

    s := ASR(h, 31) MOD 2;
    e := (h DIV 100000H) MOD 2048;
    IF e <= 896 THEN
        h := (h MOD 100000H) * 8 + (l DIV 20000000H) MOD 8 + 800000H;
        REPEAT
            h := h DIV 2;
            INC(e)
        UNTIL e = 897;
        e := 896;
        l := (h MOD 8) * 20000000H;
        h := h DIV 8
    ELSIF (1151 <= e) & (e < 2047) THEN
        e := 1151;
        h := 0;
        l := 0
    ELSIF e = 2047 THEN
        e := 1151;
        IF (h MOD 100000H # 0) OR (BITS(l) * {0..31} # {}) THEN
            h := 80000H;
            l := 0
        END
    END;
    DEC(e, 896)

    RETURN LSL(s, 31) + LSL(e, 23) + (h MOD 100000H) * 8 + (l DIV 20000000H) MOD 8
END d2s;


PROCEDURE init (VAR days: DAYS);
VAR
    i, j, n0, n1: INTEGER;

BEGIN

    FOR i := 0 TO 11 DO
        FOR j := 0 TO 30 DO
            days[i, j, 0] := 0;
            days[i, j, 1] := 0;
        END
    END;

    days[ 1, 28, 0] := -1;

    FOR i := 0 TO 1 DO
        days[ 1, 29, i] := -1;
        days[ 1, 30, i] := -1;
        days[ 3, 30, i] := -1;
        days[ 5, 30, i] := -1;
        days[ 8, 30, i] := -1;
        days[10, 30, i] := -1;
    END;

    n0 := 0;
    n1 := 0;
    FOR i := 0 TO 11 DO
        FOR j := 0 TO 30 DO
            IF days[i, j, 0] = 0 THEN
                days[i, j, 0] := n0;
                INC(n0)
            END;
            IF days[i, j, 1] = 0 THEN
                days[i, j, 1] := n1;
                INC(n1)
            END
        END
    END;

    inf := SYSTEM.INF();
    maxreal := 1.9;
    PACK(maxreal, 1023);
    Console := TRUE;
    IF Console THEN
        con_init(-1, -1, -1, -1, SYSTEM.SADR("Oberon-07 for KolibriOS"))
    END;
    ParamParse
END init;


BEGIN
    init(days)
END HOST.