from direct.showbase.PythonUtil import bound as clamp
import CogdoMazeGameGlobals as Globals
import math
import random

class CogdoMazeCameraManager:
    toonJumpSpeed = 30.0
    toonJumpDir = 1.0
    toonMaxHeight = 1.8
    maxHeightOffset = 1.5
    savedShakeStrength = 0.0
    toonIsShaking = False
    rumbleOffDuration = 2.0
    rumbleOnDuration = 1.5
    rumbleOffTimer = 0.0
    rumbleOnTimer = -1
    savedCamZ = 0.0
    savedCamX = 0.0
    shakeStrengthThreshold = 2.8
    savedRumbleTimer = 0.0
    rumbleFreq = 0.1
    rumbleSmall = 0.05
    rumbleBig = 0.16
    rumbleMagnitude = rumbleSmall

    def __init__(self, toon, maze, cam, root):
        self.toon = toon
        self.maze = maze
        self.camera = cam
        self.root = root
        self.shakeStrength = 0.0
        self.shakeOffset = 0.0
        self.shakeTime = 0.0
        self.defaultHeight = 0.0
        self.minPos = self.maze.tile2world(3, 5)
        self.maxPos = self.maze.tile2world(self.maze.width - 3, self.maze.height - 3)
        self._camAngle = Globals.CameraAngle
        self._camDistance = Globals.CameraMinDistance
        self._camTargetDistance = self._camDistance

    def enable(self):
        self.parent = self.root.attachNewNode('GameCamParent')
        self.parent.setPos(self.toon, 0, 0, 0)
        self.parent.setHpr(self.root, 180, self._camAngle, 0)
        self.camera.reparentTo(self.parent)
        self.camera.setPos(0, self._camDistance, 0)
        self.camera.lookAt(self.toon)
        self.defaultHeight = self.parent.getZ()
        self.update(0)

    def setCameraTargetDistance(self, distance):
        self._camTargetDistance = distance

    def disable(self):
        self.camera.wrtReparentTo(render)
        self.parent.removeNode()
        del self.parent

    def update(self, dt):
        toonPos = self.toon.getPos()
        self.parent.setPos(self.toon.getParent(), clamp(toonPos.getX(), self.minPos[0], self.maxPos[0]), clamp(toonPos.getY(), self.minPos[1], self.maxPos[1]), 0)
        if self._camDistance != self._camTargetDistance:
            self._updateCameraDistance()
        if self.shakeOffset > 0 or self.shakeStrength > 0:
            self.updateShake(dt)
        self.updateRumble(dt)

    def _updateCameraDistance(self):
        if self._camDistance < self._camTargetDistance:
            self._camDistance += min(0.4 * (self._camDistance / self._camTargetDistance), self._camTargetDistance - self._camDistance)
        elif self._camDistance > self._camTargetDistance:
            self._camDistance += max(-0.4 * (self._camDistance / self._camTargetDistance), self._camTargetDistance - self._camDistance)
        self.camera.setY(self._camDistance)

    def updateShake(self, dt):
        if self.shakeStrength > 0:
            if self.shakeStrength > Globals.CameraShakeMax:
                self.shakeStrength = Globals.CameraShakeMax
            height = self.defaultHeight + self.shakeStrength
            self.shakeStrength = self.shakeStrength - Globals.CameraShakeFalloff * dt
            if self.shakeStrength < 0.0:
                self.shakeStrength = 0.0
        else:
            height = self.shakeStrength

    def shake(self, strength):
        self.shakeStrength += strength * 1.5
        self.toonIsShaking = True

    def updateToonShake(self, dt):
        if self.toonIsShaking:
            newHeight = self.toon.getZ() + self.toonJumpDir * dt * self.savedShakeStrength * self.toonJumpSpeed
            maxHeight = self.savedShakeStrength * self.maxHeightOffset
            if maxHeight > self.toonMaxHeight:
                maxHeight = self.toonMaxHeight
            if newHeight >= maxHeight:
                newHeight = maxHeight
                self.toonJumpDir = -1.0
            elif newHeight <= 0.0:
                newHeight = 0.0
                self.toonJumpDir = 1.0
                self.toonIsShaking = False
            self.toon.setZ(newHeight)

    def updateRumble(self, dt):
        if self.rumbleOnTimer == -1:
            self.rumbleOffTimer += dt
            if self.rumbleOffTimer > self.rumbleOffDuration:
                self.rumbleOnTimer = 0.0
                self.savedRumbleTimer = 0.0
                self.rumbleOffTimer = -1
                self.rumbleOnDuration = 1.5
                self.savedCamZ = self.camera.getZ()
                self.savedCamX = self.camera.getX()
        else:
            if self.rumbleOnTimer > self.savedRumbleTimer + self.rumbleFreq:
                self.savedRumbleTimer += self.rumbleFreq
                self.rumble(self.rumbleMagnitude)
            self.rumbleOnTimer += dt
            if self.rumbleOnTimer > self.rumbleOnDuration:
                self.rumbleOffTimer = 0.0
                self.rumbleOnTimer = -1
                if self.shakeStrength > self.shakeStrengthThreshold:
                    self.rumbleOffDuration = 0.5
                    self.rumbleMagnitude = self.rumbleBig
                else:
                    self.rumbleOffDuration = random.uniform(1.0, 3.0)
                    self.rumbleMagnitude = self.rumbleSmall

    def rumble(self, magnitude):
        self.rumbleMagnitude *= -1.0
        dz = self.rumbleMagnitude
        self.camera.setZ(self.savedCamZ + dz)
        dx = dz
        self.camera.setZ(self.savedCamX + dx)
