#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <windows.h>

#include <zlib.h>
#include "../xsfc/tagget.h"
#include "../xsfc/drvimpl.h"

//Play!
#include "StdStream.h"
#include "MemStream.h"
#include "psx/PsxBios.h"
#include "ps2/PsfBios.h"
#include "Iop_PsfSubSystem.h"
#include "Ps2Const.h"
#include "SoundHandler.h"

#include "spu2-x/Config.h"	//spu2-x


static DWORD getdwordle(const BYTE *pData)
{
	return pData[0] | (((DWORD)pData[1]) << 8) | (((DWORD)pData[2]) << 16) | (((DWORD)pData[3]) << 24);
}

typedef struct
{
	const char *tag;
	size_t taglen;
	int level;
	int found;
} loadlibwork_t;

static unsigned refresh_rate = 0;

static uint16_t getwordle(const unsigned char *pData)
{
	return pData[0] | (((uint16_t)pData[1]) << 8);
}


int LoadPs2Recurse(PS2::CPsfBios* bios, Framework::CMemStream* input)
{
	CPsfBase psfFile(*input);

	if (psfFile.GetVersion() != CPsfBase::VERSION_PLAYSTATION2)
	{
		delete input;
		return XSF_FALSE;
	}

	if (refresh_rate == 0)
		refresh_rate = XSFTag::GetInt("_refresh", input->GetBuffer(), input->GetSize(), 0);
	bool isUTF8 = XSFTag::Exists("utf8", input->GetBuffer(), input->GetSize());

	unsigned n = 1;

	do
	{
		char lib_n[16];
		if (n == 1)
		{
			lib_n[0] = '_';
			lib_n[1] = 'l';
			lib_n[2] = 'i';
			lib_n[3] = 'b';
			lib_n[4] = '\0';
			n++;
		}
		else
#ifdef HAVE_SPRINTF_S
		sprintf_s(lib_n, "_lib%d", n++);
#else
		sprintf(lib_n, "_lib%d", n++);
#endif

		xsfc::TString libPath = XSFTag::Get(isUTF8, lib_n, input->GetBuffer(), input->GetSize());	
		
		if (libPath.GetW()[0] != L'\0')
		{
			xsfc::TStringM libPathRaw(isUTF8, libPath);
			uint8 * libbuf = 0;
			unsigned int libsize = 0;

			if (!xsf_get_lib((char*)libPathRaw.GetM(), (void**)&libbuf, &libsize))
			{
				delete input;
				return XSF_FALSE;
			}

			Framework::CMemStream* result = new Framework::CMemStream();
			result->Allocate(libsize);
			if (result->GetBuffer() == nullptr)
			{
				delete input;
				delete result;
				free(libbuf);
				return XSF_FALSE;
			}
			memcpy(result->GetBuffer(), libbuf, libsize);
			free(libbuf);

			if (FALSE == LoadPs2Recurse(bios, result))
			{
				delete input;
				return XSF_FALSE;
			}
		}
		else
			break;
	} while (n < 10);

	delete input;
	
	bios->AppendArchive(psfFile);
	return XSF_TRUE;
}


static Iop::CPsfSubSystem subSystem(true);

int LoadPs2(const uint8 * pdata, uint32 datasize)
{	
	std::shared_ptr<PS2::CPsfBios> bios = std::make_shared<PS2::CPsfBios>(subSystem.GetCpu(), subSystem.GetCpuExecutor(), subSystem.GetRam(), PS2::IOP_RAM_SIZE, subSystem.GetSpr());

	subSystem.SetBios(bios);

	Framework::CMemStream* result = new Framework::CMemStream();
	result->Allocate(datasize);
	if (result->GetBuffer() == nullptr)
	{
		delete result;
		return XSF_FALSE;
	}
	memcpy(result->GetBuffer(), pdata, datasize);

	if (XSF_FALSE == LoadPs2Recurse(static_cast<PS2::CPsfBios*>(bios.get()), result))
		return XSF_FALSE;

	bios->Start();
	return XSF_TRUE;
	
}


static double watof(const wchar_t *p){
	int i = 1;
	int s = 1;
	double r = 0;
	double b = 1;
	while (p && *p)
	{
		switch (*p)
		{
#if ((L'9' - L'0') == 9) && ((L'8' - L'0') == 8) && ((L'7' - L'0') == 7) && ((L'6' - L'0') == 6) && ((L'5' - L'0') == 5) && ((L'4' - L'0') == 4) && ((L'3' - L'0') == 3) && ((L'2' - L'0') == 2) && ((L'1' - L'0') == 1)
		case L'0':	case L'1':	case L'2':	case L'3':	case L'4':
		case L'5':	case L'6':	case L'7':	case L'8':	case L'9':
			r = r * 10.0 + (*p - L'0'); if (!i) b *= 0.1; break;
#else
		case L'0': r = r * 10 + 0; if (!i) b *= 0.1; break;
		case L'1': r = r * 10 + 1; if (!i) b *= 0.1; break;
		case L'2': r = r * 10 + 2; if (!i) b *= 0.1; break;
		case L'3': r = r * 10 + 3; if (!i) b *= 0.1; break;
		case L'4': r = r * 10 + 4; if (!i) b *= 0.1; break;
		case L'5': r = r * 10 + 5; if (!i) b *= 0.1; break;
		case L'6': r = r * 10 + 6; if (!i) b *= 0.1; break;
		case L'7': r = r * 10 + 7; if (!i) b *= 0.1; break;
		case L'8': r = r * 10 + 8; if (!i) b *= 0.1; break;
		case L'9': r = r * 10 + 9; if (!i) b *= 0.1; break;
#endif
		case L'.': i = 0; break;
		case L'-': s = -s; break;
		case L' ': break;
		}
		p++;
	}
	return s * r * b;
}

void xsf_set_extend_param(unsigned dwId, const wchar_t *lpPtr)
{
	if (!lpPtr)
		return;

}


void xsf_set_extend_param_void(unsigned dwId, const void* lpPtr)
{
	if (!lpPtr)
		return;

	switch (dwId)
	{
	case 1:	//interpolation
		Interpolation = *(int*)lpPtr;
		if (Interpolation < 0 || Interpolation > 5)
			Interpolation = 4;
		break;
	case 2:	//final volume
		FinalVolume = *(float*)lpPtr;
		if (FinalVolume <= 0 || FinalVolume > 10.0f)
			FinalVolume = 1.0f;
		break;
	case 3:	//disable effect(reverb)
		EffectsDisabled = *(unsigned*)lpPtr ? true : false;
		break;
	case 4:
		postprocess_filter_dealias = *(unsigned*)lpPtr ? true : false;
		break;
	default:
		break;
	}
	
}


class VIOPSF2SoundHandler : public CSoundHandler
{
public:
	uint8 buf[2048 << 2];
	uint32 buflen;
	uint32 buffil;
	uint32 bufcur;

	VIOPSF2SoundHandler()
	{
		this->Reset();
	}

	virtual void	Reset()
	{
		buflen = sizeof(buf);
		buffil = 0;
		bufcur = 0;
	}

	virtual void	Write(int16* finalWave, unsigned int samples, unsigned int samplerate)
	{
		uint32 length = samples <<= 1;
		if (length > buflen - buffil)
		{
			length = buflen - buffil;
		}
		if (length > 0)
		{
			CopyMemory(buf + buffil, finalWave, length);
			buffil += length;
		}
	}

	virtual bool	HasFreeBuffers()
	{
		return buflen - buffil > 0;
	}
	virtual void	RecycleBuffers()
	{
		buffil = 0;
		bufcur = 0;
	}
};

static VIOPSF2SoundHandler PSF2SoundHandler;


int xsf_start(void *pfile, unsigned bytes)
{	
	subSystem.Reset();
	PSF2SoundHandler.Reset();
	refresh_rate = 0;

	if (!LoadPs2((const uint8* )pfile, bytes))
	{
		return XSF_FALSE;
	}
	
	subSystem.SetFrameRate(refresh_rate);
	return XSF_TRUE;
}

extern "C" unsigned long dwChannelMute;
static unsigned long dwChannelMuteOld = 0;

int xsf_gen(void *pbuffer, unsigned samples)
{
	int ret = 0;
	uint32_t bytes = samples << 2;
	uint8_t *des = (uint8_t *)pbuffer;
	while (bytes > 0)
	{
		uint32_t remain = PSF2SoundHandler.buffil - PSF2SoundHandler.bufcur;
		while (remain == 0)
		{
			PSF2SoundHandler.bufcur = 0;
			PSF2SoundHandler.buffil = 0;

			subSystem.Update(false, &PSF2SoundHandler);

			remain = PSF2SoundHandler.buffil - PSF2SoundHandler.bufcur;
			
		}
		uint32_t len = remain;
		if (len > bytes)
		{
			len = bytes;
		}
		CopyMemory(des, PSF2SoundHandler.buf + PSF2SoundHandler.bufcur, len);
		bytes -= len;
		des += len;
		PSF2SoundHandler.bufcur += len;
		ret += len;
	}
	return ret;
}
#if 0
int xsf_gen(void *pbuffer, unsigned samples){return 0;}//リンカエラー回避
//by Kobarin
extern "C" int xsf_gen_f(void *pbuffer, unsigned samples, double scale)
{//64bit float 化(ついでに音量制御)
    const double k = scale / 0x8000;
	double *dst = (double*)pbuffer;
	while (samples > 0)
	{
		uint32_t remain = PSF2SoundHandler.buffil - PSF2SoundHandler.bufcur;
		while (remain == 0)
		{
			PSF2SoundHandler.bufcur = 0;
			PSF2SoundHandler.buffil = 0;
			subSystem.Update(false, &PSF2SoundHandler);

			remain = PSF2SoundHandler.buffil - PSF2SoundHandler.bufcur;
		}
		uint32_t len = remain / 2;
		if (len > samples * 2)
		{
			len = samples * 2;
		}
        short *src = (short*)(PSF2SoundHandler.buf + PSF2SoundHandler.bufcur);
        for(uint32_t i = 0; i < len; i++){
            dst[i] = src[i] * k;
        }
        samples -= len / 2;
        dst += len;
		PSF2SoundHandler.bufcur += len*2;
	}
	return 0;
}
#endif
void xsf_term(void)
{
	subSystem.Reset();	//release bios
    extern void Free_Jitter(void);
    Free_Jitter();//by Kobarin  メモリリーク対策(BasicBlock.cpp)
}

