/*
 * decaffeinate suggestions:
 * DS101: Remove unnecessary use of Array.from
 * DS102: Remove unnecessary code created because of implicit returns
 * DS208: Avoid top-level this
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
// Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>

// AnTOS Web desktop is is licensed under the GNU General Public
// License v3.0, see the LICENCE file for more information

// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.

// You should have received a copy of the GNU General Public License
//along with this program. If not, see https://www.gnu.org/licenses/.
namespace OS {
    export namespace application {

        interface FilesClipboardType {
            cut: boolean;
            file: API.VFS.BaseFileHandle;
        }
        interface FilesViewType {
            icon: boolean;
            list: boolean;
            tree: boolean;
        }
        /**
         *
         *
         * @export
         * @class Files
         * @extends {BaseApplication}
         */
        export class Files extends BaseApplication {
            private view: GUI.tag.FileViewTag;
            private navinput: HTMLInputElement;
            private navbar: GUI.tag.HBoxTag;
            private currdir: API.VFS.BaseFileHandle;
            private favo: GUI.tag.ListViewTag;
            private clipboard: FilesClipboardType;
            private viewType: FilesViewType;
            private vfs_event_flag: boolean;
            constructor(args: AppArgumentsType[]) {
                super("Files", args);
            }


            /**
             *
             *
             * @returns
             * @memberof Files
             */
            main(): void {
                this.view = this.find("fileview") as GUI.tag.FileViewTag;
                this.navinput = this.find("navinput") as HTMLInputElement;
                this.navbar = this.find("nav-bar") as GUI.tag.HBoxTag;
                if (this.args && this.args.length > 0) {
                    this.currdir = this.args[0].path.asFileHandle();
                } else {
                    this.currdir = "home://".asFileHandle();
                }
                this.favo = this.find("favouri") as GUI.tag.ListViewTag;
                this.clipboard = undefined;
                this.viewType = this._api.switcher("icon", "list", "tree");
                this.viewType.list = true;

                this.view.contextmenuHandle = (e, m) => {
                    const file = this.view.selectedFile;
                    if (!file) {
                        return;
                    }
                    const apps = [];
                    if (file.type === "dir") {
                        file.mime = "dir";
                    }

                    for (let v of this._gui.appsByMime(file.mime)) {
                        apps.push({
                            text: v.text,
                            app: v.app,
                            icon: v.icon,
                            iconclass: v.iconclass,
                        });
                    }

                    m.items = [
                        {
                            text: "__(Open with)",
                            nodes: apps,
                            onchildselect: (e: GUI.TagEventType<GUI.tag.MenuEventData>) => {
                                if (!e) {
                                    return;
                                }
                                const it = e.data.item.data;
                                return this._gui.launch(it.app, [file]);
                            },
                        },
                        this.mnFile(),
                        this.mnEdit(),
                    ];
                    m.show(e);
                };

                this.view.onfileopen = (e) => {
                    if (!e.data) {
                        return;
                    }
                    if (e.data.type === "dir") {
                        return;
                    }
                    return this._gui.openWith(e.data);
                };

                this.favo.onlistselect = (e) => {
                    return this.view.path = e.data.item.data.path;
                };

                (this.find("btback") as GUI.tag.ButtonTag).onbtclick = () => {
                    if (this.currdir.isRoot()) {
                        return;
                    }
                    const p = this.currdir.parent();
                    this.favo.selected = -1;
                    return this.view.path = p.path;
                };

                $(this.navinput).keyup((e) => {
                    if (e.keyCode === 13) {
                        return this.view.path = $(this.navinput).val() as string;
                    }
                }); //enter

                this.view.fetch = (path) => {
                    return new Promise((resolve, reject) => {
                        let dir = path.asFileHandle();
                        dir
                            .read()
                            .then((d) => {
                                if (d.error) {
                                    return reject(d.error);
                                }
                                if (!dir.isRoot()) {
                                    const p = dir.parent();
                                    p.filename = "[..]";
                                    p.type = "dir";
                                    d.result.unshift(p);
                                }
                                this.currdir = dir;
                                $(this.navinput).val(dir.path);
                                return resolve(d.result);
                            })
                            .catch((e) => reject(__e(e)));
                    });
                };

                this.vfs_event_flag = true;
                this.view.ondragndrop = (e) => {
                    if (!e) {
                        return;
                    }
                    const src = e.data.from.data;
                    const des = e.data.to.data;
                    if (des.type === "file") {
                        return;
                    }
                    const file = src.path.asFileHandle();
                    // disable the vfs event on
                    // we update it manually
                    this.vfs_event_flag = false;
                    return file
                        .move(`${des.path}/${file.basename}`)
                        .then(() => {
                            if (this.view.view === "icon") {
                                this.view.path = this.view.path;
                            } else {
                                this.view.update(file.parent().path);
                                this.view.update(des.path);
                            }
                            //reenable the vfs event
                            return (this.vfs_event_flag = true);
                        })
                        .catch((e: Error) => {
                            // reenable the vfs event
                            this.vfs_event_flag = true;
                            return this.error(
                                __(
                                    "Unable to move: {0} -> {1}",
                                    src.path,
                                    des.path
                                ),
                                e
                            );
                        });
                };

                // application setting
                if (this.setting.sidebar === undefined) {
                    this.setting.sidebar = true;
                }
                if (this.setting.nav === undefined) {
                    this.setting.nav = true;
                }
                if (this.setting.showhidden === undefined) {
                    this.setting.showhidden = false;
                }
                this.applyAllSetting();

                // VFS mount point and event
                const mntpoints =  [];
                for(let v of this.systemsetting.VFS.mountpoints)
                {
                    mntpoints.push({
                        text: v.text,
                        path: v.path,
                        selected: false
                    });
                }
                this.favo.data = mntpoints;
                //@favo.set "selected", -1
                if (this.setting.view) {
                    this.view.view = this.setting.view;
                }
                this.subscribe("VFS", (d) => {
                    if (!this.vfs_event_flag) {
                        return;
                    }
                    if (["read", "publish", "download"].includes(d.data.m)) {
                        return;
                    }
                    if (
                        d.data.file.hash() === this.currdir.hash() ||
                        d.data.file.parent().hash() === this.currdir.hash()
                    ) {
                        return this.view.path = this.currdir.path;
                    }
                });

                // bind keyboard shortcut
                this.bindKey("CTRL-F", () =>
                    this.actionFile(`${this.name}-mkf`)
                );
                this.bindKey("CTRL-D", () =>
                    this.actionFile(`${this.name}-mkdir`)
                );
                this.bindKey("CTRL-U", () =>
                    this.actionFile(`${this.name}-upload`)
                );
                this.bindKey("CTRL-S", () =>
                    this.actionFile(`${this.name}-share`)
                );
                this.bindKey("CTRL-I", () =>
                    this.actionFile(`${this.name}-info`)
                );

                this.bindKey("CTRL-R", () =>
                    this.actionEdit(`${this.name}-mv`)
                );
                this.bindKey("CTRL-M", () =>
                    this.actionEdit(`${this.name}-rm`)
                );
                this.bindKey("CTRL-X", () =>
                    this.actionEdit(`${this.name}-cut`)
                );
                this.bindKey("CTRL-C", () =>
                    this.actionEdit(`${this.name}-copy`)
                );
                this.bindKey("CTRL-P", () =>
                    this.actionEdit(`${this.name}-paste`)
                );

                (this.find("btgrid") as GUI.tag.ButtonTag).onbtclick = (e) => {
                    this.view.view = "icon";
                    this.viewType.icon = true;
                };

                (this.find("btlist") as GUI.tag.ButtonTag).onbtclick = (e) => {
                    this.view.view = "list";
                    this.viewType.list = true;
                };
                this.view.path = this.currdir.path;
            }

            protected applySetting(k: string): void{
                // view setting
                switch (k) {
                    case "showhidden":
                        return this.view.showhidden = this.setting.showhidden;
                    case "nav":
                        return this.toggleNav(this.setting.nav);
                    case "sidebar":
                        return this.toggleSidebar(this.setting.sidebar);
                }
            }

            private mnFile(): GUI.BasicItemType{
                //console.log file
                const arr: GUI.BasicItemType = {
                    text: "__(File)",
                    nodes: [
                        {
                            text: "__(New file)",
                            dataid: `${this.name}-mkf`,
                            shortcut: "C-F",
                        },
                        {
                            text: "__(New folder)",
                            dataid: `${this.name}-mkdir`,
                            shortcut: "C-D",
                        },
                        {
                            text: "__(Upload)",
                            dataid: `${this.name}-upload`,
                            shortcut: "C-U",
                        },
                        {
                            text: "__(Download)",
                            dataid: `${this.name}-download`,
                        },
                        {
                            text: "__(Share file)",
                            dataid: `${this.name}-share`,
                            shortcut: "C-S",
                        },
                        {
                            text: "__(Properties)",
                            dataid: `${this.name}-info`,
                            shortcut: "C-I",
                        },
                    ],
                    onchildselect: (e: GUI.TagEventType<GUI.tag.MenuEventData>) =>
                        this.actionFile(e.data.item.data.dataid),
                };
                return arr;
            }
            private mnEdit(): GUI.BasicItemType{
                return {
                    text: "__(Edit)",
                    nodes: [
                        {
                            text: "__(Rename)",
                            dataid: `${this.name}-mv`,
                            shortcut: "C-R",
                        },
                        {
                            text: "__(Delete)",
                            dataid: `${this.name}-rm`,
                            shortcut: "C-M",
                        },
                        {
                            text: "__(Cut)",
                            dataid: `${this.name}-cut`,
                            shortcut: "C-X",
                        },
                        {
                            text: "__(Copy)",
                            dataid: `${this.name}-copy`,
                            shortcut: "C-C",
                        },
                        {
                            text: "__(Paste)",
                            dataid: `${this.name}-paste`,
                            shortcut: "C-P",
                        },
                    ],
                    onchildselect: (e: GUI.TagEventType<GUI.tag.MenuEventData>) =>
                        this.actionEdit(e.data.item.data.dataid),
                };
            }
            protected menu(): GUI.BasicItemType[]{
                const menu = [
                    this.mnFile(),
                    this.mnEdit(),
                    {
                        text: "__(View)",
                        nodes: [
                            {
                                text: "__(Refresh)",
                                dataid: `${this.name}-refresh`,
                            },
                            {
                                text: "__(Sidebar)",
                                switch: true,
                                checked: this.setting.sidebar,
                                dataid: `${this.name}-side`,
                            },
                            {
                                text: "__(Navigation bar)",
                                switch: true,
                                checked: this.setting.nav,
                                dataid: `${this.name}-nav`,
                            },
                            {
                                text: "__(Hidden files)",
                                switch: true,
                                checked: this.setting.showhidden,
                                dataid: `${this.name}-hidden`,
                            },
                            {
                                text: "__(Type)",
                                nodes: [
                                    {
                                        text: "__(Icon view)",
                                        radio: true,
                                        checked: this.viewType.icon,
                                        dataid: `${this.name}-icon`,
                                        type: "icon",
                                    },
                                    {
                                        text: "__(List view)",
                                        radio: true,
                                        checked: this.viewType.list,
                                        dataid: `${this.name}-list`,
                                        type: "list",
                                    },
                                    {
                                        text: "__(Tree view)",
                                        radio: true,
                                        checked: this.viewType.tree,
                                        dataid: `${this.name}-tree`,
                                        type: "tree",
                                    },
                                ],
                                onchildselect: (e: GUI.TagEventType<GUI.tag.MenuEventData>) => {
                                    const { type } = e.data.item.data;
                                    this.view.view = type;
                                    return (this.viewType[type] = true);
                                },
                            },
                        ],
                        onchildselect: (e: GUI.TagEventType<GUI.tag.MenuEventData>) => this.actionView(e),
                    },
                ];
                return menu;
            }

            private toggleSidebar(b: boolean): void {
                if (b) {
                    $(this.favo).show();
                } else {
                    $(this.favo).hide();
                }
                return this.trigger("resize");
            }

            private toggleNav(b: boolean): void {
                if (b) {
                    $(this.navbar).show();
                } else {
                    $(this.navbar).hide();
                }
                return this.trigger("resize");
            }

            private actionView(e: GUI.TagEventType<GUI.tag.MenuEventData>): void{
                const data = e.data.item.data;
                switch (data.dataid) {
                    case `${this.name}-hidden`:
                        //@.view.set "showhidden", e.item.data.checked
                        return this.registry("showhidden", data.checked);
                    //@.setting.showhidden = e.item.data.checked
                    case `${this.name}-refresh`:
                        this.view.path = this.currdir.path;
                        return;
                    case `${this.name}-side`:
                        return this.registry("sidebar", data.checked);
                    //@setting.sidebar = e.item.data.checked
                    //@toggleSidebar e.item.data.checked
                    case `${this.name}-nav`:
                        return this.registry("nav", data.checked);
                }
            }
            //@setting.nav = e.item.data.checked
            //@toggleNav e.item.data.checked

            private actionEdit(e: string): void{
                const file = this.view.selectedFile;
                switch (e) {
                    case `${this.name}-mv`:
                        if (!file) {
                            return;
                        }
                        this.openDialog("PromptDialog", {
                            title: "__(Rename)",
                            label: "__(File name)",
                            value: file.filename,
                        }).then(async (d) => {
                            if (d === file.filename) {
                                return;
                            }
                            try {
                                return file.path
                                    .asFileHandle()
                                    .move(`${this.currdir.path}/${d}`);
                            }
                            catch (e) {
                                return this.error(__("Fail to rename: {0}", file.path), e);
                            }
                        });
                        break;

                    case `${this.name}-rm`:
                        if (!file) {
                            return;
                        }
                        this.openDialog("YesNoDialog", {
                            title: "__(Delete)",
                            iconclass: "fa fa-question-circle",
                            text: __(
                                "Do you really want to delete: {0}?",
                                file.filename
                            ),
                        }).then(async (d) => {
                            if (!d) {
                                return;
                            }
                            try {
                                return file.path
                                    .asFileHandle()
                                    .remove();
                            }
                            catch (e) {
                                return this.error(__("Fail to delete: {0}", file.path), e);
                            }
                        });
                        break;

                    case `${this.name}-cut`:
                        if (!file) {
                            return;
                        }
                        this.clipboard = {
                            cut: true,
                            file: file.path.asFileHandle(),
                        };
                        return this.notify(__("File {0} cut", file.filename));

                    case `${this.name}-copy`:
                        if (!file && file.type !== "dir") {
                            return;
                        }
                        this.clipboard = {
                            cut: false,
                            file: file.path.asFileHandle(),
                        };
                        return this.notify(
                            __("File {0} copied", file.filename)
                        );

                    case `${this.name}-paste`:
                        if (!this.clipboard) {
                            return;
                        }
                        if (this.clipboard.cut) {
                            this.clipboard.file
                                .move(
                                    `${this.currdir.path}/${this.clipboard.file.basename}`
                                )
                                .then((r) => {
                                    return (this.clipboard = undefined);
                                })
                                .catch((e) => {
                                    return this.error(
                                        __(
                                            "Fail to paste: {0}",
                                            this.clipboard.file.path
                                        ),
                                        e
                                    );
                                });
                        } else {
                            this.clipboard.file
                                .read("binary")
                                .then(async (d) => {
                                    const blob = new Blob([d], {
                                        type: this.clipboard.file.info.mime,
                                    });
                                    const fp = `${this.currdir.path}/${this.clipboard.file.basename}`.asFileHandle();
                                    fp.cache = blob;
                                    try {
                                        const r = await fp.write(this.clipboard.file.info.mime);
                                        return (this.clipboard = undefined);
                                    }
                                    catch (e) {
                                        return this.error(__("Fail to paste: {0}", this.clipboard.file.path), e);
                                    }
                                })
                                .catch((e) => {
                                    return this.error(
                                        __(
                                            "Fail to read: {0}",
                                            this.clipboard.file.path
                                        ),
                                        e
                                    );
                                });
                        }
                        break;
                    default:
                        this._api.handle.setting();
                }
            }

            private actionFile(e: string): void{
                const file = this.view.selectedFile;
                switch (e) {
                    case `${this.name}-mkdir`:
                        this.openDialog("PromptDialog", {
                            title: "__(New folder)",
                            label: "__(Folder name)",
                        }).then(async (d) => {
                            try {
                                return this.currdir.mk(d);
                            }
                            catch (e) {
                                return this.error(__("Fail to create: {0}", d), e);
                            }
                        });
                        break;
                    case `${this.name}-mkf`:
                        this.openDialog("PromptDialog", {
                            title: "__(New file)",
                            label: "__(File name)",
                        }).then(async (d) => {
                            const fp = `${this.currdir.path}/${d}`.asFileHandle();
                            try {
                                return fp.write("text/plain");
                            }
                            catch (e) {
                                return this.error(__("Fail to create: {0}", fp.path));
                            }
                        });
                        break;
                    case `${this.name}-info`:
                        if (!file) {
                            return;
                        }
                        this.openDialog("InfoDialog", file);
                        break;

                    case `${this.name}-upload`:
                        this.currdir.upload().catch((e) => {
                            return this.error(
                                __("Fail to upload: {0}", e.toString()),
                                e
                            );
                        });
                        break;

                    case `${this.name}-share`:
                        if (!file || file.type !== "file") {
                            return;
                        }
                        file.path
                            .asFileHandle()
                            .publish()
                            .then((r) => {
                                return this.notify(
                                    __("Shared url: {0}", r.result)
                                );
                            })
                            .catch((e) => {
                                return this.error(
                                    __("Fail to publish: {0}", file.path),
                                    e
                                );
                            });
                        break;
                    case `${this.name}-download`:
                        if (file.type !== "file") {
                            return;
                        }
                        file.path
                            .asFileHandle()
                            .download()
                            .catch((e) => {
                                return this.error(
                                    __("Fail to download: {0}", file.path),
                                    e
                                );
                            });
                        break;
                    default:
                        return console.log(e);
                }
            }
        }
    }
}
