// 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.

// mywgt.c -- Sample DVX widget with full BASIC interface
//
// A simple widget that draws a colored rectangle with centered text.
// Demonstrates:
//   - Widget class registration (calcMinSize, paint, destroy)
//   - Public C API (create function + API struct)
//   - BASIC interface (properties, methods, events, WgtIfaceT)
//
// Build:
//   i586-pc-msdosdjgpp-gcc -O2 -Wall -I../../include/core -c -o mywgt.o mywgt.c
//   dxe3gen -o mywgt.wgt -U mywgt.o
//
// Deploy: copy mywgt.wgt to WIDGETS/<vendor>/MYWGT/ on the target.
// Optionally include MYWGT.DHS (C API docs) and MYWGT.BHS (BASIC docs).

#define DVX_WIDGET_IMPL
#include "dvxWgt.h"
#include "dvxWgtP.h"
#include "dvxDraw.h"
#include "dvxVideo.h"

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

// ============================================================
// Widget data
// ============================================================

typedef struct {
    char    *text;
    int32_t  style;     // 0=flat, 1=raised, 2=sunken
    bool     bold;
} MyWidgetDataT;

static int32_t sTypeId = -1;

// ============================================================
// Style enum names (for BASIC property panel dropdown)
// ============================================================

static const char *sStyleNames[] = { "Flat", "Raised", "Sunken", NULL };

// ============================================================
// Widget methods (vtable handlers)
// ============================================================

static void myCalcMinSize(WidgetT *w, const BitmapFontT *font) {
    w->calcMinW = font->charWidth * 10;
    w->calcMinH = font->charHeight + 8;
}


static void myPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
    MyWidgetDataT *data = (MyWidgetDataT *)w->data;
    uint32_t bg = colors->buttonFace;
    uint32_t fg = colors->contentFg;

    // Fill background
    rectFill(d, ops, w->x, w->y, w->w, w->h, bg);

    // Draw border based on style
    if (data && data->style == 1) {
        BevelStyleT bevel = BEVEL_RAISED(colors, 2);
        drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
    } else if (data && data->style == 2) {
        BevelStyleT bevel = BEVEL_SUNKEN(colors, bg, 2);
        drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
    }

    // Draw text centered
    if (data && data->text) {
        int32_t textW = textWidth(font, data->text);
        int32_t tx    = w->x + (w->w - textW) / 2;
        int32_t ty    = w->y + (w->h - font->charHeight) / 2;
        drawText(d, ops, font, tx, ty, data->text, fg, bg, true);
    }
}


static void myDestroy(WidgetT *w) {
    MyWidgetDataT *data = (MyWidgetDataT *)w->data;

    if (data) {
        free(data->text);
        free(data);
        w->data = NULL;
    }
}

// ============================================================
// Widget class definition
// ============================================================

static const WidgetClassT sClass = {
    .version  = WGT_CLASS_VERSION,
    .flags    = WCLASS_FOCUSABLE,
    .handlers = {
        [WGT_METHOD_PAINT]         = (void *)myPaint,
        [WGT_METHOD_CALC_MIN_SIZE] = (void *)myCalcMinSize,
        [WGT_METHOD_DESTROY]       = (void *)myDestroy,
    }
};

// ============================================================
// C API — called by apps via wgtGetApi("mywgt")
// ============================================================

static WidgetT *myWidgetCreate(WidgetT *parent, const char *text) {
    WidgetT *w = widgetAlloc(parent, sTypeId);

    if (w) {
        MyWidgetDataT *data = (MyWidgetDataT *)calloc(1, sizeof(MyWidgetDataT));

        if (data) {
            data->text  = text ? strdup(text) : NULL;
            data->style = 0;
            data->bold  = false;
        }

        w->data = data;
    }

    return w;
}


static void myWidgetSetStyle(WidgetT *w, int32_t style) {
    MyWidgetDataT *data = (MyWidgetDataT *)w->data;

    if (data) {
        data->style = style;
        wgtInvalidatePaint(w);
    }
}


static int32_t myWidgetGetStyle(const WidgetT *w) {
    MyWidgetDataT *data = (MyWidgetDataT *)w->data;
    return data ? data->style : 0;
}


static void myWidgetSetBold(WidgetT *w, bool bold) {
    MyWidgetDataT *data = (MyWidgetDataT *)w->data;

    if (data) {
        data->bold = bold;
        wgtInvalidatePaint(w);
    }
}


static bool myWidgetGetBold(const WidgetT *w) {
    MyWidgetDataT *data = (MyWidgetDataT *)w->data;
    return data ? data->bold : false;
}


static void myWidgetClear(WidgetT *w) {
    MyWidgetDataT *data = (MyWidgetDataT *)w->data;

    if (data) {
        free(data->text);
        data->text = NULL;
        wgtInvalidatePaint(w);
    }
}


static const struct {
    WidgetT *(*create)(WidgetT *parent, const char *text);
    void     (*setStyle)(WidgetT *w, int32_t style);
    int32_t  (*getStyle)(const WidgetT *w);
    void     (*setBold)(WidgetT *w, bool bold);
    bool     (*getBold)(const WidgetT *w);
    void     (*clear)(WidgetT *w);
} sApi = {
    .create   = myWidgetCreate,
    .setStyle = myWidgetSetStyle,
    .getStyle = myWidgetGetStyle,
    .setBold  = myWidgetSetBold,
    .getBold  = myWidgetGetBold,
    .clear    = myWidgetClear,
};

// ============================================================
// BASIC interface — describes properties, methods, and events
// for the form runtime and IDE property panel
// ============================================================

// Properties accessible from BASIC (e.g., MyWidget1.Style = 2)
static const WgtPropDescT sProps[] = {
    // String property: Caption (read/write via SET_TEXT/GET_TEXT)
    // Common properties like Caption, Left, Top, Width, Height are
    // handled automatically by the form runtime. Only list type-specific
    // properties here.

    // Enum property: Style (Flat=0, Raised=1, Sunken=2)
    { "Style", WGT_IFACE_ENUM, (void *)myWidgetGetStyle, (void *)myWidgetSetStyle, sStyleNames },

    // Boolean property: Bold
    { "Bold",  WGT_IFACE_BOOL, (void *)myWidgetGetBold,  (void *)myWidgetSetBold,  NULL },
};

// Methods callable from BASIC (e.g., MyWidget1.Clear)
static const WgtMethodDescT sMethods[] = {
    { "Clear", WGT_SIG_VOID, (void *)myWidgetClear },
};

// Events beyond the common set (Click, DblClick, Change, GotFocus, LostFocus).
// Only list EXTRA events here — common events are wired automatically.
static const WgtEventDescT sEvents[] = {
    { "StyleChange" },
};

static const WgtIfaceT sIface = {
    .basName      = "MyWidget",          // VB-style control name (used in .frm files)
    .props        = sProps,
    .propCount    = sizeof(sProps) / sizeof(sProps[0]),
    .methods      = sMethods,
    .methodCount  = sizeof(sMethods) / sizeof(sMethods[0]),
    .events       = sEvents,
    .eventCount   = sizeof(sEvents) / sizeof(sEvents[0]),
    .createSig    = WGT_CREATE_PARENT_TEXT, // create(parent, text)
    .createArgs   = {0},                    // no default numeric args
    .isContainer  = false,                  // cannot hold child widgets
    .defaultEvent = "Click",               // double-click in IDE opens this event
    .namePrefix   = "MyWidget",            // auto-names: MyWidget1, MyWidget2, ...
};

// ============================================================
// Registration — called by the loader at startup
// ============================================================

void wgtRegister(void) {
    sTypeId = wgtRegisterClass(&sClass);
    wgtRegisterApi("mywgt", &sApi);
    wgtRegisterIface("mywgt", &sIface);
}
