//
//  ViewController.swift
//  HeiseARKit
//
//  Created by Gero Gerber on 17.06.17.
//  Copyright © 2017 Gero Gerber. All rights reserved.
//

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate, CAAnimationDelegate {

    @IBOutlet var sceneView: ARSCNView!
    
    var ufoScene : SCNScene?
    var currentUfoNode : SCNNode?
    var landingAnimation : CABasicAnimation?
    var startingAnimation : CABasicAnimation?
    var targetImage : UIImage?
    var ufoAudioSource : SCNAudioSource?
    var initialUfoPosition : SCNVector3?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the view's delegate
        sceneView.delegate = self
        
        // Just needed for ARKit debug rendering
        // sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin]
        // sceneView.showsStatistics = true
        
        // Create a new scene
        let scene = SCNScene()
        
        // Set the scene to the view
        sceneView.scene = scene
        
        self.view.isUserInteractionEnabled = true
        
        self.targetImage = UIImage(named: "Media.scnassets/target.png")
        
        // Eine Audio-Datei wird geladen und als räumliche Audio-Quelle definiert
        self.ufoAudioSource = SCNAudioSource(fileNamed: "Media.scnassets/rocket-launch.wav")
        self.ufoAudioSource?.volume = 0.5
        self.ufoAudioSource?.isPositional = true
        self.ufoAudioSource?.shouldStream = false
        self.ufoAudioSource?.loops = true
        self.ufoAudioSource?.load()
        
        // Ufo Szene laden
        self.ufoScene = SCNScene(named: "Media.scnassets/Ufo.scn")
        self.initialUfoPosition = ufoScene!.rootNode.position
    }
    
    override func viewDidAppear(_ animated: Bool) {
        if !ARWorldTrackingConfiguration.isSupported {
            let controller = UIAlertController(title: "Warning", message: "Your Device does not support World-Tracking!", preferredStyle: .alert)
            let action = UIAlertAction(title: "Ok", style: .default, handler: nil)
            controller.addAction(action)
            self.present(controller, animated: true, completion: nil)
            
            let configuration = AROrientationTrackingConfiguration()
            sceneView.session.run(configuration)
        }
        else {
            let configuration = ARWorldTrackingConfiguration()
            configuration.worldAlignment = .gravity
            configuration.planeDetection = .horizontal
            sceneView.session.run(configuration)
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // Pause the view's session
        sceneView.session.pause()
    }
    
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        // Nur ein Ufo soll gleichzeitig aktiv sein
        if(currentUfoNode != nil) {
            return
        }
        
        let configuration = ARWorldTrackingConfiguration()
        configuration.worldAlignment = .gravity
        configuration.planeDetection = []
        sceneView.session.run(configuration)
        
        // Punktlicht an der Kamera erzeugen
        sceneView.pointOfView?.light = SCNLight()
        sceneView.pointOfView?.light?.type = SCNLight.LightType.omni
        sceneView.pointOfView?.light?.color = UIColor(white: 0.75, alpha: 1.0)
        
        // Der Root-Node der Szene ist das Ufo Objekt
        currentUfoNode = ufoScene!.rootNode
        currentUfoNode?.position = self.initialUfoPosition!
        currentUfoNode?.isHidden = false
        
        // An der Anker-Position erzeugen wir eine texturierte SCNPlane
        let plane = SCNPlane(width: CGFloat(0.8), height: CGFloat(0.8))
        plane.firstMaterial?.diffuse.contents = self.targetImage
        
        let planeNode = SCNNode(geometry: plane)
        planeNode.name = "Plane"
        planeNode.transform = SCNMatrix4MakeRotation(-Float.pi / 2.0, 1.0, 0.0, 0.0)
        planeNode.renderingOrder = 0
        
        // Ufo + Fläche dem neuen Node hinzufügen
        node.addChildNode(currentUfoNode!)
        node.addChildNode(planeNode)
        
        // Die Audio-Quelle mittels SCNAudioPlayer an den Ufo Node hängen
        currentUfoNode!.addAudioPlayer(SCNAudioPlayer(source: self.ufoAudioSource!))
        
        // Translations-Animation erzeugen um Ufo landen zu lassen
        landingAnimation = CABasicAnimation(keyPath: "position")
        landingAnimation!.fromValue = NSValue(scnVector3: SCNVector3(x:0, y:currentUfoNode!.position.y + 3, z:0))
        landingAnimation!.duration = 15.0
        landingAnimation!.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
        landingAnimation!.delegate = self
        
        // Rotations-Animation erzeugen für etwas mehr Dynamik beim Landen
        let animationRotate = CABasicAnimation(keyPath: "rotation")
        animationRotate.fromValue = NSValue(scnVector4: SCNVector4(x: 0, y: 1, z: 0, w: Float.pi * 2))
        animationRotate.duration = 12.0
        animationRotate.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
        
        // Beide Animationen dem Ufo Node hinzufügen
        currentUfoNode!.addAnimation(animationRotate, forKey: nil)
        currentUfoNode!.addAnimation(landingAnimation!, forKey: nil)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        // Ist kein Ufo vorhanden, ignorieren wir Touch-Events
        if(currentUfoNode == nil) {
            return
        }
        
        // Läuft gerade eine Lande- oder Start-Animation werden
        // Touches ignoriert
        if(landingAnimation != nil || startingAnimation != nil) {
            return
        }
        
        // Über alle Touch-Events iterieren
        for t in touches {
            // Touch-Position im View berechnen
            let point = t.location(in: self.view)
            // Nach getroffenen Nodes in der Szene suchen
            let result = sceneView.hitTest(point, options: [SCNHitTestOption.boundingBoxOnly: true])
            
            // Alle getroffenen Nodes testen
            for r in result {
                if(r.node == currentUfoNode || r.node == currentUfoNode?.childNode(withName: r.node.name!, recursively: true)) {
                    // Bei dem getroffenen Node handelt es sich um das Ufo oder um einen Child-Node dessen
                    
                    // Umgekehrte Animationen für den Start erzeugen
                    startingAnimation = CABasicAnimation(keyPath: "position")
                    startingAnimation!.toValue = NSValue(scnVector3: SCNVector3(x:0, y:(currentUfoNode?.position.y)! + 3, z:0))
                    startingAnimation!.duration = 15.0
                    startingAnimation!.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
                    startingAnimation!.delegate = self
                    
                    let animationRotate = CABasicAnimation(keyPath: "rotation")
                    animationRotate.fromValue = NSValue(scnVector4: SCNVector4(x: 0, y: 1, z: 0, w: Float.pi * 2))
                    animationRotate.duration = 12.0
                    animationRotate.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
                    
                    currentUfoNode?.addAnimation(animationRotate, forKey: nil)
                    currentUfoNode?.addAnimation(startingAnimation!, forKey: nil)
                    
                    return
                }
            }
        }
    }
    
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        if(anim == landingAnimation) {
            landingAnimation = nil
        } else if (anim == startingAnimation) {
            let configuration = ARWorldTrackingConfiguration()
            configuration.worldAlignment = .gravity
            configuration.planeDetection = .horizontal
            sceneView.session.run(configuration, options: [ARSession.RunOptions.removeExistingAnchors])
            startingAnimation = nil
            ufoScene?.rootNode.isHidden = true
            currentUfoNode = nil
        }
    }
    
    override var prefersStatusBarHidden: Bool {
        return true
    }
}
