//
//  SlideAnimationEvents.swift
//  SceneKitSessionWWDC2014
//
//  Created by haharsw on 2019/10/13.
//  Copyright © 2019 haharsw. All rights reserved.
//

import SceneKit
import SpriteKit

fileprivate enum CharacterAnimation: Int {
    case attack = 0
    case walk
    case die
    case count
}

class SlideAnimationEvents: SlideBase {
    var heroSkeletonNode: SCNNode!
    var animations = [CAAnimation?](repeating: nil, count: CharacterAnimation.count.rawValue)
    //let swipeSound = SKAction.playSoundFileNamed("swipe", waitForCompletion: false)
    //let deathSound = SKAction.playSoundFileNamed("death", waitForCompletion: false)
    let attack4Sound = SKAction.playSoundFileNamed("attack4", waitForCompletion: false)
    //let walkSound = SKAction.playSoundFileNamed("walk", waitForCompletion: false)
    let bossaggroSound = SKAction.playSoundFileNamed("bossaggro", waitForCompletion: false)
    
    override func setupSlide(slideManager: SlideManager) {
        // Load the character and add it to the scene
        let SCALE: Float = 0.015
        let heroNode = groundNode.addChildNodeNamed("bossGroup", from: "Scenes.scnassets/boss/boss.dae", with: 0.0)!
        heroNode.scale = SCNVector3(SCALE, SCALE, SCALE)
        heroNode.position = SCNVector3Make(3.0, 0.0, 15.0)
        
        // Convert sceneTime-based animations into systemTime-based animations.
        // Animations loaded from DAE files will play according to the `currentTime` property of the scene renderer if this one is playing
        // (see the SCNSceneRenderer protocol). Here we don't play a specific DAE so we want the animations to animate as soon as we add
        // them to the scene (i.e have them to play according the time of the system when the animation was added).
        
        heroSkeletonNode = heroNode.childNode(withName: "skeleton", recursively: true)
        
        for animationKey in heroSkeletonNode.animationKeys {
            // Find all the animations. Make them system time based and repeat forever.
            // And finally replace the old animation.
            
            let animation = heroSkeletonNode.animation(forKey: animationKey)
            animation!.usesSceneTimeBase = false
            animation!.repeatCount = .greatestFiniteMagnitude
            heroSkeletonNode.addAnimation(animation!, forKey: animationKey)
        }
        
        // Load other animations so that we will use them later
        setAnimation(index: .attack, name: "attackID", from: "Scenes.scnassets/boss/boss_attack")
        setAnimation(index: .die, name: "DeathID", from: "Scenes.scnassets/hero/death")
        setAnimation(index: .walk, name: "WalkID", from: "Scenes.scnassets/hero/walk")
    }
    
    override func numberOfSteps() -> Int {
        return 8
    }
    
    override func presentStep(index: Int, slideManager: SlideManager) {
        switch index {
        case 0:
            let _ = textManager.setTitle("Animation Extensions")
            let _ = textManager.addBullet("SCNAnimationEvent", at: 0)
            let _ = textManager.addBullet("Smooth transitions", at: 0)
            
            let node = textManager.addCode("#SCNAnimationEvent# *anEvent = \n" +
                                           "  [SCNAnimationEvent #animationEventWithKeyTime:#0.6 #block:#aBlock]; \n" +
                                           "anAnimation.#animationEvents# = @[anEvent, anotherEvent];")
            
            node.position = SCNVector3(node.position.x, node.position.y+0.75, node.position.z)
            
            // Warm up NSSound by playing an empty sound.
            // Otherwise the first sound may take some time to start playing and will be desynchronised.
            SlideManager.overLay?.run(bossaggroSound)
        case 1:
            // Trigger the attack animation
            heroSkeletonNode.addAnimation(animations[CharacterAnimation.attack.rawValue]!, forKey: "attack")
        case 2:
            textManager.fadeOutTextOfType(.code)
            let node = textManager.addCode("\n\n\n\n\n\n" +
                                           "anAnimation.#fadeInDuration# = 0.0;\n" +
                                           "anAnimation.#fadeOutDuration# = 0.0;")
            node.position = SCNVector3(node.position.x, node.position.y+0.55, node.position.z)
        case 3: fallthrough
        case 4:
            animations[CharacterAnimation.attack.rawValue]!.fadeInDuration = 0
            animations[CharacterAnimation.attack.rawValue]!.fadeOutDuration = 0
            
            // Trigger the attack animation
            heroSkeletonNode.addAnimation(animations[CharacterAnimation.attack.rawValue]!, forKey: "attack")
        case 5:
            textManager.fadeOutTextOfType(.code)
            let node = textManager.addCode("\n\n\n\n\n\n" +
                                           "anAnimation.fadeInDuration = #0.3#;\n" +
                                           "anAnimation.fadeOutDuration = #0.3#;")
            node.position = SCNVector3(node.position.x, node.position.y+0.55, node.position.z)
        case 6: fallthrough
        case 7:
            animations[CharacterAnimation.attack.rawValue]!.fadeInDuration = 0.3
            animations[CharacterAnimation.attack.rawValue]!.fadeOutDuration = 0.3
            
            // Trigger the attack animation
            heroSkeletonNode.addAnimation(animations[CharacterAnimation.attack.rawValue]!, forKey: "attack")
        default:
            break
        }
    }
    
    fileprivate func setAnimation(index: CharacterAnimation, name: String, from sceneName: String) {
        // Load the DAE using SCNSceneSource in order to be able to retrieve the animation by its identifier
        let url = Bundle.main.url(forResource: sceneName, withExtension: "dae")!
        let sceneSource = SCNSceneSource(url: url, options: [SCNSceneSource.LoadingOption.animationImportPolicy: SCNSceneSource.AnimationImportPolicy.play])
        
        let animation = sceneSource?.entryWithIdentifier(name, withClass: CAAnimation.self)
        animations[index.rawValue] = animation
        
        // Blend animations for smoother transitions
        animation?.fadeInDuration = 0.3
        animation?.fadeOutDuration = 0.3
        
        if index == .die {
            // We want the "death" animation to remain at its final state at the end of the animation
            animation!.isRemovedOnCompletion = false
            animation!.fillMode = .both
            
            // Create animation events and set them to the animation
            let swipeSoundEventBlock: SCNAnimationEventBlock = { _,_,_ in
                //SlideManager.overLay?.run(self.swipeSound)
            }
            
            let deathSoundEventBlock: SCNAnimationEventBlock = { _,_,_ in
                //SlideManager.overLay?.run(self.deathSound)
            }
            
            animation?.animationEvents = [
                SCNAnimationEvent(keyTime: 0.0, block: swipeSoundEventBlock),
                SCNAnimationEvent(keyTime: 0.3, block: deathSoundEventBlock)
            ]
        }
        
        if index == .attack {
            // Create an animation event and set it to the animation
            let swordSoundEventBlock: SCNAnimationEventBlock = { _,_,_ in
                SlideManager.overLay?.run(self.attack4Sound)
            }
            
            animation?.animationEvents = [
                SCNAnimationEvent(keyTime: 0.4, block: swordSoundEventBlock)
            ]
        }
        
        if index == .walk {
            // Repeat the walk animation 3 times
            animation!.repeatCount = 3
            
            // Create an animation event and set it to the animation
            let stepSoundEventBlock: SCNAnimationEventBlock = { _,_,_ in
                //SlideManager.overLay?.run(self.walkSound)
            }
            
            animation?.animationEvents = [
                SCNAnimationEvent(keyTime: 0.2, block: stepSoundEventBlock),
                SCNAnimationEvent(keyTime: 0.7, block: stepSoundEventBlock)
            ]
        }
    }
}
