/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define([
    "require",
    "backbone",
    "hbs!tmpl/graph/TypeSystemTreeView_tmpl",
    "collection/VLineageList",
    "models/VEntity",
    "LineageHelper",
    "d3",
    "dagreD3",
    "d3-tip",
    "utils/CommonViewFunction",
    "utils/Utils",
    "platform",
    "jquery-ui"
], function(require, Backbone, TypeSystemTreeViewTmpl, VLineageList, VEntity, LineageHelper, d3, dagreD3, d3Tip, CommonViewFunction, Utils, platform) {
    "use strict";

    /** @lends TypeSystemTreeView */
    var TypeSystemTreeView = Backbone.Marionette.LayoutView.extend({
        _viewName: "TypeSystemTreeViewTmpl",

        template: TypeSystemTreeViewTmpl,
        templateHelpers: function() {
            return {
                modalID: this.viewId,
                width: "100%",
                height: "300px"
            };
        },

        /** Layout sub regions */
        regions: {
            RTypeSystemTreeViewPage: "#r_typeSystemTreeViewPage"
        },

        /** ui selector cache */
        ui: {
            typeSystemTreeViewPage: "[data-id='typeSystemTreeViewPage']",
            boxClose: '[data-id="box-close"]',
            nodeDetailTable: '[data-id="nodeDetailTable"]',
            attributeTable: '[data-id="attribute-table"]',
            typeSearch: '[data-id="typeSearch"]',
            filterServiceType: '[data-id="filterServiceType"]',
            onZoomIn: '[data-id="zoom-in"]',
            onZoomOut: '[data-id="zoom-out"]',
            filterBox: ".filter-box",
            searchBox: ".search-box",
            settingBox: '.setting-box',
            filterToggler: '[data-id="filter-toggler"]',
            settingToggler: '[data-id="setting-toggler"]',
            searchToggler: '[data-id="search-toggler"]',
            reset: '[data-id="reset"]',
            fullscreenToggler: '[data-id="fullScreen-toggler"]',
            noValueToggle: "[data-id='noValueToggle']",
            showOnlyHoverPath: '[data-id="showOnlyHoverPath"]',
            showTooltip: '[data-id="showTooltip"]',
            saveSvg: '[data-id="saveSvg"]',
        },
        /** ui events hash */
        events: function() {
            var events = {};
            events["click " + this.ui.boxClose] = "toggleBoxPanel";
            events["click " + this.ui.onZoomIn] = "onClickZoomIn";
            events["click " + this.ui.onZoomOut] = "onClickZoomOut";
            events["click " + this.ui.filterToggler] = "onClickFilterToggler";
            events["click " + this.ui.settingToggler] = 'onClickSettingToggler';
            events["click " + this.ui.searchToggler] = "onClickSearchToggler";
            events["click " + this.ui.saveSvg] = 'onClickSaveSvg';
            events["click " + this.ui.fullscreenToggler] = "onClickFullscreenToggler";
            events["click " + this.ui.reset] = "onClickReset";
            events["click " + this.ui.noValueToggle] = function() {
                this.showAllProperties = !this.showAllProperties;
                this.ui.noValueToggle.attr("data-original-title", (this.showAllProperties ? "Hide" : "Show") + " empty values");
                Utils.togglePropertyRelationshipTableEmptyValues({
                    "inputType": this.ui.noValueToggle,
                    "tableEl": this.ui.nodeDetailTable
                });
            };
            return events;
        },

        /**
         * @constructs
         */
        initialize: function(options) {
            _.extend(this, _.pick(options, "entityDefCollection"));
        },
        onShow: function() {
            this.$(".fontLoader").show();
            this.initializeGraph();
            this.fetchGraphData();
        },
        onRender: function() {
            var that = this;
            this.$el.on("click", "code li[data-def]", function() {
                if (that.selectedDetailNode) {
                    var dataObj = $(this).data(),
                        defObj = that.selectedDetailNode[dataObj.def],
                        newData = null;
                    if (dataObj.def === "businessAttributes") {
                        newData = defObj[dataObj.attribute];
                    } else {
                        newData = _.filter(defObj, { name: dataObj.attribute });
                    }
                    that.ui.attributeTable.find("pre").html('<code style="max-height: 100%">' + Utils.JSONPrettyPrint(newData, function(val) {
                        return val;
                    }) + '</code>');
                    that.$el.find('[data-id="typeAttrDetailHeader"]').text(dataObj.def);
                    that.ui.nodeDetailTable.hide("slide", { direction: "right" }, 400);
                    that.ui.attributeTable.show("slide", { direction: "left" }, 400);
                    that.$el.find(".typeDetailHeader").hide();
                    that.$el.find(".typeAttrDetailHeader").show()
                }
            });
            this.$el.on("click", "span[data-id='box-back']", function() {
                that.ui.nodeDetailTable.show("slide", { direction: "right" }, 400);
                that.ui.attributeTable.hide("slide", { direction: "left" }, 400);
                that.$el.find(".typeDetailHeader").show();
                that.$el.find(".typeAttrDetailHeader").hide()
            })
        },
        fetchGraphData: function(options) {
            var that = this;
            var entityTypeDef = that.entityDefCollection.fullCollection.toJSON();
            this.$(".fontLoader").show();
            this.$("svg").empty();
            if (that.isDestroyed) {
                return;
            }
            if (entityTypeDef.length) {
                that.generateData($.extend(true, {}, { data: entityTypeDef }, options)).then(function(graphObj) {
                    that.createGraph(options);
                });
            }
        },
        generateData: function(options) {
            return new Promise(
                function(resolve, reject) {
                    try {
                        var that = this,
                            newHashMap = {},
                            styleObj = {
                                fill: "none",
                                stroke: "#ffb203",
                                width: 3
                            },
                            makeNodeData = function(relationObj) {
                                if (relationObj) {
                                    if (relationObj.updatedValues) {
                                        return relationObj;
                                    }
                                    var obj = _.extend(relationObj, {
                                        shape: "img",
                                        updatedValues: true,
                                        label: relationObj.name.trunc(18),
                                        toolTipLabel: relationObj.name,
                                        id: relationObj.guid,
                                        isLineage: true,
                                        isIncomplete: false
                                    });
                                    return obj;
                                }
                            },
                            getStyleObjStr = function(styleObj) {
                                return "fill:" + styleObj.fill + ";stroke:" + styleObj.stroke + ";stroke-width:" + styleObj.width;
                            },
                            setNode = function(guid, obj) {
                                var node = that.LineageHelperRef.getNode(guid);
                                if (!node) {
                                    var nodeData = makeNodeData(obj);
                                    that.LineageHelperRef.setNode(guid, nodeData);
                                    return nodeData;
                                } else {
                                    return node;
                                }
                            },
                            setEdge = function(fromNodeGuid, toNodeGuid) {
                                that.LineageHelperRef.setEdge(fromNodeGuid, toNodeGuid, {
                                    arrowhead: "arrowPoint",
                                    style: getStyleObjStr(styleObj),
                                    styleObj: styleObj
                                });
                            },
                            setGraphData = function(fromEntityId, toEntityId) {
                                setNode(fromEntityId);
                                setNode(toEntityId);
                                setEdge(fromEntityId, toEntityId);
                            };

                        if (options.data) {
                            if (options.filter) {
                                // filter
                                var pendingSuperList = {},
                                    outOfFilterData = {},
                                    doneList = {};

                                var linkParents = function(obj) {
                                    if (obj && obj.superTypes.length) {
                                        _.each(obj.superTypes, function(superType) {
                                            var fromEntityId = obj.guid;
                                            var tempObj = doneList[superType] || outOfFilterData[superType];
                                            if (tempObj) {
                                                if (!doneList[superType]) {
                                                    setNode(tempObj.guid, tempObj);
                                                }
                                                setEdge(tempObj.guid, fromEntityId);
                                                linkParents(tempObj);
                                            } else {
                                                if (pendingSuperList[superType]) {
                                                    pendingSuperList[superType].push(fromEntityId);
                                                } else {
                                                    pendingSuperList[superType] = [fromEntityId];
                                                }
                                            }
                                        });
                                    }
                                }
                                _.each(options.data, function(obj) {
                                    var fromEntityId = obj.guid;
                                    if (pendingSuperList[obj.name]) {
                                        doneList[obj.name] = obj;
                                        setNode(fromEntityId, obj);
                                        _.map(pendingSuperList[obj.name], function(guid) {
                                            setEdge(fromEntityId, guid);
                                        });
                                        delete pendingSuperList[obj.name];
                                        linkParents(obj);
                                    }
                                    if (obj.serviceType === options.filter) {
                                        doneList[obj.name] = obj;
                                        setNode(fromEntityId, obj);
                                        linkParents(obj);
                                    } else if (!doneList[obj.name] && !outOfFilterData[obj.name]) {
                                        outOfFilterData[obj.name] = obj;
                                    }
                                });
                                pendingSuperList = null;
                                doneList = null;
                                outOfFilterData = null;
                            } else {
                                // Without filter
                                var pendingList = {},
                                    doneList = {};

                                _.each(options.data, function(obj) {
                                    var fromEntityId = obj.guid;
                                    doneList[obj.name] = obj;
                                    setNode(fromEntityId, obj);
                                    if (pendingList[obj.name]) {
                                        _.map(pendingList[obj.name], function(guid) {
                                            setEdge(guid, fromEntityId);
                                        });
                                        delete pendingList[obj.name];
                                    }
                                    if (obj.subTypes.length) {
                                        _.each(obj.subTypes, function(subTypes) {
                                            //var subTypesObj = _.find(options.data({ name: superTypes });
                                            //setNode(superTypeObj.attributes.guid, superTypeObj.attributes);
                                            if (doneList[subTypes]) {
                                                setEdge(fromEntityId, doneList[subTypes].guid);
                                            } else {
                                                if (pendingList[subTypes]) {
                                                    pendingList[subTypes].push(fromEntityId);
                                                } else {
                                                    pendingList[subTypes] = [fromEntityId];
                                                }
                                            }
                                        });
                                    }
                                });
                                pendingList = null;
                                doneList = null;
                            }
                        }
                        resolve(this.g);
                    } catch (e) {
                        reject(e);
                    }
                }.bind(this)
            );
        },
        toggleBoxPanel: function(options) {
            var el = options && options.el,
                nodeDetailToggler = options && options.nodeDetailToggler,
                currentTarget = options.currentTarget;
            this.$el.find(".show-box-panel").removeClass("show-box-panel");
            if (el && el.addClass) {
                el.addClass("show-box-panel");
            }
            this.$("circle.node-detail-highlight").removeClass("node-detail-highlight");
        },
        onClickNodeToggler: function(options) {
            this.toggleBoxPanel({ el: this.$(".lineage-node-detail"), nodeDetailToggler: true });
        },
        onClickZoomIn: function() {
            this.LineageHelperRef.zoomIn();
        },
        onClickZoomOut: function() {
            this.LineageHelperRef.zoomOut();
        },
        onClickFilterToggler: function() {
            this.toggleBoxPanel({ el: this.ui.filterBox });
        },
        onClickSettingToggler: function() {
            this.toggleBoxPanel({ el: this.ui.settingBox });
        },
        onClickSearchToggler: function() {
            this.toggleBoxPanel({ el: this.ui.searchBox });
        },
        onClickReset: function() {
            this.fetchGraphData({ refresh: true });
            this.ui.typeSearch.data({ refresh: true }).val("").trigger("change");
            this.ui.filterServiceType.data({ refresh: true }).val("").trigger("change");
        },
        onClickSaveSvg: function(e, a) {
            this.LineageHelperRef.exportLineage({ downloadFileName: "TypeSystemView" });
        },
        onClickFullscreenToggler: function(e) {
            var icon = $(e.currentTarget).find("i"),
                panel = $(e.target).parents(".tab-pane").first();
            icon.toggleClass("fa-expand fa-compress");
            if (icon.hasClass("fa-expand")) {
                icon.parent("button").attr("data-original-title", "Full Screen");
            } else {
                icon.parent("button").attr("data-original-title", "Default View");
            }
            panel.toggleClass("fullscreen-mode");
            var node = this.$("svg.main").parent()[0].getBoundingClientRect();
            this.LineageHelperRef.updateOptions({
                width: node.width,
                height: node.height
            });
        },
        updateDetails: function(data) {
            this.$("[data-id='typeName']").text(Utils.getName(data));

            this.selectedDetailNode = {};
            this.selectedDetailNode.atttributes = data.attributeDefs;
            this.selectedDetailNode.businessAttributes = data.businessAttributeDefs;
            this.selectedDetailNode.relationshipAttributes = data.relationshipAttributeDefs;
            //atttributes
            data["atttributes"] = (data.attributeDefs || []).map(function(obj) {
                return obj.name;
            });
            //businessAttributes
            data["businessAttributes"] = _.keys(data.businessAttributeDefs);
            //relationshipAttributes
            data["relationshipAttributes"] = (data.relationshipAttributeDefs || []).map(function(obj) {
                return obj.name;
            });

            this.ui.nodeDetailTable.html(
                CommonViewFunction.propertyTable({
                    scope: this,
                    guidHyperLink: false,
                    getValue: function(val, key) {
                        if (key && key.toLowerCase().indexOf("time") > 0) {
                            return Utils.formatDate({ date: val });
                        } else {
                            return val;
                        }
                    },
                    getArrayOfStringElement: function(val, key) {
                        var def = null,
                            classname = "json-string";
                        if (key === "atttributes" || key === "businessAttributes" || key === "relationshipAttributes") {
                            def = key;
                            classname += " cursor";
                        }
                        return "<li class='" + classname + "' " + (def ? 'data-def="' + def + '" data-attribute="' + val + '"' : '') + ">" + val + "</li>"
                    },
                    getArrayOfStringFormat: function(valueArray) {
                        return valueArray.join("");
                    },
                    getEmptyString: function(key) {
                        if (key === "subTypes" || key === "superTypes" || key === "atttributes" || key === "relationshipAttributes") {
                            return "[]";
                        }
                        return "N/A";
                    },
                    valueObject: _.omit(data, ["id", "attributeDefs", "relationshipAttributeDefs", "businessAttributeDefs", "isLineage", "isIncomplete", "label", "shape", "toolTipLabel", "updatedValues"]),
                    sortBy: true
                })
            );
        },
        createGraph: function(opt) {
            this.LineageHelperRef.createGraph();
        },
        filterData: function(value) {
            this.LineageHelperRef.refresh();
            this.fetchGraphData({ filter: value });
        },
        initializeGraph: function() {
            //ref - https://bl.ocks.org/seemantk/80613e25e9804934608ac42440562168
            var that = this,
                node = this.$("svg.main").parent()[0].getBoundingClientRect();
            this.$("svg").attr("viewBox", "0 0 " + node.width + " " + node.height);
            this.LineageHelperRef = new LineageHelper.default({
                el: this.$("svg.main")[0],
                legends: false,
                setDataManually: true,
                width: node.width,
                height: node.height,
                zoom: true,
                fitToScreen: true,
                dagreOptions: {
                    rankdir: "tb"
                },
                toolTipTitle: "Type",
                isShowHoverPath: function() { return that.ui.showOnlyHoverPath.prop('checked') },
                isShowTooltip: function() { return that.ui.showTooltip.prop('checked') },
                onNodeClick: function(d) {
                    that.onClickNodeToggler();
                    that.updateDetails(that.LineageHelperRef.getNode(d.clickedData, true));
                },
                beforeRender: function() {
                    that.$(".fontLoader").show();
                },
                afterRender: function() {
                    that.graphOptions = that.LineageHelperRef.getGraphOptions();
                    that.renderTypeFilterSearch();
                    that.$(".fontLoader").hide();
                    return;
                }
            });
        },
        renderTypeFilterSearch: function(data) {
            var that = this;
            var searchStr = "<option></option>",
                filterStr = "<option></option>",
                tempFilteMap = {};
            var nodes = that.LineageHelperRef.getNodes();
            if (!_.isEmpty(nodes)) {
                _.each(nodes, function(obj) {
                    searchStr += '<option value="' + obj.guid + '">' + obj.name + "</option>";
                    if (obj.serviceType && !tempFilteMap[obj.serviceType]) {
                        tempFilteMap[obj.serviceType] = obj.serviceType;
                        filterStr += '<option value="' + obj.serviceType + '">' + obj.serviceType + "</option>";
                    }
                });
            }
            this.ui.typeSearch.html(searchStr);
            if (!this.ui.filterServiceType.data("select2")) {
                this.ui.filterServiceType.html(filterStr);
            }

            this.initilizeTypeFilterSearch();
        },
        initilizeTypeFilterSearch: function() {
            var that = this;
            this.ui.typeSearch
                .select2({
                    closeOnSelect: true,
                    placeholder: "Select Type"
                })
                .on("change.select2", function(e) {
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    if (!that.ui.typeSearch.data("refresh")) {
                        var selectedNode = $('[data-id="typeSearch"]').val();
                        that.LineageHelperRef.searchNode({ guid: selectedNode });
                    } else {
                        that.ui.typeSearch.data("refresh", false);
                    }
                });
            if (!this.ui.filterServiceType.data("select2")) {
                this.ui.filterServiceType
                    .select2({
                        closeOnSelect: true,
                        placeholder: "Select ServiceType"
                    })
                    .on("change.select2", function(e) {
                        e.stopPropagation();
                        e.stopImmediatePropagation();
                        if (!that.ui.filterServiceType.data("refresh")) {
                            var selectedNode = $('[data-id="filterServiceType"]').val();
                            that.filterData(selectedNode);
                        } else {
                            that.ui.filterServiceType.data("refresh", false);
                        }
                    });
            }
        }
    });
    return TypeSystemTreeView;
});