import Foundation

/// main class that parses the server response into its underlying object's variables
class BaseResult: NSObject, XMLParserDelegate {
    public var isArray: Bool { false }
    public var instantiate: (() -> AnyObject)? { nil }
    private var baseArray: Array<Any> = []
    private var elementName: String = ""
    private var errorText: String = ""
    private var completion: (BaseResult) -> Void
    
    init(completion: @escaping (BaseResult) -> Void) {
        self.completion = completion
    }
    
    /// delelegate function that gets called when parser encounters an starting element
    /// - Parameters:
    ///   - parser: XMLParser
    ///   - elementName: element name
    ///   - namespaceURI: namespace
    ///   - qName: qualified name
    ///   - attributeDict: attribute dictionary
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        if isArray {
            self.elementName = elementName
            let property = Mirror(reflecting: self).children.compactMap { $0.label }.first
            if elementName == property {
                setValue(instantiate!(), forKey: "item")
            }
        } else {
            self.elementName = elementName
        }
    }
    
    /// delelegate function that gets called when parser encounters an ending element
    /// - Parameters:
    ///   - parser: XMLParser
    ///   - elementName: element name
    ///   - namespaceURI: namespace
    ///   - qName: qualified name
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName.contains("soap:Text") {
            if (errorText.contains("odhlášen")) {
                Auth.shared.logOut()
            } else {
                errorText = ""
            }
        }
        if isArray {
            let property = Mirror(reflecting: self).children.compactMap { $0.label }.first
            if elementName == "\(type(of: self))" {
                setValue(baseArray, forKeyPath: property!)
                completion(self)
                return
            } else if elementName == property {
                baseArray.append(value(forKeyPath: "item")!)
            }
        } else {
            if elementName == "\(type(of: self))" {
                completion(self)
            }
        }
    }
    
    /// delegate function that gets called when parser encounters an value for element
    /// - Parameters:
    ///   - parser: XMLParser
    ///   - string: found character
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        let data = string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        
        if (!data.isEmpty) {
            if isArray {
                let properties = Mirror(reflecting: value(forKeyPath: "item")!).children.compactMap { $0.label }
                if properties.contains(elementName) {
                    var val = value(forKeyPath: "item.\(elementName)") as! String
                    val.append(contentsOf: data)
                    setValue(val, forKeyPath: "item.\(elementName)")
                }
            } else {
                let properties = Mirror(reflecting: self).children.compactMap { $0.label }
                if properties.contains(elementName) {
                    var val = value(forKeyPath: elementName) as! String
                    val.append(contentsOf: data)
                    setValue(val, forKey: elementName)
                }
            }
            if elementName.contains("soap:Text") {
                errorText.append(data)
            }
        }
    }
}

class UserDetailResult: BaseResult {
    @objc var ID: String = ""
    @objc var ID_Person: String = ""
    @objc var Person: String = ""
}

@objc class Role: NSObject {
    @objc var ID_Unit: String = ""
    @objc var IsActive: String = ""
}

class UserRoleAllResult: BaseResult {
    @objc var UserRoleAllOutput: [Role] = []
    @objc var item: Role = Role()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Role() } }
}

@objc class Person: NSObject {
    @objc var ID: String = ""
    @objc var DisplayName: String = ""
    @objc var Phone: String = ""
    @objc var Email: String = ""
}

class PersonAllResult: BaseResult {
    @objc var PersonAllOutput: [Person] = []
    @objc var item: Person = Person()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Person() } }
}

class PersonAllCatalogResult: BaseResult {
    @objc var PersonAllCatalogOutput: [Person] = []
    @objc var item: Person = Person()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Person() } }
}

@objc class Contact: NSObject {
    @objc var ID: String = ""
    @objc var ContactType: String = ""
    @objc var Value: String = ""
    @objc var IsMain: String = ""
}

class PersonContactAllResult: BaseResult {
    @objc var PersonContactAllOutput: [Contact] = []
    @objc var item: Contact = Contact()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Contact() } }
}

class PersonDetailResult: BaseResult {
    @objc var ID: String = ""
    @objc var FullName: String = ""
    @objc var Email: String = ""
    @objc var Phone: String = ""
    @objc var Street: String = ""
    @objc var City: String = ""
    @objc var Postcode: String = ""
    @objc var Address: String = ""
}

@objc class Qualification: NSObject {
    @objc var ID: String = ""
    @objc var QualificationType: String = ""
}

class QualificationAllResult: BaseResult {
    @objc var QualificationAllOutput: [Qualification] = []
    @objc var item: Qualification = Qualification()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Qualification() } }
}

@objc class Function: NSObject {
    @objc var ID: String = ""
    @objc var Unit: String = ""
    @objc var FunctionType: String = ""
}

class FunctionAllPersonResult: BaseResult {
    @objc var FunctionAllOutput: [Function] = []
    @objc var item: Function = Function()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Function() } }
}

@objc class Membership: NSObject {
    @objc var ID: String = ""
    @objc var Unit: String = ""
    @objc var MembershipType: String = ""
    @objc var Person: String = ""
    @objc var ID_Person: String = ""
}

class MembershipAllPersonResult: BaseResult {
    @objc var MembershipAllOutput: [Membership] = []
    @objc var item: Membership = Membership()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Membership() } }
}

class MembershipAllResult: BaseResult {
    @objc var MembershipAllOutput: [Membership] = []
    @objc var item: Membership = Membership()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Membership() } }
}

@objc class Offer: NSObject {
    @objc var ID: String = ""
    @objc var OfferType: String = ""
}

class OfferAllResult: BaseResult {
    @objc var OfferAllOutput: [Offer] = []
    @objc var item: Offer = Offer()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { Offer() } }
}

class PersonPhotoResult: BaseResult {
    @objc var ID: String = ""
    @objc var PhotoContent: String = ""
}

class UnitDetailResult: BaseResult {
    @objc var ID: String = ""
    @objc var ID_UnitParent: String = ""
    @objc var DisplayName: String = ""
    @objc var Street: String = ""
    @objc var City: String = ""
    @objc var Postcode: String = ""
    @objc var PostalFirstLine: String = ""
    @objc var PostalStreet: String = ""
    @objc var PostalCity: String = ""
    @objc var PostalPostcode: String = ""
}

@objc class UnitContact: NSObject {
    @objc var ID: String = ""
    @objc var ContactType: String = ""
    @objc var Value: String = ""
    @objc var Publish: String = ""
}

class UnitContactAllResult: BaseResult {
    @objc var UnitContactAllOutput: [UnitContact] = []
    @objc var item: UnitContact = UnitContact()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { UnitContact() } }
}

@objc class FunctionUnit: NSObject {
    @objc var ID: String = ""
    @objc var FunctionType: String = ""
    @objc var Person: String = ""
    @objc var ID_FunctionType: String = ""
    @objc var ID_Person: String = ""
}

class FunctionAllResult: BaseResult {
    @objc var FunctionAllOutput: [FunctionUnit] = []
    @objc var item: FunctionUnit = FunctionUnit()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { FunctionUnit() } }
}

@objc class UnitTree: NSObject {
    @objc var ID: String = ""
    @objc var ID_Unit: String = ""
    @objc var Unit: String = ""
    @objc var RegistrationNumber: String = ""
}

class UnitTreeAllResult: BaseResult {
    @objc var UnitTreeAllOutput: [UnitTree] = []
    @objc var item: UnitTree = UnitTree()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { UnitTree() } }
}

@objc class UnitRegistration: NSObject {
    @objc var ID: String = ""
    @objc var Year: String = ""
}

class UnitRegistrationAllResult: BaseResult {
    @objc var UnitRegistrationAllOutput: [UnitRegistration] = []
    @objc var item: UnitRegistration = UnitRegistration()
    override var isArray: Bool  { true }
    override var instantiate: (() -> AnyObject)? { { UnitRegistration() } }
}

@objc class Unit: NSObject {
    @objc var UnitType: String = ""
    @objc var Count: String = ""
}

@objc class MemberAge: NSObject {
    @objc var DisplayName: String = ""
    @objc var AgeCategory: String = ""
    @objc var Count: String = ""
    @objc var Row: String = ""
}

@objc class MemberCategory: NSObject {
    @objc var DisplayName: String = ""
    @objc var MembershipCategory: String = ""
    @objc var Count: String = ""
    @objc var Row: String = ""
}

@objc class LoginUpdateRefreshResult: BaseResult {
    @objc var DateLogout: String = ""
}


/// class that parses a registration data into object's variables
class UnitRegistrationMembersResult: NSObject, XMLParserDelegate {
    @objc var Units: [Unit] = []
    private var unit: Unit
    @objc var MembersAge: [MemberAge] = []
    private var memberAge: MemberAge
    @objc var MembersCategory: [MemberCategory] = []
    private var memberCategory: MemberCategory
    private var completion: (UnitRegistrationMembersResult) -> Void
    private var elementName: String = ""
    private var unitName: String = ""
    
    init(completion: @escaping (UnitRegistrationMembersResult) -> Void) {
        self.completion = completion
        self.unit = Unit()
        self.memberAge = MemberAge()
        self.memberCategory = MemberCategory()
    }
    
    /// delelegate function that gets called when parser encounters an starting element
    /// - Parameters:
    ///   - parser: XMLParser
    ///   - elementName: element name
    ///   - namespaceURI: namespace
    ///   - qName: qualified name
    ///   - attributeDict: attribute dictionary
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        if elementName == "UnitRegistrationMembersUnits" {
            unit = Unit()
            unitName = elementName
        }
        if elementName == "UnitRegistrationMembersAge" {
            memberAge = MemberAge()
            unitName = elementName
        }
        if elementName == "UnitRegistrationMembersCategory" {
            memberCategory = MemberCategory()
            unitName = elementName
        }
        self.elementName = elementName
    }

    /// delelegate function that gets called when parser encounters an ending element
    /// - Parameters:
    ///   - parser: XMLParser
    ///   - elementName: element name
    ///   - namespaceURI: namespace
    ///   - qName: qualified name
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName == "UnitRegistrationMembersUnits" {
            Units.append(self.unit)
        }
        if elementName == "UnitRegistrationMembersAge" {
            MembersAge.append(self.memberAge)
        }
        if elementName == "UnitRegistrationMembersCategory" {
            MembersCategory.append(self.memberCategory)
        }
        if elementName == "UnitRegistrationMembersResult" {
            completion(self)
        }
    }

    /// delegate function that gets called when parser encounters an value for element
    /// - Parameters:
    ///   - parser: XMLParser
    ///   - string: found character
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        let data = string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)

        var object: AnyObject?
        switch unitName {
        case "UnitRegistrationMembersUnits":
            object = unit
        case "UnitRegistrationMembersAge":
            object = memberAge
        case "UnitRegistrationMembersCategory":
            object = memberCategory
        default:
            break
        }
        if (!data.isEmpty) {
            let properties = Mirror(reflecting: object!).children.compactMap { $0.label }
            if properties.contains(elementName) {
                var val = object?.value(forKeyPath: elementName) as! String
                val.append(contentsOf: data)
                object?.setValue(val, forKey: elementName)
            }
        }
    }
}


