<commit_before>//
//  SBAProfileSection.swift
//  BridgeAppSDK
//
//  Copyright © 2017 <NAME>. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1.  Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2.  Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// 3.  Neither the name of the copyright holder(s) nor the names of any contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission. No license is granted to the trademarks of
// the copyright holders even if such marks are included in this software.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

import Foundation
import ResearchUXFactory

@objc
public protocol SBAProfileSection: NSObjectProtocol {
    var title: String? { get }
    var icon: String? { get }
    var items: [SBAProfileTableItem] { get }
}

@objc
public protocol SBAProfileTableItem: NSObjectProtocol {
    var title: String { get }
    var detail: String? { get }
    var isEditable: Bool { get }
    var onSelected: SBAProfileOnSelectedAction { get }
}

open class SBAProfileSectionObject: SBADataObject, SBAProfileSection {
    open dynamic var title: String?
    open dynamic var icon: String?
    open dynamic var items: [SBAProfileTableItem] = []
    
    // MARK: SBADataObject overrides
    
    override open func dictionaryRepresentationKeys() -> [String] {
        return super.dictionaryRepresentationKeys().appending(contentsOf: [#keyPath(title), #keyPath(icon), #keyPath(items)])
    }

    override open func defaultValue(forKey key: String) -> Any? {
        if key == #keyPath(items) {
            return [SBAProfileTableItem]()
        } else {
            return super.defaultValue(forKey: key)
        }
    }
}

@objc
open class SBAProfileTableItemBase: NSObject, SBAProfileTableItem {
    public let sourceDict: [AnyHashable: Any]
    open var defaultOnSelectedAction = SBAProfileOnSelectedAction.noAction

    public required init(dictionaryRepresentation dictionary: [AnyHashable: Any]) {
        sourceDict = dictionary
        super.init()
    }

    open var title: String {
        get {
            let key = #keyPath(title)
            return sourceDict[key] as? String ?? ""
        }
    }
    
    open var detail: String? {
        get {
            let key = #keyPath(detail)
            return sourceDict[key] as? String
        }
    }
    
    open var isEditable: Bool {
        get {
            let key = #keyPath(isEditable)
            return sourceDict[key] as? Bool ?? false
        }
    }
    
    open var onSelected: SBAProfileOnSelectedAction {
        get {
            let key = #keyPath(onSelected)
            guard let rawValue = sourceDict[key] as? String else { return defaultOnSelectedAction }
            return SBAProfileOnSelectedAction(rawValue: rawValue) 
        }
    }
}

open class SBAHTMLProfileTableItem: SBAProfileTableItemBase {
    public required init(dictionaryRepresentation dictionary: [AnyHashable: Any]) {
        super.init(dictionaryRepresentation: dictionary)
        defaultOnSelectedAction = .showHTML
    }
    
    open var htmlResource: String {
        get {
            let key = #keyPath(htmlResource)
            return sourceDict[key]! as! String
        }
    }
    
    open var html: String? {
        return SBAResourceFinder.shared.html(forResource: htmlResource)
    }
    
    open var url: URL? {
        if htmlResource.hasPrefix("http") || htmlResource.hasPrefix("file") {
            return URL(string: htmlResource)
        }
        else {
            return SBAResourceFinder.shared.url(forResource: htmlResource, withExtension:"html")
        }
    }
    
    // HTML profile table items are not editable
    override open var isEditable: Bool {
        get {
            return false
        }
    }
}

open class SBAProfileItemProfileTableItem: SBAProfileTableItemBase {
    public required init(dictionaryRepresentation dictionary: [AnyHashable: Any]) {
        super.init(dictionaryRepresentation: dictionary)
        defaultOnSelectedAction = .editProfileItem
    }
    
    open var profileItemKey: String {
        let key = #keyPath(profileItemKey)
        return sourceDict[key]! as! String
    }
    
    lazy open var profileItem: SBAProfileItem = {
        let profileItems = SBAProfileManager.shared!.profileItems()
        return profileItems[self.profileItemKey]!
    }()

    override open var detail: String? {
        guard let value = profileItem.value else { return "" }
        if let surveyItem = SBASurveyFactory.profileQuestionSurveyItems?.find(withIdentifier: profileItemKey) as? SBAFormStepSurveyItem {
            if let choices = surveyItem.items as? [SBAChoice] {
                let selected = (value as? [Any]) ?? [value]
                let textList = selected.map({ (obj) -> String in
                    return choices.find({ SBAObjectEquality($0.choiceValue, obj) })?.choiceText ?? String(describing: obj)
                })
                return Localization.localizedJoin(textList: textList)
            }
        }
        return String(describing: value)
    }
}

open class SBAResourceProfileTableItem: SBAProfileTableItemBase {
    public required init(dictionaryRepresentation dictionary: [AnyHashable: Any]) {
        super.init(dictionaryRepresentation: dictionary)
        defaultOnSelectedAction = .showResource
    }
    
    open var resource: String {
        get {
            let key = #keyPath(resource)
            return sourceDict[key]! as! String
        }
    }
    
    // Resource profile table items are not editable
    override open var isEditable: Bool {
        get {
            return false
        }
    }
}
<commit_msg>support for custom answer maps when editing<commit_after>//
//  SBAProfileSection.swift
//  BridgeAppSDK
//
//  Copyright © 2017 <NAME>. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1.  Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2.  Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// 3.  Neither the name of the copyright holder(s) nor the names of any contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission. No license is granted to the trademarks of
// the copyright holders even if such marks are included in this software.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

import Foundation
import ResearchUXFactory

@objc
public protocol SBAProfileSection: NSObjectProtocol {
    var title: String? { get }
    var icon: String? { get }
    var items: [SBAProfileTableItem] { get }
}

@objc
public protocol SBAProfileTableItem: NSObjectProtocol {
    var title: String { get }
    var detail: String? { get }
    var isEditable: Bool { get }
    var onSelected: SBAProfileOnSelectedAction { get }
}

open class SBAProfileSectionObject: SBADataObject, SBAProfileSection {
    open dynamic var title: String?
    open dynamic var icon: String?
    open dynamic var items: [SBAProfileTableItem] = []
    
    // MARK: SBADataObject overrides
    
    override open func dictionaryRepresentationKeys() -> [String] {
        return super.dictionaryRepresentationKeys().appending(contentsOf: [#keyPath(title), #keyPath(icon), #keyPath(items)])
    }

    override open func defaultValue(forKey key: String) -> Any? {
        if key == #keyPath(items) {
            return [SBAProfileTableItem]()
        } else {
            return super.defaultValue(forKey: key)
        }
    }
}

@objc
open class SBAProfileTableItemBase: NSObject, SBAProfileTableItem {
    public let sourceDict: [AnyHashable: Any]
    open var defaultOnSelectedAction = SBAProfileOnSelectedAction.noAction

    public required init(dictionaryRepresentation dictionary: [AnyHashable: Any]) {
        sourceDict = dictionary
        super.init()
    }

    open var title: String {
        get {
            let key = #keyPath(title)
            return sourceDict[key] as? String ?? ""
        }
    }
    
    open var detail: String? {
        get {
            let key = #keyPath(detail)
            return sourceDict[key] as? String
        }
    }
    
    open var isEditable: Bool {
        get {
            let key = #keyPath(isEditable)
            return sourceDict[key] as? Bool ?? false
        }
    }
    
    open var onSelected: SBAProfileOnSelectedAction {
        get {
            let key = #keyPath(onSelected)
            guard let rawValue = sourceDict[key] as? String else { return defaultOnSelectedAction }
            return SBAProfileOnSelectedAction(rawValue: rawValue) 
        }
    }
}

open class SBAHTMLProfileTableItem: SBAProfileTableItemBase {
    public required init(dictionaryRepresentation dictionary: [AnyHashable: Any]) {
        super.init(dictionaryRepresentation: dictionary)
        defaultOnSelectedAction = .showHTML
    }
    
    open var htmlResource: String {
        get {
            let key = #keyPath(htmlResource)
            return sourceDict[key]! as! String
        }
    }
    
    open var html: String? {
        return SBAResourceFinder.shared.html(forResource: htmlResource)
    }
    
    open var url: URL? {
        if htmlResource.hasPrefix("http") || htmlResource.hasPrefix("file") {
            return URL(string: htmlResource)
        }
        else {
            return SBAResourceFinder.shared.url(forResource: htmlResource, withExtension:"html")
        }
    }
    
    // HTML profile table items are not editable
    override open var isEditable: Bool {
        get {
            return false
        }
    }
}

open class SBAProfileItemProfileTableItem: SBAProfileTableItemBase {
    public required init(dictionaryRepresentation dictionary: [AnyHashable: Any]) {
        super.init(dictionaryRepresentation: dictionary)
        defaultOnSelectedAction = .editProfileItem
    }
    
    open var profileItemKey: String {
        let key = #keyPath(profileItemKey)
        return sourceDict[key]! as! String
    }
    
    lazy open var profileItem: SBAProfileItem = {
        let profileItems = SBAProfileManager.shared!.profileItems()
        return profileItems[self.profileItemKey]!
    }()

    override open var detail: String? {
        guard let value = profileItem.value else { return "" }
        if let surveyItem = SBASurveyFactory.profileQuestionSurveyItems?.find(withIdentifier: profileItemKey) as? SBAFormStepSurveyItem {
            if let choices = surveyItem.items as? [SBAChoice] {
                let selected = (value as? [Any]) ?? [value]
                let textList = selected.map({ (obj) -> String in
                    return choices.find({ SBAObjectEquality($0.choiceValue, obj) })?.choiceText ?? String(describing: obj)
                })
                return Localization.localizedJoin(textList: textList)
            }
        }
        return String(describing: value)
    }
    
    open var answerMapKeys: [String: String] {
        let key = #keyPath(answerMapKeys)
        return sourceDict[key] as? [String: String] ?? [self.profileItemKey: self.profileItemKey]
    }
}

open class SBAResourceProfileTableItem: SBAProfileTableItemBase {
    public required init(dictionaryRepresentation dictionary: [AnyHashable: Any]) {
        super.init(dictionaryRepresentation: dictionary)
        defaultOnSelectedAction = .showResource
    }
    
    open var resource: String {
        get {
            let key = #keyPath(resource)
            return sourceDict[key]! as! String
        }
    }
    
    // Resource profile table items are not editable
    override open var isEditable: Bool {
        get {
            return false
        }
    }
}

{'fraction_non_alphanumeric': 0.07851776943080034, 'fraction_numerical': 0.0011557353366079169, 'mean_word_length': 2.691015729138896, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5307249', 'n_tokens_mistral': 3750, 'n_tokens_neox': 3589, 'n_words': 1233}