import Foundation
import UIKit
import ScanbotSDKNativeWrapper
import ScanbotSDK

@objc
public class RNScanbotDocumentScannerViewController: UIViewController  {

    private let defaults = DocumentScannerDefaultValues()
    private var scannerViewController: SBSDKDocumentScannerViewController = SBSDKDocumentScannerViewController()
    @objc public var delegate: RNScanbotDocumentScannerEventDelegate?
    private var detectDocumentAfterSnap = true

    public override func viewDidLoad() {
        super.viewDidLoad()

        self.sbsdk_attach(scannerViewController, in: self.view)
        scannerViewController.delegate = self
    }

    //MARK: Defaults
    private func setBehaviorDefaults(){
        scannerViewController.hideSnapButton = true
        scannerViewController.suppressDetectionStatusLabel = true
        scannerViewController.suppressPolygonLayer = !defaults.polygonEnabled

        let configuration = SBSDKDocumentScannerConfiguration()
        configuration.parameters.acceptedSizeScore = Int(defaults.acceptedSizeScore)
        configuration.parameters.acceptedAngleScore = Int(defaults.acceptedAngleScore)
        configuration.parameters.acceptedBrightnessThreshold = defaults.acceptedBrightnessThreshold
        configuration.parameters.ignoreOrientationMismatch = defaults.ignoreOrientationMismatch
        scannerViewController.setConfiguration(configuration)

        scannerViewController.autoSnappingMode = defaults.autoSnappingEnabled
        scannerViewController.autoSnappingSensitivity = CGFloat(defaults.autoSnappingSensitivity)
        scannerViewController.autoSnappingDelay = defaults.autoSnappingDelay
    }

    private func finderConfigDefaults() -> SBSDKBaseScannerViewFinderConfiguration {
        let config = SBSDKBaseScannerViewFinderConfiguration()
        config.isViewFinderEnabled = defaults.finderEnabled
        config.lineWidth = CGFloat(defaults.finderLineWidth)
        config.minimumInset = defaults.finderMinimumPadding
        config.lineColor = defaults.finderLineColor
        config.backgroundColor = defaults.finderOverlayColor
        config.aspectRatio = defaults.finderAspectRatio
        return config
    }

    private func setPolygonConfigDefaults(config: SBSDKDocumentScannerPolygonConfiguration){
        config.strokeWidth = defaults.polygonLineWidth
        config.cornerRadius = defaults.finderCornerRadius
        config.fillColor = defaults.polygonBackgroundColor
        config.strokeColor = defaults.polygonColor
    }

    private func setPolygonOKConfigDefaults(config: SBSDKDocumentScannerPolygonConfiguration){
        config.strokeWidth = defaults.polygonLineWidth
        config.cornerRadius = defaults.finderCornerRadius
        config.fillColor = defaults.polygonBackgroundColorOK
        config.strokeColor = defaults.polygonColorOK
    }

    private func setAutoSnappingPolygonConfigDefaults(config: SBSDKDocumentScannerPolygonConfiguration){
        config.enabled = defaults.polygonAutoSnapProgressEnabled
        config.strokeWidth = defaults.polygonAutoSnapProgressLineWidth
        config.cornerRadius = defaults.finderCornerRadius
        config.fillColor = defaults.polygonBackgroundColorOK
        config.strokeColor = defaults.polygonAutoSnapProgressColor
        scannerViewController.withAutoSnapConfigCopy()
    }

    @objc
    public func setDefaults(){
        setBehaviorDefaults()
        scannerViewController.viewFinderConfiguration = finderConfigDefaults()
        setPolygonConfigDefaults(config: scannerViewController.polygonConfigurationRejected)
        setPolygonOKConfigDefaults(config: scannerViewController.polygonConfigurationAccepted)
        setAutoSnappingPolygonConfigDefaults(config: scannerViewController.autoSnapProgressPolygonConfiguration)
    }

    // MARK: General settings

    @objc
    public func hardwareButtonsEnabled(_ value: Bool){
        scannerViewController.hardwareButtonsEnabled = value
    }

    @objc
    public func flashEnabled(_ value: Bool){
        scannerViewController.isFlashLightEnabled = value
    }

    @objc
    public func detectDocumentAfterSnap( _ value: Bool){
        detectDocumentAfterSnap = value
    }

    // MARK: Document Configuration

    @objc
    public func acceptedAngleScore(_ value: NSNumber?){
        let config = scannerViewController.copyCurrentConfiguration()
        config.parameters.acceptedAngleScore = value?.intValue ?? defaults.acceptedAngleScore
        scannerViewController.setConfiguration(config)
    }

    @objc
    public func acceptedBrightnessThreshold(_ value: NSNumber?){
        let config = scannerViewController.copyCurrentConfiguration()
        config.parameters.acceptedBrightnessThreshold = value?.intValue ?? defaults.acceptedBrightnessThreshold
        scannerViewController.setConfiguration(config)
    }

    @objc
    public func acceptedSizeScore(_ value: NSNumber?){
        let config = scannerViewController.copyCurrentConfiguration()
        config.parameters.acceptedSizeScore = value?.intValue ?? defaults.acceptedSizeScore
        scannerViewController.setConfiguration(config)
    }

    @objc
    public func ignoreOrientationMismatch(_ value: Bool){
        let config = scannerViewController.copyCurrentConfiguration()
        config.parameters.ignoreOrientationMismatch = value
        scannerViewController.setConfiguration(config)
    }

    @objc
    public func acceptedAspectRatioScore(_ value: NSNumber?){
        let config = scannerViewController.copyCurrentConfiguration()
        config.parameters.acceptedAspectRatioScore = value?.intValue ?? defaults.acceptedAspectRatioScore
        scannerViewController.setConfiguration(config)
    }

    @objc
    public func requiredAspectRatios(_ value: NSArray?){
        let config = scannerViewController.copyCurrentConfiguration()
        config.parameters.aspectRatios = SBSDKAspectRatio.fromArray(value as? [[String : Any]]) ?? []
        scannerViewController.setConfiguration(config)
    }
    
    @objc
    public func partiallyVisibleDocumentConfiguration(
        allowPartiallyVisibleDocuments: Bool,
        accumulationDuration: Int,
        retentionTime: Int,
        minimumBrightness: Int){
            let config = scannerViewController.copyCurrentConfiguration()
            config.partiallyVisibleDocumentConfiguration = SBSDKPartiallyVisibleDocumentConfiguration(
                allowPartiallyVisibleDocuments: allowPartiallyVisibleDocuments,
                accumulationDuration: accumulationDuration,
                retentionTime: retentionTime,
                minimumBrightness: minimumBrightness
            )
            scannerViewController.setConfiguration(config)
        }
    
    @objc
    public func partiallyVisibleDocumentConfiguration(_ value: NSDictionary? = nil){
       let defaults = SBSDKPartiallyVisibleDocumentConfiguration()
       if let dict = value {
           self.partiallyVisibleDocumentConfiguration(
            allowPartiallyVisibleDocuments: (dict["allowPartiallyVisibleDocuments"] as? Bool) ?? defaults.allowPartiallyVisibleDocuments,
            accumulationDuration: (dict["accumulationDuration"] as? Int) ?? defaults.accumulationDuration,
            retentionTime: (dict["retentionTime"] as? Int) ?? defaults.retentionTime,
            minimumBrightness: (dict["minimumBrightness"] as? Int) ?? defaults.minimumBrightness
           )
       } else {
           let config = scannerViewController.copyCurrentConfiguration()
           config.partiallyVisibleDocumentConfiguration = defaults
           scannerViewController.setConfiguration(config)
       }
    }
    
    @objc
    public func autoSnappingEnabled(_ value: Bool){
        scannerViewController.autoSnappingMode = value ? SBSDKAutoSnappingMode.enabled : SBSDKAutoSnappingMode.disabled
    }

    @objc
    public func autoSnappingSensitivity(_ value: NSNumber?){
        scannerViewController.autoSnappingSensitivity = CGFloat(value?.floatValue ?? defaults.autoSnappingSensitivity)
    }

    @objc
    public func autoSnappingDelay(_ value: NSNumber?){
        scannerViewController.autoSnappingDelay = value?.doubleValue ?? defaults.autoSnappingDelay
    }

    // MARK: Camera Configuration

    @objc
    public func cameraModule(_ value: NSString?){
        if let value = value as? String {
            scannerViewController.cameraDevice = SBComponentUtils.cameraDeviceToNative(value)
        } else {
            scannerViewController.cameraDevice = defaults.cameraModule
        }
    }

    @objc
    public func cameraPreviewMode(_ value: NSString?){
        if let previewMode = SBComponentUtils.decodeFromString(type: SBSDKVideoContentMode.self, value: value as String?) {
            scannerViewController.generalConfiguration.videoContentMode = previewMode
        } else {
            scannerViewController.generalConfiguration.videoContentMode = defaults.cameraPreviewMode
        }
    }

    @objc
    public func photoQualityPrioritization(_ value: NSString?){
        if let quality = SBComponentUtils.decodeFromString(type: SBSDKCapturePhotoQualityPrioritization.self, value: value as String?) {
            scannerViewController.generalConfiguration.photoQualityPrioritization = quality
        } else {
            scannerViewController.generalConfiguration.photoQualityPrioritization = defaults.photoQualityPrioritization
        }
    }

    // MARK: Finder Configuration

    @objc
    public func finderEnabled(_ value: Bool){
        scannerViewController.viewFinderConfiguration.isViewFinderEnabled = value
    }

    @objc
    public func finderLineColor(_ value: UIColor?){
        scannerViewController.viewFinderConfiguration.lineColor = value ?? defaults.finderLineColor
    }

    @objc
    public func finderLineWidth(_ value: NSNumber?){
        scannerViewController.viewFinderConfiguration.lineWidth = CGFloat(value?.floatValue ?? defaults.finderLineWidth)
    }

    @objc
    public func finderOverlayColor(_ value: UIColor?){
        scannerViewController.viewFinderConfiguration.backgroundColor = value ?? defaults.finderOverlayColor
    }

    @objc
    public func finderCornerRadius(_ value: NSNumber?){
        scannerViewController.viewFinderConfiguration.lineCornerRadius = value?.doubleValue ?? defaults.finderCornerRadius
    }

    @objc
    public func finderMinimumPadding(_ value: NSNumber?){
        if let minValue = value {
            scannerViewController.viewFinderConfiguration.minimumInset = UIEdgeInsets(top: CGFloat(minValue.floatValue),
                                                                                      left: CGFloat(minValue.floatValue),
                                                                                      bottom: CGFloat(minValue.floatValue),
                                                                                      right: CGFloat(minValue.floatValue))
        } else {
            scannerViewController.viewFinderConfiguration.minimumInset = defaults.finderMinimumPadding
        }
    }

    @objc
    public func finderAspectRatio(width: NSNumber, height: NSNumber){
        scannerViewController.viewFinderConfiguration.aspectRatio = SBSDKAspectRatio(width: width.doubleValue, height: height.doubleValue)
    }

    @objc
    public func finderAspectRatio(_ value: NSDictionary?){
        if let value = value, let ratio = SBSDKAspectRatio.fromDictionary(value as? [String : Any]) {
            scannerViewController.viewFinderConfiguration.aspectRatio = ratio
        } else {
            scannerViewController.viewFinderConfiguration.aspectRatio = defaults.finderAspectRatio
        }
    }

    // MARK: Overlay Configuration

    @objc
    public func polygonEnabled(_ value: Bool){
        scannerViewController.suppressPolygonLayer = !value
    }

    @objc
    public func polygonBackgroundColor(_ value: UIColor?) {
        scannerViewController.polygonConfigurationRejected.fillColor = value?.transparentAlpha ?? defaults.polygonBackgroundColor
    }

    @objc
    public func polygonBackgroundColorOK(_ value: UIColor?){
        scannerViewController.polygonConfigurationAccepted.fillColor = value?.transparentAlpha ?? defaults.polygonBackgroundColorOK
    }

    @objc
    public func polygonColor(_ value: UIColor?){
        scannerViewController.polygonConfigurationRejected.strokeColor = value ?? defaults.polygonColor
    }

    @objc
    public func polygonColorOK(_ value: UIColor?){
        scannerViewController.polygonConfigurationAccepted.strokeColor = value ?? defaults.polygonColorOK
    }

    @objc
    public func polygonLineWidth(_ value: NSNumber?){
        let width = value?.doubleValue ?? defaults.polygonLineWidth
        scannerViewController.polygonConfigurationAccepted.strokeWidth = width
        scannerViewController.polygonConfigurationRejected.strokeWidth = width
    }

    @objc
    public func polygonCornerRadius(_ value: NSNumber?){
        let radius = value?.doubleValue ?? defaults.polygonCornerRadius
        scannerViewController.polygonConfigurationAccepted.cornerRadius = radius
        scannerViewController.polygonConfigurationRejected.cornerRadius = radius
        scannerViewController.autoSnapProgressPolygonConfiguration.cornerRadius = radius
    }

    @objc
    public func polygonAutoSnapProgressColor(_ value: UIColor?){
        scannerViewController.autoSnapProgressPolygonConfiguration.strokeColor = value ?? defaults.polygonAutoSnapProgressColor
        scannerViewController.withAutoSnapConfigCopy()
    }

    @objc
    public func polygonAutoSnapProgressLineWidth(_ value: NSNumber?){
        scannerViewController.autoSnapProgressPolygonConfiguration.strokeWidth = value?.doubleValue ?? defaults.polygonAutoSnapProgressLineWidth
        scannerViewController.withAutoSnapConfigCopy()
    }

    @objc
    public func polygonAutoSnapProgressEnabled(_ value: Bool){
        scannerViewController.autoSnapProgressPolygonConfiguration.enabled = value
        scannerViewController.withAutoSnapConfigCopy()
    }

    // MARK: Commands

    @objc
    public func freezeCamera() {
        scannerViewController.freezeCamera()
    }

    @objc
    public func unfreezeCamera(){
        scannerViewController.unfreezeCamera()
    }

    @objc
    public func snapDocument(){
        scannerViewController.captureDocumentImage()
    }

    // MARK: Utils
    @objc public func attachController(parentViewController: UIViewController?, inView view: UIView){
        parentViewController?.sbsdk_attach(self, in: view)
    }

    @objc public func detachController(){
        self.sbsdk_detach(self)
    }
}

//MARK: Delegates

extension RNScanbotDocumentScannerViewController: SBSDKDocumentScannerViewControllerDelegate {

    public func documentScannerViewController(
        _ controller: ScanbotSDK.SBSDKDocumentScannerViewController,
        didSnapDocumentImage documentImage: ScanbotSDK.SBSDKImageRef,
        on originalImage: ScanbotSDK.SBSDKImageRef,
        with result: ScanbotSDK.SBSDKDocumentDetectionResult?,
        autoSnapped: Bool
    ) {
        if detectDocumentAfterSnap {
            delegate?.onScannedDocument(originalImage, documentImage, SBComponentUtils.encodeToString(value: result))
        } else {
            delegate?.onScannedDocument(originalImage, nil, nil)
        }
    }

    public func documentScannerViewController(
        _ controller: SBSDKDocumentScannerViewController,
        didSampleVideoFrame videoFrameImage: SBSDKImageRef,
        scanningResult result: SBSDKDocumentDetectionResult?
    ) {
        if let status = SBComponentUtils.encodeToString(value: result) {
            delegate?.onDocumentDetectionStatus(status)
        }
    }

    public func documentScannerViewController(
        _ controller: ScanbotSDK.SBSDKDocumentScannerViewController,
        didFailScanning error: any Error
    ) {
        delegate?.onError(SBError(error: error))
    }

}
