import { Files } from "../model/";
import { notify, upload, randomString } from "../helpers/";
import Path from "path";
import { Observable } from "rxjs/Observable";
import { t } from "../locales/";

export const sort = function(files, type) {
    if (type === "name") {
        return sortByName(files);
    } else if (type === "date") {
        return sortByDate(files);
    } else {
        return sortByType(files);
    }
    function _moveLoadingDownward(fileA, fileB) {
        if (fileA.icon === "loading" && fileB.icon !== "loading") {
            return +1;
        } else if (fileA.icon !== "loading" && fileB.icon === "loading") {
            return -1;
        }
        return 0;
    };
    function _moveFolderUpward(fileA, fileB) {
        if (["directory", "link"].indexOf(fileA.type) === -1 &&
            ["directory", "link"].indexOf(fileB.type) !== -1) {
            return +1;
        } else if (["directory", "link"].indexOf(fileA.type) !== -1 &&
                   ["directory", "link"].indexOf(fileB.type) === -1) {
            return -1;
        }
        return 0;
    };
    function _moveHiddenFilesDownward(fileA, fileB) {
        if (fileA.name[0] === "." && fileB.name[0] !== ".") return +1;
        else if (fileA.name[0] !== "." && fileB.name[0] === ".") return -1;
        return 0;
    };
    function sortByType(files) {
        return files.sort((fileA, fileB) => {
            let tmp = _moveLoadingDownward(fileA, fileB);
            if (tmp !== 0) return tmp;

            tmp = _moveFolderUpward(fileA, fileB);
            if (tmp !== 0) return tmp;

            tmp = _moveHiddenFilesDownward(fileA, fileB);
            if (tmp !== 0) return tmp;

            const aExt = Path.extname(fileA.name.toLowerCase());
            const bExt = Path.extname(fileB.name.toLowerCase());

            if (fileA.name.toLowerCase() === fileB.name.toLowerCase()) {
                return fileA.name > fileB.name ? +1 : -1;
            } else {
                if (aExt !== bExt) return aExt > bExt ? +1 : -1;
                else return fileA.name.toLowerCase() > fileB.name.toLowerCase() ? +1 : -1;
            }
        });
    }
    function sortByName(files) {
        return files.sort((fileA, fileB) => {
            let tmp = _moveLoadingDownward(fileA, fileB);
            if (tmp !== 0) return tmp;

            tmp = _moveFolderUpward(fileA, fileB);
            if (tmp !== 0) return tmp;

            tmp = _moveHiddenFilesDownward(fileA, fileB);
            if (tmp !== 0) return tmp;

            if (fileA.name.toLowerCase() === fileB.name.toLowerCase()) {
                return fileA.name > fileB.name ? +1 : -1;
            }
            return fileA.name.toLowerCase() > fileB.name.toLowerCase() ? +1 : -1;
        });
    }
    function sortByDate(files) {
        return files.sort((fileA, fileB) => {
            const tmp = _moveLoadingDownward(fileA, fileB);
            if (tmp !== 0) return tmp;

            if (fileB.time === fileA.time) {
                return fileA.name > fileB.name ? +1 : -1;
            }
            return fileB.time - fileA.time;
        });
    }
};

export const onCreate = function(path, type, file) {
    if (type === "file") {
        return Files.touch(path, file)
            .then(() => {
                notify.send(
                    t("A file named '{{VALUE}}' was created", Path.basename(path)),
                    "success",
                );
                return Promise.resolve();
            })
            .catch((err) => {
                notify.send(err, "error");
                return Promise.reject(err);
            });
    } else if (type === "directory") {
        return Files.mkdir(path)
            .then(() => notify.send(
                t("A folder named '{{VALUE}}' was created", Path.basename(path)),
                "success",
            ))
            .catch((err) => notify.send(err, "error"));
    } else {
        return Promise.reject({
            message: t("internal error: can't create a {{VALUE}}", type.toString()),
            code: "UNKNOWN_TYPE",
        });
    }
};

export const onRename = function(from, to, type) {
    return Files.mv(from, to, type)
        .then(() => notify.send(
            t("The file '{{VALUE}}' was renamed", Path.basename(from)),
            "success",
        ))
        .catch((err) => notify.send(err, "error"));
};

export const onDelete = function(path, type) {
    return Files.rm(path, type)
        .then(() => notify.send(
            t("The file {{VALUE}} was deleted", Path.basename(path)),
            "success",
        ))
        .catch((err) => notify.send(err, "error"));
};

export const onMultiDelete = function(arrOfPath) {
    return Promise.all(arrOfPath.map((p) => Files.rm(p)))
        .then(() => notify.send(t("All done!"), "success"))
        .catch((err) => notify.send(err, "error"));
};

export const onMultiDownload = function(arr) {
    return Files.zip(arr)
        .catch((err) => notify.send(err, "error"));
};

export const onMultiRename = function(arrOfPath) {
    return Promise.all(arrOfPath.map((p) => Files.mv(p[0], p[1])))
        .then(() => notify.send(t("All done!"), "success"))
        .catch((err) => notify.send(err, "error"));
};

/*
 * The upload method has a few strategies:
 * 1. user is coming from drag and drop + browser provides support to read entire folders
 * 2. user is coming from drag and drop + browser DOES NOT provides support to read entire folders
 * 3. user is coming from a upload form button as he doesn't have drag and drop with files
 */
export const onUpload = function(path, e) {
    let extractFiles = null;
    if (e.dataTransfer === undefined) { // case 3
        extractFiles = extract_upload_crappy_hack_but_official_way(e.target);
    } else {
        if (e.dataTransfer.types && e.dataTransfer.types.length >= 0) {
            if (e.dataTransfer.types[0] === "text/uri-list") {
                return;
            }
        }
        extractFiles = extract_upload_directory_the_way_that_works_but_non_official(
            e.dataTransfer.items || [], [],
        ) // case 1
            .then((files) => {
                if (files.length === 0) { // case 2
                    return extract_upload_crappy_hack_but_official_way(e.dataTransfer);
                }
                return Promise.resolve(files);
            });
    }

    extractFiles.then((files) => upload.add(path, files));

    function extract_upload_directory_the_way_that_works_but_non_official(items, files = []) {
        const traverseDirectory = (item, _files, parent_id) => {
            const file = {
                path: item.fullPath,
            };
            if (item.isFile) {
                return new Promise((done, err) => {
                    file.type = "file";
                    item.file((_file, _err) => {
                        if (!_err) {
                            file.file = _file;
                            if (parent_id) file._prior = parent_id;
                            _files.push(file);
                        }
                        done(_files);
                    });
                });
            } else if (item.isDirectory) {
                file.type = "directory";
                file.path += "/";
                file._id = randomString();
                if (parent_id) file._prior = parent_id;
                _files.push(file);

                const reader = item.createReader();
                const filereader = function(r) {
                    return new Promise((done) => {
                        r.readEntries(function(entries) {
                            Promise.all(entries.map((entry) => {
                                return traverseDirectory(entry, _files, file._id);
                            })).then((e) => {
                                if (entries.length > 0) {
                                    return filereader(r).then(done);
                                }
                                return done(e);
                            });
                        });
                    });
                };
                return filereader(reader).then(() => {
                    return Promise.resolve(_files);
                });
            } else {
                return Promise.resolve();
            }
        };
        return Promise.all(
            Array.prototype.slice.call(items).map((item) => {
                if (typeof item.webkitGetAsEntry === "function") {
                    const entry = item.webkitGetAsEntry();
                    if(entry === null) return Promise.resolve([]);
                    return traverseDirectory(entry, files.slice(0));
                }
            }).filter((e) => e),
        ).then((res) => Promise.resolve([].concat.apply([], res)));
    }

    function extract_upload_crappy_hack_but_official_way(data) {
        const _files = data.files;
        return Promise.all(
            Array.prototype.slice.call(_files).map((_file) => {
                return detectType(_file)
                    .then(transform);
                function detectType(_f) {
                    // the 4096 is an heuristic I've observed and taken from:
                    // https://stackoverflow.com/questions/25016442
                    // however the proposed answer is just wrong as it doesn't consider folder with
                    // name such as: test.png and as Stackoverflow favor consanguinity with their
                    // point system, I couldn't rectify the proposed answer. The following code is
                    // actually working as expected
                    if (_file.size % 4096 !== 0) {
                        return Promise.resolve("file");
                    }
                    return new Promise((done, err) => {
                        const reader = new window.FileReader();
                        reader.onload = function() {
                            done("file");
                        };
                        reader.onerror = function() {
                            done("directory");
                        };
                        reader.readAsText(_f);
                    });
                }

                function transform(_type) {
                    const file = {
                        type: _type,
                        path: _file.name,
                    };
                    if (file.type === "file") {
                        file.file = _file;
                    } else {
                        file.path += "/";
                    }
                    return Promise.resolve(file);
                }
            }),
        );
    }
};


export const onSearch = (keyword, path = "/") => {
    return new Observable((obs) => {
        Files.search(keyword, path)
            .then((f) => obs.next(f))
            .catch((err) => obs.error(err));
    });
};
