//
//  NSObject+MINTLControlItemEnabler.swift
//  MenuTester
//
//  Created by narita on 2017/08/18.
//  Copyright © 2017年 narita. All rights reserved.
//

import Cocoa

extension NSObject
{
    // 引数で与えられたObjCの読み書き可能なプロパティが存在すればtrueを返す
    
    // ---------------------------------------------------------------------------------------------------------
    // MARK: - can methods
    
    func respondsAndPerformUIEnable(_ item: NSValidatedUserInterfaceItem,
                                      _ closure: Optional<(_ item: NSValidatedUserInterfaceItem) -> Bool> ) -> Bool
    {
        var theResult:Bool = true
        let theSelector = makePrefixedSelector(prefix:"can", base: item.action!)
        
        if( self.responds(to:theSelector) )
        {
            typealias functionType = @convention(c) (AnyObject, Selector, AnyObject) -> Bool
            
            let theFunction = unsafeBitCast(self.method(for:theSelector), to: functionType.self)
            
            theResult =  theFunction(self, theSelector, item)
        }
        else if closure != nil
        {
            // あれば親クラスのvalidateUserInterfaceItemを呼び出す
            theResult = closure!(item)
        }
        
        return theResult
    }
    
    
    func respondsAndPerformValidatedUserInterface(_ inItem: NSValidatedUserInterfaceItem,
                                                  _ closure: Optional<(_ item: NSValidatedUserInterfaceItem) -> Bool> = nil) -> Bool
    {
        // stateメソッドの存在確認と実行
        self.updateIntgerProperty(at:"state", from: inItem)
        
        // imageソッドの存在確認と実行
        self.updateObjectProperty(at:"image", from: inItem)
        
        // titleソッドの存在確認と実行
        self.updateObjectProperty(at:"title", from: inItem)
        
        // selectedIndexメソッドの存在確認と実行
        self.updateIntgerProperty(at:"selectedIndex", from: inItem)
        
        // その他の沢山の項目
        self.updateObjectProperty(at:"label", from: inItem)
        self.updateObjectProperty(at:"attributedTitle", from: inItem)
        self.updateObjectProperty(at:"objectValue", from: inItem)
        self.updateObjectProperty(at:"stringValue", from: inItem)
        self.updateObjectProperty(at:"attributedStringValue", from: inItem)
        self.updateObjectProperty(at:"toolTip", from: inItem)
        
        return self.respondsAndPerformUIEnable(inItem, closure)
    }
    
    
}

// ---------------------------------------------------------------------------------------------------------
// MARK: - Generic methods
// ---------------------------------------------------------------------------------------------------------

// 以下の2つのメソッドは、ジェネリックスで書きたい

extension NSObject
{
    func updateObjectProperty(at name: String, from anItem: NSValidatedUserInterfaceItem)
    {
        if let theObject = anItem as? NSObject
        {
            guard anItem.action != nil else
            {
                return
            }

            // bindingされていない項目のみ実行する
            guard self.infoForBinding(NSBindingName(rawValue: name)) == nil else
            {
                return
            }

            if theObject.propertyExists(at:name) == .readWrite
            {
                let theSelector = makePrefixedSelector(prefix:name.appending("Of"), base: anItem.action!)
                
                if self.responds(to: theSelector)
                {
                    // 帰り値の型がNSObject
                    typealias functionType = @convention(c) (NSObject, Selector, NSValidatedUserInterfaceItem) -> NSObject
                    
                    let theFunction = unsafeBitCast(self.method(for:theSelector),
                                                    to: functionType.self)
                    
                    // 帰り値の値をそのままKVCする
                    let value = theFunction(self, theSelector, anItem)
                    
                    theObject.setValue(value, forKey: name as String)
                }
            }
        }
    }

    func updateIntgerProperty(at name: String, from anItem: NSValidatedUserInterfaceItem)
    {
        if let theObject = anItem as? NSObject
        {
            guard anItem.action != nil else
            {
                return
            }
            
            // bindingされていない項目のみ実行する
            guard self.infoForBinding(NSBindingName(rawValue: name)) == nil else
            {
                return
            }
            
            if theObject.propertyExists(at:name) == .readWrite
            {
                let theSelector = makePrefixedSelector(prefix:name.appending("Of"), base: anItem.action!)
                
                if self.responds(to: theSelector)
                {
                    // 帰値の型がNSInteger
                    typealias functionType = @convention(c) (NSObject, Selector, NSValidatedUserInterfaceItem) -> NSInteger
                    
                    let theFunction = unsafeBitCast(self.method(for:theSelector),
                                                    to: functionType.self)
                    
                    // 帰り値の値をNSNumberでboxingしてKVCする
                    let value = NSNumber.init(value: theFunction(self, theSelector, anItem))
                    
                    theObject.setValue(value, forKey: name)
                }
            }
        }
    }
}
