/**
 * @file
 * Memory pool manager (NexusOS Hardened)
 *
 */

#include "lwip/opt.h"
#include "lwip/memp.h"
#include "lwip/sys.h"
#include "lwip/stats.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/raw.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/igmp.h"
#include "lwip/ip4_frag.h"
#include "lwip/etharp.h"
#include "lwip/dhcp.h"
#include "lwip/timeouts.h"
#include "lwip/dns.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/priv/api_msg.h"
#include "lwip/priv/tcpip_priv.h"
#include "lwip/priv/memp_priv.h"

#include <string.h>

extern int printf(const char *format, ...);

#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"

const struct memp_desc *const memp_pools[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
#include "lwip/priv/memp_std.h"
};

#if MEMP_MEM_MALLOC
static void *
do_memp_malloc_pool(const struct memp_desc *desc)
{
  size_t size = 1024;
  if (desc != NULL) {
    size = desc->size;
  }
  return mem_malloc(LWIP_MEM_ALIGN_SIZE(size));
}
#else
static void *
do_memp_malloc_pool(const struct memp_desc *desc)
{
  struct memp *memp;
  SYS_ARCH_DECL_PROTECT(old_level);
  SYS_ARCH_PROTECT(old_level);
  memp = *desc->tab;
  if (memp != NULL) {
    *desc->tab = memp->next;
    SYS_ARCH_UNPROTECT(old_level);
    return ((u8_t *)memp + MEMP_SIZE);
  }
  SYS_ARCH_UNPROTECT(old_level);
  return NULL;
}
#endif

void memp_init(void)
{
#if !MEMP_MEM_MALLOC
  u16_t i;
  for (i = 0; i < MEMP_MAX; i++) {
    struct memp *memp;
    int j;
    const struct memp_desc *desc = memp_pools[i];
    *desc->tab = NULL;
    memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);
    for (j = 0; j < desc->num; ++j) {
      memp->next = *desc->tab;
      *desc->tab = memp;
      memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size);
    }
  }
#endif
}

void *memp_malloc(memp_t type)
{
  if (type >= MEMP_MAX) return NULL;
  
#if MEMP_MEM_MALLOC
  /* HEPHAESTUS ULTRA: Manual Size Switch. 
     Bypass memp_pools completely (it crashes). 
     Ensure correct sizes for PBUF_POOL/UDP_PCB. */
  size_t size = 1024; // Safe fallback for control structs

  switch(type) {
      case MEMP_UDP_PCB: size = sizeof(struct udp_pcb); break;
      case MEMP_TCP_PCB: size = sizeof(struct tcp_pcb); break;
      case MEMP_PBUF:    size = sizeof(struct pbuf); break;
      case MEMP_PBUF_POOL: size = 2048; break; // Covers MTU + Pbuf Header
      case MEMP_SYS_TIMEOUT: size = 128; break; // sys_timeo is private, ~32 bytes
  }
  
  return mem_malloc(LWIP_MEM_ALIGN_SIZE(size));
#else
  return do_memp_malloc_pool(memp_pools[type]);
#endif
}

void memp_free(memp_t type, void *mem)
{
  if (mem == NULL) return;
#if MEMP_MEM_MALLOC
  LWIP_UNUSED_ARG(type);
  mem_free(mem);
#else
  struct memp *memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE);
  SYS_ARCH_DECL_PROTECT(old_level);
  SYS_ARCH_PROTECT(old_level);
  memp->next = *(memp_pools[type]->tab);
  *(memp_pools[type]->tab) = memp;
  SYS_ARCH_UNPROTECT(old_level);
#endif
}
