// # Article Editor

/*global window, document, $, _, Backbone, Ghost, Showdown, CodeMirror, shortcut, Countable, JST */
(function () {
    "use strict";

    var PublishBar,
        TagWidget,
        ActionsWidget,
        MarkdownShortcuts = [
            {'key': 'Ctrl+B', 'style': 'bold'},
            {'key': 'Meta+B', 'style': 'bold'},
            {'key': 'Ctrl+I', 'style': 'italic'},
            {'key': 'Meta+I', 'style': 'italic'},
            {'key': 'Ctrl+Alt+U', 'style': 'strike'},
            {'key': 'Ctrl+Shift+K', 'style': 'code'},
            {'key': 'Meta+K', 'style': 'code'},
            {'key': 'Ctrl+Alt+1', 'style': 'h1'},
            {'key': 'Ctrl+Alt+2', 'style': 'h2'},
            {'key': 'Ctrl+Alt+3', 'style': 'h3'},
            {'key': 'Ctrl+Alt+4', 'style': 'h4'},
            {'key': 'Ctrl+Alt+5', 'style': 'h5'},
            {'key': 'Ctrl+Alt+6', 'style': 'h6'},
            {'key': 'Ctrl+Shift+L', 'style': 'link'},
            {'key': 'Ctrl+Shift+I', 'style': 'image'},
            {'key': 'Ctrl+Q', 'style': 'blockquote'},
            {'key': 'Ctrl+Shift+1', 'style': 'currentDate'},
            {'key': 'Ctrl+U', 'style': 'uppercase'},
            {'key': 'Ctrl+Shift+U', 'style': 'lowercase'},
            {'key': 'Ctrl+Alt+Shift+U', 'style': 'titlecase'},
            {'key': 'Ctrl+Alt+W', 'style': 'selectword'},
            {'key': 'Ctrl+L', 'style': 'list'},
            {'key': 'Ctrl+Alt+C', 'style': 'copyHTML'},
            {'key': 'Meta+Alt+C', 'style': 'copyHTML'}
        ];

    // The publish bar associated with a post, which has the TagWidget and
    // Save button and options and such.
    // ----------------------------------------
    PublishBar = Ghost.View.extend({

        initialize: function () {
            this.addSubview(new TagWidget({el: this.$('#entry-categories'), model: this.model})).render();
            this.addSubview(new ActionsWidget({el: this.$('#entry-actions'), model: this.model})).render();
        },

        render: function () { return this; }

    });

    // The Tag UI area associated with a post
    // ----------------------------------------
    TagWidget = Ghost.View.extend({
        render: function () { return this; }
    });

    // The Publish, Queue, Publish Now buttons
    // ----------------------------------------
    ActionsWidget = Ghost.View.extend({

        events: {
            'click [data-set-status]': 'handleStatus',
            'click .js-post-button': 'updatePost'
        },

        statusMap: {
            'draft' : 'Save Draft',
            'published': 'Update Post',
            'scheduled' : 'Save Schedued Post'
        },

        initialize: function () {
            var self = this;
            // Toggle publish
            shortcut.add("Ctrl+Alt+P", function () {
                self.toggleStatus();
            });
            shortcut.add("Ctrl+S", function () {
                self.updatePost();
            });
            shortcut.add("Meta+S", function () {
                self.updatePost();
            });
            this.listenTo(this.model, 'change:status', this.render);
            this.model.on('change:id', function (m) {
                Backbone.history.navigate('/editor/' + m.id);
            });
        },

        toggleStatus: function () {
            var view = this,
                keys = Object.keys(this.statusMap),
                model = this.model,
                prevStatus = this.model.get('status'),
                currentIndex = keys.indexOf(prevStatus),
                newIndex;


            if (keys[currentIndex + 1] === 'scheduled') { // TODO: Remove once scheduled posts work
                newIndex = currentIndex + 2 > keys.length - 1 ? 0 : currentIndex + 1;
            } else {
                newIndex = currentIndex + 1 > keys.length - 1 ? 0 : currentIndex + 1;
            }

            this.savePost({
                status: keys[newIndex]
            }).then(function () {
                Ghost.notifications.addItem({
                    type: 'success',
                    message: 'Your post: ' + model.get('title') + ' has been ' + keys[newIndex],
                    status: 'passive'
                });
            }, function (response) {
                var status = keys[newIndex];
                // Show a notification about the error
                view.reportSaveError(response, model, status);
                // Set the button text back to previous
                model.set({ status: prevStatus });
            });
        },

        handleStatus: function (e) {
            e.preventDefault();
            var view = this,
                status = $(e.currentTarget).attr('data-set-status'),
                prevStatus = this.model.get('status'),
                model = this.model;

            if (status === 'publish-on') {
                Ghost.notifications.addItem({
                    type: 'alert',
                    message: 'Scheduled publishing not supported yet.',
                    status: 'passive'
                });
            }
            if (status === 'queue') {
                Ghost.notifications.addItem({
                    type: 'alert',
                    message: 'Scheduled publishing not supported yet.',
                    status: 'passive'
                });
            }

            this.savePost({
                status: status
            }).then(function () {
                Ghost.notifications.addItem({
                    type: 'success',
                    message: 'Your post: ' + model.get('title') + ' has been ' + status,
                    status: 'passive'
                });
            }, function (response) {
                // Show a notification about the error
                view.reportSaveError(response, model, status);
                // Set the button text back to previous
                model.set({ status: prevStatus });
            });
        },

        updatePost: function (e) {
            if (e) {
                e.preventDefault();
            }
            var view = this,
                model = this.model;
            this.savePost().then(function () {
                Ghost.notifications.addItem({
                    type: 'success',
                    message: 'Your post was saved as ' + model.get('status'),
                    status: 'passive'
                });
            }, function (request) {
                var message = view.getRequestErrorMessage(request) || model.validationError;

                Ghost.notifications.addItem({
                    type: 'error',
                    message: message,
                    status: 'passive'
                });
            });
        },

        savePost: function (data) {
            // TODO: The content_raw getter here isn't great, shouldn't rely on currentView.
            _.each(this.model.blacklist, function (item) {
                this.model.unset(item);
            }, this);

            var saved = this.model.save(_.extend({
                title: $('#entry-title').val(),
                content_raw: Ghost.currentView.editor.getValue()
            }, data));

            // TODO: Take this out if #2489 gets merged in Backbone. Or patch Backbone
            // ourselves for more consistent promises.
            if (saved) {
                return saved;
            }
            return $.Deferred().reject();
        },

        reportSaveError: function (response, model, status) {
            var title = model.get('title') || '[Untitled]',
                message = 'Your post: ' + title + ' has not been ' + status;

            if (response) {
                // Get message from response
                message = this.getErrorMessageFromResponse(response);
            } else if (model.validationError) {
                // Grab a validation error
                message += "; " + model.validationError;
            }

            Ghost.notifications.addItem({
                type: 'error',
                message: message,
                status: 'passive'
            });
        },

        render: function () {
            this.$('.js-post-button').text(this.statusMap[this.model.get('status')]);
        }

    });

    // The entire /editor page's route (TODO: move all views to client side templates)
    // ----------------------------------------
    Ghost.Views.Editor = Ghost.View.extend({

        initialize: function () {

            // Add the container view for the Publish Bar
            this.addSubview(new PublishBar({el: "#publish-bar", model: this.model})).render();

            this.$('#entry-markdown').html(this.model.get('content_raw'));

            this.initMarkdown();
            this.renderPreview();

            $('.entry-content header, .entry-preview header').on('click', function () {
                $('.entry-content, .entry-preview').removeClass('active');
                $(this).closest('section').addClass('active');
            });

            $('.entry-title .icon-fullscreen').on('click', function (e) {
                e.preventDefault();
                $('body').toggleClass('fullscreen');
            });

            $('.options.up').on('click', function (e) {
                e.stopPropagation();
                $(this).next("ul").fadeToggle(200);
            });

            this.$('.CodeMirror-scroll').on('scroll', this.syncScroll);

            // Shadow on Markdown if scrolled
            this.$('.CodeMirror-scroll').on('scroll', function (e) {
                if ($('.CodeMirror-scroll').scrollTop() > 10) {
                    $('.entry-markdown').addClass('scrolling');
                } else {
                    $('.entry-markdown').removeClass('scrolling');
                }
            });

            // Shadow on Preview if scrolled
            this.$('.entry-preview-content').on('scroll', function (e) {
                if ($('.entry-preview-content').scrollTop() > 10) {
                    $('.entry-preview').addClass('scrolling');
                } else {
                    $('.entry-preview').removeClass('scrolling');
                }
            });

            // Zen writing mode shortcut
            shortcut.add("Alt+Shift+Z", function () {
                $('body').toggleClass('zen');
            });

            $('.entry-markdown header, .entry-preview header').click(function (e) {
                $('.entry-markdown, .entry-preview').removeClass('active');
                $(e.target).closest('section').addClass('active');
            });

        },

        events: {
            'click .markdown-help': 'showHelp'
        },

        syncScroll: _.debounce(function (e) {
            var $codeViewport = $(e.target),
                $previewViewport = $('.entry-preview-content'),
                $codeContent = $('.CodeMirror-sizer'),
                $previewContent = $('.rendered-markdown'),

                // calc position
                codeHeight = $codeContent.height() - $codeViewport.height(),
                previewHeight = $previewContent.height() - $previewViewport.height(),
                ratio = previewHeight / codeHeight,
                previewPostition = $codeViewport.scrollTop() * ratio;

            // apply new scroll
            $previewViewport.scrollTop(previewPostition);
        }, 50),

        showHelp: function () {
            this.addSubview(new Ghost.Views.Modal({
                model: {
                    options: {
                        close: true,
                        type: "info",
                        style: "wide",
                        animation: 'fadeIn'
                    },
                    content: {
                        template: 'markdown',
                        title: 'Markdown Help'
                    }
                }
            }));
        },

        // This updates the editor preview panel.
        // Currently gets called on every key press.
        // Also trigger word count update
        renderPreview: function () {
            var view = this,
                preview = document.getElementsByClassName('rendered-markdown')[0];
            preview.innerHTML = this.converter.makeHtml(this.editor.getValue());
            view.$('.js-drop-zone').upload({editor: true});
            Countable.once(preview, function (counter) {
                view.$('.entry-word-count').text($.pluralize(counter.words, 'word'));
                view.$('.entry-character-count').text($.pluralize(counter.characters, 'character'));
                view.$('.entry-paragraph-count').text($.pluralize(counter.paragraphs, 'paragraph'));
            });
        },

        // Markdown converter & markdown shortcut initialization.
        initMarkdown: function () {
            this.converter = new Showdown.converter({extensions: ['ghostdown']});
            this.editor = CodeMirror.fromTextArea(document.getElementById('entry-markdown'), {
                mode: 'markdown',
                tabMode: 'indent',
                tabindex: "2",
                lineWrapping: true,
                dragDrop: false
            });

            var view = this;

            // Inject modal for HTML to be viewed in
            shortcut.add("Ctrl+Alt+C", function () {
                view.showHTML();
            });
            shortcut.add("Ctrl+Alt+C", function () {
                view.showHTML();
            });

            _.each(MarkdownShortcuts, function (combo) {
                shortcut.add(combo.key, function () {
                    return view.editor.addMarkdown({style: combo.style});
                });
            });

            this.editor.on('change', function () {
                view.renderPreview();
            });
        },

        showHTML: function () {
            this.addSubview(new Ghost.Views.Modal({
                model: {
                    options: {
                        close: true,
                        type: "info",
                        style: "wide",
                        animation: 'fadeIn'
                    },
                    content: {
                        template: 'copyToHTML',
                        title: 'Copied HTML'
                    }
                }
            }));
        },

        render: function () { return this; }
    });

}());