//
//  AppIconShortcutsViewController.swift
//  Delta
//
//  Created by Riley Testut on 12/19/17.
//  Copyright © 2017 Riley Testut. All rights reserved.
//

import UIKit
import AVFoundation

import DeltaCore

import Roxas

@objc(SwitchTableViewCell)
private class SwitchTableViewCell: UITableViewCell
{
    @IBOutlet var switchView: UISwitch!
}

class AppIconShortcutsViewController: UITableViewController
{
    private lazy var dataSource = RSTCompositeTableViewPrefetchingDataSource<Game, UIImage>(dataSources: [self.modeDataSource, self.shortcutsDataSource, self.gamesDataSource])
    private let modeDataSource = RSTDynamicTableViewDataSource<Game>()
    private let shortcutsDataSource = RSTArrayTableViewPrefetchingDataSource<Game, UIImage>(items: [])
    private let gamesDataSource = RSTFetchedResultsTableViewPrefetchingDataSource<Game, UIImage>(fetchedResultsController: NSFetchedResultsController())
    
    required init?(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)
        
        self.prepareDataSource()
    }
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        self.tableView.register(GameTableViewCell.nib!, forCellReuseIdentifier: RSTCellContentGenericCellIdentifier)
        
        self.navigationItem.searchController = self.gamesDataSource.searchController
        self.navigationItem.hidesSearchBarWhenScrolling = false
        
        self.tableView.dataSource = self.dataSource
        self.tableView.prefetchDataSource = self.dataSource
        self.tableView.allowsSelectionDuringEditing = true
        
        self.updateShortcuts()
    }
}

private extension AppIconShortcutsViewController
{
    func prepareDataSource()
    {
        // Mode
        self.modeDataSource.numberOfSectionsHandler = { 1 }
        self.modeDataSource.numberOfItemsHandler = { [weak self] _ in (self?.gamesDataSource.itemCount ?? 0) > 0 ? 1 : 0 }
        self.modeDataSource.cellIdentifierHandler = { _ in "SwitchCell" }
        
        // Shortcuts
        self.shortcutsDataSource.items = Settings.gameShortcuts
        
        // Games
        let gamesFetchRequest: NSFetchRequest<Game> = Game.fetchRequest()
        gamesFetchRequest.returnsObjectsAsFaults = false
        gamesFetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Game.type, ascending: true), NSSortDescriptor(key: #keyPath(Game.name), ascending: true)]
        
        let gamesFetchedResultsController = NSFetchedResultsController(fetchRequest: gamesFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: #keyPath(Game.type), cacheName: nil)
        self.gamesDataSource.fetchedResultsController = gamesFetchedResultsController
        self.gamesDataSource.searchController.searchableKeyPaths = [#keyPath(Game.name)]
        
        // Data Source
        self.dataSource.proxy = self
        self.dataSource.cellConfigurationHandler = { [unowned self] (cell, game, indexPath) in
            if indexPath.section == 0
            {
                self.configureModeCell(cell as! SwitchTableViewCell, for: indexPath)
            }
            else
            {
                self.configureGameCell(cell as! GameTableViewCell, with: game, for: indexPath)
            }
        }
        self.dataSource.prefetchHandler = { (game, indexPath, completionHandler) in
            guard indexPath.section > 0 else { return nil }
            
            guard let artworkURL = game.artworkURL else { return nil }
            
            let imageOperation = LoadImageURLOperation(url: artworkURL)
            imageOperation.resultHandler = { (image, error) in
                completionHandler(image, error)
            }
            
            return imageOperation
        }
        self.dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
            guard indexPath.section > 0 else { return }
            
            guard let image = image else { return }
            
            let cell = cell as! GameTableViewCell
            
            let artworkDisplaySize = AVMakeRect(aspectRatio: image.size, insideRect: cell.artworkImageView.bounds)
            let offset = (cell.artworkImageView.bounds.width - artworkDisplaySize.width) / 2
            
            // Offset artworkImageViewLeadingConstraint and artworkImageViewTrailingConstraint to right-align artworkImageView
            cell.artworkImageViewLeadingConstraint.constant += offset
            cell.artworkImageViewTrailingConstraint.constant -= offset
            
            cell.artworkImageView.image = image
            cell.artworkImageView.superview?.layoutIfNeeded()
        }
        self.dataSource.rowAnimation = .fade
        
        let placeholderView = RSTPlaceholderView()
        placeholderView.textLabel.text = NSLocalizedString("No App Icon Shortcuts", comment: "")
        placeholderView.detailTextLabel.text = NSLocalizedString("You can customize the shortcuts that appear when 3D Touching the app icon once you've added some games.", comment: "")
        self.dataSource.placeholderView = placeholderView
    }
    
    func configureModeCell(_ cell: SwitchTableViewCell, for indexPath: IndexPath)
    {
        cell.textLabel?.text = NSLocalizedString("Recently Played Games", comment: "")
        cell.textLabel?.backgroundColor = .clear
        
        cell.switchView.isOn = (Settings.gameShortcutsMode == .recent)
        cell.switchView.onTintColor = self.view.tintColor
    }
    
    func configureGameCell(_ cell: GameTableViewCell, with game: Game, for indexPath: IndexPath)
    {
        cell.nameLabel.textColor = .darkText
        cell.backgroundColor = .white
        
        cell.nameLabel.text = game.name
        cell.artworkImageView.image = #imageLiteral(resourceName: "BoxArt")
        
        cell.artworkImageViewLeadingConstraint.constant = 15
        cell.artworkImageViewTrailingConstraint.constant = 15
        
        cell.separatorInset.left = cell.nameLabel.frame.minX
        
        cell.selectedBackgroundView = nil
        
        switch (indexPath.section, Settings.gameShortcutsMode)
        {
        case (1, _):
            cell.selectionStyle = .none
            cell.contentView.alpha = 1.0
            
        case (2..., .recent):
            cell.selectionStyle = .none
            cell.contentView.alpha = 0.3
            
        case (2..., .manual):
            cell.selectionStyle = .gray
            cell.contentView.alpha = 1.0
            
        default: break
        }
    }
}

private extension AppIconShortcutsViewController
{
    func updateShortcuts()
    {
        switch Settings.gameShortcutsMode
        {
        case .recent:
            let fetchRequest = Game.recentlyPlayedFetchRequest
            fetchRequest.returnsObjectsAsFaults = false
            
            do
            {
                let games = try DatabaseManager.shared.viewContext.fetch(fetchRequest)
                self.shortcutsDataSource.setItems(games, with: [])
            }
            catch
            {
                print(error)
            }
            
            self.tableView.setEditing(false, animated: true)
            
        case .manual: self.tableView.setEditing(true, animated: true)
        }
        
        Settings.gameShortcuts = self.shortcutsDataSource.items        
    }
    
    func addShortcut(for game: Game)
    {
        guard self.shortcutsDataSource.items.count < 4 else { return }
        
        guard !self.shortcutsDataSource.items.contains(game) else { return }
        
        // No need to adjust destinationIndexPath, since it forwards change directly to table view.
        let destinationIndexPath = IndexPath(row: self.shortcutsDataSource.items.count, section: 1)
        
        let insertion = RSTCellContentChange(type: .insert, currentIndexPath: nil, destinationIndexPath: destinationIndexPath)
        insertion.rowAnimation = .fade
        
        var shortcuts = self.shortcutsDataSource.items
        shortcuts.insert(game, at: destinationIndexPath.row)
        self.shortcutsDataSource.setItems(shortcuts, with: [insertion])
        
        self.updateShortcuts()
    }
}

private extension AppIconShortcutsViewController
{
    @IBAction func switchGameShortcutsMode(with sender: UISwitch)
    {
        if sender.isOn
        {
            Settings.gameShortcutsMode = .recent
        }
        else
        {
            Settings.gameShortcutsMode = .manual
        }
        
        self.tableView.beginUpdates()
        
        self.updateShortcuts()
        self.tableView.reloadSections(IndexSet(integersIn: 0 ..< self.tableView.numberOfSections), with: .fade)
        
        self.tableView.endUpdates()
    }
}

extension AppIconShortcutsViewController
{
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        guard indexPath.section == 0 else { return super.tableView(tableView, heightForRowAt: indexPath) }
        
        return 44
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
    {
        guard self.dataSource.itemCount > 0 else { return nil }
        
        switch section
        {
        case 0: return nil
        case 1: return NSLocalizedString("Shortcuts", comment: "")
        default:
            let gameType = GameType(rawValue: self.gamesDataSource.fetchedResultsController.sections![section - 2].name)
            
            let system = System(gameType: gameType)!
            return system.localizedName
        }
    }
    
    override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String?
    {
        guard self.dataSource.itemCount > 0 else { return nil }
        
        switch (section, Settings.gameShortcutsMode)
        {
        case (0, .recent): return NSLocalizedString("Your most recently played games will appear as shortcuts when 3D touching the app icon.", comment: "")
        case (0, .manual): return NSLocalizedString("The games you've selected below will appear as shortcuts when 3D touching the app icon.", comment: "")
        case (1, .recent) where self.shortcutsDataSource.itemCount == 0: return NSLocalizedString("You have no recently played games.", comment: "")
        case (1, .recent): return " " // Return non-empty string since empty string changes vertical offset of section for some reason.
        case (1, .manual): return NSLocalizedString("You may have up to 4 shortcuts.", comment: "")
            
        default: return nil
        }
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        guard indexPath.section > 1 else { return }

        guard Settings.gameShortcutsMode == .manual else { return }

        tableView.deselectRow(at: indexPath, animated: true)

        let game = self.dataSource.item(at: indexPath)
        self.addShortcut(for: game)
    }
    
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
    {
        switch editingStyle
        {
        case .none: break
        case .delete:
            let deletion = RSTCellContentChange(type: .delete, currentIndexPath: indexPath, destinationIndexPath: nil)
            deletion.rowAnimation = .fade
            
            var shortcuts = self.shortcutsDataSource.items
            shortcuts.remove(at: indexPath.row) // No need to adjust indexPath, since it forwards change directly to table view.
            self.shortcutsDataSource.setItems(shortcuts, with: [deletion])
            
        case .insert:
            let game = self.dataSource.item(at: indexPath)
            self.addShortcut(for: game)
            
        @unknown default: break
        }
        
        self.updateShortcuts()
    }
    
    override func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String?
    {
        return NSLocalizedString("Remove", comment: "")
    }
    
    override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle
    {
        switch indexPath.section
        {
        case 1: return .delete
        case 2...: return .insert
        default: return .none
        }
    }
    
    override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool
    {
        return false
    }
    
    override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool
    {
        return (indexPath.section == 1)
    }
    
    override func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath
    {
        let indexPath: IndexPath
        
        switch proposedDestinationIndexPath.section
        {
        case 0: indexPath = IndexPath(row: 0, section: 1)
        case 1: indexPath = proposedDestinationIndexPath
        default: indexPath = IndexPath(row: self.shortcutsDataSource.items.count - 1, section: 1)
        }
        
        return indexPath
    }
    
    override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
    {
        var items = self.shortcutsDataSource.items
        
        let game = items.remove(at: sourceIndexPath.row)
        items.insert(game, at: destinationIndexPath.row)
        
        self.shortcutsDataSource.items = items
        
        self.updateShortcuts()
    }
}
