// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#define DVX_WIDGET_IMPL
// widgetClass.c -- Widget class table, API registry, and dynamic registration
//
// widgetClassTable is a stb_ds dynamic array.  Widget DXEs call
// wgtRegisterClass() during wgtRegister() to append their class
// definition and receive a runtime type ID.  No widget types are
// known at compile time.
//
// The API registry uses an stb_ds string hashmap for O(1) lookup.
// Each widget DXE calls wgtRegisterApi("name", &sApi) during
// wgtRegister().  App/core code calls wgtGetApi("name") to get
// the API pointer, then casts and calls through it.

#include "dvxWgtP.h"
#include "stb_ds_wrap.h"

#include <string.h>
#include <strings.h>

// stb_ds dynamic array of class pointers.  Grows on each
// wgtRegisterClass() call.  Index = type ID.
const WidgetClassT **widgetClassTable = NULL;

// stb_ds string hashmap: key = widget name, value = API pointer.
// Both maps are created with sh_new_strdup so shput copies the key; the
// registering DXE's string is not borrowed.
typedef struct {
    char       *key;    // stb_ds string key (strdup'd copy owned by the map)
    const void *value;
} ApiMapEntryT;

static ApiMapEntryT *sApiMap = NULL;

// stb_ds string hashmap: key = widget name, value = iface + path
typedef struct {
    const WgtIfaceT *iface;
    char             path[DVX_MAX_PATH];
    int32_t          pathIndex;  // 1-based: 1 = first widget from this file, 2 = second, etc.
} IfaceEntryT;

typedef struct {
    char          *key;
    IfaceEntryT    value;
} IfaceMapEntryT;

static IfaceMapEntryT *sIfaceMap = NULL;


//
// Scan all registered interfaces for one whose basName matches
// (case-insensitive). Returns the widget type name, or NULL.

const char *wgtFindByBasName(const char *basName) {
    if (!basName || !sIfaceMap) {
        return NULL;
    }

    for (size_t i = 0; i < shlenu(sIfaceMap); i++) {
        if (sIfaceMap[i].value.iface && sIfaceMap[i].value.iface->basName) {
            if (strcasecmp(sIfaceMap[i].value.iface->basName, basName) == 0) {
                return sIfaceMap[i].key;
            }
        }
    }

    return NULL;
}


//
// Look up a widget API by name.  O(1) via stb_ds string hashmap.
// Returns NULL if the widget is not loaded.  Callers should still
// cache the result in a static local to skip even the hash.

const void *wgtGetApi(const char *name) {
    if (!name) {
        return NULL;
    }

    int32_t idx = shgeti(sApiMap, name);

    if (idx < 0) {
        return NULL;
    }

    return sApiMap[idx].value;
}


//
// Look up a widget interface descriptor by type name.

const WgtIfaceT *wgtGetIface(const char *name) {
    if (!name) {
        return NULL;
    }

    int32_t idx = shgeti(sIfaceMap, name);

    if (idx < 0) {
        return NULL;
    }

    return sIfaceMap[idx].value.iface;
}


//
// Enumerate all registered widget interfaces (indexed access).

const WgtIfaceT *wgtIfaceAt(int32_t idx, const char **outName) {
    if (!sIfaceMap || idx < 0 || idx >= (int32_t)shlenu(sIfaceMap)) {
        return NULL;
    }

    if (outName) {
        *outName = sIfaceMap[idx].key;
    }

    return sIfaceMap[idx].value.iface;
}


//
// Return the count of registered widget interfaces.

int32_t wgtIfaceCount(void) {
    return sIfaceMap ? (int32_t)shlenu(sIfaceMap) : 0;
}


// Case-insensitive lookup of a property descriptor by name.  The single
// source of truth for "does this widget interface define property X" --
// used by the form runtime (get/set) and the IDE (designer/properties)
// so both layers resolve the Visible/Enabled override identically.
// Returns NULL if iface is NULL or the property is not found.
const WgtPropDescT *wgtIfaceFindProp(const WgtIfaceT *iface, const char *propName) {
    if (!iface) {
        return NULL;
    }

    for (int32_t i = 0; i < iface->propCount; i++) {
        if (strcasecmp(iface->props[i].name, propName) == 0) {
            return &iface->props[i];
        }
    }

    return NULL;
}


const char *wgtIfaceGetPath(const char *name) {
    if (!name || !sIfaceMap) {
        return NULL;
    }

    int32_t idx = shgeti(sIfaceMap, name);

    if (idx < 0) {
        return NULL;
    }

    return sIfaceMap[idx].value.path[0] ? sIfaceMap[idx].value.path : NULL;
}


int32_t wgtIfaceGetPathIndex(const char *name) {
    if (!name || !sIfaceMap) {
        return 1;
    }

    int32_t idx = shgeti(sIfaceMap, name);

    if (idx < 0) {
        return 1;
    }

    return sIfaceMap[idx].value.pathIndex > 0 ? sIfaceMap[idx].value.pathIndex : 1;
}


void wgtIfaceSetPath(const char *name, const char *path) {
    if (!name || !path || !sIfaceMap) {
        return;
    }

    int32_t idx = shgeti(sIfaceMap, name);

    if (idx >= 0) {
        snprintf(sIfaceMap[idx].value.path, sizeof(sIfaceMap[idx].value.path), "%s", path);

        // Count how many ifaces already have this path to assign a 1-based index
        int32_t count = 0;

        for (size_t i = 0; i < shlenu(sIfaceMap); i++) {
            if (sIfaceMap[i].value.path[0] && strcmp(sIfaceMap[i].value.path, path) == 0) {
                count++;
            }
        }

        sIfaceMap[idx].value.pathIndex = count;
    }
}


//
// Register a widget's public API struct under a name.  Called by
// each widget DXE during wgtRegister().

void wgtRegisterApi(const char *name, const void *api) {
    if (!name || !api) {
        return;
    }

    if (!sApiMap) {
        sh_new_strdup(sApiMap);
    }

    shput(sApiMap, name, api);
}


//
// Appends a class to the table and returns the assigned type ID.
// The ID is simply the array index.

int32_t wgtRegisterClass(const WidgetClassT *wclass) {
    if (!wclass) {
        return -1;
    }

    int32_t id = arrlen(widgetClassTable);
    arrput(widgetClassTable, wclass);
    return id;
}


//
// Register a widget's interface descriptor under its type name.
// Called by widget DXEs during wgtRegister().

void wgtRegisterIface(const char *name, const WgtIfaceT *iface) {
    if (!name || !iface) {
        return;
    }

    IfaceEntryT entry;
    memset(&entry, 0, sizeof(entry));
    entry.iface = iface;
    if (!sIfaceMap) {
        sh_new_strdup(sIfaceMap);
    }

    shput(sIfaceMap, name, entry);
}
