import UIKit
import Flutter

@objc class ScannerViewController: UIViewController, ZXCaptureDelegate, UITextFieldDelegate {
    
    enum AppLocale {
        case ru
        case en
    }
    
    enum ButtonState {
        case card
        case phone
        
        var icon: UIImage {
            
            switch self {
            case .card: return UIImage(named: "card")!
            case .phone: return UIImage(named: "phone")!
            }
        }
        
        var searchType: String {
            switch self {
            case .card: return "card"
            case .phone: return "phone"
            }
        }
    }
    
    //    @IBOutlet weak var decodedLabel: UILabel!
    
    private var captureSizeTransform: CGAffineTransform?
    
    var buttonState: ButtonState = .card
    
    var platformChannel: FlutterMethodChannel?
    
    // Квадрат для наведения на цель (надеюсь)
    let scanRectView = UIView()
    
    //Вьюшка для верхнего меню
    let topView = UIView()
    
    //Окно ввода кода
    let textField = UITextField()
    
    //Кнопка настроек
    var settingButton: UIButton!
    
    var strings = [String:String]()
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    init(strings: [String: String]) {
        super.init(nibName: nil, bundle: nil)
        strings.forEach { (k,v) in self.strings[k] = v }
    }
    
    func getInputHint() -> String {
        switch self.buttonState {
            case .card:  return strings["enter_manual"]!
            case .phone: return strings["enter_phone"]!
        }
    }
    
    func getErrorText() -> String {
        switch self.buttonState {
            case .card:  return strings["user_card_not_found"]!
            case .phone: return strings["user_phone_not_found"]!
        }
    }
    
    func setButtonState() {
        switch self.buttonState {
            case .card: self.buttonState = .phone
            case .phone: self.buttonState = .card
        }
    }
    
    let capture: ZXCapture = ZXCapture()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        capture.camera = capture.back()
        capture.focusMode = .continuousAutoFocus
        view.layer.addSublayer((capture.layer)!)
        view.addSubview(scanRectView)
        
        settingButton = UIButton(type: .system)
        settingButton.addTarget(self, action: #selector(ScannerViewController.buttonTouch), for: .touchUpInside)
        
        topView.addSubview(textField)
        topView.addSubview(settingButton)
        view.addSubview(topView)
        view.bringSubview(toFront: topView)
        
        textField.delegate = self
        
        let tap = UITapGestureRecognizer(target: self, action: #selector(ScannerViewController.hideKeyboard))
        view.addGestureRecognizer(tap)
        
        settingButton.setImage(self.buttonState.icon, for: .normal)
        textField.placeholder = self.getInputHint()
//      textField.text = "79087654321"
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        topView.backgroundColor = UIColor.white
        textField.borderStyle = .roundedRect
        capture.delegate = self
        applyOrientation()
    }
    
    override func viewWillLayoutSubviews() {
        scanRectView.frame = view.bounds
        topView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: 56)
        settingButton.frame = CGRect(x: 8, y: 26, width: 20, height: 20)
        textField.frame = CGRect(x: settingButton.frame.maxX + 8, y: 21,
                                 width: view.frame.size.width - settingButton.frame.maxX - 16, height: 30)
    }
    
    func hideKeyboard() {
        view.endEditing(false)
    }
    
    func buttonTouch(){
        setButtonState()
        settingButton.setImage(self.buttonState.icon, for: .normal)
        textField.placeholder = self.getInputHint()
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        print("User from manual input: \(textField.text)")
        sendResult(textField.text!)
        return true
    }
    
    func sendResult(_ str: String) {
        platformChannel?.invokeMethod("findUser", arguments: [str, buttonState.searchType], result: { (result: Any?) in
            if result is FlutterError {
                print("Result is nil (ios code)");
                self.showErrorAlert(str)
            } else {
                print("Result is not nil (ios code)");
                self.dismiss(animated: true) {
                    self.platformChannel?.invokeMethod("purchase", arguments: [result, str])
                }
            }
        })
    }
    
    func showErrorAlert(_ str: String) {
        let alertController = UIAlertController(
            title: strings["error"]!,
            message: String(format: self.getErrorText(), str),
            preferredStyle: UIAlertControllerStyle.alert
        )
        alertController.addAction(UIAlertAction(title: strings["dismiss"]!, style: UIAlertActionStyle.default,handler: nil))
        self.present(alertController, animated: true, completion: nil)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    deinit {
        capture.layer.removeFromSuperlayer()
    }
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate(alongsideTransition: {(_ context: UIViewControllerTransitionCoordinatorContext) -> Void in
        }, completion: {(_ context: UIViewControllerTransitionCoordinatorContext) -> Void in
            self.applyOrientation()
        })
    }
    
    func applyOrientation() {
        print("APPLY ORIENTATION")
        let orientation: UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
        var scanRectRotation: Float
        var captureRotation: Float
        switch orientation {
        case .portrait:
            captureRotation = 0
            scanRectRotation = 90
        case .landscapeLeft:
            captureRotation = 90
            scanRectRotation = 180
        case .landscapeRight:
            captureRotation = 270
            scanRectRotation = 0
        case .portraitUpsideDown:
            captureRotation = 180
            scanRectRotation = 270
        default:
            captureRotation = 0
            scanRectRotation = 90
        }
        applyRectOfInterest(orientation)
        let transform = CGAffineTransform(rotationAngle: CGFloat(captureRotation / 180 * .pi))
        capture.transform = transform
        capture.rotation = CGFloat(scanRectRotation)
        capture.layer.frame = view.frame
    }
    
    func applyRectOfInterest(_ orientation: UIInterfaceOrientation) {
        var scaleVideo: CGFloat
        var scaleVideoX: CGFloat
        var scaleVideoY: CGFloat
        var videoSizeX: CGFloat
        var videoSizeY: CGFloat
        var transformedVideoRect: CGRect = scanRectView.frame
        videoSizeX = 720
        videoSizeY = 1280
        if UIInterfaceOrientationIsPortrait(orientation) {
            scaleVideoX = view.frame.size.width / videoSizeX
            scaleVideoY = view.frame.size.height / videoSizeY
            scaleVideo = max(scaleVideoX, scaleVideoY)
            if scaleVideoX > scaleVideoY {
                transformedVideoRect.origin.y += (scaleVideo * videoSizeY - view.frame.size.height) / 2
            }
            else {
                transformedVideoRect.origin.x += (scaleVideo * videoSizeX - view.frame.size.width) / 2
            }
        }
        else {
            scaleVideoX = view.frame.size.width / videoSizeY
            scaleVideoY = view.frame.size.height / videoSizeX
            scaleVideo = max(scaleVideoX, scaleVideoY)
            if scaleVideoX > scaleVideoY {
                transformedVideoRect.origin.y += (scaleVideo * videoSizeX - view.frame.size.height) / 2
            }
            else {
                transformedVideoRect.origin.x += (scaleVideo * videoSizeY - view.frame.size.width) / 2
            }
        }
        captureSizeTransform = CGAffineTransform(scaleX: 1 / scaleVideo, y: 1 / scaleVideo)
        capture.scanRect = transformedVideoRect.applying(captureSizeTransform!)
    }
    
    // MARK: - Private Methods
    func barcodeFormat(toString format: ZXBarcodeFormat) -> String {
        switch format {
        case kBarcodeFormatAztec:
            return "Aztec"
        case kBarcodeFormatCodabar:
            return "CODABAR"
        case kBarcodeFormatCode39:
            return "Code 39"
        case kBarcodeFormatCode93:
            return "Code 93"
        case kBarcodeFormatCode128:
            return "Code 128"
        case kBarcodeFormatDataMatrix:
            return "Data Matrix"
        case kBarcodeFormatEan8:
            return "EAN-8"
        case kBarcodeFormatEan13:
            return "EAN-13"
        case kBarcodeFormatITF:
            return "ITF"
        case kBarcodeFormatPDF417:
            return "PDF417"
        case kBarcodeFormatQRCode:
            return "QR Code"
        case kBarcodeFormatRSS14:
            return "RSS 14"
        case kBarcodeFormatRSSExpanded:
            return "RSS Expanded"
        case kBarcodeFormatUPCA:
            return "UPCA"
        case kBarcodeFormatUPCE:
            return "UPCE"
        case kBarcodeFormatUPCEANExtension:
            return "UPC/EAN extension"
        default:
            return "Unknown"
        }
    }
    
    // MARK: - ZXCaptureDelegate Methods
    func captureResult(_ capture: ZXCapture, result: ZXResult) {
        let inverse: CGAffineTransform = captureSizeTransform!.inverted()
        var points = [AnyHashable]()
        var location = ""
        for resultPoint in result.resultPoints {
            let p = resultPoint as! ZXResultPoint
            let cgPoint = CGPoint(x: CGFloat(p.x), y: CGFloat(p.y))
            var transformedPoint: CGPoint = cgPoint.applying(inverse)
            transformedPoint = scanRectView.convert(transformedPoint, to: scanRectView.window)
            let windowPointValue = NSValue(cgPoint: transformedPoint)
            location = "\(location) (\(transformedPoint.x), \(transformedPoint.y))"
            points.append(windowPointValue)
        }
        print("User from scanner: \(result.text)")
        sendResult(result.text)
        print(result.text)
        self.capture.stop()
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(2 * Double(NSEC_PER_SEC)) / Double(NSEC_PER_SEC), execute: {() -> Void in
            self.capture.start()
        })
    }
}
