// deatomiser -- KEKS atoms printer
// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <keks/atom.h>
#include <keks/dec.h>
#include <keks/err.h>

#include "../lib/hex.h"
#include "../lib/printai.h"
#include "../lib/stdinread.h"
#include "../lib/uuid.h"

int
main(void)
{
    size_t len = 0;
    size_t cap = (size_t)1 << (size_t)10;
    unsigned char *buf = NULL;
    if (!stdinread(&buf, &len, &cap)) {
        fprintf(stderr, "stdinread(): %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    struct KEKSAtom atom;
    memset(&atom, 0, sizeof(struct KEKSAtom));
    size_t off = 0;
    size_t got = 0;
    int indent = 0;
    enum KEKSErr err = KEKSErrInvalid;
    while (off < len) {
        err = KEKSAtomDecode(&got, &atom, buf + off, len - off);
        if (err != KEKSErrNo) {
            fprintf(stderr, "err: %s\n", KEKSErr2Str(err));
            return EXIT_FAILURE;
        }
        off += got;
        printf("%d %zu\t", indent, off);
        if (atom.typ == KEKSItemEOC) {
            indent--;
        }
        for (int i = 0; i < indent; i++) {
            fputs("  ", stdout);
        }
        char *hex = NULL;
        switch (atom.typ) {
        case KEKSItemInvalid:
            fputs("INVALID\n", stdout);
            break;
        case KEKSItemEOC:
            fputs("EOC\n", stdout);
            break;
        case KEKSItemNIL:
            fputs("NIL\n", stdout);
            break;
        case KEKSItemFalse:
            fputs("FALSE\n", stdout);
            break;
        case KEKSItemTrue:
            fputs("TRUE\n", stdout);
            break;
        case KEKSItemHexlet:
            UUIDPrint(atom.v.str.ptr);
            fputs("\n", stdout);
            break;
        case KEKSItemMagic:
            fputs("Magic(", stdout);
            for (size_t i = 0; i < atom.v.str.len; i++) {
                if (isprint(atom.v.str.ptr[i]) == 0) {
                    printf("\\x%02x", atom.v.str.ptr[i]);
                } else {
                    fputc(atom.v.str.ptr[i], stdout);
                }
            }
            fputs(")\n", stdout);
            break;
        case KEKSItemPint:
            fprintf(stdout, "%zu\n", atom.v.pint);
            break;
        case KEKSItemNint:
            fprintf(stdout, "%zd\n", atom.v.nint);
            break;
        case KEKSItemList:
            fputs("LIST\n", stdout);
            indent++;
            break;
        case KEKSItemMap:
            fputs("MAP\n", stdout);
            indent++;
            break;
        case KEKSItemBlob:
            printf("BLOB(l=%zu\n", atom.v.blob.chunkLen);
            break;
        case KEKSItemFloat:
            printf("FLOAT(%f)\n", atom.v.fp);
            break;
        case KEKSItemTAI64:
            err = PrintTAI64(atom.v.str.ptr, atom.v.str.len);
            if (err != KEKSErrNo) {
                fprintf(stderr, "err: %s\n", KEKSErr2Str(err));
                return EXIT_FAILURE;
            }
            break;
        case KEKSItemBin: {
            hex = HexEnc(atom.v.str.ptr, atom.v.str.len);
            fprintf(stdout, "%zu:%s\n", atom.v.str.len, hex);
            free(hex);
            break;
        }
        case KEKSItemStr: {
            hex = strndup((const char *)(atom.v.str.ptr), atom.v.str.len);
            fprintf(stdout, "\"%s\"\n", hex);
            free(hex);
            break;
        }
        case KEKSItemRaw:
            hex = HexEnc(atom.v.str.ptr, atom.v.str.len);
            fprintf(stdout, "(l=%zu v=%s)\n", atom.v.str.len, hex);
            free(hex);
            break;
        default:
            fprintf(stderr, "unknown atom\n");
            return EXIT_FAILURE;
        }
    }
    return EXIT_SUCCESS;
}
