// strip.c -- Release build stripping
//
// Removes debug information from a compiled module:
//   - Clears debug variable info (names, scopes, types)
//   - Clears debug UDT definitions
//   - Mangles procedure names that aren't needed for runtime dispatch.
//     The form runtime dispatches events by name (Control_Event pattern)
//     and SetEvent looks up handlers by name at runtime, so those proc
//     names must be preserved. Everything else becomes F1, F2, F3...
//
// OP_LINE removal is deferred to a future version (requires
// bytecode compaction and offset rewriting).

#include "strip.h"
#include "basEvents.h"
#include "../runtime/values.h"

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


// Events fired by name via basFormRtFireEvent* in formrt.c. Any proc
// ending in "_<EventName>" must keep its name so the dispatcher can
// find it.  Declared in basEvents.h; defined here as the single source
// of truth.
const char *basEventSuffixes[] = {
    "Load", "Unload", "QueryUnload", "Resize", "Activate", "Deactivate",
    "Click", "DblClick", "Change", "Timer",
    "GotFocus", "LostFocus",
    "KeyPress", "KeyDown", "KeyUp",
    "MouseDown", "MouseUp", "MouseMove",
    "Scroll", "Reposition", "Validate",
    NULL
};


// Function prototypes (alphabetical)
void basStripModule(BasModuleT *mod);
static bool nameEndsWithEventSuffix(const char *name);
static bool nameInConstantPool(const BasModuleT *mod, const char *name);


void basStripModule(BasModuleT *mod) {
    if (!mod) {
        return;
    }

    // Clear debug variable info
    free(mod->debugVars);
    mod->debugVars     = NULL;
    mod->debugVarCount = 0;

    // Clear debug UDT definitions
    if (mod->debugUdtDefs) {
        for (int32_t i = 0; i < mod->debugUdtDefCount; i++) {
            free(mod->debugUdtDefs[i].fields);
        }

        free(mod->debugUdtDefs);
        mod->debugUdtDefs     = NULL;
        mod->debugUdtDefCount = 0;
    }

    // Mangle proc names. Keep names that are needed for runtime name
    // lookup: event handlers (Control_Event pattern) and any name
    // referenced as a string constant (e.g. SetEvent's target name).
    int32_t nextMangled = 1;

    for (int32_t i = 0; i < mod->procCount; i++) {
        BasProcEntryT *proc = &mod->procs[i];

        if (proc->name[0] == '\0') {
            continue;
        }

        if (nameEndsWithEventSuffix(proc->name)) {
            continue;
        }

        if (nameInConstantPool(mod, proc->name)) {
            continue;
        }

        snprintf(proc->name, sizeof(proc->name), "F%ld", (long)nextMangled++);
    }
}


static bool nameEndsWithEventSuffix(const char *name) {
    const char *underscore = strrchr(name, '_');

    if (!underscore) {
        return false;
    }

    const char *suffix = underscore + 1;

    for (int32_t i = 0; basEventSuffixes[i]; i++) {
        if (strcasecmp(suffix, basEventSuffixes[i]) == 0) {
            return true;
        }
    }

    return false;
}


static bool nameInConstantPool(const BasModuleT *mod, const char *name) {
    for (int32_t i = 0; i < mod->constCount; i++) {
        const BasStringT *s = mod->constants[i];

        if (s && strcasecmp(s->data, name) == 0) {
            return true;
        }
    }

    return false;
}
