import SwiftUI
import ARKit

struct Fonts {
    static func avenirNextCondensedBold (size: CGFloat) -> Font {
        return Font.custom("AvenirNextCondensed-Bold", size: size)
    }
    static func neutonRegular (size: CGFloat) -> Font {
        return Font.custom("Neuton-Regular", size: size)
    }
}

struct ContentView: View {
    @State var arview = ARSCNView()
    
    var body: some View {
        VStack {
            CustomARView(view: arview)
        }
        .padding()
    }
}

struct CustomARView: UIViewRepresentable {
    typealias UIViewType = ARSCNView
    
    var view:ARSCNView
    var options: [Any] = []
    
    func makeUIView(context: Context) -> ARSCNView {
        view.session.delegate = context.coordinator
        view.scene.background.contents = UIColor.gray
        return view
    }
    
    func updateUIView(_ view: ARSCNView, context: Context) {
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self.view)
    }
}

class Coordinator: NSObject, ARSCNViewDelegate, ARSessionDelegate {
    
    private var trackingView:ARSCNView
    private var faceGeometry: ARSCNFaceGeometry!
    private var faceNode: SCNNode!
    private var spawnTime:TimeInterval = 0
    private var cylinderNode: SCNNode!
    private var coreNode: SCNNode!
    private var textNodes = [SCNNode](repeating: SCNNode(), count: 9)
    private var shadowNodes = [SCNNode](repeating: SCNNode(), count: 9)
    private var coreNodes = [SCNNode](repeating: SCNNode(), count: 9)
    private var plainNodes = [SCNNode](repeating: SCNNode(), count: 9)
    private var paperNodes = [SCNNode](repeating: SCNNode(), count: 9)
    private var colours = [UIColor](repeating: UIColor(), count: 9)
    
    private var fmotion = false
    private var bmotion = false
    private var doubleLock = true
    private var prefish = 0
    private var postfish = 0
    private var previousIndex = 0
    private var postIndex = 0
    private var currentIndex = 8 {
        didSet {
            previousIndex = currentIndex - 1
            if previousIndex < 0 {
                previousIndex = 8
            }
            previousIndex = previousIndex % 9
            prefish = previousIndex - 1
            if prefish < 0 {
                prefish = 8
            }
            prefish = prefish % 9

            currentIndex = currentIndex % 9
            postIndex = currentIndex + 1
            postIndex = postIndex % 9
            postfish = postIndex + 1
            postfish = postfish % 9
        }
    }
    private var angle = 360.0 / 9
    
    private let directs = ["2Quick","3Brown","4Fox","5Jumps","6Over","7The","8Lazy","9Dog","1The"]
    private let indirects = ["kciuQ2","nworB3","xoF4","spmuJ5","revO6","ehT7","yzaL8","goD9","ehT1"]
    private var flip = false
    
    init(_ view: ARSCNView) {
        
        self.trackingView = view
        super.init()
        
        guard ARFaceTrackingConfiguration.isSupported else {
            fatalError("Face tracking not available on this on this device model!")
        }
        let configuration = ARFaceTrackingConfiguration()
    
        self.trackingView.session.run(configuration)
        self.trackingView.delegate = self
        
        let geo = SCNCylinder(radius: 0.12, height: 0.05)
        geo.radialSegmentCount = 9
        cylinderNode = SCNNode(geometry: geo)
        cylinderNode.geometry?.firstMaterial?.fillMode = .fill
        cylinderNode.geometry?.firstMaterial?.diffuse.contents = UIColor.gray.withAlphaComponent(1)
        
        var geo2 = SCNPlane(width: 0.025, height: 0.08)
        geo2.firstMaterial?.fillMode = .fill
        //geo2.firstMaterial?.diffuse.contents = UIColor.blue.withAlphaComponent(0.9)
        
        //let newColor = UIColor(red: 255/255, green: 105/255, blue: 255/255, alpha: 0.9)
        //geo2.firstMaterial?.diffuse.contents = newColor
        
        
        
        
        for no in 1...9 {
            let character = UnicodeScalar(no + 64)
           // textNodes[no - 1] = newTextNode(Text: "\(no)")
            let doo = Double(no) * 8.4 + 10
            let doo2 = Double(no) * 8.4 + 10
            let doo3 = Double(no) * 8.4 + 10
            
            let newColor = UIColor(red: doo/255, green:doo2/255, blue: doo3/255, alpha: 0.8)
            let fixedColor = UIColor(red: 0.203922, green: 0.203922, blue: 0.203922, alpha: 0.8)
            //textNodes[no - 1] = newTextNode(Text: String(character!), newColor: fixedColor)
            colours[no - 1] = UIColor.gray
            textNodes[no - 1] = newTextNode(Text: directs[no - 1], newColor: fixedColor)
            shadowNodes[no - 1] = newTextNode(Text: indirects[no - 1], newColor: fixedColor)
            //textNodes[no - 1].simdPosition.z += 0.1
            coreNodes[no - 1] = SCNNode()
            coreNodes[no - 1].simdEulerAngles.y = GLKMathDegreesToRadians(Float(angle * Double(no)))
            cylinderNode.addChildNode(coreNodes[no - 1])
            //coreNodes[no - 1].addChildNode(textNodes[no - 1])
            var geo2 = SCNPlane(width: 0.025, height: 0.08)
            geo2.firstMaterial?.fillMode = .fill
            let foo = Double(no) * 4.4 + 80
            let foo2 = Double(no) * 6.4 + 80
            let foo3 = Double(no) * 8.4 + 80
            
            let newColor2 = UIColor(red: foo/255, green:foo2/255, blue: foo3/255, alpha: 1)
            geo2.firstMaterial?.diffuse.contents = newColor2
            
            plainNodes[no - 1] = SCNNode(geometry: geo2)
            paperNodes[no - 1] = SCNNode(geometry: geo2)
            
            plainNodes[no - 1].addChildNode(textNodes[no - 1])
            paperNodes[no - 1].addChildNode(shadowNodes[no - 1])
            
            paperNodes[no - 1].isHidden = true
            plainNodes[no - 1].isHidden = false
            
            coreNodes[no - 1].addChildNode(plainNodes[no - 1])
            coreNodes[no - 1].addChildNode(paperNodes[no - 1])
            
            
            plainNodes[no - 1].simdPosition.z += 0.14
            paperNodes[no - 1].simdPosition.z += 0.14
        }
        
//        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
//        self.trackingView.addGestureRecognizer(tapGesture)
        
        let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
        swipeLeftGesture.direction = .left
        self.trackingView.addGestureRecognizer(swipeLeftGesture)
        let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
        swipeRightGesture.direction = .right
        self.trackingView.addGestureRecognizer(swipeRightGesture)
    }
    
//    @objc func handleTap(_ sender: UIGestureRecognizer? = nil) {
//        let tapLocation = sender?.location(in: self.trackingView)
//        SCNTransaction.begin()
//        SCNTransaction.animationDuration = 1
//        cylinderNode.simdEulerAngles.y -= GLKMathDegreesToRadians(10)
//        SCNTransaction.commit()
//    }
    
    @objc func handleSwipe(_ sender: UISwipeGestureRecognizer? = nil) {
        switch sender?.direction {
        case UISwipeGestureRecognizer.Direction.right:
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 1
            cylinderNode.simdEulerAngles.y += GLKMathDegreesToRadians(Float(angle))
            currentIndex -= 1
            if currentIndex < 0 {
                currentIndex = 8
            }
            SCNTransaction.commit()
            print("RIGHT prefish \(prefish) previousIndex \(previousIndex) currentIndex \(currentIndex) postIndex \(postIndex) postfish \(postfish)")
            if postfish == 0 {
                flip.toggle()
            }
            
            if flip {
                paperNodes[postfish].isHidden = false
                plainNodes[postfish].isHidden = true
            }
            if !flip {
                paperNodes[postfish].isHidden = true
                plainNodes[postfish].isHidden = false
            }
        case UISwipeGestureRecognizer.Direction.left:
            print("LEFT prefish \(prefish) previousIndex \(previousIndex) currentIndex \(currentIndex) postIndex \(postIndex) postfish \(postfish)")
            
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 1
            cylinderNode.simdEulerAngles.y -= GLKMathDegreesToRadians(Float(angle))
            currentIndex += 1
            currentIndex = currentIndex % 9
            SCNTransaction.commit()
    
            
            if postfish == plainNodes.count - 1 {
                flip.toggle()
            }
            
            
            if flip {
                paperNodes[postfish].isHidden = false
                plainNodes[postfish].isHidden = true
            }
            if !flip {
                paperNodes[postfish].isHidden = true
                plainNodes[postfish].isHidden = false
            }
        case UISwipeGestureRecognizer.Direction.up:
            break
        case UISwipeGestureRecognizer.Direction.down:
            break
        default:
            assert(false,"NeverHappens")
        }
    }
    
    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        self.trackingView.scene.rootNode.addChildNode(cylinderNode)
        cylinderNode.simdPosition.z -= 0.4
        
        return nil
        
        let device = trackingView.device
        
        
        faceGeometry = ARSCNFaceGeometry(device: device!)
        faceNode = SCNNode(geometry: faceGeometry)
        faceNode.geometry?.firstMaterial?.fillMode = .lines
        faceNode.geometry?.firstMaterial?.diffuse.contents = UIColor.clear.withAlphaComponent(1)
        
        faceNode.addChildNode(cylinderNode)
        
        return faceNode
    }
    
    func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
        guard let faceAnchor = anchor as? ARFaceAnchor else { return }
        
        faceGeometry.update(from: faceAnchor.geometry)
    }
    
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        
        if textNodes[currentIndex].scale.x != 0.4 {
            for i in 0...7 {
                textNodes[i].scale = SCNVector3Make(0.04, 0.08, 0.04)
                let material = SCNMaterial()
                material.lightingModel = .physicallyBased
                material.metalness.contents = 0.2
                material.roughness.contents = 0.5
                //textNodes[i].geometry?.materials = [material]
                textNodes[i].geometry?.firstMaterial?.diffuse.contents = colours[i]
            }
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 0.4
            
            textNodes[previousIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black
            textNodes[previousIndex].scale = SCNVector3Make(0.04, 0.10, 0.04)
            textNodes[postIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black
            textNodes[postIndex].scale = SCNVector3Make(0.04, 0.10, 0.04)
            
            textNodes[currentIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.white
            textNodes[currentIndex].scale = SCNVector3Make(0.04, 0.12, 0.06)
            
            shadowNodes[previousIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black
            shadowNodes[previousIndex].scale = SCNVector3Make(0.04, 0.10, 0.04)
            shadowNodes[postIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black
            shadowNodes[postIndex].scale = SCNVector3Make(0.04, 0.10, 0.04)
            
            shadowNodes[currentIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.white
            shadowNodes[currentIndex].scale = SCNVector3Make(0.04, 0.12, 0.06)
            SCNTransaction.commit()
        }
        
        if time > spawnTime {
            spawnTime = time + TimeInterval(1)
        }
    }
    
    func newTextNode(Text:String, newColor:UIColor) -> SCNNode {
        let textGeo = SCNText(string: Text, extrusionDepth: 0.01)
        let font = UIFont(name: "Neuton-Regular", size: 0.7)
        
        textGeo.font = font
        textGeo.firstMaterial!.diffuse.contents = newColor
        textGeo.firstMaterial?.fillMode = .fill
        textGeo.alignmentMode = CATextLayerAlignmentMode.center.rawValue
    
        textGeo.chamferRadius = 0.001
        let (minBound, maxBound) = textGeo.boundingBox
        let textNode = SCNNode(geometry: textGeo)
        
        textNode.pivot = SCNMatrix4MakeTranslation( (maxBound.x - minBound.x)/2 , minBound.y / 0.80 , 0)
        textNode.scale = SCNVector3Make(0.04, 0.08, 0.04)
        return textNode
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}