/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets */
define(function (require, exports, module) {
    "use strict";

    var BaseServer              = brackets.getModule("LiveDevelopment/Servers/BaseServer").BaseServer,
        LiveDevelopmentUtils    = brackets.getModule("LiveDevelopment/LiveDevelopmentUtils"),
        Content                 = brackets.getModule("filesystem/impls/filer/lib/content"),
        BlobUtils               = brackets.getModule("filesystem/impls/filer/BlobUtils"),
        Filer                   = brackets.getModule("filesystem/impls/filer/BracketsFiler"),
        Path                    = Filer.Path,
        HTMLRewriter            = brackets.getModule("filesystem/impls/filer/lib/HTMLRewriter"),
        CSSRewriter             = brackets.getModule("filesystem/impls/filer/lib/CSSRewriter");

    var Compatibility           = require("lib/compatibility"),
        MouseManager            = require("lib/MouseManager"),
        LinkManager             = require("lib/LinkManager");

    var fs = Filer.fs(),
        _shouldUseBlobURL;

    function _isHTML(path) {
        return LiveDevelopmentUtils.isStaticHtmlFileExt(path);
    }

    function _isCSS(path) {
        return Content.isCSS(Path.extname(path));
    }

    function HTMLServer(config) {
        config = config || {};
        BaseServer.call(this, config);
    }

    HTMLServer.prototype = Object.create(BaseServer.prototype);
    HTMLServer.prototype.constructor = HTMLServer;

    //Returns a pre-generated blob url based on path
    HTMLServer.prototype.pathToUrl = function(path) {
        return BlobUtils.getUrl(path);
    };
    //Returns a path based on blob url
    HTMLServer.prototype.urlToPath = function(url) {
        return BlobUtils.getFilename(url);
    };

    HTMLServer.prototype.readyToServe = function () {
        var deferred = new $.Deferred();

        // Decide if we can use Blob URLs or need to document.write()
        Compatibility.supportsIFrameHTMLBlobURL(function(err, shouldUseBlobURL) {
            if(err) {
                console.error("[Brackets HTMLServer] Unexpected error:", err);
                deferred.reject();
            }

            _shouldUseBlobURL = shouldUseBlobURL;
            deferred.resolve();
        });

        return deferred.promise();
    };

    /**
     * Determines if this server can serve local file. LiveDevServerManager
     * calls this method when determining if a server can serve a file.
     * @param {string} localPath A local path to file being served.
     * @return {boolean} true When the file can be served, otherwise false.
     */
    HTMLServer.prototype.canServe = function (localPath) {
        // If we can't transform the local path to a project relative path,
        // the path cannot be served
        if (localPath === this._pathResolver(localPath)) {
            return false;
        }

        // Url ending in "/" implies default file, which is usually index.html.
        // Return true to indicate that we can serve it.
        if (localPath.match(/\/$/)) {
            return true;
        }

        return _isHTML(localPath);
    };

    /**
     * When a livedocument is added (CSS or HTML) to the server cache, make sure live
     * instrumentation is enabled
     */
    HTMLServer.prototype.add = function (liveDocument) {
        if (liveDocument.setInstrumentationEnabled) {
            // enable instrumentation
            liveDocument.setInstrumentationEnabled(true);
        }
        BaseServer.prototype.add.call(this, liveDocument);
    };

    /**
     * If a livedoc exists (HTML or CSS), serve the instrumented version of the file.
     */
    HTMLServer.prototype.serveLiveDocForUrl = function(url, callback) {
        var path = BlobUtils.getFilename(url);
        this.serveLiveDocForPath(path, callback);
    };

    HTMLServer.prototype.serveLiveDocForPath = function(path, callback) {
        var server = this;
        var liveDocument = this.get(path);

        function serveCSS(path, css, callback) {
            CSSRewriter.rewrite(path, css, function(err, css) {
                if(err) {
                    callback(err);
                    return;
                }
                callback(null, BlobUtils.createURL(path, css, "text/css"));
            });

        }

        function serveHTML(path, html, server, callback) {
            HTMLRewriter.rewrite(path, html, server, function(err, html) {
                if(err) {
                    callback(err);
                    return;
                }
                // We either serve raw HTML or a Blob URL depending on browser compatibility.
                callback(null, _shouldUseBlobURL ? BlobUtils.createURL(path, html, "text/html") : html);
            });
        }

        function serve(body) {
            if(_isHTML(path)) {
                serveHTML(path, body, server, callback);
            } else if (_isCSS(path)) {
                serveCSS(path, body, callback);
            } else {
                callback(new Error("[Brackets HTMLServer] expected .html or .css live doc type:" + path));
            }
        }

        // Prefer the LiveDoc, but use what's on disk if we have to
        if(liveDocument) {
            serve(liveDocument.getResponseData().body);
        } else {
            fs.readFile(path, "utf8", function(err, body) {
                if(err) {
                    return callback(err);
                }

                // Since we're not instrumenting this doc fully for some reason,
                // at least inject the scroll manager so we can track scroll position.
                body = body.replace(/<\/\s*head>/,
                    MouseManager.getRemoteScript(path) +
                    LinkManager.getRemoteScript() + "$&");
                serve(body);
            });
        }
    };

    exports.HTMLServer = HTMLServer;
});

@@ -5433,20 +5433,23 @@
 on(err, 
-body
+content
 ) %7B%0A    
@@ -5719,27 +5719,188 @@
     
-body = body.replace
+var scripts = MouseManager.getRemoteScript(path) + LinkManager.getRemoteScript();%0A                var scriptsWithEndTag = scripts + %22$&%22;%0A                var headRegex = new RegExp
 (/%3C%5C
@@ -5909,17 +5909,18 @@
 s*head%3E/
-,
+);
 %0A       
@@ -5932,107 +5932,640 @@
     
-    MouseManager.getRemoteScript(path) +%0A                    LinkManager.getRemoteScript() + %22$&%22);
+var htmlRegex = new RegExp(/%3C%5C/%5Cs*html%3E/);%0A%0A                // Try to inject the scripts at the end of the %3Chead%3E element%0A                // if it is present%0A                if(headRegex.test(content)) %7B%0A                    content = content.replace(headRegex, scriptsWithEndTag);%0A                %7D else if(htmlRegex.test(content)) %7B%0A                // Otherwise add them at the end of the %3Chtml%3E element%0A                    content = content.replace(htmlRegex, scriptsWithEndTag);%0A                %7D else %7B%0A                // Otherwise just add it at the end of the content%0A                    content += scripts;%0A                %7D%0A
 %0A   
@@ -6583,20 +6583,23 @@
   serve(
-body
+content
 );%0A     

Fix mozilla/thimble.mozilla.org#1514 - Missing head element breaks live preview