/*
  islzma.c, by Jordan Russell for Inno Setup
  This file is public domain (like the LZMA SDK)

  $jrsoftware: issrc/Projects/lzma2/Encoder/islzma.c,v 1.7 2010/03/24 19:55:40 jr Exp $
*/

#include <stddef.h>
#include <windows.h>
#include "../C/Alloc.h"
#include "../C/LzmaEnc.h"
#include "../C/Lzma2Enc.h"
#include "islzma.h"

// Private definition of a handle; callers of the DLL should use void*
struct LZMAHandle {
	int marker;
	BOOL LZMA2;
	CLzmaEncHandle encoder1;
	CLzma2EncHandle encoder2;
};
#define LZMA_HANDLE_MARKER 0x3E1A981F
#define LZMA_HANDLE_VALID(h) ((h) && (h)->marker == LZMA_HANDLE_MARKER)

static void *SzBigAlloc(void *p, size_t size) { return BigAlloc(size); }
static void SzBigFree(void *p, void *address) { BigFree(address); }
static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree };

static void *SzAlloc(void *p, size_t size) { return MyAlloc(size); }
static void SzFree(void *p, void *address) { MyFree(address); }
static ISzAlloc g_Alloc = { SzAlloc, SzFree };

SRes __stdcall LZMA_Init(BOOL LZMA2, struct LZMAHandle **handle)
{
	struct LZMAHandle *h = calloc(1, sizeof(*h));
	if (!h) return SZ_ERROR_MEM;

	h->marker = LZMA_HANDLE_MARKER;
	h->LZMA2 = LZMA2;

	if (LZMA2) {
		if (!(h->encoder2 = Lzma2Enc_Create(&g_Alloc, &g_BigAlloc))) {
			free(h);
			return SZ_ERROR_MEM;
		}
	} else {
		if (!(h->encoder1 = LzmaEnc_Create(&g_Alloc))) {
			free(h);
			return SZ_ERROR_MEM;
		}
	}

	*handle = h;
	return SZ_OK;
}

SRes __stdcall LZMA_SetProps(struct LZMAHandle *handle,
	struct LZMAEncoderProps *encProps, size_t encPropsSize)
{
	CLzmaEncProps props1;
	CLzma2EncProps props2;
	CLzmaEncProps *props;

	if (!LZMA_HANDLE_VALID(handle) || encPropsSize != sizeof(*encProps)) {
		return SZ_ERROR_PARAM;
	}

	if (handle->LZMA2) {
		Lzma2EncProps_Init(&props2);
		props = &props2.lzmaProps;
	} else {
		LzmaEncProps_Init(&props1);
		props = &props1;
	}

	props->algo = encProps->Algorithm;
	props->dictSize = encProps->DictionarySize;
	props->fb = encProps->NumFastBytes;
	props->btMode = encProps->BTMode;
	props->numThreads = encProps->NumThreads;

	if (handle->LZMA2) {
		props2.numBlockThreads = encProps->NumBlockThreads;
		props2.blockSize = encProps->BlockSize;
		return Lzma2Enc_SetProps(handle->encoder2, &props2);
	} else {
		props1.writeEndMark = 1;
		return LzmaEnc_SetProps(handle->encoder1, &props1);
	}
}

SRes __stdcall LZMA_Encode(struct LZMAHandle *handle, ISeqInStream *inStream,
	ISeqOutStream *outStream, ICompressProgress *progress)
{
	if (!LZMA_HANDLE_VALID(handle)) return SZ_ERROR_PARAM;

	if (handle->LZMA2) {
		Byte props;
		SizeT propsSize = sizeof(props);

		props = Lzma2Enc_WriteProperties(handle->encoder2);
		if (outStream->Write(outStream, &props, propsSize) != propsSize) {
			return SZ_ERROR_WRITE;
		};
		return Lzma2Enc_Encode(handle->encoder2, outStream, inStream, progress);
	} else {
		Byte props[LZMA_PROPS_SIZE];
		SizeT propsSize = sizeof(props);

		RINOK(LzmaEnc_WriteProperties(handle->encoder1, props, &propsSize));
		if (outStream->Write(outStream, &props, propsSize) != propsSize) {
			return SZ_ERROR_WRITE;
		};
		return LzmaEnc_Encode(handle->encoder1, outStream, inStream, progress,
			&g_Alloc, &g_BigAlloc);
	}
}

SRes __stdcall LZMA_End(struct LZMAHandle *handle)
{
	if (!LZMA_HANDLE_VALID(handle)) return SZ_ERROR_PARAM;

	handle->marker = 0;

	if (handle->LZMA2) {
		if (handle->encoder2) {
			Lzma2Enc_Destroy(handle->encoder2);
		}
	} else {
		if (handle->encoder1) {
			LzmaEnc_Destroy(handle->encoder1, &g_Alloc, &g_BigAlloc);
		}
	}

	free(handle);
	return SZ_OK;
}
