//
//  DataProviding.swift
//  GenericsDataSource
//
//  Created by Frank Courville on 2019-05-09.
//  Copyright © 2019 iOS Coach Frank. All rights reserved.
//

import UIKit

struct DataProvider<ItemStore, Item> {
    var numberOfSections: (ItemStore) -> Int
    var numberOfItemsInSection: (ItemStore, Int) -> Int
    var itemForIndexPath: (ItemStore, IndexPath) -> Item
    var indexPathForItem: (ItemStore, Item) -> IndexPath?
}

extension DataProvider where ItemStore == [Item], Item: Equatable {
    static var singleSection: DataProvider {
        return DataProvider(
            numberOfSections: { _ in return 1 },
            numberOfItemsInSection: { data, _ in data.count },
            itemForIndexPath: { data, indexPath in data[indexPath.row] },
            indexPathForItem: { data, item in
                guard let index = data.firstIndex(of: item) else {
                    return nil
                }
                
                return IndexPath(row: 0, section: index)
            }
        )
    }
}

struct TableDataPresenter<Item> {
    let registerTableView: (UITableView) -> Void
    let cellForRow: (Item, UITableView, IndexPath) -> UITableViewCell
}

struct Fetchable<ItemStore> {
    let fetch: ((Result<ItemStore, Error>) -> Void) -> ()
}

class TableViewAdapter<ItemStore, Item>: NSObject, UITableViewDataSource {
    var store: ItemStore?
    
    let dataProvider: DataProvider<ItemStore, Item>
    let dataPresenter: TableDataPresenter<Item>
    let fetchable: Fetchable<ItemStore>
    
    init(dataProvider: DataProvider<ItemStore, Item>, dataPresenter: TableDataPresenter<Item>, data: Input) {
        self.fetchable = Fetchable.init(fetch: { completion in completion(.success(data)) })
        self.dataProvider = dataProvider
        self.dataPresenter = dataPresenter
    }
    
    init(dataProvider: DataProvider<ItemStore, Item>, dataPresenter: TableDataPresenter<Item>, fetchable: Fetchable<ItemStore>) {
        self.fetchable = fetchable
        self.dataProvider = dataProvider
        self.dataPresenter = dataPresenter
    }
    
    public func loadData(completion: () -> Void) {
        fetchable.fetch({ result in
            switch result {
            case .success(let storage): self.store = store
            case .failure(_): break
            }
            
            completion()
        })
    }
    
    public func register(tableView: UITableView) {
        dataPresenter.registerTableView(tableView)
    }
    
    public func item(atIndexPath indexPath: IndexPath) -> Item? {
        guard let store = store else { return nil }

        return dataProvider.itemForIndexPath(store, indexPath)
    }
    
    public func indexPath(forItem item: Item) -> IndexPath? {
        guard let store = store else { return nil }

        return dataProvider.indexPathForItem(store, item)
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        guard let store = store else { return 0 }
        
        return dataProvider.numberOfSections(store)
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard let store = store else { return 0 }

        return dataProvider.numberOfItemsInSection(store, section)
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let store = store else { fatalError("We don't have any data yet") }

        let item = dataProvider.itemForIndexPath(store, indexPath)
        let cell = dataPresenter.cellForRow(item, tableView, indexPath)
        
        return cell
    }
}