/**
 * HTCA (Here's the code asshole)- jQuery plugin 0.1
 *
 * Requires jQuery Ajax File Upload plugin:
 *
 * http://bit.ly/1JZsV1
 *
 * Copyright (c) 2009 Mahalo.com, Inc
 *
 */
 
(function($) {
    // jQuery hook.
    $.fn.htca = function(options) {
        
        var defaults = {
            tools : ['link', 'citation', 'image', 'bulleted-list', 'numbered-list'],   
            apiKeys : [],
            imageUploadUrl : "",
            imageClearUrl : "",
            pageSlug : "",
            sectionId : ""
        };

        options = $.extend(defaults, options);
        
        return this.each(function() {
            Htca(this, options);
        });
    };
    
    // Htca object constructor
    function Htca(element, options) {
        return this instanceof Htca ? this.init(element, options) : new Htca(element, options);
    }
    
    // Here's the fanciness.
    $.extend(Htca.prototype, {
        selectedTextStart : 0,
        selectedTextEnd : 0,
        selectedText : '', // always trimmed in setSelectedText
        
        editor : null,
        options : {},
        
        init : function(element, options) {
            var self = this;
            
            self.editor = element;
            self.options = options || {};
            self.TOOLBAR = Htca.TOOLBAR;
            
            // Create the toolbar. Only works for textareas.
            if (self.editor.nodeName.toLowerCase() == 'textarea') {
                $(self.editor).before("<div class='htca'><ul class='panel'></ul><br /></div><br />");
                $(self.editor).after("<div class='htca-image'></div><div class='htca-prompts'></div><div class='htca-errors'></div>");
                
                self.container =  $(self.editor).prev().prev();
                
                // Make sure html is reset for each panel
                self.panel = self.container.children(".panel");
                
                self.image = $(self.editor).next();
                self.prompts = self.image.next();
                self.errors = self.prompts.next();
                        
                // Create those tools enabled in options
                $.each(self.options.tools, function(idx, value) {
                    // create link tool initialization
                    if (value == self.TOOLBAR.createLink.title) {
                        var tool = $("<li><a title=\"insert a link\" class='"+self.TOOLBAR.createLink.className+"'></a></li>");
                        var separator = $("<li class='"+self.TOOLBAR.separator.className+"'></li>");
                        
                        // Attach the tool to the panel
                        self.panel.append(tool);
                        
                        // Attach the link prompter to the container
                        var prompt = $(self.TOOLBAR.createLink.prompt);
                        self.prompts.append(prompt);
                        
                        // Only append the separator if this isn't the last tool
                        if(idx < self.options.tools.length - 1) {
                            self.panel.append(separator);
                        }
                        
                        // click event for create link tool
                        tool.click(function(e) {
                            e.preventDefault();
                            self.createLink();
                        });
                        
                        // click event for the link prompt ok button
                        prompt.find("."+self.TOOLBAR.createLink.promptOkButtonClassName).click(function(e) {
                            e.preventDefault();
                            
                            var link = $.trim(prompt.find("input").val());
                            
                            if(link != "") {
                                var newText = self.getLinkText(link);
                                self.insertText(self.selectedTextStart, newText);
                                prompt.slideUp();
                            }
                        });
                        
                        // click event for the link prompt cancel button
                        prompt.find("."+self.TOOLBAR.createLink.promptCancelButtonClassName).click(function(e) {
                            prompt.slideUp();
                        });
                    }
                    
                    // create citation tool initialization
                    else if (value == self.TOOLBAR.createCitation.title) {
                        var tool = $("<li><a title=\"insert a citation\" class='"+self.TOOLBAR.createCitation.className+"'></a></li>");
                        var separator = $("<li class='"+self.TOOLBAR.separator.className+"'></li>");
                        
                        // Attach the tool to the panel
                        self.panel.append(tool);
                        
                        // Attach the link prompter to the container
                        var prompt = $(self.TOOLBAR.createCitation.prompt);
                        self.prompts.append(prompt);
                        
                        // Only append the separator if this isn't the last tool
                        if(idx < self.options.tools.length - 1) {
                            self.panel.append(separator);
                        }
                        
                        // click event for create citation tool
                        tool.click(function(e) {
                            e.preventDefault();
                            self.createCitation();
                        });
                        
                        // click event for the citation prompt ok button
                        prompt.find("."+self.TOOLBAR.createCitation.promptOkButtonClassName).click(function(e) {
                            e.preventDefault();
                            var citation_url = $.trim(prompt.find("input").val());
                            
                            // make sure citation URL is valid
                            if(citation_url.match(Htca.VALIDATE_RE.url)) {
                                var newText = self.selectedText + " " + self.getCitationText(citation_url);
                                self.insertText(self.selectedTextStart, newText);
                                prompt.slideUp();
                            }
                            else {
                                self.showError(Htca.MSGS_EN.invalidUrl);
                            }
                        });
                        
                        // click event for the citation prompt cancel button
                        prompt.find("."+self.TOOLBAR.createCitation.promptCancelButtonClassName).click(function(e) {
                            e.preventDefault();
                            prompt.find("input").val("");
                            prompt.slideUp();
                        });
                    }
                    
                    // insert image tool initialization
                    else if (value == self.TOOLBAR.insertImage.title) {
                        var tool = $("<li><a title =\"insert an image\" class='"+self.TOOLBAR.insertImage.className+"'></a></li>");
                        var separator = $("<li class='"+self.TOOLBAR.separator.className+"'></li>");
                        
                        // Attach the tool to the panel
                        self.panel.append(tool);
                        
                        // Attach the image prompter to the container
                        var prompt = $(self.TOOLBAR.insertImage.prompt);
                        self.prompts.append(prompt);
                        
                        // Only append the separator if this isn't the last tool
                        if(idx < self.options.tools.length - 1) {
                            self.panel.append(separator);
                        }
                        
                        // click event for insert image tool
                        tool.click(function(e) {
                            e.preventDefault();
                            self.insertImage();
                        });
                        
                        // set the page slug for this instance of the image prompter
                        // page_slug is needed in order to tie image to a page
                        prompt.find("input[name="+self.TOOLBAR.insertImage.promptPageSlugName+"]").val(self.options.pageSlug);
                                                                                                                              
                        // set the section id for this instance of the image prompter
                        // section_id is needed in order to tie image to a section
                        prompt.find("input[name="+self.TOOLBAR.insertImage.promptSectionIdName+"]").val(self.options.sectionId);
                                                
                        // change event for the image option radio 
                        prompt.find("input[name="+self.TOOLBAR.insertImage.promptFromRadioName+"]").change(function(e) {
                            e.preventDefault();
                            
                            if ($(this).val() == "upload") {
                                $(this).siblings("."+self.TOOLBAR.insertImage.promptBrowseClassName).show();
                            }
                            else {
                                $(this).siblings("."+self.TOOLBAR.insertImage.promptBrowseClassName).hide();
                            }
                        });
                        
                        // click event for the image prompt ok button
                        prompt.find("."+self.TOOLBAR.insertImage.promptOkButtonClassName).click(function(e) {
                            e.preventDefault();
                            
                            var image_from = prompt.find("input[name="+self.TOOLBAR.insertImage.promptFromRadioName+"]:checked").val();
                                                        
                            image_from == "upload" ? self.doImageUpload() : self.setImageFromImageSection();
                        });
                        
                        // click event for the image prompt cancel button
                        prompt.find("."+self.TOOLBAR.insertImage.promptCancelButtonClassName).click(function(e) {
                            e.preventDefault();
                            prompt.find("input").val("");
                            prompt.slideUp();
                        });
                    }
                    
                    // create bulleted-list tool initialization
                    else if (value == self.TOOLBAR.createBulletedList.title) {
                        var tool = $("<li><a title=\"insert a bulleted list\" class='"+self.TOOLBAR.createBulletedList.className+"'></a></li>");
                        var separator = $("<li class='"+self.TOOLBAR.separator.className+"'></li>");
                        
                        // Attach the tool to the panel
                        self.panel.append(tool);
                        
                        // Only append the separator if this isn't the last tool
                        if(idx < self.options.tools.length - 1) {
                            self.panel.append(separator);
                        }
                        
                        // click event for insert image tool
                        tool.click(function(e) {
                            e.preventDefault();
                            self.createBulletedList();
                        });
                    }
                    
                    // create numbered-list tool initialization
                    else if (value == self.TOOLBAR.createNumberedList.title) {
                        var tool = $("<li><a title=\"insert a numbered list\" class='"+self.TOOLBAR.createNumberedList.className+"'></a></li>");
                        var separator = $("<li class='"+self.TOOLBAR.separator.className+"'></li>");
                        
                        // Attach the tool to the panel
                        self.panel.append(tool);
                        
                        // Only append the separator if this isn't the last tool
                        if(idx < self.options.tools.length - 1) {
                            self.panel.append(separator);
                        }
                        
                        // click event for insert image tool
                        tool.click(function(e) {
                            e.preventDefault();
                            self.createNumberedList();
                        });
                    }
                });

                // select event that stores selected text - also hides errors (indicates user action)
                $(self.editor).select(function(e){
                    self.setSelectedText();
                    self.errors.hide();
                });
            }
        },

        // click event for create link tool
        createLink : function() {
            var self = this;
            
            if($.trim(self.selectedText) == "") {        
                self.showError(Htca.MSGS_EN.nonSelectionLink);
            }
            else {
                //self.prompts.find("." + self.TOOLBAR.createLink.promptClassName).slideDown();
                var newText = self.getLinkText(self.selectedText);
                self.insertText(self.selectedTextStart, newText);
            }
        },
        
        // click event for create citation tool
        createCitation : function() {
            var self = this;
            
            if($.trim(self.selectedText) == "") {
                self.showError(Htca.MSGS_EN.nonSelectionCitation);
            }
            else {
                self.prompts.find("." + self.TOOLBAR.createCitation.promptClassName).slideDown();
            }
        },
        
        // click event for insert image tool
        insertImage : function() {
            var self = this;
            self.prompts.find("." + self.TOOLBAR.insertImage.promptClassName).slideDown();
        },

        // click event for create bulleted list tool
        createBulletedList : function () {
            var self = this;
            
            if($.trim(self.selectedText) == "") {        
                self.showError(Htca.MSGS_EN.nonSelectionList);
            }
            else {
                var newText = self.getListText(self.selectedText, "bulleted");
                self.insertText(self.selectedTextStart, newText);
            }
        },
        
        // click event for create ordered list tool
        createNumberedList : function() {
            var self = this;
            
            if($.trim(self.selectedText) == "") {        
                self.showError(Htca.MSGS_EN.nonSelectionList);
            }
            else {
                var newText = self.getListText(self.selectedText, "numbered");
                self.insertText(self.selectedTextStart, newText);
            }
        },

        // Set the selected text and indexes for the editor
        setSelectedText : function() {
            var self = this;
            
            // For IE
            if (self.editor.selectionStart === undefined){
                var r = document.selection.createRange();
                self.selectedTextStart = r.start;
                self.selectedTextEnd = r.end;
                self.selectedText = $.trim($(self.editor).val().substring(r.start, r.end));
            }
            
            // For others
            else {
                self.selectedTextStart = self.editor.selectionStart;
                self.selectedTextEnd = self.editor.selectionEnd;
                self.selectedText = $.trim($(self.editor).val().substring(self.editor.selectionStart, self.editor.selectionEnd));
            }
        },
        
        // Injects a message in to the appropriate error container
        showError : function(msg) {
            var self = this;
            
            self.errors.html(msg).show().animate({
                opacity: 1
            }, 3000, function() {
                self.errors.slideUp();
                self.errors.html("");
                
                //self.errors.css("opacity", "1");
            });
        },
        
        // inserts text into the editor starting at a given position
        insertText : function(pos, txt) {
            var self = this;
            
            // only do this if there is something to inject
            if(self.selectedText) {
                // rebuild the string by saving ends of insertion point and injecting text inside
                var all =  $(self.editor).val();
                var before = all.substring(0, self.selectedTextStart);
                var after = all.substring(self.selectedTextEnd, all.length);
                
                // rebuilt
                var updatedText = before + txt + after;
                
                // update entire editor text string
                $(self.editor).val(updatedText);
            }
        },
        
        // specifies the method by which wiki link text is generated
        getLinkText : function (link_title) {
            return "[[" + link_title + "]]";
        },
        
        // specifies the method by which citation text is generated
        getCitationText : function(url) {
            return "<ref>" + url + "</ref>";
        },
        
        // does the image upload
        doImageUpload : function() {
            var self = this;
            
            // image upload url has been set
            if($.trim(self.options.imageUploadUrl) != "") {  
                var args = self.prompts.find("."+self.TOOLBAR.insertImage.promptClassName).find("form").serializeArray();
                args = args.concat(self.options.apiKeys);
                
                data = {};

                $.each(args, function(idx, val){
                    var name = args[idx].name;
                    var value = args[idx].value;
                                      
                    
                    //alert("name: " + name + ", value: " + value);
                    
                    data[name] = value;
                });
                
                form = self.prompts.find("."+self.TOOLBAR.insertImage.promptClassName).find("form");
                inputField = form.find("."+self.TOOLBAR.insertImage.promptFilePathClassName);
                
                self.image.html("uploading...");   
                
                /* jQuery.ajaxupload */
                $.ajaxFileUpload({
                    url: self.options.imageUploadUrl,
                    secureuri: false,
                    fileElement: inputField,
                    data: data,
                    dataType: 'html', 
                    success: function (data, status) {
                        self.image.html("");
                         
                        if(data.status === false) {
                            self.showError(data.msg);
                        }
                        else {             
                            // do success stuff here 
                            self.image.html("Current image: <a href='" + data.msg.url + "' target='_blank'>" + data.msg.url.substring(0, 80) + "...</a>");  
                            self.prompts.find("."+self.TOOLBAR.insertImage.promptClassName).slideUp();                                      
                        }
                    },
                    error: function (xhr, msg, thrownError) {
                        self.image.html("");       
                           
                        // If the response text is a url, it's not an error.
                        // Weird, I know.
                        if(xhr.responseText.match(Htca.VALIDATE_RE.url)) {
                            data = {
                                status: true,
                                msg : {
                                    url: xhr.responseText
                                }
                            };
                            this.success(data);
                        }
                        else {
                            self.showError(xhr.responseText);
                        }
                    }
                });
            }
            else {
                self.showError(Htca.MSGS_EN.noImageUploadUrlSet);
            }
        },
        
        // clears the image from being associated with a page.
        setImageFromImageSection : function() {
            var self = this;
            
            // image clear url has been set
            if($.trim(self.options.imageClearUrl) != "") {     
                var args = self.prompts.find("."+self.TOOLBAR.insertImage.promptClassName).find("form").serializeArray();
                args = args.concat(self.options.apiKeys);    
                
                // try to do the "unset".
                $.post(self.options.imageClearUrl, args, function (data) { 
                    // update failed
                    if (data.status === false) {
                        self.showError(data.msg);
                    }
                    // update succeeded
                    else {
                        self.image.html("");
                        self.prompts.find("."+self.TOOLBAR.insertImage.promptClassName).slideUp();
                    }
                }, "json");
            }
            else { 
                self.showError(Htca.MSGS_EN.noImageClearUrlSet);
            }
        },
        
        // specifies the method by which wiki list text is generated
        // handles two types, bulleted and numbered, defaults to bulleted
        getListText : function (text, type) {
            var listComponents = text.split("\n");
            var newText = '';
            for(var i=0; i<listComponents.length; i++) { 
                if(type == "numbered") {
                    newText += "#" + listComponents[i] + "\n";
                }
                else {
                    newText += "*" + listComponents[i] + "\n";
                }
            }
            return newText;
        }
    });
    
    // HTCA static properties
    $.extend(Htca, {
        TOOLBAR : {
            // TODO: move all static strings to this part of the object
            // TODO: remove all "Name" suffixes 
            createLink : {
                title                       : "link",
                className                   : "link", 
                prompt                      : " <div class='create-link-prompt'>\
                                                    Link URL: <input type='text' name='create-link-prompt' class='textedit' />\
                                                    <button class='create-link-ok fg-button ui-state-default ui-corner-all'>Ok</button>\
                                                    <button class='create-link-cancel fg-button ui-state-default ui-corner-all'>Cancel</button>\
                                                </div>",
                                                
                // these should match those that are used in the prompt markup
                promptClassName             : "create-link-prompt",
                promptOkButtonClassName     : "create-link-ok",
                promptCancelButtonClassName : "create-link-cancel"
            },
            
            createCitation : {
                title                       : "citation",
                className                   : "citation",
                prompt                      : " <div class='create-citation-prompt'>\
                                                    Citation URL: <input type='text' name='create-citation-prompt' class='textedit' />\
                                                    <button class='create-citation-ok fg-button ui-state-default ui-corner-all'>Ok</button>\
                                                    <button class='create-citation-cancel fg-button ui-state-default ui-corner-all'>Cancel</button>\
                                                </div>",
                
                // these should match those that are used in the prompt markup
                promptClassName             : "create-citation-prompt",
                promptOkButtonClassName     : "create-citation-ok",
                promptCancelButtonClassName : "create-citation-cancel"
            },
            
            insertImage : {
                title                       : "image",
                className                   : "image",
                prompt                      : " <div class='insert-image-prompt'>\
                                                    Insert Image: <br />\
                                                    <form name='insert-image-form' enctype='multipart/form-data'> \
                                                        <input type='hidden' name='insert-image-page-slug' value='' /> \
                                                        <input type='hidden' name='insert-image-section-id' value='' /> \
                                                        <input type='radio' name='insert-image-from' value='upload' /> upload \
                                                        <input type='radio' name='insert-image-from' value='image-section' checked /> use photo section \
                                                        <div class='insert-image-browse'>\
                                                            <input type='file' name='insert-image-file-path' class='insert-image-file-path' />\
                                                        </div>\
                                                        <button class='insert-image-ok fg-button ui-state-default ui-corner-all'>Ok</button>\
                                                        <button class='insert-image-cancel fg-button ui-state-default ui-corner-all'>Cancel</button>\
                                                    </form>\
                                                </div>",
                                                
                // these should match those that are used in the prompt markup
                promptClassName             : "insert-image-prompt",
                promptFormName              : "insert-image-form",
                promptPageSlugName          : "insert-image-page-slug",
                promptSectionIdName         : "insert-image-section-id",
                promptFromRadioName         : "insert-image-from",
                promptBrowseClassName       : "insert-image-browse",
                promptFilePathName          : "insert-image-file-path", 
                promptFilePathClassName     : "insert-image-file-path", 
                promptOkButtonClassName     : "insert-image-ok",
                promptCancelButtonClassName : "insert-image-cancel"
            },
            
            createBulletedList : {
                title     : "bulleted-list",
                className : "bulleted-list"
            },
            
            createNumberedList : {
                title     : "numbered-list",
                className : "numbered-list"
            },
            
            separator : {
                className : "separator"
            }
        },
        
        MSGS_EN : {
            nonSelectionLink     : 'Please select the text you wish to link.',
            nonSelectionCitation : 'Please select the text you wish to create a citation for.',
            nonSelectionList     : 'Please select the text you wish to create a list for.',
            noImageUploadUrlSet  : 'We were unable to upload your image (no image upload URL has been set).',
            noImageClearUrlSet   : 'We were unable to clear your image (no image clear URL has been set).',
            imageUploadError     : 'Sorry, the upload failed. Please try again.',
            invalidUrl           : 'Please enter a valid URL (ex. http://google.com)'
        },
        
        VALIDATE_RE : {
            url : /https?:\/\/[A-Za-z0-9\.-]{3,}\.[A-Za-z]{3}/
        }
    });
})(jQuery);
