//
//  Visage.swift
//  FaceDetection
//
//  Created by Julian Abentheuer on 21.12.14.
//  Copyright (c) 2014 Aaron Abentheuer. All rights reserved.
//

import UIKit
import CoreImage
import AVFoundation
import ImageIO
import AssetsLibrary
import Photos

public let kScreenWidth = UIScreen.main.bounds.size.width
public let kScreenHeight = UIScreen.main.bounds.size.height
//public var didSetHuman = false
//public var didSetNoHumam = false

class Visage: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
    
    var timeString: String? = nil
    var exif:AnyObject? = nil
    var volice:Float?
    var imgWidth:CGFloat = 0
    var beginToTakePicture = false
    var didTakePicture = false
    var isnoCheckFace = false
    var getFace = 0
    var visageCameraView : UIView = UIView(frame: CGRect(x: 0, y: 0, width: kScreenWidth,  height: kScreenHeight))
    var options = [String : AnyObject]()
    
    fileprivate var rightFace = 0
    fileprivate var farFace = 0
    fileprivate var nearFace = 0
    fileprivate var closeEyeTimes = 0
    let faceStatePromter = FaceStatePromter()

    fileprivate(set) var leftEyeClosed : Bool?
    fileprivate(set) var rightEyeClosed : Bool?
    fileprivate let visageTakenPictureNotification = Notification(name: Notification.Name(rawValue: "visageTakenPictureNotification"), object: nil)
    
    fileprivate var captureDevice : AVCaptureDevice!
    fileprivate var faceDetector : CIDetector?
    fileprivate var videoDataOutput : AVCaptureVideoDataOutput?
    fileprivate var videoDataOutputQueue : DispatchQueue?
    fileprivate var captureSession : AVCaptureSession = AVCaptureSession()
    fileprivate let notificationCenter : NotificationCenter = NotificationCenter.default
    fileprivate var currentOrientation : Int?
    fileprivate var stillImageOutput: AVCaptureStillImageOutput? = AVCaptureStillImageOutput()
    
    var onlyFireNotificatonOnStatusChange : Bool = true
    
    enum DetectorAccuracy {
        case batterySaving
        case higherPerformance
    }
    // 摄像头方向
    enum CameraDevice {
        case iSightCamera
        case faceTimeCamera
    }
    
     // MARK: - 初始化
    init(cameraPosition : CameraDevice, optimizeFor : DetectorAccuracy) {
        super.init()
        volice = AVAudioSession.sharedInstance().outputVolume
        switch cameraPosition {
        case .faceTimeCamera : self.captureSetup(AVCaptureDevice.Position.front)
        case .iSightCamera : self.captureSetup(AVCaptureDevice.Position.back)
        }
        
        var faceDetectorOptions : [String : String]?
        switch optimizeFor {
        case .batterySaving : faceDetectorOptions = [CIDetectorAccuracy : CIDetectorAccuracyLow as String]
        case .higherPerformance : faceDetectorOptions = [CIDetectorAccuracy : CIDetectorAccuracyHigh as String]
        }
        self.faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: faceDetectorOptions)
    }
    
    required convenience init!(coder aDecoder: NSCoder!) {
        fatalError("init(coder:) has not been implemented")
    }

    fileprivate func captureSetup (_ position : AVCaptureDevice.Position) {
        for testedDevice in AVCaptureDevice.devices(for: AVMediaType.video){
            if ((testedDevice as AnyObject).position == position) {
                captureDevice = testedDevice
            }
        }
        if (captureDevice == nil) {
            captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
        }
        
        if (captureDevice.hasFlash && captureDevice.hasTorch){
            do {
                try captureDevice.lockForConfiguration()
                if kScreenWidth < 420 {
                    if captureDevice.isFlashModeSupported(AVCaptureDevice.FlashMode.on) {
                        captureDevice.flashMode = AVCaptureDevice.FlashMode.on
                    }
                }
                captureDevice.unlockForConfiguration()
            } catch let error as NSError {
                print("Could not lock device for configuration\(error)")
            }
        }
        
        var deviceInput : AVCaptureDeviceInput
        do {
            deviceInput = try AVCaptureDeviceInput(device: captureDevice)
            captureSession.sessionPreset = AVCaptureSession.Preset.photo
            if (captureSession.canAddInput(deviceInput)) {
                captureSession.addInput(deviceInput)
            }
            self.videoDataOutput = AVCaptureVideoDataOutput()
            self.videoDataOutput!.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable as! String : Int(kCVPixelFormatType_32BGRA)]
            self.videoDataOutput!.alwaysDiscardsLateVideoFrames = true
            self.videoDataOutputQueue = DispatchQueue(label: "VideoDataOutputQueue", attributes: [])
            self.videoDataOutput!.setSampleBufferDelegate(self, queue: self.videoDataOutputQueue)
            if (captureSession.canAddOutput(self.videoDataOutput!)) {
                captureSession.addOutput(self.videoDataOutput!)
            }
            visageCameraView.frame = CGRect(x: 0, y: 0, width: kScreenWidth, height: kScreenHeight)
            let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
            previewLayer.frame = CGRect(x: 0, y: 0, width: kScreenWidth,  height: kScreenHeight)
            previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
            visageCameraView.layer.addSublayer(previewLayer)
            stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
            if captureSession.canAddOutput(stillImageOutput!) {
                captureSession.addOutput(stillImageOutput!)
            }
        } catch _ {}
    }
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        if isnoCheckFace == true {
            return
        }
        if (!beginToTakePicture){
            guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {return}
            
            let sourceImage = CIImage(cvPixelBuffer: imageBuffer)
            _ = imageFormSampleBuffer(sampleBuffer)
            options = [CIDetectorSmile : false as AnyObject, CIDetectorEyeBlink: true as AnyObject, CIDetectorImageOrientation : 6 as AnyObject]
            
            // MARK: - 识别图像人脸
            guard let face = self.faceDetector else {return}
            
            let features = face.features(in: sourceImage, options: options)
            if (features.count != 0) { // 如果等于0 就是未检测到人脸
                let feature = features[0] as! CIFaceFeature
                let faceOriginX = feature.bounds.origin.x
                let faceOriginY = feature.bounds.origin.y
                let faceWidth = feature.bounds.width
                let faceHeight = feature.bounds.height
                let focusPoint = CGPoint(x: (faceOriginX + faceWidth)/2, y: (faceOriginY + faceHeight)/2)
                
                if (feature.leftEyeClosed || feature.rightEyeClosed) {
                    closeEyeTimes += 1
                }else {
                    closeEyeTimes = 0
                }
                if faceWidth > imgWidth * 0.6 && faceWidth < imgWidth * 0.76 {
                    getFace += 1
                    farFace = 0
                    nearFace = 0
                    rightFace += 1
                }else if faceWidth < imgWidth * 0.6{
                    getFace = 0
                    farFace += 1
                    nearFace = 0
                    rightFace = 0
                }else if faceWidth > imgWidth * 0.76 {
                    getFace = 0
                    nearFace += 1
                    farFace = 0
                    rightFace = 0
                }
                
                switch UIDevice.current.modelName {
                case "iPhone 5","iPhone 5c","iPhone 5s","iPhone 6","iPhone 6 Plus":
                    if farFace >= 2{
                        faceStatePromter.state = FaceState.far
                    }
                    if nearFace >= 2{
                        faceStatePromter.state = FaceState.near
                    }
                    if rightFace >= 2 {
                        faceStatePromter.state = FaceState.right
                    }
                default:
                    if farFace >= 3{
                        faceStatePromter.state = FaceState.far
                    }
                    if nearFace >= 3{
                        faceStatePromter.state = FaceState.near
                    }
                    if rightFace >= 3 {
                        faceStatePromter.state = FaceState.right
                    }
                }
                if (feature.leftEyeClosed && feature.rightEyeClosed && faceStatePromter.state == FaceState.right) {
                    closeEyeTimes += 1
                }else {
                    closeEyeTimes = 0
                }
                
                switch UIDevice.current.modelName {
                case "iPhone 5","iPhone 5c","iPhone 5s","iPhone 6","iPhone 6 Plus":
                    if getFace > 1 {
                        if (closeEyeTimes >= 2 && !beginToTakePicture && !didTakePicture) {
                            distanceRightAndCloseEyes(focusPoint: focusPoint)
                        }
                    }
                default:
                    if getFace > 2 {
                        if (closeEyeTimes >= 3 && !beginToTakePicture && !didTakePicture) {
                            distanceRightAndCloseEyes(focusPoint: focusPoint)
                        }
                    }
                }
                
            }
        }
    }
    
    // 距离合适且已经闭眼  符合拍照模式
    func distanceRightAndCloseEyes(focusPoint:CGPoint){
        faceStatePromter.initialAudioPlayer.stop()
        self.captureImage(focusPoint)
        self.beginToTakePicture = true
        captureDevice.unlockForConfiguration()
        setHumanVoice()
        if AVAudioSession.sharedInstance().outputVolume != volice {
            DaiVolume.setVolume(volice!)
        }
    }
    
    //MARK  - 设置人声
    func setHumanVoice() {
        didSetHuman = false
        didSetNoHumam = false
        if !UserDefaults.standard.bool(forKey: "noHumanVoice") {
            // 人声 戴耳机 拍照后设置音量
            if isEarPhone() {
                if AVAudioSession.sharedInstance().outputVolume > 0.6 {
                    UserDefaults.standard.set(0.6, forKey: "humanVoice_earPhone")
                }else if AVAudioSession.sharedInstance().outputVolume < 0.1 {
                    UserDefaults.standard.set(0.1, forKey: "humanVoice_earPhone")
                }else {
                    UserDefaults.standard.set(AVAudioSession.sharedInstance().outputVolume, forKey: "humanVoice_earPhone")
                }
                
            }else {
                // 人声 无耳机 拍照后设置音量
                if AVAudioSession.sharedInstance().outputVolume > 0.9 {
                    UserDefaults.standard.set(0.9, forKey: "humanVoice")
                }else if AVAudioSession.sharedInstance().outputVolume < 0.3 {
                    UserDefaults.standard.set(0.3, forKey: "humanVoice")
                }else {
                    UserDefaults.standard.set(AVAudioSession.sharedInstance().outputVolume, forKey: "humanVoice")
                }
            }
            
        }else {
            if isEarPhone() {
                if AVAudioSession.sharedInstance().outputVolume > 0.6 {
                    UserDefaults.standard.set(0.6, forKey: "doVoice_earPhone")
                }else if AVAudioSession.sharedInstance().outputVolume < 0.1 {
                    UserDefaults.standard.set(0.1, forKey: "doVoice_earPhone")
                }else {
                    UserDefaults.standard.set(AVAudioSession.sharedInstance().outputVolume, forKey: "doVoice_earPhone")
                }
            }else {
                if AVAudioSession.sharedInstance().outputVolume < 0.3 {
                    UserDefaults.standard.set(0.3, forKey: "duVoice")
                }else if AVAudioSession.sharedInstance().outputVolume > 0.9 {
                    UserDefaults.standard.set(0.9, forKey: "duVoice")
                }else {
                    UserDefaults.standard.set(AVAudioSession.sharedInstance().outputVolume, forKey: "duVoice")
                }
                
            }
        }
    }
    
    // MARK: - 初始化
    func beginFaceDetection() {
        self.captureSession.startRunning()
    }
    
    func endFaceDetection() {
        self.captureSession.stopRunning()
    }
    
    func isEarPhone() -> Bool {
        let route = AVAudioSession.sharedInstance().currentRoute
        for desc in route.outputs {
            if desc.portType == AVAudioSession.Port.headphones {
                return true
            }
        }
        return false
    }
    
    func convertCGImageToCIImage(_ inputImage: CGImage) -> CIImage! {
        let ciImage = CIImage(cgImage: inputImage)
        return ciImage
    }
    
    func imageFormSampleBuffer(_ sampleBuffer:CMSampleBuffer) {
        let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
        CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0)))
        let height = CVPixelBufferGetHeight(imageBuffer!)
        imgWidth = CGFloat(height)
    }
    
    func captureImage(_ focusPoint: CGPoint){
        do {
            try self.captureDevice!.lockForConfiguration()
        } catch let error as NSError {
            NSLog("Could not lock device for configuration: %@", error)
        }
        self.captureDevice!.unlockForConfiguration()
        if let videoConnection = stillImageOutput!.connection(with: AVMediaType.video) {
            videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
            
            stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: {(sampleBuffer, error) in
                if  (error == nil) {
                    if (sampleBuffer != nil) {
                        self.endFaceDetection()
                        if (CMGetAttachment(sampleBuffer!, key: kCGImagePropertyExifDictionary, attachmentModeOut: nil) != nil) {
                            //尝试捕捉
                            guard let buffer = sampleBuffer else {
                                return
                            }
                            self.exif = CMGetAttachment(buffer, key: kCGImagePropertyExifDictionary, attachmentModeOut: nil) as! NSDictionary
                        }
                        guard let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer!) else {
                            return
                        }
                        self.saveToFileSystem(imageData)
                        self.didTakePicture = true
                        do {
                            try self.captureDevice.lockForConfiguration()
                        } catch _ {
                        }
                        self.captureDevice.unlockForConfiguration()
                        self.notificationCenter.post(self.visageTakenPictureNotification)
                        // 难道是这?
                        self.notificationCenter.removeObserver(self.visageTakenPictureNotification)
                    }
                }
            })
        }
    }
    deinit {
        captureDevice = nil
    }
    
    func saveToFileSystem(_ imageData: Data) {
        let dataProvider = CGDataProvider(data: imageData as CFData)
        var cgImage = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)!
        if cgImage.width > 3264 {
            cgImage = resizeCGImageBySize(cgImage: cgImage, width: 3264, height: 2448)
        }
        let takenImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: UIImage.Orientation.up)
        let rotatedImage = takenImage.imageRotatedByDegrees(90, flip: false)
        let date = Date()
        // 存exif信息
        let container = ExifContainer()
        container.addCreationDate(Date.init(timeIntervalSinceNow: 0))
        container.addExifDictionary(self.exif as? [AnyHashable : Any])
        let imagedataWithExif = rotatedImage.addExif(container)
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyyMMdd_HHmmss"
        timeString = formatter.string(from: date)
        if imagedataWithExif.count == 0 {
            DataHelper.SharedDataHelper.imageData = rotatedImage.jpegData(compressionQuality: 0.4)!
        }else {
            DataHelper.SharedDataHelper.imageData = imagedataWithExif
        }
    }
    
    //    MARK: resize CGimage
    func resizeCGImageBySize(cgImage: CGImage, width: Int, height: Int) -> CGImage {
        let bitsPerComponent = cgImage.bitsPerComponent
        let bytesPerRow = cgImage.bytesPerRow
        let colorSpace = cgImage.colorSpace
        let bitmapInfo = cgImage.bitmapInfo
        let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace!, bitmapInfo: bitmapInfo.rawValue)
        context!.interpolationQuality = CGInterpolationQuality.high
        context?.draw(cgImage, in: CGRect(origin: CGPoint.zero, size: CGSize(width: CGFloat(width), height: CGFloat(height))))
        let cgImage = context?.makeImage()
        return cgImage!
    }
}




