/*
 * DWN - Desktop Window Manager
 * Widget Plugin Manager Implementation
 */

#include "plugins/widget_plugin.h"
#include "core/wm_list.h"
#include "core/wm_hashmap.h"
#include "util.h"

#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>

/*==============================================================================
 * Helper for casting dlsym result to function pointer (avoids ISO C warning)
 *============================================================================*/

union dlsym_cast {
    void *obj_ptr;
    WidgetPluginEntryFunc func_ptr;
};

/*==============================================================================
 * Internal Structures
 *============================================================================*/

struct WidgetManager {
    WmList *plugins;           /* List of WidgetPlugin* */
    WmHashMap *by_id;          /* Map: widget ID -> WidgetPlugin* */
    bool initialized;
};

struct WidgetPanel {
    WmList *left_widgets;
    WmList *center_widgets;
    WmList *right_widgets;
    WmRect geometry;
    bool needs_redraw;
};

/*==============================================================================
 * Global State
 *============================================================================*/

static WidgetManager *g_widget_manager = NULL;

/*==============================================================================
 * Widget Manager Lifecycle
 *============================================================================*/

WidgetManager* wm_widget_manager_get(void) {
    return g_widget_manager;
}

bool wm_widget_manager_init(void) {
    if (g_widget_manager) return true;
    
    g_widget_manager = calloc(1, sizeof(WidgetManager));
    if (!g_widget_manager) return false;
    
    g_widget_manager->plugins = wm_list_new();
    g_widget_manager->by_id = wm_hashmap_new_string_key();
    
    if (!g_widget_manager->plugins || !g_widget_manager->by_id) {
        wm_widget_manager_shutdown();
        return false;
    }
    
    LOG_INFO("Widget manager initialized");
    return true;
}

void wm_widget_manager_shutdown(void) {
    if (!g_widget_manager) return;
    
    /* Destroy all instances and plugins */
    while (!wm_list_is_empty(g_widget_manager->plugins)) {
        WidgetPlugin *plugin = wm_list_first(g_widget_manager->plugins);
        wm_widget_manager_unload(g_widget_manager, plugin);
    }
    
    wm_list_destroy(g_widget_manager->plugins);
    wm_hashmap_destroy(g_widget_manager->by_id);
    
    free(g_widget_manager);
    g_widget_manager = NULL;
    
    LOG_INFO("Widget manager shutdown");
}

/*==============================================================================
 * Widget Plugin Management
 *============================================================================*/

bool wm_widget_manager_register(WidgetManager *mgr, WidgetPlugin *plugin) {
    if (!mgr || !plugin || !plugin->interface) return false;
    
    /* Check API version */
    if (plugin->interface->api_version != WM_WIDGET_PLUGIN_API_VERSION) {
        LOG_ERROR("Widget plugin '%s' has incompatible API version %d (expected %d)",
                  plugin->interface->info.id, plugin->interface->api_version,
                  WM_WIDGET_PLUGIN_API_VERSION);
        return false;
    }
    
    /* Check if already registered */
    if (wm_hashmap_contains_string(mgr->by_id, plugin->interface->info.id)) {
        LOG_WARN("Widget plugin '%s' is already registered", 
                 plugin->interface->info.id);
        return false;
    }
    
    /* Initialize plugin */
    if (plugin->interface->init) {
        if (!plugin->interface->init(plugin)) {
            LOG_ERROR("Failed to initialize widget plugin '%s'", 
                      plugin->interface->info.id);
            return false;
        }
    }
    
    /* Add to lists */
    wm_list_append(mgr->plugins, plugin);
    wm_hashmap_insert_string(mgr->by_id, plugin->interface->info.id, plugin);
    
    LOG_INFO("Registered widget plugin: %s (%s)", 
             plugin->interface->info.id, plugin->interface->info.name);
    return true;
}

void wm_widget_manager_unregister(WidgetManager *mgr, WidgetPlugin *plugin) {
    if (!mgr || !plugin) return;
    
    /* Destroy all instances */
    WidgetInstance *instance = plugin->instances;
    while (instance) {
        WidgetInstance *next = instance->next;
        wm_widget_destroy_instance(instance);
        instance = next;
    }
    
    /* Shutdown plugin */
    if (plugin->interface && plugin->interface->shutdown) {
        plugin->interface->shutdown(plugin);
    }
    
    /* Remove from lists */
    wm_list_remove(mgr->plugins, plugin);
    wm_hashmap_remove_string(mgr->by_id, plugin->interface->info.id);
    
    LOG_INFO("Unregistered widget plugin: %s", plugin->interface->info.id);
}

WidgetPlugin* wm_widget_manager_load(WidgetManager *mgr, const char *path) {
    if (!mgr || !path) return NULL;
    
    void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
    if (!handle) {
        LOG_ERROR("Failed to load widget plugin '%s': %s", path, dlerror());
        return NULL;
    }
    
    /* Get entry point */
    union dlsym_cast cast;
    cast.obj_ptr = dlsym(handle, WM_WIDGET_PLUGIN_ENTRY);
    WidgetPluginEntryFunc entry = cast.func_ptr;
    if (!entry) {
        LOG_ERROR("Widget plugin '%s' missing entry point '%s'", 
                  path, WM_WIDGET_PLUGIN_ENTRY);
        dlclose(handle);
        return NULL;
    }
    
    /* Get interface */
    const WidgetPluginInterface *iface = entry();
    if (!iface) {
        LOG_ERROR("Widget plugin '%s' returned NULL interface", path);
        dlclose(handle);
        return NULL;
    }
    
    /* Create plugin instance */
    WidgetPlugin *plugin = calloc(1, sizeof(WidgetPlugin));
    if (!plugin) {
        dlclose(handle);
        return NULL;
    }
    
    plugin->interface = iface;
    plugin->handle = handle;
    plugin->path = strdup(path);
    plugin->enabled = true;
    
    /* Register */
    if (!wm_widget_manager_register(mgr, plugin)) {
        free(plugin->path);
        free(plugin);
        dlclose(handle);
        return NULL;
    }
    
    return plugin;
}

void wm_widget_manager_unload(WidgetManager *mgr, WidgetPlugin *plugin) {
    if (!mgr || !plugin) return;
    
    void *handle = plugin->handle;
    
    wm_widget_manager_unregister(mgr, plugin);
    
    free(plugin->path);
    free(plugin);
    
    if (handle) {
        dlclose(handle);
    }
}

WidgetPlugin* wm_widget_manager_get_widget(WidgetManager *mgr, const char *id) {
    if (!mgr || !id) return NULL;
    return wm_hashmap_get_string(mgr->by_id, id);
}

const char** wm_widget_manager_list(WidgetManager *mgr, int *count) {
    if (!mgr || !count) return NULL;
    
    *count = wm_list_size(mgr->plugins);
    if (*count == 0) return NULL;
    
    const char **ids = malloc(*count * sizeof(const char*));
    if (!ids) return NULL;
    
    size_t idx = 0;
    int i = 0;
    while (idx < wm_list_size(mgr->plugins)) {
        WidgetPlugin *plugin = wm_list_get(mgr->plugins, idx++);
        if (plugin) ids[i++] = plugin->interface->info.id;
    }
    
    return ids;
}

/*==============================================================================
 * Widget Instance Operations
 *============================================================================*/

WidgetInstance* wm_widget_create_instance(WidgetPlugin *plugin,
                                          const WmWidgetConfig *config) {
    if (!plugin || !plugin->interface) return NULL;
    
    WidgetInstance *instance = NULL;
    
    if (plugin->interface->instance_create) {
        instance = plugin->interface->instance_create(plugin, config);
    } else {
        /* Create basic instance */
        instance = calloc(1, sizeof(WidgetInstance));
    }
    
    if (!instance) return NULL;
    
    instance->plugin = plugin;
    instance->state = WM_WIDGET_STATE_NORMAL;
    instance->constraints = plugin->interface->info.default_constraints;
    
    /* Add to plugin's instance list */
    instance->next = plugin->instances;
    if (plugin->instances) {
        plugin->instances->prev = instance;
    }
    plugin->instances = instance;
    plugin->instance_count++;
    
    LOG_DEBUG("Created widget instance of '%s'", plugin->interface->info.id);
    return instance;
}

void wm_widget_destroy_instance(WidgetInstance *instance) {
    if (!instance || !instance->plugin) return;
    
    WidgetPlugin *plugin = instance->plugin;
    
    /* Remove from plugin's instance list */
    if (instance->prev) {
        instance->prev->next = instance->next;
    } else {
        plugin->instances = instance->next;
    }
    
    if (instance->next) {
        instance->next->prev = instance->prev;
    }
    
    plugin->instance_count--;
    
    /* Destroy */
    if (plugin->interface->instance_destroy) {
        plugin->interface->instance_destroy(plugin, instance);
    } else {
        free(instance);
    }
    
    LOG_DEBUG("Destroyed widget instance of '%s'", plugin->interface->info.id);
}

void wm_widget_render(WidgetInstance *instance, WidgetContext *ctx) {
    if (!instance || !instance->plugin || !ctx) return;
    
    const WidgetPluginInterface *iface = instance->plugin->interface;
    
    if (iface->render && !(instance->state & WM_WIDGET_STATE_HIDDEN)) {
        instance->context = ctx;
        iface->render(instance->plugin, instance, ctx);
    }
}

bool wm_widget_send_event(WidgetInstance *instance, const WmWidgetEvent *event) {
    if (!instance || !instance->plugin || !event) return false;
    
    const WidgetPluginInterface *iface = instance->plugin->interface;
    
    if (iface->handle_event) {
        return iface->handle_event(instance->plugin, instance, event);
    }
    
    return false;
}

void wm_widget_update(WidgetInstance *instance) {
    if (!instance || !instance->plugin) return;
    
    const WidgetPluginInterface *iface = instance->plugin->interface;
    
    if (iface->update) {
        iface->update(instance->plugin, instance);
    }
}

void wm_widget_get_preferred_size(WidgetInstance *instance, 
                                  int *width, int *height) {
    if (!instance || !instance->plugin || !width || !height) return;
    
    const WidgetPluginInterface *iface = instance->plugin->interface;
    
    *width = instance->constraints.min_width;
    *height = instance->constraints.min_height;
    
    if (iface->get_preferred_size) {
        iface->get_preferred_size(instance->plugin, instance, width, height);
    }
}

/*==============================================================================
 * Widget Panel Operations
 *============================================================================*/

WidgetPanel* wm_widget_panel_create(void) {
    WidgetPanel *panel = calloc(1, sizeof(WidgetPanel));
    if (!panel) return NULL;
    
    panel->left_widgets = wm_list_new();
    panel->center_widgets = wm_list_new();
    panel->right_widgets = wm_list_new();
    panel->needs_redraw = true;
    
    if (!panel->left_widgets || !panel->center_widgets || !panel->right_widgets) {
        wm_widget_panel_destroy(panel);
        return NULL;
    }
    
    return panel;
}

void wm_widget_panel_destroy(WidgetPanel *panel) {
    if (!panel) return;
    
    /* Destroy all widget instances */
    WidgetInstance *instance;
    
    while ((instance = wm_list_first(panel->left_widgets)) != NULL) {
        wm_widget_destroy_instance(instance);
    }
    while ((instance = wm_list_first(panel->center_widgets)) != NULL) {
        wm_widget_destroy_instance(instance);
    }
    while ((instance = wm_list_first(panel->right_widgets)) != NULL) {
        wm_widget_destroy_instance(instance);
    }
    
    wm_list_destroy(panel->left_widgets);
    wm_list_destroy(panel->center_widgets);
    wm_list_destroy(panel->right_widgets);
    
    free(panel);
}

void wm_widget_panel_add(WidgetPanel *panel, WidgetInstance *instance,
                         WmWidgetPosition position) {
    if (!panel || !instance) return;
    
    WmList *list;
    switch (position) {
        case WM_WIDGET_POSITION_LEFT:
            list = panel->left_widgets;
            break;
        case WM_WIDGET_POSITION_CENTER:
            list = panel->center_widgets;
            break;
        case WM_WIDGET_POSITION_RIGHT:
            list = panel->right_widgets;
            break;
        default:
            list = panel->left_widgets;
    }
    
    wm_list_append(list, instance);
    panel->needs_redraw = true;
}

void wm_widget_panel_remove(WidgetPanel *panel, WidgetInstance *instance) {
    if (!panel || !instance) return;
    
    wm_list_remove(panel->left_widgets, instance);
    wm_list_remove(panel->center_widgets, instance);
    wm_list_remove(panel->right_widgets, instance);
    
    panel->needs_redraw = true;
}

void wm_widget_panel_arrange(WidgetPanel *panel, const WmRect *panel_geometry) {
    if (!panel || !panel_geometry) return;
    
    panel->geometry = *panel_geometry;
    
    int x = panel_geometry->x;
    int y = panel_geometry->y;
    int height = panel_geometry->height;
    int center_x = panel_geometry->x + panel_geometry->width / 2;
    int right_x = panel_geometry->x + panel_geometry->width;
    
    /* Arrange left widgets */
    size_t idx = 0;
    while (idx < wm_list_size(panel->left_widgets)) {
        WidgetInstance *instance = wm_list_get(panel->left_widgets, idx++);
        if (!instance) continue;
        
        int w, h;
        wm_widget_get_preferred_size(instance, &w, &h);
        
        instance->geometry.x = x;
        instance->geometry.y = y + (height - h) / 2;
        instance->geometry.width = w;
        instance->geometry.height = h;
        
        x += w;
    }
    
    /* Arrange right widgets (in reverse order) */
    idx = wm_list_size(panel->right_widgets);
    while (idx-- > 0) {
        WidgetInstance *instance = wm_list_get(panel->right_widgets, idx);
        if (!instance) continue;
        
        int w, h;
        wm_widget_get_preferred_size(instance, &w, &h);
        
        right_x -= w;
        instance->geometry.x = right_x;
        instance->geometry.y = y + (height - h) / 2;
        instance->geometry.width = w;
        instance->geometry.height = h;
    }
    
    /* Arrange center widgets */
    int total_center_width = 0;
    idx = 0;
    while (idx < wm_list_size(panel->center_widgets)) {
        WidgetInstance *instance = wm_list_get(panel->center_widgets, idx++);
        if (!instance) continue;
        
        int w, h;
        wm_widget_get_preferred_size(instance, &w, &h);
        total_center_width += w;
    }
    
    x = center_x - total_center_width / 2;
    idx = 0;
    while (idx < wm_list_size(panel->center_widgets)) {
        WidgetInstance *instance = wm_list_get(panel->center_widgets, idx++);
        if (!instance) continue;
        
        int w, h;
        wm_widget_get_preferred_size(instance, &w, &h);
        
        instance->geometry.x = x;
        instance->geometry.y = y + (height - h) / 2;
        instance->geometry.width = w;
        instance->geometry.height = h;
        
        x += w;
    }
    
    panel->needs_redraw = true;
}

void wm_widget_panel_render(WidgetPanel *panel) {
    if (!panel) return;
    
    /* Render left widgets */
    size_t idx = 0;
    while (idx < wm_list_size(panel->left_widgets)) {
        WidgetInstance *instance = wm_list_get(panel->left_widgets, idx++);
        if (instance) {
            WidgetContext ctx = {0};
            ctx.geometry = instance->geometry;
            wm_widget_render(instance, &ctx);
        }
    }
    
    /* Render center widgets */
    idx = 0;
    while (idx < wm_list_size(panel->center_widgets)) {
        WidgetInstance *instance = wm_list_get(panel->center_widgets, idx++);
        if (instance) {
            WidgetContext ctx = {0};
            ctx.geometry = instance->geometry;
            wm_widget_render(instance, &ctx);
        }
    }
    
    /* Render right widgets */
    idx = 0;
    while (idx < wm_list_size(panel->right_widgets)) {
        WidgetInstance *instance = wm_list_get(panel->right_widgets, idx++);
        if (instance) {
            WidgetContext ctx = {0};
            ctx.geometry = instance->geometry;
            wm_widget_render(instance, &ctx);
        }
    }
    
    panel->needs_redraw = false;
}

bool wm_widget_panel_handle_event(WidgetPanel *panel, 
                                  const WmWidgetEvent *event) {
    if (!panel || !event) return false;
    
    /* Check all widgets for hit testing */
    WmList *lists[] = {
        panel->left_widgets,
        panel->center_widgets,
        panel->right_widgets
    };
    
    for (int i = 0; i < 3; i++) {
        size_t idx = 0;
        while (idx < wm_list_size(lists[i])) {
            WidgetInstance *instance = wm_list_get(lists[i], idx++);
            if (!instance) continue;
            
            WmWidgetGeometry *geom = &instance->geometry;
            
            /* Simple hit testing */
            if (event->data.point.x >= geom->x &&
                event->data.point.x < geom->x + geom->width &&
                event->data.point.y >= geom->y &&
                event->data.point.y < geom->y + geom->height) {
                
                if (wm_widget_send_event(instance, event)) {
                    return true;
                }
            }
        }
    }
    
    return false;
}
