#include "mxdsaction.h"

#include <file.h>
#include <filesystem>
#include <fstream>
#include <interleaf.h>
#include <object.h>

void ZeroInitObject(si::Object* o)
{
	o->unknown1_ = 0;
	o->flags_ = 0;
	o->unknown4_ = 0;
	o->duration_ = 0;
	o->loops_ = 0;
	o->unknown26_ = 0;
	o->unknown27_ = 0;
	o->unknown28_ = 0;
	o->unknown29_ = 0;
	o->unknown30_ = 0;
	o->volume_ = 0;
	o->time_offset_ = 0;
}

si::Interleaf::Version version = si::Interleaf::Version2_2;
uint32_t bufferSize = 65536;
uint32_t bufferCount = 8;

std::string out;
std::ofstream depfile;
si::MemoryBuffer mxHd;

void CreateWidescreen()
{
	std::string result = out + "/widescreen.si";
	struct AssetView {
		std::string name;
		std::string extra;
		int32_t z;
	};
	const AssetView widescreenBitmaps[] = {
		{"GaraDoor_Background_Wide",
		 "World:current, StartWith:\\Lego\\Scripts\\Isle\\Isle;1160, RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1161",
		 -10},
		{"Police_Background_Wide",
		 "World:\\lego\\scripts\\police\\police;0, StartWith:\\Lego\\Scripts\\Police\\Police;0, "
		 "RemoveWith:\\Lego\\Scripts\\Police\\Police;0",
		 10},
		{"Hospital_Background_Wide",
		 "World:\\lego\\scripts\\hospital\\hospital;0, StartWith:\\Lego\\Scripts\\Hospital\\Hospital;0, "
		 "RemoveWith:\\Lego\\Scripts\\Hospital\\Hospital;0",
		 10},
		{"SeaView_Background_Wide",
		 "World:\\Lego\\Scripts\\Isle\\Isle;0, StartWith:\\Lego\\Scripts\\Isle\\Isle;1140, "
		 "RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1141",
		 -10},
		{"ElevRide_Background_Wide",
		 "World:\\Lego\\Scripts\\Isle\\Isle;0, StartWith:\\Lego\\Scripts\\Isle\\Isle;1050, "
		 "RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1051",
		 -10},
		{"Observe_Background_Wide",
		 "World:current, StartWith:\\Lego\\Scripts\\Isle\\Isle;1118, "
		 "RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1119",
		 -10},
		{"ElevDown_Background_Wide",
		 "World:\\Lego\\Scripts\\Isle\\Isle;0, StartWith:\\Lego\\Scripts\\Isle\\Isle;1145, "
		 "RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1146",
		 -10},
		{"ElevOpen_Background_Wide",
		 "World:\\Lego\\Scripts\\Isle\\Isle;0, StartWith:\\Lego\\Scripts\\Isle\\Isle;1114, "
		 "RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1115",
		 -10},
		{"PoliDoor_Background_Wide",
		 "World:\\Lego\\Scripts\\Isle\\Isle;0, StartWith:\\Lego\\Scripts\\Isle\\Isle;1150, "
		 "RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1151",
		 -10}
	};

	si::Interleaf si;
	mxHd.seek(0, si::MemoryBuffer::SeekStart);
	si.Read(&mxHd);

	int i = 0;
	for (const AssetView& asset : widescreenBitmaps) {
		si::Object* object = new si::Object;
		std::string file = std::string("widescreen/") + asset.name + ".bmp";

		object->id_ = i;
		object->type_ = si::MxOb::Bitmap;
		object->flags_ = MxDSAction::c_enabled | MxDSAction::c_bit4;
		object->duration_ = -1;
		object->loops_ = 1;
		object->extra_ = si::bytearray(asset.extra.c_str(), asset.extra.length() + 1);
		object->presenter_ = "MxStillPresenter";
		object->name_ = asset.name;
		object->filetype_ = si::MxOb::STL;
		object->location_ = si::Vector3(-240, 0.0, asset.z);
		object->direction_ = si::Vector3(0, 0, 0);
		object->up_ = si::Vector3(0, 1, 0);

		if (!object->ReplaceWithFile(file.c_str())) {
			abort();
		}

		si.AppendChild(object);
		depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;
		i++;
	}

	si.Write(result.c_str());
}

void CreateHDMusic()
{
	std::string result = out + "/hdmusic.si";
	struct AssetView {
		std::string name;
		std::string extra;
		uint32_t duration;
		uint32_t loops;
		uint32_t flags;
	};
	const AssetView wavAudio[] = {
		{"BrickstrChase_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;3",
		 82850,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"BrickHunt_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;4",
		 192630,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"ResidentalArea_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;5",
		 89540,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"BeachBlvd_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;6",
		 152600,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"Cave_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;7",
		 69240,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"CentralRoads_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;8",
		 193380,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"Jail_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;9",
		 68820,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"Hospital_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;10",
		 211990,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"InformationCenter_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;11",
		 154510,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"PoliceStation_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;12",
		 57090,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"Park_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;13",
		 91210,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"RaceTrackRoad_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;16",
		 189000,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"Beach_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;17",
		 127490,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"JetskiRace_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;19",
		 64440,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"Act3Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;20",
		 80510,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3},
		{"JBMusic1_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;55", 125850, 1, MxDSAction::c_enabled},
		{"JBMusic2_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;56", 162900, 1, MxDSAction::c_enabled},
		{"JBMusic3_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;57", 122750, 1, MxDSAction::c_enabled},
		{"JBMusic4_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;58", 140000, 1, MxDSAction::c_enabled},
		{"JBMusic5_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;59", 72720, 1, MxDSAction::c_enabled},
		{"JBMusic6_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;60", 57030, 1, MxDSAction::c_enabled},
		{"InfoCenter_3rd_Floor_Music_HD",
		 "Replace:\\Lego\\Scripts\\Isle\\Jukebox;61",
		 114520,
		 10000,
		 MxDSAction::c_enabled | MxDSAction::c_bit3}
	};

	si::Interleaf si;
	mxHd.seek(0, si::MemoryBuffer::SeekStart);
	si.Read(&mxHd);

	int i = 0;
	for (const AssetView& asset : wavAudio) {
		si::Object* object = new si::Object;
		std::string file = std::string("hdmusic/") + asset.name + ".wav";

		object->id_ = i;
		object->type_ = si::MxOb::Sound;
		object->flags_ = asset.flags;
		object->duration_ = asset.duration * asset.loops;
		object->loops_ = asset.loops;
		object->extra_ = si::bytearray(asset.extra.c_str(), asset.extra.length() + 1);
		object->presenter_ = "MxWavePresenter";
		object->name_ = asset.name;
		object->filetype_ = si::MxOb::WAV;
		object->location_ = si::Vector3(0, 0, 0);
		object->direction_ = si::Vector3(0, 0, 1);
		object->up_ = si::Vector3(0, 1, 0);
		object->volume_ = 79;

		if (!object->ReplaceWithFile(file.c_str())) {
			abort();
		}

		si.AppendChild(object);
		depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;
		i++;
	}

	si.Write(result.c_str());
}

void CreateBadEnd()
{
	std::string result = out + "/badend.si";

	si::Interleaf si;
	mxHd.seek(0, si::MemoryBuffer::SeekStart);
	si.Read(&mxHd);

	si::Object* composite = new si::Object;
	std::string extra = "Replace:\\lego\\scripts\\intro:4";
	composite->id_ = 0;
	composite->type_ = si::MxOb::Presenter;
	composite->flags_ = MxDSAction::c_enabled;
	composite->duration_ = 0;
	composite->loops_ = 1;
	composite->extra_ = si::bytearray(extra.c_str(), extra.length() + 1);
	composite->presenter_ = "MxCompositeMediaPresenter";
	composite->name_ = "BadEnd_Movie";
	si.AppendChild(composite);

	si::Object* smk = new si::Object;
	std::string file = std::string("badend/BadEnd_Smk.smk");
	smk->id_ = 1;
	smk->type_ = si::MxOb::Video;
	smk->flags_ = MxDSAction::c_enabled;
	smk->duration_ = 75100;
	smk->loops_ = 1;
	smk->presenter_ = "MxSmkPresenter";
	smk->name_ = "BadEnd_Smk";
	smk->filetype_ = si::MxOb::SMK;
	smk->location_ = si::Vector3(0, 0, -10000);
	smk->direction_ = si::Vector3(0, 0, 1);
	smk->up_ = si::Vector3(0, 1, 0);
	smk->unknown29_ = 1; // Palette management = yes
	if (!smk->ReplaceWithFile(file.c_str())) {
		abort();
	}

	composite->AppendChild(smk);
	depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;

	si::Object* wav = new si::Object;
	file = std::string("badend/BadEnd_Wav.wav");
	wav->id_ = 2;
	wav->type_ = si::MxOb::Sound;
	wav->flags_ = MxDSAction::c_enabled;
	wav->duration_ = 77800;
	wav->loops_ = 1;
	wav->presenter_ = "MxWavePresenter";
	wav->name_ = "BadEnd_Wav";
	wav->filetype_ = si::MxOb::WAV;
	wav->location_ = si::Vector3(0, 0, 0);
	wav->direction_ = si::Vector3(0, 0, 1);
	wav->up_ = si::Vector3(0, 1, 0);
	wav->volume_ = 79;
	if (!wav->ReplaceWithFile(file.c_str())) {
		abort();
	}

	composite->AppendChild(wav);
	depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;

	si.Write(result.c_str());
}

void CreateRabbits()
{
	std::string result = out + "/rabbits.si";

	si::Interleaf si;
	mxHd.seek(0, si::MemoryBuffer::SeekStart);
	si.Read(&mxHd);

	// Object 0 (id=0): Blaubart model (fire-and-forget, loaded by chained Prepend)
	si::Object* blaubart = new si::Object;
	ZeroInitObject(blaubart);
	std::string file = std::string("rabbits/blaubart.mod");
	std::string extra = "Prepend:/lego/extra/rabbits;1";
	blaubart->id_ = 0;
	blaubart->type_ = si::MxOb::Object;
	blaubart->presenter_ = "LegoModelPresenter";
	blaubart->name_ = "blaubart";
	blaubart->flags_ = MxDSAction::c_enabled;
	blaubart->filetype_ = si::MxOb::OBJ;
	blaubart->loops_ = 1;
	blaubart->unknown28_ = 1;
	blaubart->unknown29_ = 1;
	blaubart->location_ = si::Vector3(-65.5f, 14.0f, 23.5f);
	blaubart->direction_ = si::Vector3(0, 0, 1);
	blaubart->up_ = si::Vector3(0, 1, 0);
	blaubart->extra_ = si::bytearray(extra.c_str(), extra.length() + 1);
	if (!blaubart->ReplaceWithFile(file.c_str())) {
		abort();
	}

	si.AppendChild(blaubart);
	depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;

	// Object 1 (id=1): Fluse model (fire-and-forget, loaded by chained Prepend from Blaubart)
	si::Object* fluse = new si::Object;
	ZeroInitObject(fluse);
	file = std::string("rabbits/fluse.mod");
	fluse->id_ = 1;
	fluse->type_ = si::MxOb::Object;
	fluse->presenter_ = "LegoModelPresenter";
	fluse->name_ = "fluse";
	fluse->flags_ = MxDSAction::c_enabled;
	fluse->filetype_ = si::MxOb::OBJ;
	fluse->loops_ = 1;
	fluse->unknown28_ = 1;
	fluse->unknown29_ = 1;
	fluse->location_ = si::Vector3(-62.0f, 14.0f, 28.0f);
	fluse->direction_ = si::Vector3(0, 0, 1);
	fluse->up_ = si::Vector3(0, 1, 0);
	if (!fluse->ReplaceWithFile(file.c_str())) {
		abort();
	}

	si.AppendChild(fluse);
	depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;

	// Object 2 (id=2): LegoAnimMMPresenter (composite)
	// EnableWith triggers when Isle world becomes active
	// Prepend chains: loads Blaubart model (id=0), which chains to Fluse model (id=1)
	si::Object* composite = new si::Object;
	ZeroInitObject(composite);
	extra = "EnableWith:\\Lego\\Scripts\\Isle\\Isle;0, Prepend:/lego/extra/rabbits;0";
	composite->id_ = 2;
	composite->type_ = si::MxOb::Presenter;
	composite->presenter_ = "LegoAnimMMPresenter";
	composite->flags_ = MxDSAction::c_enabled | MxDSAction::c_bit3;
	composite->loops_ = 1;
	composite->extra_ = si::bytearray(extra.c_str(), extra.length() + 1);
	composite->name_ = "rabbits_composite";
	composite->direction_ = si::Vector3(0, 0, 1);
	composite->up_ = si::Vector3(0, 1, 0);
	si.AppendChild(composite);

	// Child (id=3): Combined animation (both rabbits in one anim tree)
	si::Object* anim = new si::Object;
	ZeroInitObject(anim);
	file = std::string("rabbits/rabbits.ani");
	anim->id_ = 3;
	anim->type_ = si::MxOb::Object;
	anim->presenter_ = "LegoLoopingAnimPresenter";
	anim->name_ = "rabbits_anim";
	anim->flags_ = MxDSAction::c_enabled | si::MxOb::NoLoop;
	anim->filetype_ = si::MxOb::OBJ;
	anim->duration_ = -1;
	anim->loops_ = 1;
	anim->unknown28_ = 1;
	anim->unknown29_ = 1;
	anim->location_ = si::Vector3(0, 0, 0);
	anim->direction_ = si::Vector3(0, 0, 1);
	anim->up_ = si::Vector3(0, 1, 0);
	if (!anim->ReplaceWithFile(file.c_str())) {
		abort();
	}

	composite->AppendChild(anim);
	depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;

	si.Write(result.c_str());
}

int main(int argc, char* argv[])
{
	out = argv[1];
	depfile = std::ofstream(argv[2]);

	mxHd.WriteU32(si::RIFF::MxHd);
	mxHd.WriteU32(3 * sizeof(uint32_t));
	mxHd.WriteU32(version);
	mxHd.WriteU32(bufferSize);
	mxHd.WriteU32(bufferCount);

	CreateWidescreen();
	CreateHDMusic();
	CreateBadEnd();
	CreateRabbits();
	return 0;
}
