#include "ehs/EHS.h"
#include "ehs/Log.h"
#include "ehs/Version.h"
#include "ehs/io/Console.h"
#include "ehs/GarbageCollector.h"
#include "ehs/io/audio/Audio.h"
#include "ehs/io/img/Img.h"
#include "ehs/io/img/PNG.h"
#include "ehs/io/RIFF.h"
#include "ehs/io/mdl/Mdl.h"

#include <zlib.h>

#if defined(EHS_OS_LINUX)
    #include <sys/mman.h>
#endif

namespace ehs
{
    constexpr Char_32 name_32[] = U"Event Horizon Suite";
    constexpr Char_16 name_16[] = L"Event Horizon Suite";
    constexpr Char_8 name_8[] = "Event Horizon Suite";
    constexpr Char_32 acronym_32[] = U"EHS";
    constexpr Char_16 acronym_16[] = L"EHS";
    constexpr Char_8 acronym_8[] = "EHS";
    constexpr Char_32 versionId_32[] = U"Release";
    constexpr Char_16 versionId_16[] = L"Release";
    constexpr Char_8 versionId_8[] = "Release";

	Str_8 appName;
	Str_8 appVerId;
	Version appVer;

    const Char_32* GetName_32()
    {
        return name_32;
    }

    const Char_16* GetName_16()
    {
        return name_16;
    }

    const Char_8* GetName_8()
    {
	    return name_8;
    }

	Str_8 GetAppName_8()
	{
		return appName;
	}

    const Char_32* GetAcronym_32()
    {
        return acronym_32;
    }

    const Char_16* GetAcronym_16()
    {
        return acronym_16;
    }

    const Char_8* GetAcronym_8()
    {
        return acronym_8;
    }

    const Char_32* GetVersionId_32()
    {
        return versionId_32;
    }

    const Char_16* GetVersionId_16()
    {
        return versionId_16;
    }

    const Char_8* GetVersionId_8()
    {
        return versionId_8;
    }

	Str_8 GetAppVersionId_8()
	{
		return appVerId;
	}

    Version GetVersion()
    {
        return {1, 2, 0};
    }

	Version GetAppVersion()
	{
		return appVer;
	}
}

void LogRaised(const ehs::Log& log)
{
	ehs::Array<ehs::Str_8> tags = log.GetTags();

	ehs::Str_8 result = "{";

	for (ehs::UInt_32 i = 0; i < tags.Size(); ++i)
	{
		result += tags[i];
		if (i != tags.Size() - 1)
			result += ", ";
	}

	result += "} (" + ehs::Str_8::FromNum(log.GetCode()) + "): " + log.GetMsg();

	ehs::Console::Write_8(result);
}

int main()
{
	ehs::Console::Attach();

	ehs::Log::SetCallback(LogRaised);

	ehs::Audio::AddCodec({
		"Waveform Audio",
		"wav",
		ehs::Endianness::LE,
		nullptr,
		ehs::DecodeWAV
	});

	ehs::Audio::AddCodec({
		"Event Horizon Audio",
		"eha",
		ehs::Endianness::LE,
		ehs::EncodeEHA,
		ehs::DecodeEHA
	});

	ehs::Img::AddCodec({
		"Portable Network Graphic",
		"png",
		ehs::Endianness::BE,
		nullptr,
		ehs::DecodePNG
	});

	ehs::Img::AddCodec({
		"Quite OK Image",
		"qoi",
		ehs::Endianness::BE,
		ehs::EncodeQOI,
		ehs::DecodeQOI
	});

	ehs::Mdl::AddCodec({
		"Event Horizon Model",
		"ehm",
		ehs::Endianness::LE,
		ehs::EncodeEHM,
		ehs::DecodeEHM
	});

	ehs::GarbageCollector::Start();

	const ehs::SInt_32 code = Main(&ehs::appName, &ehs::appVerId, &ehs::appVer);
	if (code)
		EHS_LOG("Warning", 0, "Executable exited with code #" + ehs::Str_8::FromNum(code) + ".");

	ehs::GarbageCollector::Stop();

	return code;
}