state("LastHalfOfDarkness-Win64-Shipping")
{
    int levelTransition : 0x6631BB8;
}

startup
{
{
		if (timer.CurrentTimingMethod == TimingMethod.RealTime)
// Asks user to change to game time if LiveSplit is currently set to Real Time.
    {        
        var timingMessage = MessageBox.Show (
            "This game uses Time without Loads (Game Time) as the main timing method.\n"+
            "LiveSplit is currently set to show Real Time (RTA).\n"+
            "Would you like to set the timing method to Game Time?",
            "LiveSplit | Last Half of Darkness",
            MessageBoxButtons.YesNo,MessageBoxIcon.Question
        );
        
        if (timingMessage == DialogResult.Yes)
        {
            timer.CurrentTimingMethod = TimingMethod.GameTime;
        }
    }
}

    //creates text components for variable information
	vars.SetTextComponent = (Action<string, string>)((id, text) =>
	{
	        var textSettings = timer.Layout.Components.Where(x => x.GetType().Name == "TextComponent").Select(x => x.GetType().GetProperty("Settings").GetValue(x, null));
	        var textSetting = textSettings.FirstOrDefault(x => (x.GetType().GetProperty("Text1").GetValue(x, null) as string) == id);
	        if (textSetting == null)
	        {
	        var textComponentAssembly = Assembly.LoadFrom("Components\\LiveSplit.Text.dll");
	        var textComponent = Activator.CreateInstance(textComponentAssembly.GetType("LiveSplit.UI.Components.TextComponent"), timer);
	        timer.Layout.LayoutComponents.Add(new LiveSplit.UI.Components.LayoutComponent("LiveSplit.Text.dll", textComponent as LiveSplit.UI.Components.IComponent));
	
	        textSetting = textComponent.GetType().GetProperty("Settings", BindingFlags.Instance | BindingFlags.Public).GetValue(textComponent, null);
	        textSetting.GetType().GetProperty("Text1").SetValue(textSetting, id);
	        }
	
	        if (textSetting != null)
	        textSetting.GetType().GetProperty("Text2").SetValue(textSetting, text);
    });

    //Parent setting
	settings.Add("Variable Information", true, "Variable Information");
	//Child settings that will sit beneath Parent setting
	settings.Add("Camera", true, "Current Camera Target", "Variable Information");
    settings.Add("Map", true, "Current Map", "Variable Information");
    settings.Add("Loading", false, "Current Loading", "Variable Information");
}

init
{
    // Scanning the MainModule for static pointers to GSyncLoadCount, UWorld, UEngine and FNamePool
    var scn = new SignatureScanner(game, game.MainModule.BaseAddress, game.MainModule.ModuleMemorySize);
    //var LoadingTrg = new SigScanTarget(2, "89 05 ?? ?? ?? ?? e8 ?? ?? ?? ?? 84 c0 75 ?? e8 ?? ?? ?? ?? 84 c0 48 8b c3 74 ?? 48 8b c5 f3 0f 10 04 06 f3 0f 11 05 ?? ?? ?? ?? e9") { OnFound = (p, s, ptr) => ptr + 0x4 + game.ReadValue<int>(ptr) };
    //var Loading1 = scn.Scan(LoadingTrg);

    var uWorldTrg = new SigScanTarget(3, "48 8b 1d ?? ?? ?? ?? 48 85 db 74 ?? 41 b0") { OnFound = (p, s, ptr) => ptr + 0x4 + game.ReadValue<int>(ptr) };
    var uWorld = scn.Scan(uWorldTrg);
    var gameEngineTrg = new SigScanTarget(3, "48 89 05 ?? ?? ?? ?? 48 85 c9 74 ?? e8 ?? ?? ?? ?? 48 8d 4d") { OnFound = (p, s, ptr) => ptr + 0x4 + game.ReadValue<int>(ptr) };
    var gameEngine = scn.Scan(gameEngineTrg);
    var fNamePoolTrg = new SigScanTarget(3, "48 8d 05 ?? ?? ?? ?? eb ?? 48 8d 0d ?? ?? ?? ?? e8 ?? ?? ?? ?? c6 05 ?? ?? ?? ?? ?? 0f 10 07") { OnFound = (p, s, ptr) => ptr + 0x4 + game.ReadValue<int>(ptr) };
    var fNamePool = scn.Scan(fNamePoolTrg);

    // Throwing in case any base pointers can't be found (yet, hopefully)
    if(uWorld == IntPtr.Zero || gameEngine == IntPtr.Zero || fNamePool == IntPtr.Zero)
    {
        throw new Exception("One or more base pointers not found - retrying");
    }

	vars.Watchers = new MemoryWatcherList
    {
        //new MemoryWatcher<int>(new DeepPointer(Loading1)) { Name = "Loading"},
        // UWorld.Name
        new MemoryWatcher<ulong>(new DeepPointer(uWorld, 0x18)) { Name = "worldFName"},
        // GameEngine.GameInstance.LocalPlayers[0].PlayerController.PlayerCameraManager.ViewTarget.Target.Name
        new MemoryWatcher<ulong>(new DeepPointer(gameEngine, 0xFC0, 0x38, 0x0, 0x30, 0x348, 0x12C0, 0x18)) { Name = "camViewTargetFName"},
    };

    // Translating FName to String, this *could* be cached
    vars.FNameToString = (Func<ulong, string>)(fName =>
    {
        var number   = (fName & 0xFFFFFFFF00000000) >> 0x20;
        var chunkIdx = (fName & 0x00000000FFFF0000) >> 0x10;
        var nameIdx  = (fName & 0x000000000000FFFF) >> 0x00;
        var chunk = game.ReadPointer(fNamePool + 0x10 + (int)chunkIdx * 0x8);
        var nameEntry = chunk + (int)nameIdx * 0x2;
        var length = game.ReadValue<short>(nameEntry) >> 6;
        var name = game.ReadString(nameEntry + 0x2, length);
        return number == 0 ? name : name;
    });

    vars.Watchers.UpdateAll(game);

    //sets the var world from the memory watcher
    current.world = old.world = vars.FNameToString(vars.Watchers["worldFName"].Current);
    
    //helps with null values throwing errors - i dont exactly know why, but thanks to Nikoheart for this
    current.camTarget = "";
    current.world = "";
}

update
{
    vars.Watchers.UpdateAll(game);

    // Get the current world name as string, only if *UWorld isnt null
    var worldFName = vars.Watchers["worldFName"].Current;
    current.world = worldFName != 0x0 ? vars.FNameToString(worldFName) : old.world;

    // Get the Name of the current target for the CameraManager
    current.camTarget = vars.FNameToString(vars.Watchers["camViewTargetFName"].Current);
    //current.LevelTransition = vars.Watchers["Loading"].Current;

    //Prints the camera target to the Livesplit layout if the setting is enabled
        if(settings["Camera"]) 
    {
        vars.SetTextComponent("Camera Target:",current.camTarget.ToString());
        if (old.camTarget != current.camTarget) print("Camera Target:" + current.camTarget.ToString());
    }

    //Prints the current map to the Livesplit layout if the setting is enabled
        if(settings["Map"]) 
    {
        vars.SetTextComponent("Map:",current.world.ToString());
        if (old.world != current.world) print("Map:" + current.world.ToString());
    }

        //Prints the camera target to the Livesplit layout if the setting is enabled
        if(settings["Loading"]) 
    {
        vars.SetTextComponent("Loading:",current.levelTransition.ToString());
    }

//DEBUG CODE
    print("isLoading? " + current.levelTransition.ToString());
    //print("Loaded Map = " + current.world.ToString());
    //print("Camera Target = " + current.camTarget.ToString());
    //print("Horizontal Position:" + current.horizontalPos.ToString());
    //print(modules.First().ModuleMemorySize.ToString());
}

start
{
    return old.world == "BeachMenu" && current.world != "BeachMenu" || old.world != "Beach" && current.world == "Beach";
}

split
{
    return old.world != current.world || old.camTarget == "LastHalfCharacter_C" && current.camTarget == "CineCameraActor";
}

isLoading
{
    return current.levelTransition == 0;
}