//
//  Utilities.swift
//  HeiseARKit2
//
//  Created by Gero Gerber on 04.09.18.
//  Copyright © 2018 Gero Gerber. All rights reserved.
//

import ARKit
import Foundation
import SceneKit

class StateMapper {
    class func trackingStateDescription(trackingState: ARCamera.TrackingState) -> String {
        switch (trackingState) {
        case .notAvailable:
            return "Not Available"
        case .limited(let reason):
            switch (reason) {
            case .initializing:
                return "Initializing"
            case .excessiveMotion:
                return "Device moving too fast"
            case .insufficientFeatures:
                return "Not enough features in visible scene"
            case .relocalizing:
                return "Resume after interruption"
            }
        case .normal:
            return "Normal"
        }
    }
    
    class func trackingStateColor(trackingState: ARCamera.TrackingState) -> UIColor {
        switch (trackingState) {
        case .notAvailable:
            return .red
        case .limited:
            return .yellow
        case .normal:
            return .green
        }
    }
    
    class func worldMappingStatusDescription(worldMappingStatus: ARFrame.WorldMappingStatus) -> String{
        switch (worldMappingStatus) {
        case .extending:
            return "Extending"
        case .limited:
            return "Limited"
        case .notAvailable:
            return "Not available"
        case .mapped:
            return "Mapped"
        }
    }
    
    class func worldMappingStatusColor(worldMappingStatus: ARFrame.WorldMappingStatus) -> UIColor{
        switch (worldMappingStatus) {
        case .extending:
            return .yellow
        case .limited:
            return .orange
        case .notAvailable:
            return .red
        case .mapped:
            return .green
        }
    }
}

class Screenshot {
    class func createScreenshot(frame: ARFrame) -> UIImage? {
        var orientation: UIImage.Orientation = .right
        
        switch UIDevice.current.orientation {
        case .portrait:
            orientation = .right
        case .portraitUpsideDown:
            orientation = .left
        case .landscapeLeft:
            orientation = .up
        case .landscapeRight:
            orientation = .down
        default:
            break
        }
        
        let ciImage = CIImage(cvPixelBuffer: frame.capturedImage)
        let context = CIContext()
        
        if let cgimage = context.createCGImage(ciImage, from: ciImage.extent) {
            return UIImage(cgImage: cgimage, scale: 1.0, orientation: orientation)
        }
        
        return nil
    }
}

class ARObjectSerializer {
    static let arObjectSuffix = ".arobject"
    
    class func writeARObject(withName name: String, object: ARReferenceObject, screenshot: UIImage) {
        let fileURL = FileManager.getDocumentsDirectoryURL().appendingPathComponent("\(name)\(arObjectSuffix)")
        
        DispatchQueue.global().async {
            do {
                try object.export(to: fileURL, previewImage: screenshot)
            } catch {
                fatalError("Error while saving file: \(error.localizedDescription)")
            }
        }
    }
    
    class func enumerateARObjects() -> [URL] {
        var arObjects = [URL]()
        
        let documentsURL = FileManager.getDocumentsDirectoryURL()
        let items = try! FileManager.default.contentsOfDirectory(atPath: documentsURL.path)
        
        for item in items {
            if item.hasSuffix(arObjectSuffix) {
                arObjects.append(URL(fileURLWithPath: documentsURL.appendingPathComponent(item).path))
            }
        }
        
        return arObjects
    }
}

class BoundingBox {
    class func createBoundingBoxNode(size: simd_float3, transparency: CGFloat, color: UIColor = .gray) -> SCNNode {
        let sizeHalfX = size.x / 2.0
        let sizeHalfY = size.y / 2.0
        let sizeHalfZ = size.z / 2.0
        
        let boxGeometry = SCNBox(width: CGFloat(size.x), height: CGFloat(size.y), length: CGFloat(size.z), chamferRadius: 0)
        boxGeometry.materials.first?.transparency = transparency
        boxGeometry.materials.first?.diffuse.contents = color
        
        let boundingBoxNode = SCNNode(geometry: boxGeometry)
        
        let v0 = SCNVector3(-sizeHalfX, -sizeHalfY, sizeHalfZ)
        let v1 = SCNVector3(sizeHalfX, -sizeHalfY, sizeHalfZ)
        let v2 = SCNVector3(sizeHalfX, sizeHalfY, sizeHalfZ)
        let v3 = SCNVector3(-sizeHalfX, sizeHalfY, sizeHalfZ)
        let v4 = SCNVector3(-sizeHalfX, -sizeHalfY, -sizeHalfZ)
        let v5 = SCNVector3(sizeHalfX, -sizeHalfY, -sizeHalfZ)
        let v6 = SCNVector3(sizeHalfX, sizeHalfY, -sizeHalfZ)
        let v7 = SCNVector3(-sizeHalfX, sizeHalfY, -sizeHalfZ)
        
        let indices: [Int32] = [0, 1, 1, 2, 2, 3, 3, 0,
                                1, 5, 5, 6, 6, 2, 2, 1,
                                0, 1, 1, 5, 5, 4, 4, 0,
                                5, 4, 4, 7, 7, 6, 6, 5,
                                4, 0, 0, 3, 3, 7, 7, 4,
                                3, 2, 2, 6, 6, 7, 7, 3]
        
        let source = SCNGeometrySource(vertices: [v0, v1, v2, v3, v4, v5, v6, v7])
        let element = SCNGeometryElement(indices: indices, primitiveType: .line)
        
        let line = SCNGeometry(sources: [source], elements: [element])
        line.materials.first?.diffuse.contents = UIColor.gray
        boundingBoxNode.addChildNode(SCNNode(geometry: line))
        
        return boundingBoxNode
    }
    
    class func createBoundingBoxNode(size: CGFloat, transparency: CGFloat, color: UIColor = .gray) -> SCNNode {
        return createBoundingBoxNode(size: simd_float3(Float(size), Float(size), Float(size)), transparency: transparency, color: color)
    }
}

class USDZItems {
    static let usdzItems = ["cupandsaucer", "gramophone", "plantpot", "redchair", "retrotv", "stratocaster", "teapot", "trowel", "tulip", "wateringcan", "wheelbarrow"]
    static let fileExtension = "usdz"
    
    class func indexOf(item: String) -> Int? {
        return usdzItems.firstIndex(of: item)
    }
    
    class func URLForItem(index: Int) -> URL {
        return Bundle.main.url(forResource: "Assets.scnassets/\(USDZItems.usdzItems[index])", withExtension: USDZItems.fileExtension)!
    }
}

extension FileManager {
    class func getDocumentsDirectoryURL() -> URL {
        return try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    }
}

extension CGImagePropertyOrientation {
    init(_ uiOrientation: UIImage.Orientation) {
        switch uiOrientation {
        case .up: self = .up
        case .upMirrored: self = .upMirrored
        case .down: self = .down
        case .downMirrored: self = .downMirrored
        case .left: self = .left
        case .leftMirrored: self = .leftMirrored
        case .right: self = .right
        case .rightMirrored: self = .rightMirrored
        }
    }
}
