//
//  CheckboxModel.swift
//  MVMCoreUI
//
//  Created by Chintakrinda, Arun Kumar (Arun) on 21/01/20.
//  Copyright © 2020 Verizon Wireless. All rights reserved.
//

/// Protocol to apply to any model of a UI Control with a binary on/off nature.
///
/// Example classes: Checkbox or Toggle.
@objc public protocol SelectableMoleculeModelProtocol {
    var selected: Bool { get set }
}

@objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableMoleculeModelProtocol, FormFieldProtocol, UIUpdatableModelProtocol {
    
    //--------------------------------------------------
    // MARK: - Properties
    //--------------------------------------------------
    
    public static var identifier: String = "checkbox"
    public var backgroundColor: Color?
    public var accessibilityIdentifier: String?
    public var selected: Bool = false
    public var enabled: Bool = true
    public var readOnly: Bool = false
    public var animated: Bool = true
    public var inverted: Bool = false
    public var round: Bool = false
    public var borderWidth: CGFloat = 1
    public var borderColor: Color = Color(uiColor: .mvmBlack)
    public var checkColor: Color = Color(uiColor: .mvmBlack)
    public var unCheckedBackgroundColor: Color = Color(uiColor: .clear)
    public var checkedBackgroundColor: Color = Color(uiColor: .clear)
    public var disabledBackgroundColor: Color = Color(uiColor: .clear)
    public var disabledBorderColor: Color = Color(uiColor: .mvmCoolGray3)
    public var disabledCheckColor: Color = Color(uiColor: .mvmCoolGray3)
    public var invertedColor: Color = Color(uiColor: .mvmWhite)
    public var invertedBackgroundColor: Color = Color(uiColor: .mvmBlack)
    public var action: ActionModelProtocol?
    public var offAction: ActionModelProtocol?

    public var fieldKey: String?
    public var groupName: String = FormValidator.defaultGroupName
    public var baseValue: AnyHashable?
    public var updateUI: ActionBlock?

    //--------------------------------------------------
    // MARK: - Keys
    //--------------------------------------------------
    
    private enum CodingKeys: String, CodingKey {
        case moleculeName
        case accessibilityIdentifier
        case checked
        case enabled
        case readOnly
        case inverted
        case animated
        case round
        case borderWidth
        case borderColor
        case checkColor
        case invertedColor
        case invertedBackgroundColor
        case unCheckedBackgroundColor
        case checkedBackgroundColor
        case disabledBackgroundColor
        case disabledCheckColor
        case disabledBorderColor
        case action
        case fieldKey
        case groupName
        case offAction
    }
    
    //--------------------------------------------------
    // MARK: - Form Validation
    //--------------------------------------------------
    
    public func formFieldValue() -> AnyHashable? {
        guard enabled else { return nil }
        return selected
    }
    
    //--------------------------------------------------
    // MARK: - Initializer
    //--------------------------------------------------
    
    public init(isChecked: Bool = false) {
        self.selected = isChecked
        baseValue = isChecked
    }
    
    //--------------------------------------------------
    // MARK: - Codec
    //--------------------------------------------------
    
    required public init(from decoder: Decoder) throws {
        let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
        
        accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
        
        if let borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) {
            self.borderWidth = borderWidth
        }
        
        if let borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) {
            self.borderColor = borderColor
        }
        
        if let checkColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkColor) {
            self.checkColor = checkColor
        }
        
        if let unCheckedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .unCheckedBackgroundColor) {
            self.unCheckedBackgroundColor = unCheckedBackgroundColor
        }
        
        if let checkedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkedBackgroundColor) {
            self.checkedBackgroundColor = checkedBackgroundColor
        }
        
        if let disabledBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBackgroundColor) {
            self.disabledBackgroundColor = disabledBackgroundColor
        }
        
        if let disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) {
            self.disabledBorderColor = disabledBorderColor
        }
        
        if let disabledCheckColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledCheckColor) {
            self.disabledCheckColor = disabledCheckColor
        }
        
        if let invertedColor = try typeContainer.decodeIfPresent(Color.self, forKey: .invertedColor) {
            self.invertedColor = invertedColor
        }
        
        if let invertedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .invertedBackgroundColor) {
            self.invertedBackgroundColor = invertedBackgroundColor
        }
        
        if let checked = try typeContainer.decodeIfPresent(Bool.self, forKey: .checked) {
            self.selected = checked
        }
        
        baseValue = selected
        
        if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) {
            self.animated = animated
        }
        
        if let round = try typeContainer.decodeIfPresent(Bool.self, forKey: .round) {
            self.round = round
        }
        
        if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) {
            self.inverted = inverted
        }
        
        enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true
        readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false
        action = try typeContainer.decodeModelIfPresent(codingKey: .action)
        fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
        
        if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
             self.groupName = groupName
        }
        offAction = try typeContainer.decodeModelIfPresent(codingKey: .offAction)
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(moleculeName, forKey: .moleculeName)
        try container.encodeIfPresent(groupName, forKey: .groupName)
        try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
        try container.encodeIfPresent(borderColor, forKey: .borderColor)
        try container.encode(borderWidth, forKey: .borderWidth)
        try container.encode(selected, forKey: .checked)
        try container.encode(inverted, forKey: .inverted)
        try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
        try container.encodeIfPresent(checkColor, forKey: .checkColor)
        try container.encodeIfPresent(invertedColor, forKey: .invertedColor)
        try container.encodeIfPresent(invertedBackgroundColor, forKey: .invertedBackgroundColor)
        try container.encodeIfPresent(unCheckedBackgroundColor, forKey: .unCheckedBackgroundColor)
        try container.encodeIfPresent(checkedBackgroundColor, forKey: .checkedBackgroundColor)
        try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor)
        try container.encodeIfPresent(disabledBackgroundColor, forKey: .disabledBackgroundColor)
        try container.encodeIfPresent(disabledCheckColor, forKey: .disabledCheckColor)
        try container.encodeIfPresent(animated, forKey: .animated)
        try container.encodeIfPresent(round, forKey: .round)
        try container.encode(enabled, forKey: .enabled)
        try container.encode(readOnly, forKey: .readOnly)
        try container.encodeModelIfPresent(action, forKey: .action)
        try container.encodeIfPresent(groupName, forKey: .groupName)
        try container.encodeModelIfPresent(offAction, forKey: .offAction)
    }
}
