import { Undecked } from '../Core';
import * as path from 'path'
import { Log } from '../Logger';
import * as StreamDeck from '@elgato-stream-deck/node'
import { Deck, Deck_Config } from './Deck';

import * as fs from 'fs-extra';
var { writeFile, pathExists, readJson } = fs;

declare var Undecked: Undecked;

export class DeckManager {
	managerConfigPath: string;

	managerConfig: DeckManager_Config;

	decks: DeckManager_Decks;

	constructor() { }

	load(callback: () => void) {
		this.managerConfigPath = path.join(Undecked.dataPath, 'decks.json');
		this.decks = {};

		this.loadConfig(() => {
			for (let i = 0; i < this.managerConfig.decks.length; i++) {
				var deckConfig = this.managerConfig.decks[i];
				this.decks[deckConfig.serialNumber] = new Deck(deckConfig, null);
			}

			this.ensureDecks((newDecks: number) => {
				Log(
					'info',
					`Loaded ${Object.keys(this.decks).length - newDecks} existing deck(s) and ${newDecks} new deck(s)`
				);
				callback();
			});
		});
	}

	loadConfig(callback: () => void) {
		pathExists(this.managerConfigPath, (err, exists) => {
			if (err) throw err;
			if (exists) {
				readJson(this.managerConfigPath, (err, json) => {
					if (err) throw err;
					this.managerConfig = json;
					callback();
				});
			} else {
				this.managerConfig = defaultDeckConfig;
				this.saveConfig(callback);
			}
		});
	}

	saveConfig(callback?: () => void) {
		var toSave: DeckManager_Config = {
			decks: []
		};
		for (var serialNumber in this.decks) toSave.decks.push(this.decks[serialNumber].export());

		writeFile(this.managerConfigPath, JSON.stringify(toSave, null, 4), (err) => {
			if (err) Log('error', 'Error whilst saving manager config', err.message);
			if (callback) callback();
		});
	}

	hasDeck(serialNumber: string): boolean {
		return this.decks[serialNumber] != undefined;
	}

	getDeck(serialNumber: string): Deck {
		if (this.decks[serialNumber]) return this.decks[serialNumber];
		return null;
	}

	ensureDecks(callback: (newDecks: number) => void) {
		var currentDecks = Object.keys(this.decks).length;
		var decks = StreamDeck.listStreamDecks();
		for (let i = 0; i < decks.length; i++) {
			if (!this.hasDeck(decks[i].serialNumber))
				this.decks[decks[i].serialNumber] = new Deck(
					{
						serialNumber: decks[i].serialNumber,
						model: decks[i].model,
						name: `Unnamed ${decks[i].model}`
					},
					decks[i].path
				);
			else this.decks[decks[i].serialNumber].setDevicePath(decks[i].path);
		}

		if (Object.keys(this.decks).length > currentDecks)
			this.saveConfig(() => callback(Object.keys(this.decks).length - currentDecks));
		else callback(0);
	}

	getList(): { serialNumber: string; name: string }[] {
		var list: { serialNumber: string; name: string }[] = [];
		for (var serialNumber in this.decks) {
			list.push({ serialNumber, name: this.decks[serialNumber].getName() });
		}

		return list;
	}
}

var defaultDeckConfig: DeckManager_Config = {
	decks: []
};

// export interface DeckManager {
// 	decks: DeckManager_Decks;

// 	load: (callback: () => void) => void;
// 	getList: () => { serialNumber: string; name: string }[];
// 	hasDeck: (serialNumber: string) => boolean;
// 	getDeck: (serialNumber: string) => Deck;
// }

export interface DeckManager_Config {
	decks: Deck_Config[];
}

export interface DeckManager_Decks {
	[serialNumber: string]: Deck;
}
