//
//  Attribute.swift
//  SwifSoup
//
//  Created by Nabil Chatbi on 29/09/16.
//  Copyright © 2016 Nabil Chatbi.. All rights reserved.
//

import Foundation

open class Attribute {
    /// The element type of a dictionary: a tuple containing an individual
    /// key-value pair.

    static let booleanAttributes: [String] = [
        "allowfullscreen", "async", "autofocus", "checked", "compact", "declare", "default", "defer", "disabled",
        "formnovalidate", "hidden", "inert", "ismap", "itemscope", "multiple", "muted", "nohref", "noresize",
        "noshade", "novalidate", "nowrap", "open", "readonly", "required", "reversed", "seamless", "selected",
        "sortable", "truespeed", "typemustmatch"
    ]

    var key: String
    var value: String

    public init(key: String, value: String) throws {
        try Validate.notEmpty(string: key)
        self.key = key.trim()
        self.value = value
    }

    /**
     Get the attribute key.
     @return the attribute key
     */
    open func getKey() -> String {
        return key
    }

    /**
     Set the attribute key; case is preserved.
     @param key the new key; must not be null
     */
    open func setKey(key: String) throws {
        try Validate.notEmpty(string: key)
        self.key = key.trim()
    }

    /**
     Get the attribute value.
     @return the attribute value
     */
    open func getValue() -> String {
        return value
    }

    /**
     Set the attribute value.
     @param value the new attribute value; must not be null
     */
    @discardableResult
    open func setValue(value: String) -> String {
        let old = self.value
        self.value = value
        return old
    }

    /**
     Get the HTML representation of this attribute; e.g. {@code href="index.html"}.
     @return HTML
     */
    public func html() -> String {
        let accum =  StringBuilder()
		html(accum: accum, out: (Document("")).outputSettings())
        return accum.toString()
    }

    public func html(accum: StringBuilder, out: OutputSettings ) {
        accum.append(key)
        if (!shouldCollapseAttribute(out: out)) {
            accum.append("=\"")
            Entities.escape(accum, value, out, true, false, false)
            accum.append("\"")
        }
    }

    /**
     Get the string representation of this attribute, implemented as {@link #html()}.
     @return string
     */
    open func toString() -> String {
        return html()
    }

    /**
     * Create a new Attribute from an unencoded key and a HTML attribute encoded value.
     * @param unencodedKey assumes the key is not encoded, as can be only run of simple \w chars.
     * @param encodedValue HTML attribute encoded value
     * @return attribute
     */
    public static func createFromEncoded(unencodedKey: String, encodedValue: String) throws ->Attribute {
        let value = try Entities.unescape(string: encodedValue, strict: true)
        return try Attribute(key: unencodedKey, value: value)
    }

    public func isDataAttribute() -> Bool {
        return key.startsWith(Attributes.dataPrefix) && key.count > Attributes.dataPrefix.count
    }

    /**
     * Collapsible if it's a boolean attribute and value is empty or same as name
     *
     * @param out Outputsettings
     * @return  Returns whether collapsible or not
     */
    public final func shouldCollapseAttribute(out: OutputSettings) -> Bool {
        return ("" == value  || value.equalsIgnoreCase(string: key))
            && out.syntax() == OutputSettings.Syntax.html
            && isBooleanAttribute()
    }

    public func isBooleanAttribute() -> Bool {
        return Attribute.booleanAttributes.contains(key)
    }

    public func hashCode() -> Int {
        var result = key.hashValue
        result = 31 * result + value.hashValue
        return result
    }

    public func clone() -> Attribute {
        do {
            return try Attribute(key: key, value: value)
        } catch Exception.Error( _, let  msg) {
            print(msg)
        } catch {

        }
        return try! Attribute(key: "", value: "")
    }
}

extension Attribute: Equatable {
	static public func == (lhs: Attribute, rhs: Attribute) -> Bool {
		return lhs.value == rhs.value && lhs.key == rhs.key
	}

}
