/*
	This script file is part of the Sonic 3 A.I.R. script software distribution.
	Copyright (C) 2017-2024 by Eukaryot

	Published under the GNU GPLv3 open source software license, see license.txt
	or https://www.gnu.org/licenses/gpl-3.0.en.html
*/



//-------------------------------------------------------//
// Debug watches                                         //
//-------------------------------------------------------//

function void Debugging.setupDebugWatches()
{
	// This function gets called on each script reload.
	// It's a good place to temporarily add your custom debug watches when you need them.

	//debugWatch(0xffffb02a, 1)
}



//-------------------------------------------------------//
// Optional debug output                                 //
//-------------------------------------------------------//

global bool debugDrawFlag
global bool key0_state

function void Debugging.debugDraw()
{
	if (debugDrawFlag)
	{
		Debugging.debugDrawDynamicObjects()
	}

	if (debug_mode.state != 0)
	{
		Standalone.debugOutputMultiSampleCollision()
	}

	if (Key0)
	{
		if (!key0_state)
		{
			debugDrawFlag = !debugDrawFlag
			if (debugDrawFlag)
				System.writeDisplayLine("Enabled collision box debug output")
			else
				System.writeDisplayLine("Disabled collision box debug output")
			key0_state = true
		}
	}
	else
	{
		key0_state = false
	}

	if (Key1)
	{
		move_area.left = 0
		move_area.right = 0x8000
		move_area.bottom.target = 0x2000
		move_area.bottom.current = 0x2000
		level.vertical_wrap = 0
	}
}

function void Debugging.debugDrawDynamicObjects()
{
	// Draw positions of dynamic objects
	A0 = 0xffffb000
	for (u8 i = 0; i < 0x6e; ++i)
	{
		if (objA0.update_address != 0)
		{
			debugDrawRect(objA0.position.x.u16 - objA0.hitbox_extends.x, objA0.position.y.u16 - objA0.hitbox_extends.y, objA0.hitbox_extends.x * 2 + 1, objA0.hitbox_extends.y * 2 + 1)
		}

		// Go on to next object
		A0 += 0x4a
	}
}



//-------------------------------------------------------//
// Debug Side Panel setup                                //
//-------------------------------------------------------//

// Note that this function gets called outside of usual game simulation and even if simulation is paused.
// It should not make any changes affecting game simulation, but only read from memory instead.

function void OxygenCallback.setupCustomSidePanelEntries()
{
	setupCharactersSidePanelEntry()
	setupGameObjectsSidePanelEntry()
}

function void setupCharactersSidePanelEntry()
{
	// Characters panel
	if (System.SidePanel.setupCustomCategory("C", "CHARACTERS"))
	{
		bool showCharacter1 = System.SidePanel.addOption("Show first character", true)
		bool showCharacter2 = System.SidePanel.addOption("Show second character", false)

		for (u8 index = 0; index < 2; ++index)
		{
			u32 address = 0xffffb000 + index * 0x4a
			A0 = address
			if (address == 0xffffb000)
			{
				if (!showCharacter1)
					continue
				System.SidePanel.addEntry("Character 1 (0xffffb000)")
			}
			else
			{
				if (!showCharacter2)
					continue
			}

			System.SidePanel.addEntry(address)
			System.SidePanel.addLine(stringformat("Character %d (0x%08x)", index + 1, address), 0)

			System.SidePanel.addLine(stringformat("- Update routine: 0x%06x", char.update_address), 8)
			System.SidePanel.addLine(stringformat("- Base state:     0x%02x", char.base_state), 8)
			System.SidePanel.addLine(stringformat("- Position:       0x%04x, 0x%04x", char.position.x.u16, char.position.y.u16), 8)
			System.SidePanel.addLine(stringformat("- Anim sprite:    0x%02x", char.animation.sprite), 8)
			System.SidePanel.addLine(stringformat("- Anim frame:     0x%02x", char.animation.frame), 8)
			System.SidePanel.addLine(stringformat("- Anim timer:     0x%02x", char.animation.timer), 8)
			System.SidePanel.addLine(stringformat("- Flags:          0x%02x", char.flags), 8)
			System.SidePanel.addLine(stringformat("- Control flags:  0x%02x", char.control_flags), 8)
			System.SidePanel.addLine(stringformat("- Layer flags:    0x%04x", char.layer_flags), 8)
		}
	}
}

function void setupGameObjectsSidePanelEntry()
{
	// Game Objects panel
	if (System.SidePanel.setupCustomCategory("O", "GAME OBJECTS"))
	{
		bool showEmptySlots    = System.SidePanel.addOption("Show empty object slots", true)
		bool showUpdateRoutine = System.SidePanel.addOption("Show update routine", true)
		bool showPosition      = System.SidePanel.addOption("Show position", false)
		bool showSubType       = System.SidePanel.addOption("Show subtype", false)

		for (u32 address = 0xffffb000; address < 0xffffcfcc; address += 0x4a)
		{
			u32 updateRoutine = u32[address]
			if (updateRoutine == 0)
			{
				if (showEmptySlots)
				{
					System.SidePanel.addEntry(address)
					System.SidePanel.addLine(stringformat("0x%08x", address), 0, 0xc0c0c0c0)
					if (showUpdateRoutine)
						System.SidePanel.addLine(stringformat("---", updateRoutine), 8, 0xc0c0c0c0)
					if (showPosition)
						System.SidePanel.addLine("---", 8, 0xc0c0c0c0)
					if (showSubType)
						System.SidePanel.addLine("---", 8, 0xc0c0c0c0)
				}
				continue
			}

			u16 px = u16[address + 0x10]
			u16 py = u16[address + 0x14]

			System.SidePanel.addEntry(address)
			{
				System.SidePanel.addLine(stringformat("0x%08x", address), 0)
				if (showUpdateRoutine)
					System.SidePanel.addLine(stringformat("- Update routine: 0x%06x", updateRoutine), 8)
				if (showPosition)
					System.SidePanel.addLine(stringformat("- Position: 0x%04x, 0x%04x", px, py), 8)
				if (showSubType)
					System.SidePanel.addLine(stringformat("- Subtype: 0x%02x", u8[address + 0x2c]), 8)
			}

			if (System.SidePanel.isEntryHovered(address))
			{
				debugDrawRect(px - 15, py - 15, 31, 31, 0xc00080ff)
			}
		}
	}
}



//-------------------------------------------------------//
// Abstraction Rendering incl. Alt+LMB / Alt+RMB         //
//-------------------------------------------------------//

global bool Debug.useAbstractionRendering = false

constant u8 Debug.ClickFlags.ShiftPressed = 0x01

function void OxygenCallback.debugAltT()
{
	Debug.useAbstractionRendering = !Debug.useAbstractionRendering

	if (Debug.useAbstractionRendering)
		System.writeDisplayLine("Renderer mode: Abstraction (allows for Alt-click / Alt-rightclick)")
	else
		System.writeDisplayLine("Renderer mode: Normal rendering")
}

function void OxygenCallback.debugAltLeftMouseDown(s16 mx, s16 my, u8 flags)
{
	if (Debug.useAbstractionRendering)
	{
		bool shiftPressed = (flags & Debug.ClickFlags.ShiftPressed)
		Debug.changeChunkAtScreenPosition(mx, my, shiftPressed ? 0x10 : 0x01)
	}
}

function void OxygenCallback.debugAltRightMouseDown(s16 mx, s16 my, u8 flags)
{
	if (Debug.useAbstractionRendering)
	{
		bool shiftPressed = (flags & Debug.ClickFlags.ShiftPressed)
		Debug.changeChunkAtScreenPosition(mx, my, shiftPressed ? -0x10 : -0x01)
	}
}

function void Debug.changeChunkAtScreenPosition(s16 mx, s16 my, s8 change)
{
	u16 globalColumn = camera.position.x.u16 + mx
	u16 globalRow    = camera.position.y.u16 + my

	u16 chunkColumn = globalColumn / 128
	u16 chunkRow    = (globalRow / 128) & 0x1f		// Looks like there are only 32 chunks in y-direction allowed in a level; that makes a maximum level height of 4096 pixels

	u32 chunkAddress = 0xffff0000 + u16[0xffff8008 + chunkRow * 4] + chunkColumn
	u8[chunkAddress] += change

	System.writeDisplayLine(stringformat("Chunk at %08x changed to: 0x%02x", chunkAddress, u8[chunkAddress]))
}
