//
//  MINTLFontWell.swift
//  ControlSikenjo
//
//  Created by narita on 2018/12/04.
//  Copyright © 2018 narita. All rights reserved.
//


import Cocoa

class MINTLFontWellCell: NSButtonCell
{
    // MARK: - class methods

    private static let didBecomeExclusiveNotification = NSNotification.Name("MINTLFontWellDidBecomeExclusiveNotification")


    // MARK: - init methods
        
    override public init(textCell string: String)
    {
        super.init(textCell: "")
        
        self.setButtonType(.pushOnPushOff)
        self.bezelStyle = .shadowlessSquare
        self.allowsMixedState = false

        self.fontValue = NSFont.systemFont(ofSize: NSFont.systemFontSize)
    }
    
    override public convenience init(imageCell image: NSImage?)
    {
        self.init(textCell: "")
    }

    public convenience init()
    {
        self.init(textCell: "")
    }

    // MARK: - dealloc
    
    // MARK: - NSCopying, hash, isEqual:
    
    // MARK: - NSCoding methods

    required init(coder: NSCoder)
    {
        super.init(coder: coder)
        
        self.setButtonType(.pushOnPushOff)
        self.bezelStyle = .shadowlessSquare
        self.allowsMixedState = false
        
        self.fontValue = NSFont.systemFont(ofSize: NSFont.systemFontSize)
    }
    
    override func encode(with aCoder: NSCoder)
    {
        super.encode(with: aCoder)
    }
    
    // MARK: - restorableState methods
    
    // MARK: - life cycle methods
    
    override func awakeFromNib()
    {
        self.updateTitle()
    }
    
    // MARK: - action methods

    @IBAction func takeFontValueFrom(_ sender: Any?)
    {
        if let theObject = sender as? NSObject
        {
            if theObject.responds(to: #selector(getter: fontValue))
            {
                if let theFontValue = theObject.value(forKey: "fontValue") as? NSFont
                {
                    self.fontValue = theFontValue
                }
            }
        }
    }
    
    @IBAction func changeFont(_ sender: Any?)
    {
        if let theOldFont = self.fontValue
        {
            let theNewFont = NSFontManager.shared.convert(theOldFont)
            
            self.fontValue = theNewFont
        }
        else
        {
            let theNewFont = NSFontManager.shared.convert(NSFont.systemFont(ofSize: NSFont.systemFontSize))
            
            self.fontValue = theNewFont
        }
    }

    // MARK: - event handling methods

    @objc func activate()
    {
        let theNotifiCenter = NotificationCenter.default
        let theFontPanel    = NSFontPanel.shared
        let theFontMgr      = NSFontManager.shared
        
        // activeになったことを通知する
        theNotifiCenter.post(name: MINTLFontWellCell.didBecomeExclusiveNotification,
                             object: self)
        
        // 自分以外がactiveになった事を受信してdeactiveにする。
        // つまり他のObjectの上記のdidBecomeExclusiveNotificationを受信してdeactiveにする
        theNotifiCenter.addObserver(self,
                                    selector: #selector(self.deactivate),
                                    name: MINTLFontWellCell.didBecomeExclusiveNotification,
                                    object: nil)

        // font panelが閉じられた場合も、deactiveにする
        theNotifiCenter.addObserver(self,
                                    selector: #selector(self.deactivate),
                                    name: NSWindow.willCloseNotification,
                                    object: theFontPanel)
        
        theFontMgr.target = self
        theFontMgr.action = #selector(self.changeFont)
        
        if let theFontValue = self.fontValue
        {
            theFontPanel.setPanelFont(theFontValue, isMultiple: false)
        }

        theFontPanel.orderFront(self)
    }
    
    @objc func deactivate()
    {
        self.state = .off
        NSFontManager.shared.target = nil

        NotificationCenter.default.removeObserver(self)
    }
    
    // MARK: - drawing methods
    
    // MARK: - delegate/datasource methods
    
    // MARK: - accessor methods (in pairs)
    
    override var state: NSControl.StateValue
    {
        didSet(oldState)
        {
            // On -> Off => 何もしない
            // Off -> On => active
            // Off -> Off || On -> On => 何もしない
            
            if self.state != oldState
            {
                if oldState == .off && self.state == .on
                {
                    self.activate()
                }
                
                if oldState == .on && self.state == .off
                {
                    self.deactivate()
                }
            }
        }
    }

    @objc var fontValue: NSFont? = nil
    {
        didSet(oldFont)
        {
            self.updateTitle()
         
            // bindingInfoの情報を使って逆伝播させる
            if let theInfo = self.infoForBinding(NSBindingName("fontValue"))
            {
                let theObject  = theInfo[.observedObject] as? NSObject
                let theKeyPath = theInfo[.observedKeyPath] as? String
                
                theObject!.setValue(self.fontValue,
                                    forKeyPath: theKeyPath!)
            }
            
            self.controlView?.setValue(self.fontValue, forKeyPath: "fontValue")
        }
    }
    
    @objc var fontNameAndPointString: String
    {
        if let theFont = self.fontValue
        {
            return "\(String(describing: theFont.displayName!)) \(theFont.pointSize)"
        }
        else
        {
            return ""
        }
    }
    
    // MARK: - Utility methods
    
    @objc func updateTitle()
    {
        if let fv = self.fontValue
        {
            let fm = NSFontManager.shared
            
            self.title = self.fontNameAndPointString
            self.font  = fm.font(withFamily: fv.familyName!,
                                 traits: fm.traits(of: fv),
                                 weight: fm.weight(of: fv),
                                 size: self.font!.pointSize)
        }
    }
    
}

class MINTLFontWell: NSButton
{
    // MARK: - class methods
    
    class func cellClass() -> AnyClass?
    {
        return MINTLFontWellCell.self
    }

    // MARK: - init methods
    
    override init(frame frameRect: NSRect)
    {
        super.init(frame: frameRect)
    }
    
    // MARK: - dealloc
    
    // MARK: - NSCopying, hash, isEqual:
    
    // MARK: - NSCoding methods
    
    required init?(coder: NSCoder)
    {
        super.init(coder: coder)
        
        if     ( self.cell == nil )
            ||
               ( self.cell!.className != MINTLFontWellCell.className() )
        {
            self.cell = MINTLFontWellCell(textCell: "")
        }
    }
    
    // MARK: - restorableState methods
    
    // MARK: - life cycle methods
    
    // MARK: - action methods
    
    // MARK: - event handling methods
    
    // MARK: - drawing methods

    // MARK: - delegate/datasource methods
    
    // MARK: - accessor methods (in pairs)
    
    @objc var fontValue: NSFont?
    {
        get
        {
            if let theFontWellCell = self.cell as? MINTLFontWellCell
            {
                return theFontWellCell.fontValue
            }
            
            return nil
        }
        
        set(newValue)
        {
            if let theFontWellCell = self.cell as? MINTLFontWellCell, newValue != theFontWellCell.fontValue
            {
                theFontWellCell.fontValue = newValue
            }
            
            // bindingInfoの情報を使って逆伝播させる
            if let theInfo = self.infoForBinding(NSBindingName("fontValue"))
            {
                let theObject  = theInfo[.observedObject] as? NSObject
                let theKeyPath = theInfo[.observedKeyPath] as? String

                theObject!.setValue(newValue, forKeyPath: theKeyPath!)
            }
        }
    }

    
    
    // MARK: - Utility methods
}
