/**
 * All our interaction with the file system generally goes through here
 */
import { FileModel } from "./fileModel";
import { TypedEvent } from "../../common/events";
import * as fsu from "../utils/fsu";
import * as types from "../../common/types";
import * as chalk from 'chalk';

export const savedFileChangedOnDisk = new TypedEvent<{ filePath: string; contents: string }>();
export const didEdits = new TypedEvent<{ filePath: string; edits: CodeEdit[] }>();
export const didStatusChange = new TypedEvent<types.FileStatus>();
export const editorOptionsChanged = new TypedEvent<{ filePath: string; editorOptions: types.EditorOptions }>();
export const didOpenFile = new TypedEvent<{ filePath: string, contents: string }>();

let openFiles: FileModel[] = [];
export function getOpenFile(filePath: string) {
    if (openFiles.some(f => f.config.filePath == filePath)) {
        return openFiles.filter(f => f.config.filePath == filePath)[0];
    }
}
export function getOrCreateOpenFile(filePath: string, autoCreate = false) {
    filePath = fsu.consistentPath(filePath);
    var file = getOpenFile(filePath);
    if (!file) {
        /** If you request a file that isn't there ... we are going to create it */
        if (!fsu.existsSync(filePath) && autoCreate) {
            fsu.writeFile(filePath, '');
        }

        file = new FileModel({
            filePath: filePath
        });
        didOpenFile.emit({
            filePath,
            contents: file.getContents()
        });
        file.onSavedFileChangedOnDisk.on((evt) => {
            savedFileChangedOnDisk.emit({ filePath, contents: evt.contents });
        });
        file.didEdits.on((evt) => {
            didEdits.emit({ filePath, edits: evt.codeEdits });
        });
        file.didStatusChange.on((evt) => {
            didStatusChange.emit({ filePath, saved: evt.saved, eol: evt.eol });
        });
        file.editorOptionsChanged.on((editorOptions) => {
            editorOptionsChanged.emit({ filePath, editorOptions });
        });
        openFiles.push(file);
    }
    return file;
}
export function closeOpenFile(filePath: string) {
    var file = getOpenFile(filePath);
    if (file) {
        file.save();
        // Right now we still keep the file open indefinitely
        // openFiles = openFiles.filter(f=> f.config.filePath !== filePath);
    }
}

export function getOpenFiles(): FileModel[] {
    return openFiles;
}

export function isFileOpen(filePath: string) {
    return !!getOpenFile(filePath);
}

export function saveOpenFile(filePath: string) {
    let file = getOpenFile(filePath);
    file.save();
}

/**
 * Editor Config Stuff
 */
export function watchedEditorConfigChanged() {
    getOpenFiles().forEach(fm => fm.recheckEditorOptions());
    // TODO:
    // Recheck editor config for all open files :-/
    // The files should emit '`editorConfigChanged`' individually
    // We should be listening to these events and pushing them out
    // The front end editor should be listening to this event by filePath too.
}


/**
 * File Tree managment functions
 */
import * as mkdirp from "mkdirp";
export function addFolder(filePath: string) {
    mkdirp.sync(filePath);
}
export function deleteFromDisk(data: { files: string[], dirs: string[] }) {
    data.files.forEach(filePath => {
        var file = getOpenFile(filePath);
        if (file) {
            file.delete();
            openFiles = openFiles.filter(f => f.config.filePath !== filePath);
        }
        else {
            fsu.deleteFile(filePath);
        }
    });
    data.dirs.forEach(dirPath => {
        // delete any open files
        let toClose = (filePath: string) => {
            return filePath.startsWith(dirPath);
        }
        openFiles.filter(f => toClose(f.config.filePath)).forEach(f => f.delete());
        openFiles = openFiles.filter(f => !toClose(f.config.filePath));

        // delete the dir
        fsu.deleteDir(dirPath);
    });
}

export function duplicateFile(data: { src: string, dest: string }) {
    let contents = fsu.readFile(data.src);
    fsu.writeFile(data.dest, contents);
}

import { ncp } from "ncp";
export function duplicateDir(data: { src: string, dest: string }) {
    return new Promise<string>((resolve) => {
        ncp(data.src, data.dest, (err) => {
            if (err) console.log('Move failed', err);
            resolve(JSON.stringify(err));
        });
    });
}

import * as mv from "mv";
export function movePath(data: { src: string, dest: string }): Promise<string> {
    return new Promise<string>((resolve) => {
        mv(data.src, data.dest, { mkdirp: true, clobber: true }, (err) => {
            if (err) console.log('Move failed', err);
            resolve(JSON.stringify(err));
        });
    });
}
import * as open from "open";
export function launchDirectory(data: { filePath: string }): Promise<{ error?: Error }> {
    return new Promise<{ error?: Error }>((resolve) => {
        open(data.filePath);
        resolve({ error: null })
    });
}
import * as cp from 'child_process';
export function launchTerminal(data: { filePath: string }): Promise<{ error?: Error }> {
    return new Promise<{ error?: Error }>((resolve) => {
        if (process.platform === 'darwin') {
            cp.execSync(`osascript -e 'tell application "Terminal" to activate' -e 'tell application "Terminal" to do script "cd ${data.filePath}"'`);
        }

        else if (process.platform === 'win32') {
            cp.execSync(`start cmd.exe /K "cd ${data.filePath}"`);
        }
        else {
            // http://stackoverflow.com/a/31737949/390330
            console.error(chalk.red("We don't have a command for your OS. Would love for you to help us"));
        }

        resolve({ error: null })
    });
}
