define(function() {

    function ChartFolderTree(raw_folders, current, charts) {
        this.tree = genTree(raw_folders);
        this.list = genList(this.tree);
        this.current = current;
        this.charts = charts;
        this.rendercallbacks = {};
        this.current_folder_funcs = {};
        this.search = { active: false };
        this.dndcallback = function(){};
    }

    function genTree(raw) {
        raw.forEach(function(group) {
            if (group.type === "user")
                group.organization = false;
            delete(group.type);
            group.folders.sort(function(a, b) {
                return a.name.localeCompare(b.name);
            });
            group.folders.forEach(function(folder) {
                delete(folder.type);
                delete(folder.user);
                folder.sub = group.folders.filter(function(potential_subfolder) {
                    return (potential_subfolder.parent == folder.id) ? true : false;
                });
                if (!folder.sub.length)
                    folder.sub = false;
            });
            group.folders = group.folders.filter(function(folder) {
                return (folder.parent === null) ? true : false;
            });
        });

        return raw.sort(function(a, b) {
            if (!a.organization) return -1;
            if (!b.organization) return 1;
            return a.organization.name.localeCompare(b.organization.name);
        });
    }

    function genList(tree) {
        var list = [];

        function traverse(folder, path_obj) {
            if (folder.sub) {
                var new_path_obj = {
                    strings: path_obj.strings.concat(folder.name),
                    ids: path_obj.ids.concat(folder.id)
                };
                folder.sub.forEach(function(sub_folder) {
                    traverse(sub_folder, new_path_obj);
                });
            }
            list[folder.id] = {
                folder: folder,
                path_info: path_obj
            };
        }

        tree.forEach(function(group) {
            group.folders.forEach(function(folder) {
                traverse(folder, {
                    strings: [],
                    ids: []
                });
            });
        });

        return list;
    }


    ChartFolderTree.prototype = {
        debugTree: function() {
            console.log(this.tree, this.list, this.charts);
        },
        updateCharts: function(charts) {
            this.charts = charts;
        },
        getFolderById: function(f_id) {
            return (typeof this.list[f_id] !== "undefined") ? this.list[f_id].folder : false;
        },
        getFolderNameById: function(f_id) {
            return (typeof this.list[f_id] !== "undefined") ? this.list[f_id].folder.name : false;
        },
        getFolderOrgById: function(f_id) {
            return (typeof this.list[f_id] !== "undefined") ? this.list[f_id].folder.organization : false;
        },
        setFolderName: function(f_id, name) {
            if (typeof this.list[f_id] !== "undefined")
                this.list[f_id].folder.name = name;
        },
        isParentFolder: function(child, dest) {
            var child_folder_obj = (typeof this.list[child] !== "undefined") ? this.list[child].folder : false,
                dest_folder_obj = (typeof this.list[dest.id] !== "undefined") ? this.list[dest.id].folder : this.getRoot(dest.organization),
                parent_folder_obj = (child_folder_obj) ? ((child_folder_obj.parent) ? this.list[child_folder_obj.parent].folder : this.getRoot(child_folder_obj.organization)) : false;

            if (!child_folder_obj) {
                console.warn('Source folder can not be a root folder. Operation prohibited.');
                return true;
            }
            if (parent_folder_obj && parent_folder_obj.id) {
                // my parent is a folder
                if (dest_folder_obj && dest_folder_obj.id) {
                    // destination is a folder
                    return parent_folder_obj.id == dest_folder_obj.id;
                }
                return false;
            } else {
                // my parent is a root
                if (dest_folder_obj && dest_folder_obj.id) {
                    // destination is a folder
                    return false;
                }
                // two roots, but are they the same?
                if (dest_folder_obj.organization && parent_folder_obj.organization) {
                    // two organizations!!!
                    return dest_folder_obj.organization.id == parent_folder_obj.organization.id;
                }
                return !(dest_folder_obj.organization || parent_folder_obj.organization);
            }
        },
        isUserToOrgMove: function(drag_data, target) {
            var curFolder = drag_data.type === 'folder' ? this.list[drag_data.id].folder : this.current;
            var curOrg = curFolder ? curFolder.organization : null;
            return (!curOrg && target.organization) || (curOrg != target.organization && target.organization);
        },
        isOrgToUserMove: function(drag_data, target) {
            var curFolder = drag_data.type === 'folder' ? this.list[drag_data.id].folder : this.current;
            var curOrg = curFolder ? curFolder.organization : null;
            return curOrg && !target.organization;
        },
        getParentFolder: function(id) {
            var parent = (typeof this.list[id.folder] !== "undefined") ? this.list[id.folder].folder.parent : false,
                parent_folder_obj = (parent) ? this.getFolderById(parent) : this.getRoot(id.organization);

            return {
                folder: (parent_folder_obj.id) ? parent_folder_obj.id : false,
                organization: (parent_folder_obj.organization) ? ((parent_folder_obj.organization.id) ? parent_folder_obj.organization.id : parent_folder_obj.organization) : false
            };
        },
        getPathToFolder: function(f_id) {
            return (typeof this.list[f_id] !== "undefined") ? this.list[f_id].path_info.strings : false;
        },
        getIdsToFolder: function(f_id) {
            return (typeof this.list[f_id] !== "undefined") ? this.list[f_id].path_info.ids : false;
        },
        getSubFolders: function(f_id) {
            var subfolders = (typeof this.list[f_id] !== "undefined") ? this.list[f_id].folder.sub : false;
            return (subfolders) ? subfolders : [];
        },
        hasSubFolders: function(f_id) {
            var subfolders = (typeof this.list[f_id] !== "undefined") ? this.list[f_id].folder.sub : false;
            return (subfolders);
        },
        getRoot: function(org_id) {
            if (!org_id)
                org_id = false;
            return this.tree.filter(function(group) {
                return (group.organization) ? (group.organization.id === org_id) : (group.organization == org_id);
            })[0];
        },
        getRootSubFolders: function(org_id) {
            var subfolders;
            if (!org_id) org_id = false;
            subfolders = this.tree.filter(function(group) {
                return (group.organization) ? (group.organization.id === org_id) : (group.organization == org_id);
            })[0].folders;
            return (subfolders) ? subfolders : [];
        },
        isSubfolderOf: function(source, dest) {
            var source_folder_obj = this.getFolderById(source),
                dest_folder_obj = this.getFolderById(dest);

            if (!dest_folder_obj) return false;
            if (!source_folder_obj) {
                console.warn("Root folders can not be moved. Since this id didn't resolve to a folder, further operation is prohibited.");
                return true;
            }

            function traverse(folder) {
                if (folder.id == dest_folder_obj.id) return true;
                if (!folder.sub) return false;

                return folder.sub.reduce(function(ret, sub) {
                    return (ret || traverse(sub));
                }, false);
            }

            return traverse(source_folder_obj);

        },
        getOrgNameById: function(org_id) {
            var org;
            if (!org_id) org_id = false;
            org = this.tree.filter(function(group) {
                return (group.organization) ? (group.organization.id === org_id) : (group.organization == org_id);
            })[0].organization;
            return (org) ? org.name : false;
        },
        setCurrentSort: function(sort) {
            this.current.sort = sort;
        },
        getCurrentSort: function() {
            return this.current.sort;
        },
        setCurrentPage: function(page) {
            this.current.page = page;
        },
        getCurrentPage: function() {
            return this.current.page;
        },
        setCurrentFolder: function(folder_id, org_id) {
            this.current.folder = folder_id;
            this.current.organization = org_id;
            this.rendercallbacks.changeActiveFolder(folder_id, org_id);
        },
        getCurrentFolder: function() {
            return {
                folder: this.current.folder,
                organization: this.current.organization
            };
        },
        setCurrentFolderFuncs: function(callback) {
            this.current_folder_funcs = callback;
        },
        updateCurrentFolderFuncs: function() {
            this.current_folder_funcs();
        },
        setSearchActive: function(base_url, q) {
            $('body').addClass('mycharts-search-results');
            this.search.active = true;
            if (!this.search.base_url)
                this.search.base_url = base_url;
            this.search.query = q;
        },
        setSearchDisabled: function() {
            $('body').removeClass('mycharts-search-results');
            this.search.active = false;
            this.search.base_url = null;
        },
        isSearchActive: function() {
            return this.search.active;
        },
        getSearchParams: function() {
            return {
                base_url: this.search.base_url,
                query: this.search.query
            };
        },
        setRenderCallbacks: function(callbacks) {
            this.rendercallbacks = callbacks;
        },
        setDnDCallback: function(callback) {
            this.dndcallback = callback;
        },
        reRenderTree: function() {
            var cbs = this.rendercallbacks,
                cur = this.current;
            this.tree.forEach(function(group) {
                cbs.changeChartCount(false, group.organization.id, group.charts);
                cbs.renderSubtree(group.organization.id, group.folders);
            });
            cbs.changeActiveFolder(cur.folder, cur.organization);
            this.dndcallback();
        },
        moveFolderToFolder: function(moved_id, dest) {
            var moved_folder_obj = this.getFolderById(moved_id),
                dest_folder_obj = (dest.folder) ? this.getFolderById(dest.folder) : this.getRoot(dest.organization),
                source_folder_obj = (moved_folder_obj.parent) ? this.getFolderById(moved_folder_obj.parent) : this.getRoot(moved_folder_obj.organization);


            if (dest.folder) {
                if (!dest_folder_obj.sub)
                    dest_folder_obj.sub = [];
                dest_folder_obj.sub.push(moved_folder_obj);
                dest_folder_obj.sub.sort(function(a, b) {
                    return a.name.localeCompare(b.name);
                });
            } else {
                dest_folder_obj.folders.push(moved_folder_obj);
                dest_folder_obj.folders.sort(function(a, b) {
                    return a.name.localeCompare(b.name);
                });
            }

            if (moved_folder_obj.parent) {
                source_folder_obj.sub = source_folder_obj.sub.filter(function(folder) {
                    return folder.id != moved_id;
                });
                if (source_folder_obj.sub.length === 0)
                    source_folder_obj.sub = false;
            } else {
                source_folder_obj.folders = source_folder_obj.folders.filter(function(folder) {
                    return folder.id != moved_id;
                });
            }

            moved_folder_obj.parent = dest.folder;
            moved_folder_obj.organization = dest.organization;
            this.list = genList(this.tree);
        },
        addFolder: function(folder) {
            var dest_folder_obj = (folder.parent) ? this.getFolderById(folder.parent) : this.getRoot(folder.organization),
                dest_array = (folder.parent) ? dest_folder_obj.sub : dest_folder_obj.folders;

            if (folder.parent && !dest_array)  {
                dest_folder_obj.sub = [];
                dest_array = dest_folder_obj.sub;
            }
            dest_array.push(folder);
            dest_array.sort(function(a, b) {
                return a.name.localeCompare(b.name);
            });
            this.list = genList(this.tree);
        },
        deleteFolder: function(delme) {
            var current = (typeof this.list[delme.folder] !== "undefined") ? this.list[delme.folder].folder : false,
                parent = (current) ? this.list[delme.folder].folder.parent : false,
                parent_folder_obj = (parent) ? this.getFolderById(parent) : this.getRoot(delme.organization),
                chart_ids = Object.keys(this.charts);

            if (parent_folder_obj.id) {
                parent_folder_obj.sub = parent_folder_obj.sub.filter(function(folder) {
                    return folder.id != delme.folder;
                });
                if (parent_folder_obj.sub.length === 0) {
                    parent_folder_obj.sub = false;
                }
            } else {
                parent_folder_obj.folders = parent_folder_obj.folders.filter(function(folder) {
                    return folder.id != delme.folder;
                });
                if (parent_folder_obj.folders.length === 0) {
                    parent_folder_obj.folders = false;
                }
            }

            parent_folder_obj.charts += current.charts; 
            this.list = genList(this.tree);
        },
        moveNChartsTo: function(charts, dest) {
            var num = charts.length,
                folder;

            folder = (dest.folder) ? this.list[dest.folder].folder : this.getRoot(dest.organization);
            folder.charts += num;
            this.rendercallbacks.changeChartCount(dest.folder, dest.organization, folder.charts);

            charts.forEach(function(id) {
                var chart = this.charts[id];

                folder = (chart.inFolder) ? this.list[chart.inFolder].folder : this.getRoot(chart.organizationId);
                folder.charts--;
                this.rendercallbacks.changeChartCount(chart.inFolder, chart.organizationId, folder.charts);
            }, this);
        },
        removeChartFromCurrent: function(chart_id) {
            var folder = (this.current.folder) ? this.list[this.current.folder].folder : this.getRoot(this.current.organization);
            delete this.charts[chart_id];
            this.rendercallbacks.changeChartCount(this.current.folder, this.current.organization, --folder.charts);
        }
    };

    return ChartFolderTree;
});
