// Copyright (C) 2026 Sophie Sitzler
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

// @file ductape.h
// @desc Static template generator for C 

#pragma once

#include <stdio.h>

// @macro __DUC_TEMPLDEF_TYPE
// @internal
// @desc Creates the type definitions for the template.
// @param name - The template name.
// @param param_list - The exposed template parameters.
#define __DUC_TEMPLDEF_TYPE(name, param_list) \
  typedef struct { \
    const char *__duc_unused; \
    param_list \
  } name##_t; \
  \
  typedef void (*name##_fn_t)(void *, name##_t *);

// @macro __DUC_TEMPLDEF_RENDER
// @internal
// @desc Creates the render implementation for the template.
// @param name - The template name.
#define __DUC_TEMPLDEF_RENDER(name) \
  int name##_render(void *out, void *ctx) { \
    char *path = getenv("DUC_BUILDPATH"); \
    if (path == NULL) { \
      path = "./"; \
    } \
    char dl_path[strlen(path) + strlen(__DUC_UID_##name) + 4]; \
    snprintf(dl_path, sizeof(dl_path), "%s%s.so", path, __DUC_UID_##name); \
    void *handle = dlopen(dl_path, RTLD_NOW); \
    if (!handle) { \
      fprintf(stderr, "%s\n", dlerror()); \
      return 1; \
    } \
    const size_t *hash = (size_t *) dlsym(handle, "__DUC_SIG_HASH_" #name); \
    if (!hash) { \
      fprintf(stderr, "Missing signature hash!\n"); \
      dlclose(handle); \
      return 1; \
    } \
    if (__DUC_SIG_HASH_##name != *hash) { \
      fprintf(stderr, "Invalid signature hash! (%lx != %lx)\n", __DUC_SIG_HASH_##name, *hash); \
      dlclose(handle); \
      return 1; \
    } \
    void *sym = dlsym(handle, "duc_" #name); \
    char *err = dlerror(); \
    if (err) { \
      fprintf(stderr, "%s\n", err); \
      dlclose(handle); \
      return 1; \
    } \
    name##_fn_t fn = NULL; \
    memcpy(&fn, &sym, sizeof(fn)); \
    fn(out, (name##_t *) ctx); \
    dlclose(handle); \
    return 0; \
  }

// @macro __DUC_TEMPLDEF_HEADER
// @internal
// @desc Creates the header definition for the template. Used in generated .h file.
// @param name - The template name.
// @param param_list - The exposed template parameters.
#define __DUC_TEMPLDEF_HEADER(name, param_list) \
  __DUC_TEMPLDEF_TYPE(name, param_list) \
  __DUC_TEMPLDEF_RENDER(name)

// @macro DUC_TEMPLDEF
// @desc Marks the beginning of a template definition. Used in generated .so file.
// @param name - The template name.
// @param param_list - The exposed template parameters.
#define DUC_TEMPLDEF(name, param_list) \
  __DUC_TEMPLDEF_TYPE(name, param_list) \
  \
  void duc_##name(void *out, name##_t *ctx) {

// @macro DUC_ENDTEMPL
// @desc Marks the end of a template definition.
#define DUC_ENDTEMPL }
