//
//  Consoles.swift
//  Zockerhoehle
//
//  Created by Julian-Steffen Müller on 13.09.18.
//  Copyright © 2018 Julian-Steffen Müller. All rights reserved.
//

import Foundation
import Disk
import CoreData

protocol ConsoleItemObserver {
    func consoleItemDataUpdate(for parent : FlockeEntryID)
}

protocol AttachmentObserver {
    func attachmentUpdate(attachmentID : AttachmentID)
}

class GameCollection {
    static let shared : GameCollection = GameCollection()
    
    //TODO: Zusammenfuehren von consoles und consoleItemsByParent
    var consoles : [ConsoleEntry] = []
    var consoleItemsByParent : [FlockeEntryID : [FlockeEntry]] = [:]
    var attachments : [AttachmentID : Attachment] = [:]
    
    private var consoleItemObservers : [ConsoleItemObserver] = []
    private var attachmentObservers : [AttachmentObserver] = []
    
    init() {
        do {
            self.consoles =  try Disk.retrieve("consoles.json", from: .caches, as: [ConsoleEntry].self)
            self.consoleItemsByParent =  try Disk.retrieve("consoleItems.json", from: .caches, as: [FlockeEntryID : [FlockeEntry]].self)
            self.attachments =  try Disk.retrieve("attachments.json", from: .caches, as: [AttachmentID : Attachment].self)
            
            //Fill core data on Startup
            
            print("Game Collection import begin");
            
            let cdm = CDManager.shared
            
            for console in self.consoles {
                let cdConsole = Console(entity: Console.entity(), insertInto: cdm.viewContext)
                cdConsole.name = console.name

                cdConsole.generation = Int64(console.generation ?? 0)
                cdConsole.manufacturer = console.manufacturer
            
                guard let consoleItems = self.consoleItemsByParent[console.id] else {
                    print("No games: \(console.name)")
                    continue
                }
                
                if let imgID = console.iconAttachment?.id , let img = self.attachments[imgID]?.file {
                    let cdLogo = Logo(entity: Logo.entity(), insertInto: cdm.viewContext)
                    cdLogo.image = UIImage(data: img)
                    cdConsole.logo = cdLogo
                }
                
                for item in consoleItems {
                    if (item.parents.contains(GameCollection.accessoryID)) {
                        let cdAccessory = Accessory(entity: Accessory.entity(), insertInto: cdm.viewContext)
                        
                        cdAccessory.name = item.name
                        cdAccessory.inWishlist = item.parents.contains(GameCollection.wishlistID)
                        cdConsole.addToAccessories(cdAccessory)
                        
                    }else if (item.parents.contains(GameCollection.videogameID)) {
                        let cdGame = Game(entity: Game.entity(), insertInto: cdm.viewContext)
                        
                        cdGame.name = item.name
                        cdGame.inWishlist = item.parents.contains(GameCollection.wishlistID)
                        
                        cdConsole.addToGames(cdGame)
                    }
                }
            }
            
        }catch let error as NSError {
            print("Load of Chached not possible \(error.localizedDescription)");
        }
    }
    
    func addFlockeEntryObserver(observer: ConsoleItemObserver) {
        self.consoleItemObservers.append(observer)
    }
    
    func addAttachmentObserver(observer: AttachmentObserver) {
        self.attachmentObservers.append(observer)
    }
    
    fileprivate func notifyConSoleItemObservers(_ parent: FlockeEntryID) {
        for observer in self.consoleItemObservers {
            observer.consoleItemDataUpdate(for: parent)
        }
    }
    
    func updateGameItems(items : [FlockeEntry], with parent : FlockeEntryID) {
        if parent == GameCollection.consoleID {
            updateConsoles(entrys: items)
        }else {
            self.consoleItemsByParent[parent] = items
            
            do {
                try Disk.save(self.consoleItemsByParent, to: .caches, as: "consoleItems.json")
            }catch let error as NSError {
                print("Write of GameItems not possible \(error.localizedDescription)");
            }
        }
        
        notifyConSoleItemObservers(parent)
    }
    
    func remove(from: FlockeEntry, parentID: FlockeEntryID) {
        from.parents.removeAll(where: {$0 == parentID})
        
        for parent in from.parents {
            notifyConSoleItemObservers(parent)
        }
    }
    
    func removeGameItem(from console : FlockeEntry, item: FlockeEntry) {
        guard let consoleItems : [FlockeEntry] = self.consoleItemsByParent[console.id] else {
            return
        }
        
        if let index = consoleItems.firstIndex(of: item) {
            consoleItemsByParent[console.id]!.remove(at: index);
            
            notifyConSoleItemObservers(console.id)
        }
    }
    
    func updateAttachment(attachment : Attachment) {
        self.attachments[attachment.id] = attachment
        
        do {
            try Disk.save(self.attachments, to: .caches, as: "attachments.json")
        }catch let error as NSError {
            print("Write of Attachments not possible \(error.localizedDescription)");
        }
        
        for observer in self.attachmentObservers {
            observer.attachmentUpdate(attachmentID: attachment.id)
        }
    }
    
    func updateConsoles(entrys: [FlockeEntry]) {
        self.consoles = []
        for entry in entrys {
            self.consoles.append(ConsoleEntry(entry: entry))
        }
    
        //IN-Place Sort
        self.consoles.sort {
            //1. Sort by manufacturer
            if ($0.manufacturer == $1.manufacturer) {
//                //2. Sort by console generation
                if ($0.generation == $1.generation) {
//                    //3. Sort by console name
                    return $0.name < $1.name
                }

                return $0.generation! < $1.generation!
            }
            
            return $0.manufacturer! < $1.manufacturer!
        }
        
        do {
            try Disk.save(self.consoles, to: .caches, as: "consoles.json")
        }catch let error as NSError {
            print("Write of ConsoleItem not possible \(error.localizedDescription)");
        }
    }
}

extension GameCollection {
    static let consoleID : FlockeEntryID = "5b0c2ff8ba0c0b4ae7559911"
    static let videogameID : FlockeEntryID = "5b116282ba0c0b4ae75599be"
    static let wishlistID : FlockeEntryID = "5b11628dba0c0b4ae75599bf"
    static let accessoryID : FlockeEntryID = "5b1162ccba0c0b4ae75599c1"
}
