/*!
 * googoose 1.0.2
 * https://github.com/aadel112/googoose/js/jquery.googoose.js
 * @license Apache 2.0
 *
 * Copyright (C) 2016 - aadel112.com - A project by Aaron Adel    
 */
(function ( $ ) {

    $.fn.googoose = function( options, callback ) {

        var GG = this;
        var now = new Date().getTime();
        var proto = new RegExp(/^(http|https|file):/);
        var ab = new RegExp(/^\//);

        GG.finish = function() {
            if( options.debug ) 
                GG.debug_fn('finish action');
            if (callback) {
                var blob = new Blob([options.html], {
                    type: 'application/msword'
                });
                callback(null, blob);
            } else {
                GG.saveHtmlAsFile(options.filename, options.html);
            }
        }

        var options = $.extend({
            // These are the defaults.
            area: 'div.googoose-wrapper',
            headerfooterid: 'googoose-hdrftrtbl',
            margins: '1.0in',
            zoom: '75',
            filename: 'Doc1_' + now + '.doc',
            size: '8.5in 11.0in',
            display: 'Print',
            lang: 'en-US',
            toc: 'div.googoose.toc',
            pagebreak: 'div.googoose.break',
            headerarea: 'div.googoose.header',
            footerarea: 'div.googoose.footer',
            headerid: 'googoose-header',
            footerid: 'googoose-footer',
            headermargin: '.5in',
            footermargin: '.5in',
            currentpage: 'span.googoose.currentpage',
            totalpage: 'span.googoose.totalpage',
            finishaction: GG.finish,
            html: null,
            initobj: document,
            debugtype: 'alert',
            debug: 0
        }, options );
        GG.options = options;
        
        //http://requiremind.com/memoization-speed-up-your-javascript-performance/
        GG.memoize = function(fn, resolver) {
            var memoized = function() {
                resolver  = resolver || JSON.stringify;
                var cache = memoized.cache;
                var args  = Array.prototype.slice.call(arguments);
                var key   = resolver.apply(this, args);
                if(key in cache) {
                    GG.debug_fn('hit cache');
                    return cache[key];
                }
                var result = fn.apply(this, arguments);
                cache[key] = result;
                return result;
            };
            memoized.cache = {};
            return memoized;
        }

        GG.debug_fn = function( args ) {
            options.debugtype == 'console' ? console.log( args ) : alert( args );
        }

        //http://stackoverflow.com/questions/18755750/saving-text-in-a-local-file-in-internet-explorer-10
        GG.saveHtmlAsFile = function(
                fileNameToSaveAs, textToWrite
                ) {
            /* Saves a text string as a blob file*/  
            var ie = navigator.userAgent.match(/MSIE\s([\d.]+)/),
            ie11 = navigator.userAgent.match(/Trident\/7.0/) && navigator.userAgent.match(/rv:11/),
                ieEDGE = navigator.userAgent.match(/Edge/g),
                ieVer=(ie ? ie[1] : (ie11 ? 11 : (ieEDGE ? 12 : -1)));

            if (ie && ieVer<10) {
                console.log("No blobs on IE ver<10");
                return;
            }

            var textFileAsBlob = new Blob([textToWrite], {
                type: 'application/msword'
            });

            if (ieVer>-1) {
                window.navigator.msSaveBlob(textFileAsBlob, fileNameToSaveAs);

            } else {
                var downloadLink = document.createElement("a");
                downloadLink.download = fileNameToSaveAs;
                downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
                downloadLink.onclick = function(e) { document.body.removeChild(e.target); };
                downloadLink.style.display = "none";
                document.body.appendChild(downloadLink);
                downloadLink.click();
            }
        }

        // http://stackoverflow.com/questions/7394748/whats-the-right-way-to-decode-a-string-that-has-special-html-entities-in-it
        GG.decodeHtmlEntity = function(str) {
            return str.replace(/&#(\d+);/g, function(match, dec) {
            return String.fromCharCode(dec);
            });
        }


        GG.translate_mso_features = function( html ) {
            if( options.debug ) 
                GG.debug_fn('GG.translate_mso_features');

            html = GG.decodeHtmlEntity(html);
            html = GG.remove_bad_tags(html);
            html = GG.convert_pagebreaks(html);
            html = GG.convert_toc(html);
            html = GG.convert_hdrftr(html);
            html = GG.convert_imgs(html);

            return html;
        }

        GG.remove_bad_tags = function( html ) {
            if( options.debug )
                GG.debug_fn('GG.remove_bad_tags');
            var thtml = $(html);

            
            thtml.find('noscript').each(function() {
                $(this).replaceWith('');  
            });
            thtml.each(function() {
                if($(this).is(':hidden')){
                    $(this).remove();
                }
            });

            html = thtml[0].outerHTML;
            return html;
        }

        GG.convert_pagebreaks = function( html ) {
            if( options.debug )
                GG.debug_fn('GG.convert_pagebreaks');
            //user decides in html what will be a page break in word, this converts to a page break
            if( options.pagebreak ) {
                var thtml = $(html);
                thtml.find(options.pagebreak).replaceWith(GG.get_pagebreak());
                html = thtml[0].outerHTML;
            }
            return html;
        }

        GG.convert_toc = function( html ) {
            if( options.debug )
                GG.debug_fn('GG.convert_toc');
            //user determines in html what will be the toc in word
            if( options.toc && $(options.toc).length ) {
                var thtml = $(html);
                thtml.find(options.toc).replaceWith(GG.get_toc_contents());
                html = thtml[0].outerHTML;
            }
            return html; 
        }

        GG.convert_hdrftr = function( html ) {
            if( options.debug )
                GG.debug_fn('GG.convert_hdrftr');
            var hvis = options.headerarea && $(options.headerarea).length;
            var fvis = options.footerarea && $(options.footerarea).length;
            if( hvis || fvis ) {
                var thtml = $('<div>' + html + '</div>' );
                var hdrftr = $('<table id=\'' + options.headerfooterid + '\'></table>');
                hdrftr.append('<tr><td class=h></td><td class=f></td></tr>');
                thtml.append(hdrftr);
                html = thtml[0].outerHTML;

                html = GG.convert_totalpage(html);
                html = GG.convert_currentpage(html);
            }

            var thtml = $(html);
            if( hvis ) {
                var new_header = thtml.find(options.headerarea)[0].outerHTML;
                thtml.find(options.headerarea).replaceWith('');
                thtml.find('table#' + options.headerfooterid + ' .h').append( 
                        GG.headerstart() + new_header + GG.headerend() );
                html = thtml[0].outerHTML;
            }
            if( fvis ) {
                var new_footer = thtml.find(options.footerarea)[0].outerHTML;
                thtml.find(options.footerarea).replaceWith('');
                thtml.find('table#' + options.headerfooterid + ' .f').append( 
                        GG.footerstart() + new_footer + GG.footerend());
                html = thtml[0].outerHTML;
            }
            return html;

        }

        GG.convert_imgs = function( html ) {
            if( options.debug )
                GG.debug_fn('GG.convert_imgs');
            //make sure all standard images use absolute path 
            var thtml = $(html);
            imgs = thtml.find('img');
            imgs.each(function() {
                var src = $(this)[0].src;
                var l = window.location;
                var t = l.protocol + '//' + l.host + '/';
                if( proto.test( src ) ) {
                } else if( ab.test( src ) ) {
                    src = t + src; 
                } else {
                   var p = l.path.replace('/\/[^\/.]+$/', '/' );
                   src = t + p + src;
                }
                $(this).attr( 'src', src );
            });
            html = thtml[0].outerHTML;
            return html;
        }

        GG.convert_totalpage = function(html) {
            if( options.debug )
                GG.debug_fn('GG.convert_totalpage');
            if( options.totalpage && $(options.totalpage).length ) {
                var thtml = $(html);
                thtml.find(options.totalpage).html('');
                thtml.find(options.totalpage).append( GG.get_total_page_number() );
                html = thtml[0].outerHTML;
            }
            return html;
        }

        GG.convert_currentpage = function(html) {
            if( options.debug )
                GG.debug_fn('GG.convert_currentpage');
            if( options.currentpage && $(options.currentpage).length ) {
                var thtml = $(html);
                thtml.find(options.currentpage).html('');
                thtml.find(options.currentpage).append( GG.get_page_number() );
                html = thtml[0].outerHTML;
            }
            return html;
        }

        GG.get_pagebreak = function() {
            if( options.debug ) 
                GG.debug_fn('GG.get_pagebreak');
            return '<br clear=all style=\'mso-special-character:line-break;page-break-before:always\'>';
        }

        GG.headerstart = function() {
            var html = '';
            html += '\n<div style=\'mso-element:header\' id=' + options.headerid + '>\n';
            html += '<p class="MsoHeader">\n';
            return html;
        }
        GG.headerend = function() {
            if( options.debug )
                GG.debug_fn('GG.headerend');
            return '</p></div>\n';
        }

        GG.footerstart = function() {
            if( options.debug ) 
                GG.debug_fn('GG.footerstart');
            var html = '';
            html += '<div style=\'mso-element:footer\' id=' + options.footerid + '>';
            return html;
        }
        GG.footerend = function() {
            if( options.debug )
                GG.debug_fn('GG.footerend');
            return '</div>\n';
        }

        GG.get_page_number = function() {
            if( options.debug )
                GG.debug_fn('GG.get_page_number');
            var html = '<!--[if supportFields]><span\n';
            html += 'class=MsoPageNumber><span style=\'mso-element:field-begin\'></span><span\n';
            html += 'style=\'mso-spacerun:yes\'> </span>PAGE <span style=\'mso-element:field-separator\'></span></span><![endif]--><span\n';
            html += 'class=MsoPageNumber><span style=\'mso-no-proof:yes\'>1</span></span><!--[if supportFields]><span\n';
            html += 'class=MsoPageNumber><span style=\'mso-element:field-end\'></span></span><![endif]-->';   
            return html;
        }

        GG.get_total_page_number = function() {
            if( options.debug )
                GG.debug_fn('GG.get_total_page_number');
            var html = '<!--[if supportFields]><span class=MsoPageNumber><span \n';
            html += ' style=\'mso-element:field-begin\'></span> NUMPAGES <span style=\'mso-element:field-separator\'></span></span><![endif]--><span \n';
            html += ' class=MsoPageNumber><span style=\'mso-no-proof:yes\'>1</span></span><!--[if supportFields]><span \n'
                html += ' class=MsoPageNumber><span style=\'mso-element:field-end\'></span></span><![endif]-->\n';
            return html;
        }

        GG.get_toc_contents = function() {
            if( options.debug ) 
                GG.debug_fn('GG.get_toc_contents');
            var toc = '<p class=MsoToc1>\n';
            toc += '<!--[if supportFields]>\n';
            toc += '<span style=\'mso-element:field-begin\'></span>\n';
            toc += 'TOC \o "1-3" \\u \n';
            toc += '<span style=\'mso-element:field-separator\'></span>\n';
            toc += '<![endif]-->\n';
            toc += '<span style=\'mso-no-proof:yes\'>Table of content - Please right-click and choose "Update fields".</span>\n';
            toc += '<!--[if supportFields]>\n';
            toc += '<span style=\'mso-element:field-end\'></span>\n';
            toc += '<![endif]-->\n';
            toc += '</p>\n';

            return toc;
        }

        //TODO - figure out a way to simulate a right mpuse click, update fields


        GG.include_css = function( html ) {
            if( options.debug ) 
                GG.debug_fn('GG.include_css');
            //adding any header information that may be pertinent in teh copied html
            var tags = ['style', 'link'];
            for( i = 0; i < tags.length ; ++i ) {
                $(document).find(tags[i]).each( function( ) {
                    if(tags[i] != 'link' || ($(this).attr('rel') == 'stylesheet' && proto.test($(this).attr('href')))) {
                        html += ( '\n' + $(this)[0].outerHTML + '\n' );
                    }
                } );
            }
            return html;
        }

        GG.html = function() {
            if( options.debug ) 
                GG.debug_fn('GG.html');
            if( !$(options.area).length ) {
                return null;
            }
//             // fixes IE pre tag handling
//             $('pre').each(function() {
//                 $(this)[0].outerHTML = $(this)[0].outerHTML.replace(/\n/g, "<br />\n");
//             });
            // adding the standard mso header 
            var html = '<html xmlns:o=\'urn:schemas-microsoft-com:office:office\' xmlns:w=\'urn:schemas-microsoft-com:office:word\' xmlns=\'http://www.w3.org/TR/REC-html40\'>\n';
            html += '<head>\n';
            html += '<!--[if gte mso 9]>\n';
            html += '<xml>\n';
            html += '<w:WordDocument>\n';
            html += ( '<w:View>' + options.display + '</w:View>\n' );
            html += ('<w:Zoom>'+ options.zoom +'</w:Zoom>\n');
            html += '<w:DoNotOptimizeForBrowser/>\n';
            html += '</w:WordDocument>\n';
            html += '<o:OfficeDocumentSettings>\n';
            html += '<o:AllowPNG/>\n';
            html +='</o:OfficeDocumentSettings>\n';
            html += '</xml>\n';
            html += '<![endif]-->\n';
            html += '';

            html = GG.include_css( html );
            //adding in mso style necessesities
            html += '<style>\n';
            html += '<!--\n';
            html += '@page {\n';
            html += ('\tsize:' + options.size + ';\n');
            html += ('\tmargin:' + options.margins + ';\n');
            html += '}\n';
            html += '@page Container {\n';
            html += ('\tmso-header-margin:' + options.headermargin + ';\n' );
            html += ('\tmso-footer-margin:' + options.footermargin + ';\n' );
            html += ('\tmso-header:' + options.headerid + ';\n' );
            html += ('\tmso-footer:' + options.footerid + ';\n' );
            html += '}\n';
            html += 'div.Container { page:Container; }\n';
            html += ( 'table#' + options.headerfooterid + ' {\n' );
            html += '\tmargin:0in 0in 0in 9in;\n';
            html += '}\n';
            html += '-->\n';
            html += '</style>\n';

            //close head
            html += '</head>\n';

            //start body
            html += ('<body lang=' + options.lang + '>\n<div class=Container>');

            //add area content
            if($(options.initobj).is(options.area)) {
                if(options.debug)
                    GG.debug_fn('is');
                html += GG.translate_mso_features($(options.initobj)[0].outerHTML);
            } else {
                if(options.debug)
                    GG.debug_fn('no is');
                $(options.initobj).find(options.area).each(function(){
                    html += GG.translate_mso_features($(this)[0].outerHTML);
                });
            }

            //close body
            html += '</div></body>\n';

            //close doc
            html += '</html>\n';
            return html;
        }

        //memoized fns
        //		GG.pngname = GG.memoize(GG.get_png_name);


        //execution
        if( options.debug )
            GG.debug_fn('googoose exec');
        options.html = GG.html();
        if( options.html && options.finishaction ) {
            options.finishaction();   
        }
//         return options;
        return GG;
    };
}( jQuery ));
