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

import UIKit
import React
import DynamsoftCaptureVisionBundle

// Public so this class and its props are visible from Objective-C++ (RNDynamsoftCameraViewComponentView.mm)
// via the generated Swift interop header — internal declarations don't appear in it.
@objc(RNDynamsoftCameraView)
public class RNDynamsoftCameraView: UIView {

    let cameraView = CameraView()

    // Self-maintained react-tag registry. RCTUIManager only knows about legacy-architecture
    // views and RCTSurfacePresenter lookups are main-thread-only and unavailable in bridgeless
    // mode, so each mounted view registers itself instead. Main thread only.
    private static let registry = NSMapTable<NSNumber, RNDynamsoftCameraView>.strongToWeakObjects()

    private var registeredTag: NSNumber?

    @objc public static func view(forReactTag tag: NSNumber) -> RNDynamsoftCameraView? {
        registry.object(forKey: tag)
    }

    @objc public dynamic var visibleLayerIds: [NSNumber] = [] {
        didSet {
            let layerIds = [RNDSDrawingLayerIdDBR, RNDSDrawingLayerIdDDN, RNDSDrawingLayerIdDLR]
            for layerId in layerIds {
                let layer = cameraView.getDrawingLayer(UInt(layerId))
                layer?.visible = visibleLayerIds.contains(NSNumber(value: layerId))
            }
        }
    }

    @objc public dynamic var scanRegionMaskVisible: Bool = false {
        didSet { cameraView.scanRegionMaskVisible = scanRegionMaskVisible }
    }

    @objc public dynamic var scanLaserVisible: Bool = false {
        didSet { cameraView.scanLaserVisible = scanLaserVisible }
    }

    @objc public dynamic var torchButton: NSDictionary? {
        didSet {
            guard let torchButton = torchButton else { return }

            if let location = torchButton["location"] as? NSDictionary,
               let x = (location["x"] as? NSNumber)?.floatValue,
               let y = (location["y"] as? NSNumber)?.floatValue,
               let width = (location["width"] as? NSNumber)?.floatValue,
               let height = (location["height"] as? NSNumber)?.floatValue {
                let rect = CGRect(x: CGFloat(x), y: CGFloat(y), width: CGFloat(width), height: CGFloat(height))
                var torchOnImage: UIImage?
                var torchOffImage: UIImage?
                if let onBase64 = torchButton["torchOnImageBase64"] as? String,
                   let offBase64 = torchButton["torchOffImageBase64"] as? String {
                    torchOnImage = UIImage(data: Data(base64Encoded: onBase64, options: .ignoreUnknownCharacters) ?? Data())
                    torchOffImage = UIImage(data: Data(base64Encoded: offBase64, options: .ignoreUnknownCharacters) ?? Data())
                }
                cameraView.setTorchButton(frame: rect, torchOnImage: torchOnImage, torchOffImage: torchOffImage)
            }
            if let visible = torchButton["visible"] as? NSNumber {
                cameraView.torchButtonVisible = visible.boolValue
            }
        }
    }

    @objc public dynamic var torchButtonVisible: Bool = false {
        didSet { cameraView.torchButtonVisible = torchButtonVisible }
    }

    @objc public dynamic var cameraToggleButton: NSDictionary? {
        didSet {
            guard let cameraToggleButton = cameraToggleButton else { return }

            if let location = cameraToggleButton["location"] as? NSDictionary,
               let x = (location["x"] as? NSNumber)?.floatValue,
               let y = (location["y"] as? NSNumber)?.floatValue,
               let width = (location["width"] as? NSNumber)?.floatValue,
               let height = (location["height"] as? NSNumber)?.floatValue {
                let rect = CGRect(x: CGFloat(x), y: CGFloat(y), width: CGFloat(width), height: CGFloat(height))
                var image: UIImage?
                if let base64 = cameraToggleButton["cameraToggleImageBase64"] as? String {
                    image = UIImage(data: Data(base64Encoded: base64, options: .ignoreUnknownCharacters) ?? Data())
                }
                cameraView.setCameraToggleButtonWithFrame(rect, cameraToggle: image)
            }
        }
    }

    @objc public dynamic var cameraToggleButtonVisible: Bool = false {
        didSet { cameraView.cameraToggleButtonVisible = cameraToggleButtonVisible }
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }

    private func setup() {
        addSubview(cameraView)
    }

    public override func layoutSubviews() {
        super.layoutSubviews()
        cameraView.frame = bounds
    }

    public override func didMoveToWindow() {
        super.didMoveToWindow()
        if window != nil {
            registerReactTag()
        } else {
            unregisterReactTag()
        }
    }

    private func registerReactTag() {
        guard let tag = resolvedReactTag() else { return }
        if let previous = registeredTag, previous != tag {
            Self.registry.removeObject(forKey: previous)
        }
        registeredTag = tag
        Self.registry.setObject(self, forKey: tag)
    }

    private func unregisterReactTag() {
        guard let tag = registeredTag else { return }
        if Self.registry.object(forKey: tag) === self {
            Self.registry.removeObject(forKey: tag)
        }
        registeredTag = nil
    }

    // Fabric stores the react tag on the wrapping component view's `tag`; the legacy
    // architecture sets `reactTag` on this view itself.
    private func resolvedReactTag() -> NSNumber? {
        if let tag = reactTag, tag.intValue != 0 {
            return tag
        }
        if let superTag = superview?.tag, superTag != 0 {
            return NSNumber(value: superTag)
        }
        return nil
    }
}
