#include <../source/TAL/objbase.h>
#include <../source/PAL/oshead.h>
#include <../source/vfs/drivers/fslist.h>

#ifdef	CONFIG_VFS_PEFS

typedef struct reshdr_s
{
    char    name[_MAX_PATH];
    u32_t   size;
    u16_t   lang;
    void    *base;
}   reshdr_t, *reshdr_p;

typedef struct vfs_pefs_s
{
    vfs_dev_t   vtbl;
    HANDLE      module;
    u32_t       num_ino;
    char        volume_name[_MAX_PATH];
    reshdr_t    *dir;
}   vfs_pefs_t, *vfs_pefs_p;

/*
 * PEFS methods
 */
V_LOCAL file_t  V_CALL
pefs_create_port(vfs_dev_t *self,u32_t inode)
{
    reshdr_t *dirp;
    io_section_p handle;
    if(inode>=((vfs_pefs_t*)self)->num_ino)
        return NULL;
    dirp=((vfs_pefs_t*)self)->dir+inode;
    /* Allocate section for IO, set pointer to start of data */
    handle=ioAllocSection(sizeof(io_section_t),dirp->base,dirp->size);
    return (file_t)handle;
}

V_LOCAL u32_t V_CALL
pefs_get_inode_count(vfs_dev_t *self)
{
    return ((vfs_pefs_t*)self)->num_ino;
}

V_LOCAL vboolean    V_CALL
pefs_get_file_name(vfs_dev_t *self,u32_t inode,char *name_ret)
{
    if(inode>=((vfs_pefs_t*)self)->num_ino)
        return false;
    strCopy(name_ret,((vfs_pefs_t*)self)->dir[inode].name,_MAX_PATH);
    return true;
}

V_LOCAL char_p  strDownCase(char *str)
{
    char *ptr;
    for(ptr=str;*ptr;ptr++)
        *ptr=(char)tolower(*ptr);
    return str;
}

/*
 * PEFS destructor
 * Note: we leave ref_count untouched !
 */
V_LOCAL u32_t V_CALL
pefs_destroy(vhandle_t handle)
{
    vfs_pefs_p self=(vfs_pefs_p)handle;
    if(self->vtbl.base.ref_count == 1)
    {
        FreeLibrary(self->module);
        memFreeObject(self);
        return 0;
    }
    return self->vtbl.base.ref_count;
}

V_LOCAL BOOL
pefsEnumLangsFunc(HANDLE hModule,LPCTSTR lpType,LPCTSTR lpName,
    WORD wLang,LONG lParam)
{
    reshdr_t hdr;
    HANDLE hResInfo; 
    bstack_t *dir=(bstack_t*)lParam;
    hResInfo = FindResourceEx(hModule,lpType,lpName,wLang);
    memZero(&hdr,sizeof(reshdr_t));
    strCopy(hdr.name,(char*)lpName,_MAX_PATH);
    strDownCase(hdr.name);
    hdr.lang=wLang;
    hdr.base=LockResource(LoadResource(hModule,hResInfo));
    hdr.size=SizeofResource(hModule,hResInfo);
    stackPush(dir,&hdr);
    return TRUE;
}

V_LOCAL BOOL
pefsEnumNamesFunc(HANDLE hModule,LPCTSTR lpType,LPTSTR lpName,LONG lParam)
{
    /* resource must be identified by name, not by ID */
    if((ULONG)lpName & 0xFFFF0000)
    {
        EnumResourceLanguages(hModule,lpType,lpName,
            (ENUMRESLANGPROC)pefsEnumLangsFunc,lParam); 
    } 
    return TRUE; 
} 

V_LOCAL BOOL
pefsEnumTypesFunc(HANDLE hModule,LPTSTR lpType,LONG lParam)
{
    if(lpType == RT_RCDATA)
    {
        EnumResourceNames(hModule,lpType,
            (ENUMRESNAMEPROC)pefsEnumNamesFunc,lParam);
    }
    return TRUE;
}

/*
 *  Win32 PE Image FS factory
 */
vfs_dev_p   V_CALL
vfsMountExecutable(char *volume_name)
{
    bstack_t temp_dir;
    HANDLE module;
    vfs_pefs_p dev;
    char ext[_MAX_EXT];
    char os_name[_MAX_PATH];
    fsSplitPath(volume_name,NULL,NULL,ext);
    if(strcasecmp(ext,"exe") && strcasecmp(ext,"dll"))
        return NULL;
#ifdef  CHECKED_BUILD
    conlPrintf(LOG_INFO,"vfsMountExecutable(): adding '%s' ...\n",
        volume_name);
#endif  /* CHECKED_BUILD */
    vfsUnix2Native(os_name,volume_name);
    module=LoadLibrary(os_name);
    if(!module)
        return NULL;
    stackInit(&temp_dir,sizeof(reshdr_t));
    EnumResourceTypes(module,
        (ENUMRESTYPEPROC)pefsEnumTypesFunc,(LONG)&temp_dir);
    if(!stackCount(&temp_dir))
    {
        /* No resources */
#ifdef  CHECKED_BUILD
        conlPrintf(LOG_TRACE,
            "vfsMountExecutable('%s'): no resources of type RT_RCDATA.\n",
            volume_name);
#endif  /* CHECKED_BUILD */
        FreeLibrary(module);
        return NULL;
    }
#ifdef  CHECKED_BUILD
    conlPrintf(LOG_TRACE,
        "vfsMountExecutable('%s'): added %d RT_RCDATA resources.\n",
        volume_name,stackCount(&temp_dir));
#endif  /* CHECKED_BUILD */
    dev=(vfs_pefs_t*)vfsAlloc(sizeof(vfs_pefs_t)+
        stackCount(&temp_dir)*sizeof(reshdr_t));
    dev->module=module;
    dev->dir=(reshdr_t*)(dev+1);
    memCopy(dev->dir,stackGetStorage(&temp_dir),
        stackCount(&temp_dir)*sizeof(reshdr_t));
    dev->vtbl.create_file=pefs_create_port;
    strCopy(dev->volume_name,volume_name,_MAX_PATH);
    dev->num_ino=stackCount(&temp_dir);
    dev->vtbl.get_inode_count=pefs_get_inode_count;
    dev->vtbl.get_file_name=pefs_get_file_name;
    dev->vtbl.base.destroy=pefs_destroy;
    stackClear(&temp_dir);
    return (vfs_dev_p)dev;
}

#else   /* __WATCOMC__ */

/*
 * Watcom C (11) resource compiler and/or linker seems to be
 * broken: resource enumeration functions return nonsence.
 */

vfs_dev_p   V_CALL
vfsMountExecutable(char *volume_name)
{
#ifdef  CHECKED_BUILD
    conlPuts(LOG_ERROR,"vfsMountExecutable(): WATCOM tools are broken.\n");
#endif  /* CHECKED_BUILD */
    return NULL;
}

#endif  /* CONFIG_VFS_PEFS */
