import unrealsdk
from Mods import ModMenu
from Mods.ModMenu import EnabledSaveType, Options

class SprintSetAndForget(ModMenu.SDKMod):
    Name: str = "Sprint Set-and-Forget"
    Author: str = "Ceiyne"
    Description: str = "Remembers your movement mode (sprinting or walking): if you were sprinting the last time you moved, you will automatically start sprinting the next time you move. Press your assigned Sprint key to switch between sprinting and walking at any time. Go to Options to set whether you start out sprinting or walking when the game begins."
    Version: str = "1.0.0"
    SupportedGames: ModMenu.Game = ModMenu.Game.BL2 | ModMenu.Game.TPS  # Either BL2 or TPS; bitwise OR'd together
    Types: ModMenu.ModTypes = ModMenu.ModTypes.Gameplay  # One of Utility, Content, Gameplay, Library; bitwise OR'd together
    SaveEnabledState: EnabledSaveType = EnabledSaveType.LoadWithSettings

    SprintOnOption: Options.Boolean
    bSprintOn: bool = True

    def __init__(self) -> None:
        self.SprintOnOption = ModMenu.Options.Boolean(
            Caption="Starting mode",
            Description="Whether you start out sprinting or walking when the game begins",
            StartingValue=True,
            Choices=["Walk", "Sprint"]  # False, True
        )
        
        self.Options = [
            self.SprintOnOption
        ]

    def Enable(self) -> None:
        self.bSprintOn = (self.SprintOnOption.CurrentValue == True)
        mode = "Sprint" if self.bSprintOn == True else "Walk"
        unrealsdk.Log(f"[{instance.Name}] Mod enabled in {mode} mode")
        super().Enable()
        
    @ModMenu.Hook("WillowGame.WillowPlayerInput.SprintPressed")
    def onSprintPressed(self, caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool:
        #unrealsdk.Log(f"[{instance.Name}] SprintPressed called")
        self.bSprintOn = not self.bSprintOn   # toggle sprint setting
        caller.bTryToSprint = self.bSprintOn  # set the game variable
        if self.bSprintOn == True:            # if sprinting we can let flow continue as normal
            return True
        else:                                 # if walking then we need to end sprint and stop the normal flow
            unrealsdk.GetEngine().GamePlayers[0].Actor.EndSprint()
            return False

    @ModMenu.Hook("WillowGame.WillowPlayerInput.SprintReleased")
    def onSprintReleased(self, caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool:
        #unrealsdk.Log(f"[{instance.Name}] SprintReleased called")
        caller.bTryToSprint = self.bSprintOn  # set the game variable
        return False                          # we have to stop flow when setting to True; when setting to False that's all the original function did anyway

    @ModMenu.Hook("WillowGame.WillowPlayerInput.PlayerInput")
    def onPlayerInput(self, caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool:
        #unrealsdk.Log(f"[{instance.Name}] PlayerInput called")
        caller.bTryToSprint = self.bSprintOn  # set the game variable based on our sprint toggle every time the player tries to move
        return True

    @ModMenu.Hook("WillowGame.WillowPlayerController.SpawningProcessComplete")
    def onSpawningProcessComplete(self, caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool:
        self.bSprintOn = (self.SprintOnOption.CurrentValue == True)  # set sprint mode based on options
        return True

instance = SprintSetAndForget()

if __name__ == "__main__":
    unrealsdk.Log(f"[{instance.Name}] Manually loaded")
    for mod in ModMenu.Mods:
        if mod.Name == instance.Name:
            if mod.IsEnabled:
                mod.Disable()
            ModMenu.Mods.remove(mod)
            unrealsdk.Log(f"[{instance.Name}] Removed last instance")

            # Fixes inspect.getfile()
            instance.__class__.__module__ = mod.__class__.__module__
            break

ModMenu.RegisterMod(instance)
