//
//  KMDrawView.swift
//  PDF Reader Pro
//
//  Created by lizhe on 2023/10/9.
//

import Cocoa

private var _index: Int = 0
private var _points: [CGPoint] = [CGPoint](repeating: .zero, count: 5)

@objc protocol KMDrawViewDelegate: NSObjectProtocol {
    func drawViewDidFinishTouchMode(_ drawView: KMDrawView)
}

@objcMembers class KMDrawView: NSView {
    private var drawImage: NSImage?
    var drawColor: NSColor = NSColor(red: 0, green: 0, blue: 0, alpha: 1) {
        didSet {
            self.needsDisplay = true
        }
    }
    var strokeRadius: CGFloat = 0.3 {
        didSet {
            self.needsDisplay = true
        }
    }
    private var bezierPath: NSBezierPath = NSBezierPath()
    var isAcceptsTouch: Bool = false
    private var cursorIsHidden: Bool = false
    private var mouseIsInView: Bool = false
    private var activeTouch: NSTouch?
    
    weak var delegate: KMDrawViewDelegate?
    
    var drawBezierPath: NSBezierPath {
        return bezierPath
    }
    
    var touchEndCallback: ((Bool) -> Void)?
    var changeDrawCallback: ((Bool) -> Void)?
    
//    override func acceptsFirstResponder() -> Bool {
//        return true
//    }
    
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        
        self.drawImage = NSImage(size: self.frame.size)
        self.drawColor = NSColor(red: 0, green: 0, blue: 0, alpha: 1)
        self.strokeRadius = 0.3
        
        self.wantsLayer = true
        self.layer?.borderWidth = 1.0
        self.layer?.borderColor = NSColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.05).cgColor
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    func clearImage() {
        self.drawImage = nil
        self.bezierPath.removeAllPoints()
        self.needsDisplay = true
        if let touchEndCallback = self.touchEndCallback {
            touchEndCallback(true)
        }
    }
    
    func signatureImage() -> NSImage? {
        var rect = CGRect.zero
        
        if bezierPath.isEmpty {
            return nil
        } else {
            rect = bezierPath.bounds
        }
        
        let size = CGSize(width: rect.size.width + bezierPath.lineWidth,
                          height: rect.size.height + bezierPath.lineWidth)
        
        if size.width <= 0 && size.height <= 0 {
            return nil
        }
        
        let image = NSImage(size: self.bounds.size)
        image.lockFocus()
        
        drawColor.set()
        drawBezierPath.lineWidth = strokeRadius * 2
        drawBezierPath.lineCapStyle = .round
        drawBezierPath.lineJoinStyle = .round
        drawBezierPath.stroke()
        
        image.unlockFocus()
        
        return image
    }
    
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        NSGraphicsContext.saveGraphicsState()
        NSColor.clear.set()

        if window?.firstResponder == self && mouseIsInView {
            focusRingType = .`default`
        }
        
        NSBezierPath(rect: bounds).fill()
        
        NSGraphicsContext.restoreGraphicsState()

        if let drawImage = self.drawImage {
            let imageFrame = imageFrameInRect(rect: dirtyRect)
            drawImage.draw(in: imageFrame)
        }

        drawColor.set()
        bezierPath.lineWidth = strokeRadius * 2
        bezierPath.lineCapStyle = .round
        bezierPath.lineJoinStyle = .round
        bezierPath.stroke()
    }

    
    private func imageFrameInRect(rect: CGRect) -> CGRect {
        var originX: CGFloat
        var originY: CGFloat
        var width: CGFloat
        var height: CGFloat
        
        if drawImage?.size.width ?? 0 < rect.size.width &&
           drawImage?.size.height ?? 0 < rect.size.height {
            originX = (rect.size.width - (drawImage?.size.width ?? 0)) / 2.0
            originY = (rect.size.height - (drawImage?.size.height ?? 0)) / 2.0
            width = drawImage?.size.width ?? 0
            height = drawImage?.size.height ?? 0
        } else {
            if (drawImage?.size.width ?? 0) / (drawImage?.size.height ?? 0) >
                rect.size.width / rect.size.height {
                width = rect.size.width
                height = (rect.size.width * (drawImage?.size.height ?? 0)) / (drawImage?.size.width ?? 0)
            } else {
                height = rect.size.height
                width = (rect.size.height * (drawImage?.size.width ?? 0)) / (drawImage?.size.height ?? 0)
            }
            originX = (rect.size.width - width) / 2.0
            originY = (rect.size.height - height) / 2.0
        }
        
        let rect = CGRectMake(originX, originY, width, height)
        
        return rect
    }
    
    override func viewDidMoveToWindow() {
        if window != nil {
            addTrackingRect(bounds, owner: self, userData: nil, assumeInside: false)
        }
    }
    
    override func mouseEntered(with event: NSEvent) {
        window?.makeFirstResponder(self)
        // self.mouseIsInView = true
        needsDisplay = true
    }
    
    override func mouseExited(with event: NSEvent) {
        // self.mouseIsInView = false
        needsDisplay = true
    }
    
    override func mouseDown(with event: NSEvent) {
        if acceptsTouchEvents {
            return
        }
        
        let point = convert(event.locationInWindow, from: nil)
        _index = 0
        _points[0] = point
    }
    
    override func mouseDragged(with event: NSEvent) {
        if acceptsTouchEvents {
            return
        }
        
        let point = convert(event.locationInWindow, from: nil)
        _index += 1
        _points[_index] = point
        
        if _index == 4 {
            _points[3] = CGPoint(x: (_points[2].x + _points[4].x) / 2.0,
                                 y: (_points[2].y + _points[4].y) / 2.0)
            bezierPath.move(to: _points[0])
            bezierPath.curve(to: _points[3], controlPoint1: _points[1], controlPoint2: _points[2])
            _points[0] = _points[3]
            _points[1] = _points[4]
            _index = 1
            needsDisplay = true
            
            if let changeDrawCallback = self.changeDrawCallback {
                changeDrawCallback(true)
            }
        }
    }
    
    override func mouseUp(with event: NSEvent) {
        if acceptsTouchEvents {
            return
        }
        
        if _index < 4 {
            for i in 0..<_index {
                bezierPath.move(to: _points[i])
            }
            needsDisplay = true
        }
        
        if let touchEndCallback = self.touchEndCallback {
            touchEndCallback(false)
        }
    }
    
    override func keyDown(with event: NSEvent) {
        if let characters = event.characters, characters.count > 0 {
            let character = characters.first!
            // if character == "\u{1B}" { // Check for the Escape key
            if isAcceptsTouch {
                isAcceptsTouch = false
                if let delegate = self.delegate {
                    delegate.drawViewDidFinishTouchMode(self)
                }
                return
            }
            // }
        }
        
        super.keyDown(with: event)
    }
}