#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <uchar.h>
#include <err.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "fake-lib.h"

uint32_t HashPublicKeyInfo(void *pCertContext,
                           void *rgbSubjectKeyIdentifier,
                           uint32_t *pcbSubjectKeyIndentifier)
{
    errx(1, "HashPublicKeyInfo NYI");
}

uint32_t ResetAcls(const char16_t **pwzFiles, uint32_t cFiles)
{
    return 0;
}

typedef struct CabCreateContext {
    char *tempdir;
    char *outdir;
    char *outfile;

    char **args;
    int nargs, argsize;
} CabCreateContext;

uint32_t CreateCabBegin(const char16_t *wzCab, const char16_t *wzCabDir,
                        uint32_t dwMaxFiles, uint32_t dwMaxSize,
                        uint32_t dwMaxThresh, int compression,
                        CabCreateContext **out_ctx)
{
    CabCreateContext *ctx = snew(CabCreateContext);
    ctx->outdir = ascii(wzCabDir, true);
    ctx->outfile = dupcat(ctx->outdir, "/",
                          ascii(wzCab, true), (const char *)NULL);
    ctx->tempdir = dupcat(ctx->outdir, "/cabXXXXXX", (const char *)NULL);
    if (!mkdtemp(ctx->tempdir))
        err(1, "mkdtemp");
    ctx->nargs = 0;
    ctx->argsize = 16;
    ctx->args = snewn(ctx->argsize, char *);
    ctx->args[ctx->nargs++] = dupcat("lcab", (const char *)NULL);
    ctx->args[ctx->nargs++] = dupcat("-n", (const char *)NULL);
    *out_ctx = ctx;
    return 0;
}

uint32_t CreateCabAddFile(const char16_t *wzFile, const char16_t *wzToken,
                          void *pmfHash, CabCreateContext *ctx)
{
    char *file = ascii(wzFile, true);
    char *file_abs = realpath(file, NULL);
    char *cabname = ascii(wzToken, true);
    char *cab_abs = dupcat(ctx->tempdir, "/", cabname, (const char *)NULL);
    printf("CreateCabAddFile: %s :: %s <- %s\n", ctx->outfile,
           cabname, file_abs);
    if (symlink(file_abs, cab_abs) < 0)
        err(1, "symlink");
    if (ctx->nargs + 1 >= ctx->argsize) {
        ctx->argsize = ctx->nargs * 5 / 4 + 16;
        ctx->args = sresize(ctx->args, ctx->argsize, char *);
    }
    ctx->args[ctx->nargs++] = cab_abs;
    return 0;
}

uint32_t CreateCabAddFiles(const char16_t *const *pwzFiles,
                           const char16_t *const *pwzTokens,
                           void *pmfHash, uint32_t cFiles,
                           CabCreateContext *ctx)
{
    for (uint32_t i = 0; i < cFiles; i++)
        CreateCabAddFile(pwzFiles[i], pwzTokens[i], pmfHash, ctx);
    return 0;
}

uint32_t CreateCabFinish(CabCreateContext *ctx, void (*split_callback)(void))
{
    if (ctx->nargs + 2 >= ctx->argsize) {
        ctx->argsize = ctx->nargs * 5 / 4 + 16;
        ctx->args = sresize(ctx->args, ctx->argsize, char *);
    }
    ctx->args[ctx->nargs++] = ctx->outfile;
    ctx->args[ctx->nargs++] = NULL;
    system_argv_array(ctx->args);
    return 0;
}

void CreateCabCancel(void *hContext)
{
}

uint32_t ExtractCabBegin(void)
{
    return 0;
}

uint32_t ExtractCab(const char16_t *wzCabinet, const char16_t *wzExtractDir)
{
    char *cab = ascii(wzCabinet, true), *dir = ascii(wzExtractDir, true);
    fprintf(stderr, "ExtractCab(\"%s\", \"%s\"\n", cab, dir);
    system_argv("cabextract", "-d", dir, cab, (const char *)NULL);
    return 0;
}

void ExtractCabFinish(void)
{
}

uint32_t EnumerateCabBegin(void)
{
    return 0;
}

uint32_t EnumerateCab(const char16_t *wzCabinet, void *pfnNotify)
{
    /* FIXME: pfnNotify looks like a fn ptr again */
    fprintf(stderr, "EnumerateCab!\n");
    return 0;
}

void EnumerateCabFinish(void)
{
}