/*
 * 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.
*/
/**
 * Displays details on an activity/task
 */
define([
    "underscore", "jquery", "backbone", "brooklyn-utils", "view/viewutils", "moment",
    "model/task-summary",
    "text!tpl/apps/activity-details.html", "text!tpl/apps/activity-table.html", 

    "bootstrap", "jquery-datatables", "datatables-extensions"
], function (_, $, Backbone, Util, ViewUtils, moment,
    TaskSummary,
    ActivityDetailsHtml, ActivityTableHtml) {

    var activityTableTemplate = _.template(ActivityTableHtml),
        activityDetailsTemplate = _.template(ActivityDetailsHtml);

    function makeActivityTable($el) {
        $el.html(_.template(ActivityTableHtml));
        var $subTable = $('.activity-table', $el);
        $subTable.attr('width', 569-6-6 /* subtract padding */)

        return ViewUtils.myDataTable($subTable, {
            "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
                $(nRow).attr('id', aData[0])
                $(nRow).addClass('activity-row')
            },
            "aoColumnDefs": [ {
                    "mRender": function ( data, type, row ) { return Util.escape(data) },
                    "aTargets": [ 1, 2, 3 ]
                 }, {
                    "mRender": function ( data, type, row ) { return Util.escape(data) },
                    "bVisible": false,
                    "aTargets": [ 0 ]
                 } ],
            "aaSorting":[]  // default not sorted (server-side order)
        });
    }

    var ActivityDetailsView = Backbone.View.extend({
        template: activityDetailsTemplate,
        taskLink: '',
        task: null,
        /* children of this task; see HasTaskChildren for difference between this and sub(mitted)Tasks */
        childrenTable: null,
        /* tasks in the current execution context (this.collections) whose submittedByTask
         * is the task we are drilled down on. this defaults to the passed in collection, 
         * which will be the last-viewed entity's exec-context; when children cross exec-context
         * boundaries we have to rewire to point to the current entity's exec-context / tasks */
        subtasksTable: null,
        children: null,
        breadcrumbs: [],
        firstLoad: true,
        events:{
            "click #activities-children-table .activity-table tr":"childrenRowClick",
            "click #activities-submitted-table .activity-table tr":"submittedRowClick",
            'click .showDrillDownSubmittedByAnchor':'showDrillDownSubmittedByAnchor',
            'click .showDrillDownBlockerOfAnchor':'showDrillDownBlockerOfAnchor',
            'click .backDrillDown':'backDrillDown'
        },
        // requires taskLink or task; breadcrumbs is optional
        initialize:function () {
            var that = this;
            this.taskLink = this.options.taskLink;
            this.taskId = this.options.taskId;
            if (this.options.task)
                this.task = this.options.task;
            else if (this.options.tabView)
                this.task = this.options.tabView.collection.get(this.taskId);
            if (!this.taskLink && this.task) this.taskLink = this.task.get('links').self;
            if (!this.taskLink && this.taskId) this.taskLink = "v1/activities/"+encodeURIComponent(this.taskId);
            
            this.tabView = this.options.tabView || null;
            
            if (this.options.breadcrumbs) this.breadcrumbs = this.options.breadcrumbs;

            this.$el.html(this.template({ taskLink: this.taskLink, taskId: this.taskId, task: this.task, breadcrumbs: this.breadcrumbs }));
            this.$el.addClass('activity-detail-panel');

            this.childrenTable = makeActivityTable(this.$('#activities-children-table'));
            this.subtasksTable = makeActivityTable(this.$('#activities-submitted-table'));

            ViewUtils.attachToggler(this.$el)
        
            if (this.task) {
                this.renderTask()
                this.setUpPolling()
            } else {      
                ViewUtils.fadeToIndicateInitialLoad(this.$el);
                this.$el.css('cursor', 'wait')
                $.get(this.taskLink, function(data) {
                    ViewUtils.cancelFadeOnceLoaded(that.$el);
                    that.task = new TaskSummary.Model(data)
                    that.renderTask()
                    that.setUpPolling();
                }).fail(function() { log("unable to load "+that.taskLink) })
            }

            // initial subtasks may be available from parent, so try to render those
            // (reliable polling for subtasks, and for children, is set up in setUpPolling ) 
            this.renderSubtasks()
        },
        
        refreshNow: function(initial) {
            var that = this
            $.get(this.taskLink, function(data) {
                that.task = new TaskSummary.Model(data)
                that.renderTask()
                if (initial) that.setUpPolling();
            })
        },
        renderTask: function() {
            // update task fields
            var that = this, firstLoad = this.firstLoad;
            this.firstLoad = false;
            
            if (firstLoad  && this.task) {
//                log("rendering "+firstLoad+" "+this.task.get('isError')+" "+this.task.id);
                if (this.task.get('isError')) {
                    // on first load, expand the details if there is a problem
                    var $details = this.$(".toggler-region.task-detail .toggler-header");
                    ViewUtils.showTogglerClickElement($details);
                }
            }
            
            this.updateFields('displayName', 'entityDisplayName', 'id', 'description', 'currentStatus', 'blockingDetails');
            this.updateFieldWith('blockingTask',
                function(v) { 
                    return "<a class='showDrillDownBlockerOfAnchor handy' link='"+encodeURI(v.link)+"' id='"+_.escape(v.metadata.id)+"'>"+
                        that.displayTextForLinkedTask(v)+"</a>" })
            this.updateFieldWith('result',
                function(v) {
                    // use display string (JSON.stringify(_.escape(v)) because otherwise list of [null,null] is just ","  
                    var vs = Util.toDisplayString(v);
                    if (vs.trim().length==0) {
                        return " (empty result)";
                    } else if (vs.length<20 &&  !/\r|\n/.exec(v)) {
                        return " with result: <span class='result-literal'>"+vs+"</span>";
                    } else {
                        return "<div class='result-literal'>"+vs.replace(/\n+/g,"<br>")+"</div>"
                    }
                 })
            this.updateFieldWith('tags', function(tags) {
                var tagBody = "";
                for (var tag in tags) {
                    tagBody += "<div class='activity-tag-giftlabel'>"+Util.toDisplayString(tags[tag])+"</div>";
                }
                return tagBody;
            })
            
            var submitTimeUtc = this.updateFieldWith('submitTimeUtc',
                function(v) { return v <= 0 ? "-" : moment(v).format('D MMM YYYY H:mm:ss.SSS')+" &nbsp; <i>"+moment(v).fromNow()+"</i>" })
            var startTimeUtc = this.updateFieldWith('startTimeUtc',
                function(v) { return v <= 0 ? "-" : moment(v).format('D MMM YYYY H:mm:ss.SSS')+" &nbsp; <i>"+moment(v).fromNow()+"</i>" })
            this.updateFieldWith('endTimeUtc',
                function(v) { return v <= 0 ? "-" : moment(v).format('D MMM YYYY H:mm:ss.SSS')+" &nbsp; <i>"+moment(v).from(startTimeUtc, true)+" later</i>" })

            ViewUtils.updateTextareaWithData(this.$(".task-json .for-textarea"), 
                Util.toTextAreaString(this.task), false, false, 150, 400)

            ViewUtils.updateTextareaWithData(this.$(".task-detail .for-textarea"), 
                this.task.get('detailedStatus'), false, false, 30, 250)

            this.updateFieldWith('streams',
                function(streams) {
                    // Stream names presented alphabetically
                    var keys = _.keys(streams);
                    keys.sort();
                    var result = "";
                    for (var i = 0; i < keys.length; i++) {
                        var name = keys[i];
                        var stream = streams[name];
                        result += "<div class='activity-stream-div'>" +
                                "<span class='activity-label'>" +
                                _.escape(name) +
                                "</span><span>" +
                                "<a href='" + encodeURI(stream.link) + "'>download</a>" +
                                (stream.metadata["sizeText"] ? " (" + _.escape(stream.metadata["sizeText"]) + ")" : "") +
                                "</span></div>";
                    }
                    return result; 
                });

            this.updateFieldWith('submittedByTask',
                function(v) { return "<a class='showDrillDownSubmittedByAnchor handy' link='"+encodeURI(v.link)+"' id='"+_.escape(v.metadata.id)+"'>"+
                    that.displayTextForLinkedTask(v)+"</a>" })

            if (this.task.get("children").length==0)
                this.$('.toggler-region.tasks-children').hide();
        },
        setUpPolling: function() {
            var that = this

            // on first load, clear any funny cursor
            this.$el.css('cursor', 'auto')

            this.task.url = this.taskLink;
            this.task.on("all", this.renderTask, this)
            
            ViewUtils.get(this, this.taskLink, function(data) {
                // if we can get the data, then start fetching certain things repeatedly
                // (would be good to skip the immediate "doitnow" below but not a big deal)
                ViewUtils.fetchRepeatedlyWithDelay(that, that.task, { doitnow: true });
                
                // and set up to load children (now that the task is guaranteed to be loaded)
                that.children = new TaskSummary.Collection()
                that.children.url = that.task.get("links").children
                that.children.on("reset", that.renderChildren, that)
                ViewUtils.fetchRepeatedlyWithDelay(that, that.children, { 
                    fetchOptions: { reset: true }, doitnow: true, fadeTarget: that.$('.tasks-children') });
            }).fail( function() { that.$('.toggler-region.tasks-children').hide() } );


            $.get(this.task.get("links").entity, function(entity) {
                if (that.collection==null || entity.links.activities != that.collection.url) {
                    // need to rewire collection to point to the right ExecutionContext
                    that.collection = new TaskSummary.Collection()
                    that.collection.url = entity.links.activities
                    that.collection.on("reset", that.renderSubtasks, that)
                    ViewUtils.fetchRepeatedlyWithDelay(that, that.collection, { 
                        fetchOptions: { reset: true }, doitnow: true, fadeTarget: that.$('.tasks-submitted') });
                } else {
                    that.collection.on("reset", that.renderSubtasks, that)
                    that.collection.fetch({reset: true});
                }
            });
        },
        
        renderChildren: function() {
            var that = this
            var children = this.children
            ViewUtils.updateMyDataTable(this.childrenTable, children, function(task, index) {
                return [ task.get("id"),
                         (task.get("entityId") && task.get("entityId")!=that.task.get("entityId") ? task.get("entityDisplayName") + ": " : "") + 
                         task.get("displayName"),
                         task.get("submitTimeUtc") <= 0 ? "-" : moment(task.get("submitTimeUtc")).calendar(),
                         task.get("currentStatus")
                    ]; 
                });
            if (children && children.length>0) {
                this.$('.toggler-region.tasks-children').show();
            } else {
                this.$('.toggler-region.tasks-children').hide();
            }
        },
        renderSubtasks: function() {
            var that = this
            var taskId = this.taskId || (this.task ? this.task.id : null);
            if (!this.collection) {
                this.$('.toggler-region.tasks-submitted').hide();
                return;
            }
            if (!taskId) {
                // task not available yet; just wait for it to be loaded
                // (and in worst case, if it can't be loaded, this panel stays faded)
                return;
            } 
            
            // find tasks submitted by this one which aren't included as children
            // this uses collections -- which is everything in the current execution context
            var subtasks = []
            for (var taskI in this.collection.models) {
                var task = this.collection.models[taskI]
                var submittedBy = task.get("submittedByTask")
                if (submittedBy!=null && submittedBy.metadata!=null && submittedBy.metadata["id"] == taskId &&
                        (!this.children || this.children.get(task.id)==null)) {
                    subtasks.push(task)
                }
            }
            ViewUtils.updateMyDataTable(this.subtasksTable, subtasks, function(task, index) {
                return [ task.get("id"),
                         (task.get("entityId") && (!that.task || task.get("entityId")!=that.task.get("entityId")) ? task.get("entityDisplayName") + ": " : "") + 
                         task.get("displayName"),
                         task.get("submitTimeUtc") <= 0 ? "-" : moment(task.get("submitTimeUtc")).calendar(),
                         task.get("currentStatus")
                    ];
                });
            if (subtasks && subtasks.length>0) {
                this.$('.toggler-region.tasks-submitted').show();
            } else {
                this.$('.toggler-region.tasks-submitted').hide();
            }
        },
        
        displayTextForLinkedTask: function(v) {
            return v.metadata.taskName ? 
                    (v.metadata.entityDisplayName ? _.escape(v.metadata.entityDisplayName)+" <b>"+_.escape(v.metadata.taskName)+"</b>" : 
                        _.escape(v.metadata.taskName)) :
                    v.metadata.taskId ? _.escape(v.metadata.taskId) : 
                    _.escape(v.link)
        },
        updateField: function(field) {
            return this.updateFieldWith(field, _.escape)
        },
        updateFields: function() {
            _.map(arguments, this.updateField, this);
        },
        updateFieldWith: function(field, f) {
            var v = this.task.get(field)
            if (v !== undefined && v != null && 
                    (typeof v !== "object" || _.size(v) > 0)) {
                this.$('.updateField-'+_.escape(field), this.$el).html( f(v) );
                this.$('.ifField-'+_.escape(field), this.$el).show();
            } else {
                // blank if there is no value
                this.$('.updateField-'+_.escape(field)).empty();
                this.$('.ifField-'+_.escape(field)).hide();
            }
            return v
        },
        childrenRowClick:function(evt) {
            var row = $(evt.currentTarget).closest("tr");
            var id = row.attr("id");
            this.showDrillDownTask("subtask of", this.children.get(id).get("links").self, id, this.children.get(id))
        },
        submittedRowClick:function(evt) {
            var row = $(evt.currentTarget).closest("tr");
            var id = row.attr("id");
            // submitted tasks are guaranteed to be in the collection, so this is safe
            this.showDrillDownTask("subtask of", this.collection.get(id).get('links').self, id)
        },
        
        showDrillDownSubmittedByAnchor: function(from) {
            var $a = $(from.target).closest('a');
            this.showDrillDownTask("submitter of", $a.attr("link"), $a.attr("id"))
        },
        showDrillDownBlockerOfAnchor: function(from) {
            var $a = $(from.target).closest('a');
            this.showDrillDownTask("blocker of", $a.attr("link"), $a.attr("id"))
        },
        showDrillDownTask: function(relation, newTaskLink, newTaskId, newTask) {
            var that = this;
            
            var newBreadcrumbs = [ relation + ' ' +
                this.task.get('entityDisplayName') + ' ' +
                this.task.get('displayName') ].concat(this.breadcrumbs)
                
            var activityDetailsPanel = new ActivityDetailsView({
                taskLink: newTaskLink,
                taskId: newTaskId,
                task: newTask,
                tabView: that.tabView,
                collection: this.collection,
                breadcrumbs: newBreadcrumbs
            });
            activityDetailsPanel.addToView(this.$el);
        },
        addToView: function(parent) {
            if (this.parent) {
                log("WARN: adding details to view when already added")
                this.parent = parent;
            }
            
            if (Backbone.history && (!this.tabView || !this.tabView.openingQueuedTasks)) {
                Backbone.history.navigate(Backbone.history.fragment+"/"+"subtask"+"/"+encodeURIComponent(this.taskId));
            }

            var $t = parent.closest('.slide-panel');
            var $t2 = $t.after('<div>').next();
            $t2.addClass('slide-panel');

            // load the drill-down page
            $t2.html(this.render().el)

            var speed = (!this.tabView || !this.tabView.openingQueuedTasks) ? 300 : 0;
            $t.animate({
                    left: -600
                }, speed, function() { 
                    $t.hide() 
                });

            $t2.show().css({
                    left: 600
                    , top: 0
                }).animate({
                    left: 0
                }, speed);
        },
        backDrillDown: function(event) {
//            log("activities drill back from "+this.taskLink)
            var that = this
            var $t2 = this.$el.closest('.slide-panel')
            var $t = $t2.prev()

            if (Backbone.history) {
                var fragment = Backbone.history.fragment
                var thisLoc = fragment.indexOf("/subtask/"+encodeURIComponent(this.taskId));
                if (thisLoc>=0)
                    Backbone.history.navigate( fragment.substring(0, thisLoc) );
            }

            $t2.animate({
                    left: 569 //prevTable.width()
                }, 300, function() {
                    that.$el.empty()
                    $t2.remove()
                    that.remove()
                });

            $t.show().css({
                    left: -600 //-($t2.width())
                }).animate({
                    left: 0
                }, 300);
        }
    });

    return ActivityDetailsView;
});
