/*
 * Copyright © Dynamsoft Corporation. All rights reserved.
 */

import UIKit
import React
import DynamsoftCaptureVisionBundle

@objc(RNDynamsoftImageEditorViewManager)
public class RNDynamsoftImageEditorViewManager: RCTViewManager {

    private static let maxAttempts = 10
    private static let retryDelay: TimeInterval = 0.1 // 100ms

    public override func view() -> UIView! {
        return RNDynamsoftImageEditorView()
    }

    @objc func setQuads(_ tag: NSNumber, quadArray: NSArray, layerId: NSInteger) {
        trySetQuads(tag: tag, quadArray: quadArray, layerId: layerId, attempt: 1, completion: nil)
    }

    // TurboModule/codegen path expects a promise-based selector with `quads` label.
    @objc(setQuads:quads:layerId:resolve:reject:)
    func setQuads(_ tag: NSNumber,
                  quads: NSArray,
                  layerId: NSInteger,
                  resolve: @escaping RCTPromiseResolveBlock,
                  reject: @escaping RCTPromiseRejectBlock) {
        trySetQuads(tag: tag, quadArray: quads, layerId: layerId, attempt: 1) { success in
            if success {
                resolve(nil)
            } else {
                reject("set_quads_failed", "Cannot find RNDynamsoftImageEditorView with tag #\(tag)", nil)
            }
        }
    }

    private func trySetQuads(tag: NSNumber,
                             quadArray: NSArray,
                             layerId: NSInteger,
                             attempt: Int,
                             completion: ((Bool) -> Void)?) {
        bridge.uiManager.addUIBlock { [weak self] uiManager, _ in
            guard let self = self else { return }
            guard let uiManager = uiManager,
                  let view = uiManager.view(forReactTag: tag) as? RNDynamsoftImageEditorView else {
                if attempt < Self.maxAttempts {
                    DispatchQueue.main.asyncAfter(deadline: .now() + Self.retryDelay) {
                        self.trySetQuads(tag: tag, quadArray: quadArray, layerId: layerId, attempt: attempt + 1, completion: completion)
                    }
                } else {
                    RNDynamsoftLogError("Cannot find RNDynamsoftImageEditorView with tag #\(tag) after \(attempt) attempts")
                    completion?(false)
                }
                return
            }

            guard quadArray.count > 0 else {
                completion?(true)
                return
            }

            var items: [QuadDrawingItem] = []
            for case let dic as [AnyHashable: Any] in quadArray {
                if let quad = Quadrilateral(dictionary: dic) {
                    items.append(QuadDrawingItem(quadrilateral: quad))
                }
            }
            let layer = view.editorView.getDrawingLayer(UInt(layerId))
            layer?.drawingItems = items
            completion?(true)
        }
    }

    @objc func getSelectedQuad(_ tag: NSNumber, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        getSelectedQuadInternal(tag: tag, resolve: resolve, reject: reject)
    }

    // TurboModule/codegen path expects `getSelectedQuad:resolve:reject:`.
    @objc(getSelectedQuad:resolve:reject:)
    func getSelectedQuad(_ tag: NSNumber,
                         resolve: @escaping RCTPromiseResolveBlock,
                         reject: @escaping RCTPromiseRejectBlock) {
        getSelectedQuadInternal(tag: tag, resolve: resolve, reject: reject)
    }

    private func getSelectedQuadInternal(tag: NSNumber,
                                         resolve: @escaping RCTPromiseResolveBlock,
                                         reject: @escaping RCTPromiseRejectBlock) {
        bridge.uiManager.addUIBlock { uiManager, _ in
            guard let uiManager = uiManager,
                  let view = uiManager.view(forReactTag: tag) as? RNDynamsoftImageEditorView else {
                RNDynamsoftLogError("Cannot find RNDynamsoftImageEditorView with tag #\(tag)")
                resolve(nil)
                return
            }

            guard let item = view.editorView.getSelectedDrawingItem(),
                  item.mediaType == .quadrilateral,
                  let quadItem = item as? QuadDrawingItem else {
                resolve(nil)
                return
            }
            resolve(quadItem.quad.toJson())
        }
    }

    // Invoked from the JSI installer (RNDynamsoftImageEditorViewManagerJSI.mm) once the JS-side
    // image data host object has already been converted to an ImageData instance.
    @objc(trySetImageDataWithTag:data:attempt:)
    public func trySetImageData(tag: NSNumber, data: ImageData, attempt: Int) {
        bridge.uiManager.addUIBlock { [weak self] uiManager, _ in
            guard let self = self else { return }
            guard let uiManager = uiManager,
                  let view = uiManager.view(forReactTag: tag) as? RNDynamsoftImageEditorView else {
                if attempt < Self.maxAttempts {
                    DispatchQueue.main.asyncAfter(deadline: .now() + Self.retryDelay) {
                        self.trySetImageData(tag: tag, data: data, attempt: attempt + 1)
                    }
                } else {
                    RNDynamsoftLogError("Cannot find RNDynamsoftImageEditorView with tag #\(tag) after \(attempt) attempts")
                }
                return
            }
            view.editorView.imageData = data
        }
    }
}
