//
//  Conversation.swift
//
//  Copyright © 2020 Apple Inc. All rights reserved.
//


import SpriteKit
import SPCScene
import Canvas
import PlaygroundSupport

open class Conversation : Scene, SceneAnimating, Assessable {
    static let scrollButtons = [#imageLiteral(resourceName: "ViewScroll-button-up.png"), #imageLiteral(resourceName: "ViewScroll-button-pressed.png")]
    
    
    public enum Participant : String {
        case lizard = "lizard", swan = "swan"
        
        var graphic: Graphic {
            switch self {
            case .lizard:
                let lizard = Graphic(image: #imageLiteral(resourceName: "Lizard-idle_1024fit-0.png"), name: "Lizard Conversing")
                lizard.enableAccessibility(label: "The Lizard, who is talking.")
                return lizard
            case .swan:
                let swan = Graphic(image: #imageLiteral(resourceName: "swan_1024fit-0.png"), name: "Swan Conversing")
                swan.enableAccessibility(label: "The Swan, who is talking.")
                return swan
            }
        }
        
        var prefix: String {
            switch self {
            case .lizard:
                return "The Lizard says: "
            case .swan:
                return "The Swan says: "
            }
        }
        
        var background: Graphic {
            switch self {
            case .lizard:
                return Graphic(image: #imageLiteral(resourceName: "Convo_1024-LizardBackground.png"), name: "Hut Background")
            case .swan:
                return Graphic(image: #imageLiteral(resourceName: "Convo_1024-SwanBackground.png"), name: "Throne room Background")
            }
        }
        
        var animation: [Image] {
            switch self {
            case .lizard:
                return [#imageLiteral(resourceName: "Lizard-talking_1024fit-0.png"),#imageLiteral(resourceName: "Lizard-talking_1024fit-1.png"),#imageLiteral(resourceName: "Lizard-talking_1024fit-2.png"),#imageLiteral(resourceName: "Lizard-talking_1024fit-3.png"),#imageLiteral(resourceName: "Lizard-talking_1024fit-4.png"),#imageLiteral(resourceName: "Lizard-talking_1024fit-5.png")].map { return Image(with: $0) }
            case .swan:
                return [#imageLiteral(resourceName: "swan_1024fit-0.png"),#imageLiteral(resourceName: "swan_1024fit-1.png"),#imageLiteral(resourceName: "swan_1024fit-2.png"),#imageLiteral(resourceName: "swan_1024fit-3.png"),#imageLiteral(resourceName: "swan_1024fit-4.png"),#imageLiteral(resourceName: "swan_1024fit-5.png"),#imageLiteral(resourceName: "swan_1024fit-6.png"),#imageLiteral(resourceName: "swan_1024fit-7.png")].map { return Image(with: $0) }
            }
        }
        
        var fps: TimeInterval {
            switch self {
            case .lizard: return 2.0
            case .swan: return 4.0
            }
        }
        
        var location: Point {
            switch self {
            case .lizard:
                return Point(x: 256.0, y: 240.0)
            case .swan:
                return Point(x: 256.0, y: 240.0)
            }
        }
    }
    
    let participant: Graphic
    let hero: Graphic
    let speech: Text
    fileprivate var left: GraphicButton!
    fileprivate var right: GraphicButton!
    
    var assessment: (()->Bool)?
    public var performance: ((_ owner: Assessable)->Void)?
    
    var mode: Participant
    
    public var script: Script
    
    public init(with character: Participant, using: Script, completion: (()->Bool)? = nil) {
        participant = character.graphic
        hero = Graphic(image: #imageLiteral(resourceName: "turtle-1024fit-convo.png"), name: "Turtle Conversing")
        hero.enableAccessibility(label: "The Turtle, who is listening.")
        
        script = using
        speech = Text.conversation("")
        mode = character
        
        super.init(size: Scene.sceneSize)
        
        name = "Conversation with the Turtle"
        
        left = GraphicButton(images: (#imageLiteral(resourceName: "button-left-up.png"), #imageLiteral(resourceName: "button-left-pressed.png"), #imageLiteral(resourceName: "button-left-disabled.png")), name: "Left Arrow") {
            self.showPreviousLabel()
        }
        left.enableAccessibility(label: "Previous. Button.")
        
        right = GraphicButton(images: (#imageLiteral(resourceName: "button-right-up.png"), #imageLiteral(resourceName: "button-right-pressed.png"), #imageLiteral(resourceName: "button-right-disabled.png")), name: "Right Arrow") {
            self.showNextLabel()
        }
        right.enableAccessibility(label: "Next. Button.")
        
        // speech.enableAccessibility(label: "", select: true)
        
        self.place(character.background, at: Point.center)
        
        self.place(hero, at: Point(x: -256.0, y: -104.0))
        self.place(participant, at: character.location)
        
        let textBackground = Graphic(image: #imageLiteral(resourceName: "ConvoTextBox_1024_v1.png"), name: "Speech background")
        self.place(textBackground, at: Point(x: 0, y: -332.0))
        self.place(speech, at: Point(x: 0, y: -316.0))
        
        self.place(left, at: Point(x: 354.0, y: -438.0))
        self.place(right, at: Point(x: 424.0, y: -438.0))
        
        participant.animate(with: mode.animation, fps: mode.fps, loop: true)
        
        assessment = completion
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public func showNextLabel(shouldAnnounce: Bool = true) {
        let words = script.next()
        speech.text = words
        
        let desc = "\(mode.prefix)\(words)"
        participant.enableAccessibility(label: desc)
        let _ = accessibilityElements
        
        if scrollAppearsAtIndex.0 == script.index {
            showScroll(scrollAppearsAtIndex.1)
        }
        
        let delay = shouldAnnounce ? 0 : 1.0
        DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
            UIAccessibility.post(notification: .layoutChanged, argument: self.participant.axElement!)
        }
        
        right.enabled = script.hasNext()
        left.enabled = script.hasPrevious()
        if !script.hasNext() {
            playPerformance()
        }
    }
    
    public func showPreviousLabel() {
        let words = script.previous()
        speech.text = words
        
        let desc = "\(mode.prefix)\(words)"
        participant.enableAccessibility(label: words)
        let _ = accessibilityElements
        
        UIAccessibility.post(notification: .layoutChanged, argument: self.participant.axElement!)

        right.enabled = script.hasNext()
        left.enabled = script.hasPrevious()
    }
    
    public func playPerformance() {
        guard let perf = performance else {
            // if there is no performance, finish the scene
            self.run(SKAction.wait(forDuration: 4.0)) {
                self.animationDelegate?.sceneDidFinishAnimations(self)
            }
            return
        }
        perf(self)
    }
    
    public var scrollAppearsAtIndex: (index: Int, kind: Scroll.Contents) = (-999, .challenge4)
    
    fileprivate func showScroll(_ kind: Scroll.Contents) {
        let buttons = Conversation.scrollButtons.map { Image(with: $0) }
        let scrollButton = GraphicButton(images: (buttons[0], buttons[1], nil), name: "View Scroll") {
            
            let faded = Graphic(shape: .rectangle(width: 1024, height: 1024, cornerRadius: 0), color: .black)
            faded.alpha = 0
            faded.name = "faded-background"
            let scroll = Scroll(contents: kind, parent: self)
            scroll.alpha = 0
            scroll.enableAccessibility(label: kind.description)
            self.place(faded, at: Point.center)
            self.place(scroll, at: Point.center)
            DispatchQueue.main.async {
                let _ = self.accessibilityElements
                UIAccessibility.post(notification: .layoutChanged, argument: scroll.axElement!)
            }
            scroll.run(SKAction.fadeAlpha(to: 1.0, duration: 1.0))
            faded.run(SKAction.fadeAlpha(to: 0.8, duration: 0.5))
        }
        scrollButton.enableAccessibility(label: "Look at Scroll. Button.")
        
        self.place(scrollButton, at: Point(x: 428, y: -76))
    }
    
    // MARK: - Assessable
    
    public func endPerformance() {
        guard let assess = assessment, assess() else {
            failedAssessment()
            return
        }
        passedAssessment()
    }
    
    public func passedAssessment() {
        PlaygroundPage.current.assessmentStatus = .pass(message: nil)
        let action = SKAction.run {
            self.speech.text = self.script.success
            self.run(SKAction.wait(forDuration: 4.0)) {
                self.animationDelegate?.sceneDidFinishAnimations(self)
            }
        }
        self.run(action)
    }
    
    public func failedAssessment() {
        speech.text = script.tryAgain
    }
    
    // MARK: - SceneAnimating
    
    public var sceneTransition: SKTransition?
    public var animationDelegate: SceneAnimatingDelegate?
    public var loadingDescription: String? {
        didSet {
            enableAccessibility(label: loadingDescription!)
        }
    }
    
    public func sceneIsPresented() {
        showNextLabel(shouldAnnounce: false)
        animationDelegate?.sceneDidLoad(self)
    }
    
    public func sceneWillDismiss() {
        
    }
    
}


public class Scroll: GraphicButton {
    static let custom = #imageLiteral(resourceName: "ScrollBackground-1024.png")
    static let challenge3 = #imageLiteral(resourceName: "ScrollBackground-1024-Challenge3.png")
    static let challenge4 = #imageLiteral(resourceName: "ScrollBackground-1024-Challenge4.png")
    
    public enum Contents {
        case challenge3
        case challenge4
        case custom(text: String)
        
        var textContents: Text? {
            switch self {
            case .custom(let text):
                return Text.artifact(text)
                
            default:
                return nil
            }
        }
        
        var description: String {
            switch self {
            case .custom(let text):
                return "An ancient scroll with the numbers enscribed: \(text). Double-tap to close."
            case .challenge3:
                return "An ancient scroll with music notes for the Swan's favorite tune. Treble clef, four measures of Common time. Double-tap to close."
            case .challenge4:
                return "An ancient scroll with with music notes. Its a two part harmony for a duet of instruments. Bass and Treble clef, nine measures of common time. Double-tap to close."
            }
        }
        
        var background: Image {
            switch self {
            case .custom(_):
                return Image(with: Scroll.custom)
            case .challenge3:
                return Image(with: Scroll.challenge3)
            case .challenge4:
                return Image(with: Scroll.challenge4)
            }
        }
    }
    
    public init(contents: Contents, parent: Conversation) {
        super.init(images: (contents.background, nil, nil), name: "Ancient Scroll", completion: nil)
        self.action = {
            if let bg = parent.getGraphics(named: "faded-background").first {
                self.run(SKAction.fadeOut(withDuration: 0.5))
                bg.run(SKAction.fadeOut(withDuration: 0.5)) {
                    parent.remove(self)
                    parent.remove(bg)
                }
                UIAccessibility.post(notification: .layoutChanged, argument: parent.right.axElement!)
            }
        }
        if let text = contents.textContents {
            self.addChild(text)
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
