/*
 * This file is part of mpv.
 *
 * mpv is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * mpv is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
 */

import Cocoa

class TitleBar: NSVisualEffectView {
    unowned var common: Common
    var option: OptionHelper { get { return common.option } }

    var systemBar: NSView? {
        get { return common.window?.standardWindowButton(.closeButton)?.superview }
    }
    static var height: CGFloat {
        get { return NSWindow.frameRect(forContentRect: CGRect.zero, styleMask: .titled).size.height }
    }
    var buttons: [NSButton] {
        get { return ([.closeButton, .miniaturizeButton, .zoomButton] as [NSWindow.ButtonType]).compactMap { common.window?.standardWindowButton($0) } }
    }

    override var material: NSVisualEffectView.Material {
        get { return super.material }
        set {
            super.material = newValue
            // fix for broken deprecated materials
            if material == .light || material == .dark || material == .mediumLight ||
               material == .ultraDark
            {
                state = .active
            } else {
                state = .followsWindowActiveState
            }

        }
    }

    init(frame: NSRect, window: NSWindow, common com: Common) {
        let f = NSMakeRect(0, frame.size.height - TitleBar.height,
                           frame.size.width, TitleBar.height + 1)
        common = com
        super.init(frame: f)
        buttons.forEach { $0.isHidden = true }
        isHidden = true
        alphaValue = 0
        blendingMode = .withinWindow
        autoresizingMask = [.width, .minYMargin]
        systemBar?.alphaValue = 0
        state = .followsWindowActiveState
        wantsLayer = true

        window.contentView?.addSubview(self, positioned: .above, relativeTo: nil)
        window.titlebarAppearsTransparent = true
        window.styleMask.insert(.fullSizeContentView)
        set(appearance: Int(option.mac.macos_title_bar_appearance))
        set(material: Int(option.mac.macos_title_bar_material))
        set(color: option.mac.macos_title_bar_color)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // catch these events so they are not propagated to the underlying view
    override func mouseDown(with event: NSEvent) { }

    override func mouseUp(with event: NSEvent) {
        if event.clickCount > 1 {
            let def = UserDefaults.standard
            var action = def.string(forKey: "AppleActionOnDoubleClick")

            // macOS 10.10 and earlier
            if action == nil {
                action = def.bool(forKey: "AppleMiniaturizeOnDoubleClick") == true ?
                    "Minimize" : "Maximize"
            }

            if action == "Minimize" {
                window?.miniaturize(self)
            } else if action == "Maximize" {
                window?.zoom(self)
            }
        }

        common.window?.isMoving = false
    }

    func set(appearance: Any) {
        if appearance is Int {
            window?.appearance = appearanceFrom(string: String(appearance as? Int ?? 0))
        } else {
            window?.appearance = appearanceFrom(string: appearance as? String ?? "auto")
        }
    }

    func set(material: Any) {
        if material is Int {
            self.material = materialFrom(string: String(material as? Int ?? 0))
        } else {
            self.material = materialFrom(string: material as? String ?? "titlebar")
        }
    }

    func set(color: Any) {
        if color is String {
            layer?.backgroundColor = NSColor(hex: color as? String ?? "#00000000").cgColor
        } else {
            let col = color as? m_color ?? m_color(r: 0, g: 0, b: 0, a: 0)
            let red   = CGFloat(col.r)/255
            let green = CGFloat(col.g)/255
            let blue  = CGFloat(col.b)/255
            let alpha = CGFloat(col.a)/255
            layer?.backgroundColor = NSColor(calibratedRed: red, green: green,
                                             blue: blue, alpha: alpha).cgColor
        }
    }

    func show() {
        guard let window = common.window else { return }
        if !window.border && !window.isInFullscreen { return }
        let loc = common.view?.convert(window.mouseLocationOutsideOfEventStream, from: nil)

        buttons.forEach { $0.isHidden = false }
        NSAnimationContext.runAnimationGroup({ (context) -> Void in
            context.duration = 0.20
            systemBar?.animator().alphaValue = 1
            if !window.isInFullscreen && !window.isAnimating {
                animator().alphaValue = 1
                isHidden = false
            }
        }, completionHandler: nil )

        if loc?.y ?? 0 > TitleBar.height {
            hideDelayed()
        } else {
            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hide), object: nil)
        }
    }

    @objc func hide(_ duration: TimeInterval = 0.20) {
        guard let window = common.window else { return }
        if window.isInFullscreen && !window.isAnimating {
            alphaValue = 0
            isHidden = true
            return
        }
        NSAnimationContext.runAnimationGroup({ (context) -> Void in
            context.duration = duration
            systemBar?.animator().alphaValue = 0
            animator().alphaValue = 0
        }, completionHandler: {
            self.buttons.forEach { $0.isHidden = true }
            self.isHidden = true
        })
    }

    func hideDelayed() {
        NSObject.cancelPreviousPerformRequests(withTarget: self,
                                                 selector: #selector(hide),
                                                   object: nil)
        perform(#selector(hide), with: nil, afterDelay: 0.5)
    }

    func appearanceFrom(string: String) -> NSAppearance? {
        switch string {
        case "1", "aqua":
            return NSAppearance(named: .aqua)
        case "2", "darkAqua":
            return NSAppearance(named: .darkAqua)
        case "3", "vibrantLight":
            return NSAppearance(named: .vibrantLight)
        case "4", "vibrantDark":
            return NSAppearance(named: .vibrantDark)
        case "5", "aquaHighContrast":
            return NSAppearance(named: .accessibilityHighContrastAqua)
        case "6", "darkAquaHighContrast":
            return NSAppearance(named: .accessibilityHighContrastDarkAqua)
        case "7", "vibrantLightHighContrast":
            return NSAppearance(named: .accessibilityHighContrastVibrantLight)
        case "8", "vibrantDarkHighContrast":
            return NSAppearance(named: .accessibilityHighContrastVibrantDark)
        case "0", "auto": fallthrough
        default:
            return nil
        }
    }

    func materialFrom(string: String) -> NSVisualEffectView.Material {
        switch string {
        case "0",  "titlebar":              return .titlebar
        case "1",  "selection":             return .selection
        case "2,", "menu":                  return .menu
        case "3",  "popover":               return .popover
        case "4",  "sidebar":               return .sidebar
        case "5,", "headerView":            return .headerView
        case "6",  "sheet":                 return .sheet
        case "7",  "windowBackground":      return .windowBackground
        case "8",  "hudWindow":             return .hudWindow
        case "9",  "fullScreen":            return .fullScreenUI
        case "10", "toolTip":               return .toolTip
        case "11", "contentBackground":     return .contentBackground
        case "12", "underWindowBackground": return .underWindowBackground
        case "13", "underPageBackground":   return .underPageBackground
        case "14", "dark":                  return .dark
        case "15", "light":                 return .light
        case "16", "mediumLight":           return .mediumLight
        case "17", "ultraDark":             return .ultraDark
        default:                            return .titlebar
        }
    }
}
