//
//  GMFetchViewModel.swift
//  test
//
//  Created by wangyang on 16/3/16.
//  Copyright © 2016年 北京更美互动信息科技有限公司. All rights reserved.
//

import Foundation
import GM_Swift_Observable
import GMNetworking

public extension NSDictionary {
    /// 适用于api返回的json快速取出data下的字典
    public var dic: NSDictionary {
        return self["data"] as! NSDictionary
    }

    /// 适用于api返回的json快速取出data下的数组
    public var array: [NSDictionary] {
        return self["data"] as! [NSDictionary]
    }
}

@available(*, deprecated, message: "NetworkResult闭包不在使用，考虑重构使用该闭包的方法为更简洁的方式")
public typealias NetworkResult = (_ result: Bool, _ message: String) -> Void
@available(*, deprecated, message: "NetworkSuccess闭包不在使用，考虑重构使用该闭包的方法为更简洁的方式")
public typealias NetworkSuccess = (_ responseObject: [String: Any], _ message: String) -> Void
@available(*, deprecated, message: "NetworkFailed闭包不在使用，考虑重构使用该闭包的方法为更简洁的方式")
public typealias NetworkFailed = (_ message: String) -> Void
@available(*, deprecated, message: "NetworkEmpty闭包不在使用，考虑重构使用该闭包的方法为更简洁的方式")
public typealias NetworkEmpty = (_ errorCode: Int, _ message: String) -> Void

open class GMFetchViewModel<T>: ViewModelProtocol {

    open var dataArray = [T]()
    open var fetchDataResult: Observable<Int> = Observable(-1)
    open var url = ""
    open var startNum = 0
    open var parameters = [String: Any]()

    open var message = ""
    public var response: GMResponse<Any>!

    /// 去重被去掉的个数
    open var deleteCount = 0
    /**
     *  @author wangyang, 15-11-23 15:11:30
     *
     *  @brief  去重字典。保存那些已经加载过的数据的 key，value 都为 YES。详见 _deduplicationKey
     因为服务器返回的数据有可能是重复的，所以添加一个字典，新添加的数据的唯一标识放在这里，追加的数据要从这里检查，如果存在，就不添加
     *
     *  @since 5.5.0
     */
    open var deduplicationDic = [AnyHashable: Any]()

    /**
     *  @author wangyang, 15-11-23 15:11:16
     *
     *  @brief  去重时需要用到的实体属性 key。例如帖子列表页使用 id 属性来去重，那么 _deduplicationKey = @"id"
     *  @note   默认为 nil，如果不为 nil，就会启动去重功能
     *  @since 5.5.0
     */
    open var deduplicationKey = ""

    public required init() {
        parameters = ["start_num": startNum, "count": 10 ]
    }

    open func buildParameters() {
        parameters["start_num"] = startNum
    }

    open func fetchRemoteData() {
        if self.url.isEmpty {
            return
        }
        buildParameters()
        GMNetworking.request(url, .get, parameters) { [weak self] (response: GMResponse<Any>) in
            guard let safeSelf = self else { return }
            safeSelf.response = response
            
            if !response.isSuccess {
                safeSelf.message = "哎呀！出错了！"
                safeSelf.fetchDataResult.value = 2
                return
            }

            guard let json = (try? JSONSerialization.jsonObject(with: response.responseData!, options: .allowFragments)) as? NSDictionary else {
                return
            }

            if safeSelf.shouldClearDataForResponse(json) {
                safeSelf.clearData()
            }

            safeSelf.message = response.message

            if response.error == 0 {
                safeSelf.build(json)
            }
            safeSelf.fetchDataResult.value = response.error
        }
    }

    open func build(_ response: NSDictionary) {}
    open func shouldClearDataForResponse(_ response: NSDictionary) -> Bool {
        return startNum == 0
    }

    open func clearData() {
        startNum = 0
        dataArray.removeAll()
        deduplicationDic.removeAll()
    }

    open func isDataArrayEmpty() -> Bool {
        return dataArray.count == 0
    }

    open func willRefresh() {
        startNum = 0
    }

    open func willLoadMore() {
        startNum = dataArray.count + deleteCount
    }

    /**
     给viewModel加下标访问的好处就是可以使用泛型

     - parameter indexPath

     - returns: 泛型
     */
    open subscript(indexPath: IndexPath) -> T {
        // 这种写法可以用来试探dataArray是否是二维数组，是的话，返回indexPath对应的object
        if let first = dataArray[indexPath.section] as? [AnyObject] {
            return first[indexPath.row] as! T
        }
        // 上面的代码没有成功的话，说明是一维数组
        assert(dataArray.count >= indexPath.row, "indexPath.row(\(indexPath.row))不能大于dataArray.count(\(dataArray.count))")
        return dataArray[indexPath.row]
    }

    open func deduplicate<T>(_ array: [[String: Any]], buildModelBlock: (NSDictionary) -> (T)) -> [T] {

        if deduplicationKey.isEmpty {
            return [T]()
        }
        var result = [T]()
        for obj in array {

            // 因为value会作为 deduplicationDic 的key，所以必须符合 Hashable 协议。
            // 下面使用 NSObject 原因有两个。第一：value是从json解析来的，所以肯定是某个 NSObject；第二：NSObject 是符合 Hashable 协议的
            guard let value = obj[deduplicationKey] as? NSObject else {
                assert(false, "待去重的value不可以为空")
                return [T]()
            }

            if deduplicationDic[value] == nil {
                let object = buildModelBlock(obj as NSDictionary)
                result.append(object)
                deduplicationDic[value] = true
            } else {
                deleteCount += 1
            }
        }
        return result
    }
}

/// 在ListProtocol中起重要作用
public protocol ViewModelProtocol {

    var url: String { get set }
    var startNum: Int { get set }
    var message: String { get set }
    var fetchDataResult: Observable<Int> { get set }

    /**
     协议中的构造器要求，必须实现init方法
     */
    init()
    
    func clearData()
    func isDataArrayEmpty() -> Bool
    func fetchRemoteData()
    func willRefresh()
    func willLoadMore()
}
