let path = require('path')
let fs = require('fs');
const shell = require('electron').shell;
let fileExtensionToImage: object;
import {Themes} from "./themes";

export enum ConfigPaths {
    ApplicationConfigName = "dashboard.json",
    UserConfigName = "user.json",
    FileExtensionsName = "file-extensions.json"
}

export module Configurator {

    export function getFileExtensionToImageMap(): Object {
        if (!fileExtensionToImage) {
            fileExtensionToImage = this.loadAppConfig(ConfigPaths.FileExtensionsName);
        }
        return fileExtensionToImage;
    }

    export function loadAppConfig(fileName: string): object {
        let filePath = FileUtils.getPathToConfig(fileName);
        let fileBuffer = fs.readFileSync(filePath);
        let content = fileBuffer.toString('utf8')
        return JSON.parse(content)
    }

    export function loadUserConfig(fileName: string): object {
        let userConfigPath = FileUtils.getPathToUserDir(fileName);
        if (!fs.existsSync(userConfigPath)) {
            return buildDefaultConfig();
        } else {
            let content = fs.readFileSync(userConfigPath);
            return JSON.parse(content);
        }
    }

    export function saveUserConfig(fileName: string, obj: any) {
        let userConfigPath = FileUtils.getPathToUserDir(fileName);
        let content = JSON.stringify(obj);
        fs.writeFile(userConfigPath, content, (err) => {
            if (err) {
                console.log(err);
                throw err;
            }
        });
    }

    function buildDefaultConfig(): object {
        let userConfig = {};
        userConfig['theme'] = Themes.AppTheme.Dark;
        saveUserConfig(ConfigPaths.UserConfigName, userConfig)
        return userConfig
    }

}

export module FileUtils {
    export function getPathToView(templateName: string): string {
        return path.join(getPathToAssets(), "views", templateName + ".mustache")
    }

    export function getPathToImage(imageName: string): string {
        return path.join(getPathToAssets(), "images", imageName)
    }

    export function getPathToDocument(documentName: string): string {
        return path.join(getPathToAssets(), "documents", documentName)
    }

    export function getPathToConfig(documentName: string): string {
        return path.join(getPathToAssets(), "conf", documentName)
    }

    export function getPathToAssets(): string {
        return path.join(__dirname, "..", "assets");
    }

    export function getFileExtension(fileName: string): string {
        return path.extname(fileName);
    }

    export function getPathToUserDir(fileName: string): string {
        let dirPath = path.join(__dirname, "..", "user");
        let filePath = path.join(dirPath, fileName);
        touchDir(dirPath);
        return filePath;
    }

    export function touchDir(dirPath: string) {
        if (!fs.existsSync(dirPath)) {
            fs.mkdirSync(dirPath);
        }
    }
}


export class DocumentDirectory {
    /**
     * you *must* provide an absolute file path here
     */
    constructor(filePath: string) {
        this.root = new DirectoryNode(filePath, null);
    }

    root: DirectoryNode;

}

export class FileNode {

    filePath: string;
    parent: FileNode;

    constructor(filePath: string, parent: FileNode) {
        this.filePath = filePath;
        this.parent = parent;
    }

    open() {
        shell.openItem(this.filePath);
    }

    static compare(a: FileNode, b: FileNode): number {
        return a.filePath.localeCompare(b.filePath);
    }
}

export class DirectoryNode extends FileNode {
    children: FileNode[] = [];

    constructor(filePath: string, parent: FileNode) {
        super(filePath, parent);
        if (fs.existsSync(filePath)) {
            let stats = fs.lstatSync(filePath);
            if (stats.isDirectory()) {
                let contents = fs.readdirSync(filePath);
                for (let i = 0, l = contents.length; i < l; i++) {
                    let childPath = path.join(filePath, contents[i]);
                    let childStats = fs.lstatSync(childPath);
                    if (childStats.isDirectory()) {
                        this.children.push(new DirectoryNode(childPath, this));
                    } else {
                        this.children.push(new DocumentNode(childPath, this));
                    }
                }
            } else {
                throw new DirectoryIsActuallyFileError(filePath);
            }
        } else {
            throw new DirectoryDoesNotExistError(filePath);
        }
    }

    getDocuments(): DocumentNode[] {
        return this.children.filter((x) => {
            return x instanceof DocumentNode
        }) as DocumentNode[]
    }

    getDirectories(): DirectoryNode[] {
        return this.children.filter((x) => {
            return x instanceof DirectoryNode
        }) as DirectoryNode[]
    }
}


export class DocumentNode extends FileNode {
    constructor(filePath: string, parent: FileNode) {
        super(filePath, parent);
    }

}

export class DirectoryIsActuallyFileError extends Error {
    constructor(filePath: string) {
        super(`attempted to scan non-directory: ${filePath}`);
    }
}

export class DirectoryDoesNotExistError extends Error {
    constructor(filePath: string) {
        super(`attempted to load document directory for non-existing directory: ${filePath}`);
    }
}

