/*
 * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
 * except in compliance with the License. A copy of the License is located at
 *
 *    http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
 * the specific language governing permissions and limitations under the License.
 */

import UIKit

extension UISplitViewController {
    var primaryViewController: UIViewController? {
        return self.viewControllers.first
    }
    
    var secondaryViewController: UIViewController? {
        return self.viewControllers.count > 1 ? self.viewControllers[1] : nil
    }
}

class MasterViewController: UITableViewController {
    var analyticsService: AnalyticsService? = nil
    var dataService : DataService? = nil
    var detailViewController: DetailViewController? = nil
    var addButton: UIBarButtonItem? = nil
    var notes = [Note]() {
        didSet {
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "My Notes"
        
        // Get a reference to the analytics service from the AppDelegate
        analyticsService = (UIApplication.shared.delegate as! AppDelegate).analyticsService
        
        // Get a reference to the data service from the AppDelegate
        dataService = (UIApplication.shared.delegate as! AppDelegate).dataService

        // Do any additional setup after loading the view, typically from a nib.
        navigationItem.leftBarButtonItem = editButtonItem

        addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
        navigationItem.rightBarButtonItem = addButton
        if let split = splitViewController {
            let controllers = split.viewControllers
            detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
        }
    }
    
    override func viewWillAppear(_ animated: Bool) {
        clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
        super.viewWillAppear(animated)
        
        NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(noteChanged(notification:)), name: Notification.Name("NoteChangedIdentifier"), object: nil)
        
        analyticsService?.recordEvent("StartListView", parameters: nil, metrics: nil)
        
        // Load the notes from the data service whenever we refresh
        DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
            self.loadNotesFromDataService()
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
        NotificationCenter.default.removeObserver(self, name: Notification.Name("NoteChangedIdentifier"), object: nil)
    }
    
    @objc func rotated() {
        if (UIDevice.current.orientation != .landscapeLeft && UIDevice.current.orientation != .landscapeRight) {
            if tableView.indexPathForSelectedRow != nil {
                self.performSegue(withIdentifier: "showDetail", sender: (Any).self)
            }
        }
    }
    
    @objc func noteChanged(notification: Notification) {
        let note = notification.object as! Note
        var index = notes.index(where: { n in n.id == note.id })
        if (index != nil) {
            notes[index!] = note
        } else {
            notes.append(note)
            index = notes.count - 1
        }
        
        let indexPath = IndexPath(row: index!, section: 0)
        tableView.selectRow(at: indexPath, animated: true, scrollPosition: .bottom)
    }

    func loadNotesFromDataService() {
        dataService?.loadNotes() { (notesFromNetwork, error) in
            if error == nil {
                if notesFromNetwork == nil {
                    self.notes = [Note]() // Clear the notes out
                } else {
                    self.notes = notesFromNetwork!
                }
            } else {
                self.analyticsService?.recordEvent("Error", parameters: ["op":"loadNotes"], metrics: nil)
                self.showErrorAlert(error?.localizedDescription ?? "Unknown data service error", title: "LoadNotes Error")
            }
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @objc
    func insertNewObject(_ sender: Any) {
        analyticsService?.recordEvent("AddNewNote", parameters: nil, metrics: nil)
        self.performSegue(withIdentifier: "showDetail", sender: sender)
    }

    // MARK: - Segues
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showDetail" {
            var note: Note? = nil
            
            // If the sender is the add button, then create a new note, otherwise load the note that you clicked on.
            if let barButton = sender as? UIBarButtonItem {
                if addButton == barButton {
                    note = Note()
                    if let indexPath = tableView.indexPathForSelectedRow {
                        tableView.deselectRow(at: indexPath, animated: true)
                    }
                }
            } else if let indexPath = tableView.indexPathForSelectedRow {
                note = notes[indexPath.row]
            }
            
            if (note != nil) {
                analyticsService?.recordEvent("StartDetailView", parameters: [ "id" : note!.id ?? "unknown" ], metrics: nil)
                let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
                controller.setNoteDetail(note)
                controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
                controller.navigationItem.leftItemsSupplementBackButton = true
            }
        }
    }

    // MARK: - Table View
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return notes.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel!.text = notes[indexPath.row].title ?? ""
        return cell
    }

    // Enable swipe-to-delete
    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    // Handle a swipe into swipe-to-delete
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            guard let noteId = notes[indexPath.row].id else {
                analyticsService?.recordEvent("Error", parameters: ["op":"swipeToDelete"], metrics: nil)
                showErrorAlert("Invalid note ID presented for swipe-to-delete", title: "Bad Error")
                return
            }
            
            // Delete the note from the backend
            analyticsService?.recordEvent("DeleteNote", parameters: [ "id" : noteId ], metrics: nil)
            dataService?.deleteNote(noteId) { (error) in
                if error == nil {
                    self.notes.remove(at: indexPath.row)
                    DispatchQueue.main.async {
                        tableView.reloadData()
                    }
                    // If the detail view is showing the current note, remove it.
                    if let view = self.splitViewController {
                        if (view.displayMode == UISplitViewControllerDisplayMode.allVisible) {
                            if let detail = self.splitViewController?.secondaryViewController?.childViewControllers.first as! DetailViewController? {
                                if noteId == detail.detailItem?.id {
                                    // We are deleting the item that is currently displayed
                                    detail.detailItem = nil
                                }
                            }
                        }
                    }
                } else {
                    self.analyticsService?.recordEvent("Error", parameters: ["op":"deleteNote"], metrics: nil)
                    self.showErrorAlert(error?.localizedDescription ?? "Unknown Error", title: "Row not removed")
                }
            }
        }
    }
    
    // Display the given error message as an alert pop-up
    func showErrorAlert(_ errorMessage: String, title: String?) {
        let alertController = UIAlertController(title: title ?? "Error", message: errorMessage, preferredStyle: .alert)
        let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alertController.addAction(defaultAction)
        present(alertController, animated: true, completion: nil)
    }
}

