///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2026 Wizardry and Steamworks - License: MIT          //
///////////////////////////////////////////////////////////////////////////

#include <stddef.h>
#include <stdarg.h>

/* Standard Amiga Version Tag */
const char ver[] = "\0$VER: arbor 2.4 (24.08.2026) Wizardry and Steamworks";

#if !defined(__amigaos__)
    #if defined(__AMIGA__) || defined(AMIGA) || defined(__STORM__) || defined(__amigappc__) || defined(__MORPHOS__) || defined(__AROS__)
        #define __amigaos__ 1
    #endif
#endif

/* WarpOS doesn't support GetArgStr() properly - use standard argc/argv */
#if defined(__WARPOS__) || defined(__POWERUP__)
    #define USE_STD_ARGC_ARGV 1
#endif

#ifdef __amigaos__
    #include <exec/types.h>
    #include <dos/dos.h>

    #ifdef __cplusplus
    extern "C" {
    #endif
        /* Native Amiga Prototypes (No POSIX) */
        extern void* AllocVec(ULONG, ULONG);
        extern void FreeVec(void*);
        extern void CopyMem(void*, void*, ULONG);
        extern LONG Printf(STRPTR, ...);
        #ifndef USE_STD_ARGC_ARGV
            extern STRPTR GetArgStr(void);
        #endif
    #ifdef __cplusplus
    }
    #endif

    typedef BOOL arbor_bool;
    #define ARBOR_TRUE  TRUE
    #define ARBOR_FALSE FALSE
    #define ArborPrintf Printf
#else
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>
    typedef bool arbor_bool;
    #define ARBOR_TRUE  true
    #define ARBOR_FALSE false
    typedef char* STRPTR;
    typedef long LONG;
    #define ArborPrintf printf
#endif

#define COMPONENT_SEPARATOR '/'
#define INITIAL_CAPACITY 8

/* ========================================================================
   INTERNAL UTILITIES (NO POSIX)
   ======================================================================== */

#ifdef __amigaos__
    static void* arbor_malloc(size_t size) { return AllocVec((ULONG)size, 0x10001); } 
    static void arbor_free(void* ptr) { if(ptr) FreeVec(ptr); }
    static void arbor_memcpy(void* dest, const void* src, size_t n) { CopyMem((void*)src, dest, (ULONG)n); }
    static void arbor_memset(void* s, int c, size_t n) {
        unsigned char* p = (unsigned char*)s;
        while(n--) *p++ = (unsigned char)c;
    }
#else
    #define arbor_malloc(s) calloc(1, s)
    #define arbor_free free
    #define arbor_memcpy memcpy
    #define arbor_memset memset
#endif

static size_t arbor_strlen(const char* s) {
    size_t len = 0; if(!s) return 0;
    while(s[len]) len++; return len;
}

static int arbor_strcmp(const char* s1, const char* s2) {
    const unsigned char *p1 = (const unsigned char *)s1;
    const unsigned char *p2 = (const unsigned char *)s2;
    while (*p1 && (*p1 == *p2)) { p1++; p2++; }
    return *p1 - *p2;
}

static int arbor_memcmp(const void* s1, const void* s2, size_t n) {
    const unsigned char *p1 = (const unsigned char *)s1, *p2 = (const unsigned char *)s2;
    while(n--) { if(*p1 != *p2) return *p1 - *p2; p1++; p2++; }
    return 0;
}

static void* arbor_realloc(void* old_ptr, size_t old_size, size_t new_size) {
    void* new_ptr = arbor_malloc(new_size);
    if (new_ptr) {
        if (old_ptr) {
            arbor_memcpy(new_ptr, old_ptr, old_size < new_size ? old_size : new_size);
            arbor_free(old_ptr);
        }
    }
    return new_ptr;
}

/* ========================================================================
   AST NODE MANAGEMENT
   ======================================================================== */
 
typedef struct ASTNode ASTNode;
struct ASTNode {
    char *label;
    ASTNode **children;
    size_t children_count;
    size_t children_capacity;
};

static char *string_duplicate(const char *src) {
    size_t len; char *dst;
    if (!src) return NULL;
    len = arbor_strlen(src);
    dst = (char *)arbor_malloc(len + 1);
    if (dst) arbor_memcpy(dst, src, len + 1);
    return dst;
}

static char *string_range_duplicate(const char *start, size_t length) {
    char *dst = (char *)arbor_malloc(length + 1);
    if (!dst) return NULL;
    if (length) arbor_memcpy(dst, start, length);
    dst[length] = '\0';
    return dst;
}

static ASTNode *create_node(const char *label) {
    ASTNode *node = (ASTNode *)arbor_malloc(sizeof(ASTNode));
    if (!node) return NULL;
    node->label = string_duplicate(label);
    node->children_capacity = INITIAL_CAPACITY;
    node->children = (ASTNode **)arbor_malloc(node->children_capacity * sizeof(ASTNode *));
    node->children_count = 0;
    return node;
}

static void free_tree(ASTNode *node) {
    size_t i; if (!node) return;
    for (i = 0; i < node->children_count; ++i) free_tree(node->children[i]);
    arbor_free(node->children); arbor_free(node->label); arbor_free(node);
}

static ASTNode *find_child(const ASTNode *parent, const char *label) {
    size_t i;
    for (i = 0; i < parent->children_count; ++i) {
        if (arbor_strcmp(parent->children[i]->label, label) == 0) return parent->children[i];
    }
    return NULL;
}

static ASTNode *add_or_get_child(ASTNode *parent, const char *label) {
    ASTNode *child = find_child(parent, label);
    if (child) return child;
    if (parent->children_count == parent->children_capacity) {
        size_t old_sz = parent->children_capacity * sizeof(ASTNode *);
        parent->children_capacity *= 2;
        parent->children = (ASTNode **)arbor_realloc(parent->children, old_sz, parent->children_capacity * sizeof(ASTNode *));
    }
    child = create_node(label);
    if (child) parent->children[parent->children_count++] = child;
    return child;
}

static void insert_path(ASTNode *root, const char *path) {
    const char *p = path; ASTNode *current = root;
    if (!path || !*path) return;
    while (*p) {
        const char *start; size_t length; char *comp_str;
        while (*p == COMPONENT_SEPARATOR) p++;
        if (*p == '\0') break;
        start = p; 
        while (*p && *p != COMPONENT_SEPARATOR) p++;
        length = (size_t)(p - start);
        if (length > 0) {
            comp_str = string_range_duplicate(start, length);
            if (comp_str) {
                current = add_or_get_child(current, comp_str);
                arbor_free(comp_str);
            }
        }
    }
}

/* ========================================================================
   RENDERING (RULE 5 & 6)
   ======================================================================== */

typedef struct {
    arbor_bool any_nonroot_path;
    arbor_bool all_first_components_equal;
    arbor_bool every_nonroot_path_is_single_component;
    char *common_first_component;
} InputProperties;

static ASTNode *coalesce_node(ASTNode *node, char **display_out) {
    size_t total_length = arbor_strlen(node->label) + 1;
    ASTNode *current = node; size_t offset = 0; char *display;
    while (current->children_count == 1 && current->children[0]->children_count > 0) {
        current = current->children[0];
        total_length += 1 + arbor_strlen(current->label);
    }
    display = (char *)arbor_malloc(total_length);
    if (!display) return node;
    current = node; offset = 0;
    for (;;) {
        size_t l = arbor_strlen(current->label);
        if (offset != 0) display[offset++] = COMPONENT_SEPARATOR;
        arbor_memcpy(display + offset, current->label, l);
        offset += l; display[offset] = '\0';
        if (current->children_count == 1 && current->children[0]->children_count > 0) {
            current = current->children[0]; continue;
        }
        break;
    }
    *display_out = display; return current;
}

typedef struct { arbor_bool *vertical; size_t capacity; } RenderContext;

static void print_indent(size_t count, const arbor_bool *vert) {
    size_t i; 
    for (i = 0; i < count; ++i) {
        ArborPrintf((STRPTR)(vert[i] ? "|" : " "));
    }
}

static arbor_bool render_subtree(ASTNode *node, size_t indent, RenderContext *ctx, arbor_bool root_marker) {
    ASTNode *target; char *display = NULL; size_t center, i;
    if (!node) return ARBOR_FALSE;
    if (indent >= ctx->capacity) {
        size_t old_sz = ctx->capacity * sizeof(arbor_bool); ctx->capacity = (indent + 1) * 2;
        ctx->vertical = (arbor_bool *)arbor_realloc(ctx->vertical, old_sz, ctx->capacity * sizeof(arbor_bool));
    }
    if (node->label[0] == '\0') {
        ArborPrintf((STRPTR)"/\n"); center = 0; target = node;
    } else {
        target = coalesce_node(node, &display);
        if (root_marker) ArborPrintf((STRPTR)"/");
        print_indent(indent, ctx->vertical);
        ArborPrintf((STRPTR)"+---+ %s\n", (STRPTR)display);
        center = indent + (root_marker ? 1 : 0) + 6 + (arbor_strlen(display) ? (arbor_strlen(display) - 1) / 2 : 0);
    }
    if (target->children_count > 0) {
        if (center >= ctx->capacity) {
             size_t old_sz = ctx->capacity * sizeof(arbor_bool); ctx->capacity = (center + 1) * 2;
             ctx->vertical = (arbor_bool *)arbor_realloc(ctx->vertical, old_sz, ctx->capacity * sizeof(arbor_bool));
        }
        print_indent(center, ctx->vertical); ArborPrintf((STRPTR)"+\n");
        print_indent(center, ctx->vertical); ArborPrintf((STRPTR)"|\n");
        for (i = 0; i < target->children_count; ++i) {
            if (i > 0) { print_indent(center, ctx->vertical); ArborPrintf((STRPTR)"|\n"); }
            ctx->vertical[center] = (i + 1 < target->children_count);
            render_subtree(target->children[i], center, ctx, ARBOR_FALSE);
            ctx->vertical[center] = ARBOR_FALSE;
        }
    }
    if (display) arbor_free(display); return ARBOR_TRUE;
}

static void inspect_path(InputProperties *props, const char *path) {
    const char *p = path; size_t count = 0; const char *f_start = NULL; size_t f_len = 0;
    while (*p) {
        while (*p == COMPONENT_SEPARATOR) p++;
        if (!*p) break;
        if (count == 0) f_start = p;
        while (*p && *p != COMPONENT_SEPARATOR) p++;
        if (count == 0) f_len = (size_t)(p - f_start);
        count++;
    }
    if (count == 0) return;
    props->any_nonroot_path = ARBOR_TRUE;
    if (count != 1) props->every_nonroot_path_is_single_component = ARBOR_FALSE;
    if (!props->common_first_component) {
        props->common_first_component = string_range_duplicate(f_start, f_len);
    } else if (arbor_strlen(props->common_first_component) != f_len || 
               arbor_memcmp(props->common_first_component, f_start, f_len) != 0) {
        props->all_first_components_equal = ARBOR_FALSE;
    }
}

/* ========================================================================
   COMMAND LINE PARSER
   ======================================================================== */

#ifdef __amigaos__
#ifndef USE_STD_ARGC_ARGV
/* AmigaOS with GetArgStr() - for 68k, MorphOS, AROS */
static int get_argv_amiga(char ***argv_out) {
    STRPTR p = GetArgStr();
    char **argv = NULL; int argc = 0; char buffer[2048]; int bpos; arbor_bool in_q;
    if (!p) return 0;
    while (*p) {
        while (*p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
        if (!*p) break;
        bpos = 0; in_q = ARBOR_FALSE;
        while (*p) {
            if (*p == '\"') in_q = !in_q;
            else if (!in_q && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) break;
            else if (bpos < 2047) buffer[bpos++] = *p;
            p++;
        }
        buffer[bpos] = '\0';
        if (bpos > 0) {
            size_t sz = argc * sizeof(char *);
            argv = (char **)arbor_realloc(argv, sz, (argc + 1) * sizeof(char *));
            argv[argc++] = string_duplicate(buffer);
        }
    }
    *argv_out = argv; return argc;
}
#endif
#endif

/* ========================================================================
   MAIN
   ======================================================================== */

#ifdef __amigaos__
#ifndef USE_STD_ARGC_ARGV
/* AmigaOS with GetArgStr() - 68k, MorphOS, AROS */
int main(void) {
    char **argv = NULL; int argc = get_argv_amiga(&argv); int arg_start = 0;
#else
/* WarpOS / PowerUP - use standard argc/argv */
int main(int argc, char **argv) {
    int arg_start = 1;
#endif
#else
/* Standard C / Kickstart 1.3 */
int main(int argc, char **argv) {
    int arg_start = 1;
#endif
    ASTNode *root = create_node("");
    RenderContext ctx = { NULL, 64 };
    InputProperties props = { ARBOR_FALSE, ARBOR_TRUE, ARBOR_TRUE, NULL };
    int i;

    if (argc <= arg_start || (argc > arg_start && arbor_strcmp(argv[arg_start], "?") == 0)) {
        ArborPrintf((STRPTR)"Usage: arbor PATH [PATH...]\n");
        ArborPrintf((STRPTR)"Examples:\n");
        ArborPrintf((STRPTR)"  arbor /a/b /a/c\n");
        ArborPrintf((STRPTR)"  arbor \"a/b/c\" \"a/aa\"\n");
#ifdef __WARPOS__
        ArborPrintf((STRPTR)"\n");
        ArborPrintf((STRPTR)"WarpOS Note: If you see a 'ChangeStack' error dialog on\n");
        ArborPrintf((STRPTR)"MorphOS/OS4, click 'Continue' - the program works.\n");
        ArborPrintf((STRPTR)"Real WarpOS hardware does not show this error.\n");
        ArborPrintf((STRPTR)"For a completely clean experience, use the 68k version.\n");
#endif
        goto cleanup;
    }

    for (i = arg_start; i < argc; i++) {
        inspect_path(&props, argv[i]);
        insert_path(root, argv[i]);
    }

    if (props.any_nonroot_path) {
        ctx.vertical = (arbor_bool *)arbor_malloc(ctx.capacity * sizeof(arbor_bool));
        arbor_memset(ctx.vertical, 0, ctx.capacity * sizeof(arbor_bool));
        
        /* Rule 5: Root Suppression Logic */
        if (!props.all_first_components_equal || props.every_nonroot_path_is_single_component) {
            render_subtree(root, 0, &ctx, ARBOR_FALSE);
        } else {
            ASTNode *common = find_child(root, props.common_first_component);
            if (common) {
                char *disp = NULL; ASTNode *target = coalesce_node(common, &disp);
                ArborPrintf((STRPTR)"/%s\n", (STRPTR)disp);
                size_t center = 1 + (arbor_strlen(disp) ? (arbor_strlen(disp) - 1) / 2 : 0);
                if (target->children_count > 0) {
                    print_indent(center, ctx.vertical); ArborPrintf((STRPTR)"+\n");
                    print_indent(center, ctx.vertical); ArborPrintf((STRPTR)"|\n");
                    for (i = 0; i < (int)target->children_count; i++) {
                        if (i > 0) { print_indent(center, ctx.vertical); ArborPrintf((STRPTR)"|\n"); }
                        ctx.vertical[center] = (i + 1 < (int)target->children_count);
                        render_subtree(target->children[i], center, &ctx, ARBOR_FALSE);
                        ctx.vertical[center] = ARBOR_FALSE;
                    }
                }
                if (disp) arbor_free(disp);
            }
        }
    } else { ArborPrintf((STRPTR)"/\n"); }

cleanup:
#ifdef __amigaos__
#ifndef USE_STD_ARGC_ARGV
    if (argv) { for (i = 0; i < argc; i++) arbor_free(argv[i]); arbor_free(argv); }
#endif
#endif
    if (props.common_first_component) arbor_free(props.common_first_component);
    if (ctx.vertical) arbor_free(ctx.vertical);
    free_tree(root);
    return 0;
}
