/// <reference path="v4PrintDriver-Intellisense.js" />
// Version 3.10
//
// Note: See http://san-xwiki-1.cscr.hp.com:8080/xwiki/bin/view/CSL_Components/v4BiDiUSBExtension 
// for documentation on the query strings and functions used to build queries.

getSchemas = function(scriptContext, printerStream, schemaRequests, printerBidiSchemaResponses) {
  //debugger;
	var pageCache = new PageCache(scriptContext, printerStream);
	var requestProcessor = new RequestProcessor(printerBidiSchemaResponses, pageCache, scriptContext);

    var i = 0;
    for (i = 0; i < schemaRequests.length; i++) {
        var args = schemaRequests[i].split(';');

		if (args.length >=3) {
			var queryStr = args[0];
			var procFunc = args[1];
			var ledmResource = args[2];
			var params = [];
			if (args.length >= 4) {
				params = args.slice(3);
			}
			if (requestProcessor.ValidGetFunctions.indexOf(procFunc) != -1) {
				requestProcessor[procFunc](queryStr, ledmResource, params);
			}
        }
    }
    return 0;
};

setSchema = function(scriptContext, printerStream, printerBidiSchemaElement) {
	//debugger;
	var pageCache = new PageCache(scriptContext, printerStream);
	var value = printerBidiSchemaElement.value;
	try {
		if (printerBidiSchemaElement.type === "BIDI_STRING") {
			value = value.replace(/&lt;/g,"<").replace(/&gt;/g,">");
		}
		var response = pageCache.RawRequest(value);
		printerBidiSchemaElement.value = response;
	} catch (err) {
		pageCache.log = "setSchema exception caught: " + err;
	}
    return 0;
};

getStatus = function(scriptContext, printerStream, printerBidiSchemaResponses) {
    return 2;
};

requestStatus = function (scriptContext, printerStream, printerBidiSchemaResponses) {
    var retVal = 2;
    var pageCache = new PageCache(scriptContext, printerStream);
    var requestProcessor = new RequestProcessor(printerBidiSchemaResponses, pageCache, scriptContext);
    pageCache.statusRead = true;
    var statusContent = pageCache.GetContent("/DevMgmt/ProductStatusDyn.xml", false);
    if ((null != statusContent) && (statusContent !="" )) {
        var statusValue = requestProcessor.InnerXml("psdyn:Status", statusContent);
        var statusMsg = requestProcessor.InnerXml("pscat:StatusCategory", statusValue);
        if (statusMsg.toUpperCase() == "DoorOpen".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'DoorOpen');
        else if (statusMsg.toUpperCase() == "closeDoorOrCover".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'closeDoorOrCover');
        else if (statusMsg.toUpperCase() == "MediaEmpty".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'MediaEmpty');
        else if ((statusMsg.toUpperCase() == "toner-empty-error".toUpperCase()))
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'toner-empty-error');
        else if (statusMsg.toUpperCase() == "MediaJam".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'MediaJam');
        else if (statusMsg.toUpperCase() == "jamInPrinter".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'jamInPrinter');
        else if (statusMsg.toUpperCase() == "output-area-full".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'output-area-full');
        else if (statusMsg.toUpperCase() == "toner-empty-warning".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'toner-empty-warning');
        else if (statusMsg.toUpperCase() == "toner-low-report".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'toner-low-report');
		else if (statusMsg.toUpperCase() == "trayEmptyOrOpen".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'trayEmptyOrOpen');
		else if (statusMsg.toUpperCase() == "cartridgeMissing".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'cartridgeMissing');
        else if (statusMsg.toUpperCase() == "paused".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'paused');
        else if (statusMsg.toUpperCase() == "cartridgeLow".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'cartridgeLow');
		else if (statusMsg.toUpperCase() == "replaceCartridgeOut".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'replaceCartridgeOut');
		else if (statusMsg.toUpperCase() == "cartridgeVeryLow".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'cartridgeVeryLow');
		else if (statusMsg.toUpperCase() == "cartridgeCounterfeitQuestion".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'cartridgeCounterfeitQuestion');
		else if (statusMsg.toUpperCase() == "cartridgeFailure".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'cartridgeFailure');
		else if (statusMsg.toUpperCase() == "incompatibleConsumable".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'incompatibleConsumable');
		else if (statusMsg.toUpperCase() == "insertSETUPCartridge".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'insertSETUPCartridge');
		else if (statusMsg.toUpperCase() == "upgradableSupply".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'upgradableSupply');
		else if (statusMsg.toUpperCase() == "nonHPSupplyDetected".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'nonHPSupplyDetected');
		else if (statusMsg.toUpperCase() == "genuineHP".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'genuineHP');
		else if (statusMsg.toUpperCase() == "startupRoutineFailed".toUpperCase())
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'startupRoutineFailed');
		else if (statusMsg.toUpperCase() == "inReserveMode".toUpperCase())
		    printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'inReserveMode');
		else if (statusMsg.toUpperCase() == "printFailure".toUpperCase())
		    printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'printFailure');
		else if (statusMsg.toUpperCase() == "printerError".toUpperCase())
		    printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'printerError');
		else if (statusMsg.toUpperCase() == "sizeMismatchInTray".toUpperCase())
		    printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'sizeMismatchInTray');
		else if (statusMsg.toUpperCase() == "outputBinOpened".toUpperCase())
		    printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'outputBinOpened');
		else if (statusMsg.toUpperCase() == "tooManyPagesToStaple".toUpperCase())
		    printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'tooManyPagesToStaple');
        else
            printerBidiSchemaResponses.AddString('\\Printer.Status.Summary:StateReason', 'None');
        retVal = 0;
    }
    return retVal;

};

RequestProcessor = function(printerBidiSchemaResponses, pageCache, scriptContext) {
	this.responses = printerBidiSchemaResponses;
	this.pageCache = pageCache;
	this.Context = scriptContext;
	this.content = "";
	this.ValidGetFunctions = "FindIndexXmlStr,TagContainsBool,TagAnyContainsBool,FindIndexXmlConvertStr,AllContentStr," +
		"FindIndexXmlInt, InnerXmlInt,InnerXmlStr,InnerXmlMapStr,GetQueuePropStr,QueueRequestStr,StrCmpBool,MultiTagContainsBool";
	// ,ElementListStr,NthElementStr
	this.ValidSetFunctions = "SetRequest";
};

RequestProcessor.prototype.FindIndexXmlInt = function(bidiQueryStr, contentName, queryArray) {
	var content = this.pageCache.GetContent(contentName, true);
	this.content = "";
    if (content) 
    {
	    var index = queryArray.pop();
	    var child = queryArray.pop();
	    if (queryArray.length > 0) 
        {
		    var result = this.FindIndexedXmlValue(content, queryArray, child, index);
		    if ((null != result) && (null != this.responses)) 
            {
			    this.responses.AddInt32(bidiQueryStr,result);
		    }
	    }
        return true;
    }
    return false;
};

RequestProcessor.prototype.FindIndexXmlStr = function(bidiQueryStr, contentName, queryArray) {
	var content = this.pageCache.GetContent(contentName, true);
	this.content = "";

    if (content) 
    {
	    var index = queryArray.pop();
	    var child = queryArray.pop();
	    if (queryArray.length > 0) 
        {
		    var result = this.FindIndexedXmlValue(content, queryArray, child, index);
		    if ((null != result) && (null != this.responses)) 
            {
			    this.responses.AddString(bidiQueryStr, result);
		    }
	    }
        return true;
    }
    return false;
};

// FindIndexXmlConvertStr() and FindConversionValue() functions added by Albin for converting between string data types 
// For example converting Mediatype value from 'stationerylightweight'->'light'

RequestProcessor.prototype.FindIndexXmlConvertStr = function (bidiQueryStr, contentName, queryArray) {
    var content = this.pageCache.GetContent(contentName, true);
    this.content = "";
    if (content) 
    {
        if (queryArray.length > 2) 
        {
            var tempArray = queryArray.slice(0, 1);
            var result = this.FindIndexedXmlValue(content, tempArray, queryArray[1], queryArray[2]);
            if ((null != result) && (null != this.responses)) 
            {
                result = this.FindConversionValue(result, queryArray);
                this.responses.AddString(bidiQueryStr, result);
            }
        }
        return true;
    }
    return false;
};


RequestProcessor.prototype.FindConversionValue = function (result, queryArray, minSize) {
	var MIN_SIZE=5;
	if (null != result) {
		if(typeof minSize=='undefined')
			minSize=MIN_SIZE;
        var i = minSize;
        while (i <= queryArray.length) 
        {
            if (result == queryArray[i - 2]) 
            {
                return queryArray[i - 1];
            }

            i = i + 2;
        }
    }
    return result;
};

RequestProcessor.prototype.TagContainsBool = function(bidiQueryStr, contentName, queryArray) {
	if ((null != queryArray) && (queryArray.length >= 2))
    {
		this.content = "";
		var content = this.pageCache.GetContent(contentName, true);
        if (content) 
        {
            var result = false;
            var queryPattern = new RegExp(queryArray.pop(), "gm");
            var value = this.InnerXmlMultiple(queryArray, content);
            
            if (null != value) 
            {
                result = queryPattern.test(value);
            }
            
            if (null != this.responses) 
            {
                this.responses.AddBool(bidiQueryStr, result);
            }
            return true;
        }
        return false;
    }
    return false;
};


// StrCmpBool() function added by Gurudatt for absolute string compare

RequestProcessor.prototype.StrCmpBool = function (bidiQueryStr, contentName, queryArray) {
    if ((null != queryArray) && (queryArray.length >= 2)) 
    {
        this.content = "";
        var content = this.pageCache.GetContent(contentName, true);
        if (content) 
        {
            var result = false;
            var value = this.InnerXml(queryArray[0], content);
            if (null != value) 
            {
                if (value == queryArray[1])
                    result = true;
            }

            if (null != this.responses) 
            {
                this.responses.AddBool(bidiQueryStr, result);
            }
            return true;
        }
        return false;
    }
    return false;
};


// MultiTagContainsBool() function added by Gurudatt for checking values of multiple nodes

//example usage:

//bidiQueryStr - \Printer.Configuration.DuplexUnit:Installed
//contentName - /DevMgmt/ProductConfigDyn.xml
//queryArray - dd:DuplexUnit,dd:DuplexBindingOption;Installed;longEdge;shortEdge
//dd: DuplexUnit nodevalue returned from device is checked for Installed||longEdge||shortEdge
//dd: DuplexBindingOption nodevalue returned from device is checked for Installed||longEdge||shortEdge



RequestProcessor.prototype.MultiTagContainsBool = function (bidiQueryStr, contentName, queryArray) {
    var content = "";
    var result = false;
    content = this.pageCache.GetContent(contentName, true);
    if (content) 
    {
        var option = "";
        var args = queryArray[0].split(',');
        for (i = 0; i < args.length; i++) 
        {
            if (-1 != content.indexOf(args[i])) 
            {
                option = this.InnerXml(args[i], content);
                if (option != null) 
                {
                    result = this.MultiStrCompBool(queryArray, option);
                    if (result == true)
                        break;
                }
            }
        }
        this.responses.AddBool(bidiQueryStr, result);
        return true;
    }
    return false;
};


RequestProcessor.prototype.MultiStrCompBool = function (queryArray, option) {

    for (j = queryArray.length; j > 1; j--) 
    {
        if (option == queryArray.pop()) 
        {
            return true;
        }
    }
    return false;
};




RequestProcessor.prototype.TagAnyContainsBool = function (bidiQueryStr, contentName, queryArray) {
    if ((null != queryArray) && (queryArray.length >= 2)) 
    {
        var content = this.pageCache.GetContent(contentName, true);
        this.content = "";
        
        if (content) 
        {
            var queryPattern = new RegExp(queryArray.pop(), "gm");
            var result = false;
            
            do 
            {
                var value = this.InnerXmlMultiple(queryArray, content);
                if ((null != value) && queryPattern.test(value)) 
                {
                    result = true;
                }
                content = this.content;
            } while (null != value);

            if (null != this.responses) 
            {
                this.responses.AddBool(bidiQueryStr, result);
            }
            return true;
        }
        return false;
    }
    return false;
};


RequestProcessor.prototype.InnerXmlInt = function (bidiQueryStr, contentName, queryArray) {
    if (null != queryArray) 
    {
        this.content = "";
        var content = this.InnerXmlMultiple(queryArray, this.pageCache.GetContent(contentName, true));
        
        if ((null != content) && (null != this.responses)) 
        {
            this.responses.AddInt32(bidiQueryStr, content);
        }

        if (content) 
        {
            return true;
        }
    }
    return false;
};


RequestProcessor.prototype.InnerXmlStr = function(bidiQueryStr,contentName, queryArray) {
    if (null != queryArray) 
    {
        this.content = "";
        var content = this.InnerXmlMultiple(queryArray,this.pageCache.GetContent(contentName,true));
        
        if ((null != content) && (null != this.responses)) 
        {
            this.responses.AddString(bidiQueryStr, content);
        }

        if (content) 
        {
            return true;
        }
    }
    return false;
};


RequestProcessor.prototype.AllContentStr = function(bidiQueryStr,contentName,queryArray) {
    var cacheContent = false;
    
    if (null != queryArray) 
    {
        var truePattern = /true/;
        cacheContent = truePattern.test(queryArray[0]);
    }

    var content = this.pageCache.GetContent(contentName, cacheContent);
    
    if ((null != content) && (null != this.responses)) 
    {
        var xmlTagRe = /<\?.*\?>\r\n/gm;
        if (xmlTagRe.test(content)) 
        {
            content = content.substring(xmlTagRe.lastIndex);
        }
        this.responses.AddString(bidiQueryStr, content);
        return true;
    }
    return false;
};


RequestProcessor.prototype.InnerXmlMapStr = function(bidiQueryStr, contentName, queryArray) {
	var mapIndex = 0;
	var mapRe = /=/g;
	for (mapIndex=0; mapIndex < queryArray.length; mapIndex++) 
    {
		if (mapRe.test(queryArray[mapIndex])) 
        {
			break;
		}
	}
	var tagArray = queryArray.slice(0,mapIndex);
	var mapArray = queryArray.slice(mapIndex);
    var defaultValue = "Idle";

	this.content = "";
	var content = this.InnerXmlMultiple(tagArray,this.pageCache.GetContent(contentName,true));
	if (null != content) 
    {
		var re = new RegExp(content,"gm");
		for (var i=0;i<mapArray.length;i++) 
        {
			var idx = mapArray[i].indexOf("=");
            var name = mapArray[i].substr(0,idx);
            var value = mapArray[i].substring(idx+1);
			if ((idx >= 0) && re.test(name)) 
            {
				this.responses.AddString(bidiQueryStr, value);
				return;
			}

            if (name.indexOf("default") === 0) 
            {
                defaultValue = value;
            }
        }

        if (null != this.responses) 
        {
            this.responses.AddString(bidiQueryStr,defaultValue);
        }
        return true;
    }
    return false;
};


RequestProcessor.prototype.FindIndexedXmlValue = function(xml, parent, child, index) {
    if (null != xml) {
        this.content = xml;
        var indexRe = new RegExp(index, "gm");

		do {
			var innerXml = this.InnerXmlMultiple(parent, this.content);
			if ((null != innerXml) && indexRe.test(innerXml)) {
				innerXml = this.InnerXml(child, innerXml);
				return innerXml;
			}
		} while (null != innerXml);
	}
	return null;
};


RequestProcessor.prototype.InnerXml = function(tag, xml) {
	if ((null != xml) && (null != tag)) 
    {
	    // If there is an attribute in the opening tag then we failed until I split the openRe in half and 
		// detected the close below (openIdx)
		var openRe = new RegExp("<" + tag, "gm");
		var closeRe = new RegExp("</" + tag + ">", "gm");

		if (openRe.test(xml) && closeRe.test(xml)) 
        {
			var openIdx = xml.indexOf(">",openRe.lastIndex)+1;
			var inner = xml.substring(openIdx, closeRe.lastIndex - tag.length - 3);
			this.content = xml.substring(closeRe.lastIndex);
			return inner;
		}
	}
	return null;
};


RequestProcessor.prototype.InnerXmlMultiple = function(tagArray, xml) {
	if ((null != xml) && (null != tagArray) && (tagArray.length > 0))
    {
		var index=0;
		// Save the context for the parent so that this.content is correct when we leave
		var parentContext = null;
		var content = xml;
		for (index=0;index<tagArray.length;index++) 
        {
			if (null != content) 
            {
				content = this.InnerXml(tagArray[index],content);
				if ((null != content) && (null == parentContext)) 
                {
					parentContext = this.content;
				}
			}
			else 
            {
				break;
			}
		}

		if (null != parentContext) 
        {
			this.content = parentContext;
		}
		return content;
	}
	return null;
};


PageCache = function(scriptContext, printerStream) {
	this.Context = scriptContext;
	this.printerStream = printerStream;
	this.ContentArray = [];
	this.readContent = "";
    	this.ToMatch = "";
	// lastHeader is used in error reporting when we fail a request.
	this.lastHeader = null;
        this.readAttempts = 0;
        this.statusRead = false;
};


PageCache.prototype.GetContent = function(contentName, saveInCache) {
    var content = null;
    if (saveInCache) 
    {
        content = this.ContentArray[contentName];
    }

    if ((content === undefined) || (content === null)) 
    {
        content = this.FetchLedmContent(contentName);
        if ((content != undefined) && (content != null) && saveInCache) 
        {
			this.SetContent(contentName, content);
        }
    }
    return content;
};


PageCache.prototype.SetContent = function(contentName, content) 
{
    if (null != contentName) 
    {
        this.ContentArray[contentName] = content;
    }
};


// Break out RawRequest (it is common for all gets and sets) and simplify FetchLedmContent and SetLedmContent
PageCache.prototype.FetchLedmContent = function(path) {
	if (null != path) 
    {
        if (this.VerifyChannel()) 
        {
            var requestStr = "GET ".concat(path, " HTTP/1.1\r\nHOST:localhost\r\n\r\n");
            return this.RawRequest(requestStr);
        }
    }
	return null;
};


PageCache.prototype.VerifyChannel = function () {
    if (this.ChannelVerified === undefined) {
        //var pjlcommand = [0x40, 0x50, 0x4A, 0x4C, 0x20, 0x43, 0x4F, 0x4D, 0x4D, 0x45, 0x4E, 0x54, 0x20, 0x22, 0x22, 0x0D, 0x0A, 0x0D, 0x0A];
        var pjlcommand = [0x1B, 0x25, 0x2D, 0x31, 0x32, 0x33, 0x34, 0x35, 0x58, 0x40, 0x50, 0x4A, 0x4C, 0x20, 0x43, 0x4F, 0x4D, 0x4D, 0x45, 0x4E, 0x54, 0x20, 0x22, 0x22, 0x0D, 0x0A, 0x1B, 0x25, 0x2D, 0x31, 0x32, 0x33, 0x34, 0x35, 0x58];
        var writtenStrLen = this.printerStream.Write(pjlcommand);

        if (this.IsWebServerConnected(500)) {
            this.ChannelVerified = true;

        } else {
            this.ChannelVerified = false;
        }
    }
    return this.ChannelVerified;
};


PageCache.prototype.SetLedmContent = function (method, path, content) {
    if (null != path) {
        if (null == method) method = "GET";

        var requestStr = method.concat(" ", path, " HTTP/1.1\r\n");
        if (null == content) 
        {
            requestStr = requestStr.concat("\r\n");
        } else 
        {
            var contentLength = content.length;
            requestStr = requestStr.concat("Content-Length: ", contentLength, "\r\n\r\n", content);
        }
        return this.RawRequest(requestStr);
    }
    return null;
};

PageCache.prototype.RawRequest = function (requestStr) {
    var requestBytes = this.BytesFromString(requestStr);
    var writtenStrLen = this.printerStream.Write(requestBytes);
    if (writtenStrLen > 0) 
    {
        var response = this.GetHttpResponse();
        return response;
    }

    return null;
};

PageCache.prototype.GetHttpResponse = function () {
    var httpHeader = new HttpHeader(this);
    if (httpHeader.ReadHeaderNow()) {
        var ledmResponse = "";
        if (httpHeader.ContentLength > 0) 
        {
            ledmResponse = this.ReadResponse(httpHeader.ContentLength);
        } 
        else if (httpHeader.IsChunked) 
        {
            ledmResponse = this.ReadChunkedResponse();
        }
        return ledmResponse;
    }
    return null;
};

PageCache.prototype.StringFromBytes = function (bytes) {
    if ((bytes === undefined) || (bytes === null)) {
        return null;
    }
    var length = bytes.length;
    var result = "";
    for (var i =0; i < length;i++) 
    {
        if (bytes[i] != undefined) 
        {
            result += String.fromCharCode(bytes[i]);
        }
    }
    return result;
};


PageCache.prototype.BytesFromString = function(content) {
    if (content === null) 
    {
        return null;
    }

    var bytes = new Array(content.length);

    for (var i=0; i<content.length; i++) {
        bytes[i] = content.charCodeAt(i);
    }
    return bytes;
};
PageCache.prototype.ReadBlock = function (requestLength)
{
    var totalLength = 0;
    var zeroCount = 60;
    var reqLen = 512;
    do
    {
	    try 
        {
	        // Delay the Read after handling huge data packets, this will make further responses from the device better
            // Try to avoid the Read crashes as much as possible
            if(this.readAttempts > 50)
            {
                if(this.statusRead == false)
                {
                    sleep(1000);
                    this.readAttempts = 0;
                }
            }
	        var tempBytes = this.printerStream.Read(reqLen);
	    }
        catch (err) 
        {
	        return -1;
	    }
        this.readAttempts++;
        if (tempBytes.length > 0) 
        {
            var actLen = tempBytes.length;
            this.readContent = this.readContent += this.StringFromBytes(tempBytes);
            totalLength += actLen;
            requestLength -= actLen;
            zeroCount = 60;

            var StrContent = String(this.readContent);
            if ((StrContent != "") && (StrContent.indexOf(this.ToMatch) >= 0)) 
            {
                this.ToMatch = "";
                break;
            }
        }
        else 
        {
	        sleep(3);
            zeroCount--;
        }
    }while ((requestLength > 0) && (zeroCount > 0));

    return totalLength;
};
PageCache.prototype.ReadResponse = function(len) {
	var loopLimit = 100;
	if (len != NaN) 
    {
		do 
        {
			var remLen = len - this.readContent.length;
			var actLen = this.ReadBlock(remLen);
			if ((actLen > 0) && (len == this.readContent.length)) 
            {
				var temp = this.readContent;
				this.readContent = "";
				return temp;
			}
		} while (loopLimit-- > 0);
	}
	return null;
};


PageCache.prototype.ReadChunkedResponse = function() {
    var results = "";
    var temp = "";
    do {
        temp = this.ReadChunk();
        if (temp === "")
            break;
        if (null != temp) {
            results = results.concat(temp);
        }
		else {
			this.ReadBlock(2);
		}
    } while (temp != null);
    return results;
};


PageCache.prototype.ReadChunkHeader = function() {
	var i = this.readContent.indexOf("\r\n");
	if (i > 0) {
        var len = parseInt(this.readContent, 16);        
		this.readContent = this.readContent.substr(i+2);
		return len;
	}
	return -1;
};


PageCache.prototype.ReadChunk = function() {
	var chunkLength = this.ReadChunkHeader();
	if (chunkLength < 0) {
		this.ReadBlock(8);
		chunkLength = this.ReadChunkHeader();
	}

	var chunkCountdown = 100;
	if (chunkLength != 0) 
    {
		do {
			var remLen = chunkLength - this.readContent.length + 2;//The 2 bytes is for the last \r\n to be read but not part of the chunk
			
            if (remLen > 0) 
            {
                i = this.ReadBlock(remLen);
			}

			if (chunkLength <= this.readContent.length) 
            {
				var temp = this.readContent.substr(0,chunkLength);
				this.readContent = this.readContent.substring(chunkLength+2);
				return temp;
			}
		} while (chunkCountdown-- > 0);
	}
    else
        return "";//this is done to handle 30 0D 0A 0D 0A
    return null;
};


PageCache.prototype.IsWebServerConnected = function (timeout) {

    var date = new Date();
    var curDate = null;
    var receivedData = false;
    var badRequest = new RegExp("HTTP", "gmi");

    do {
        curDate = new Date();
        var tempBytes = this.printerStream.Read(512);
        if ((tempBytes != undefined) && (tempBytes.length > 0) && (badRequest.test(this.StringFromBytes(tempBytes)))) 
        {
            receivedData = true;
        }
    } while ((receivedData == false) && (curDate - date < timeout));

    this.FlushChannel(100);
    return receivedData;
};


PageCache.prototype.FlushChannel = function (timeout) {
    var date = new Date();
    var curDate = null;
    do {
        curDate = new Date();
        var tempBytes = this.printerStream.Read(512);
    } while (((tempBytes != null) && (tempBytes.length > 0)) && (curDate - date < timeout));
    this.readContent = "";
};

HttpHeader = function(pageCache) {
	this.StatusCode = 0;
	this.IsChunked = false;
	this.ContentLength = 0;
	this.Content = "";
    this.pageCache = pageCache;
};

HttpHeader.prototype.ReadHeaderNow = function () {
    try {
        if (this.ReadHeader() == true) {
            this.pageCache.lastHeader = this;
        } else {
            return false;
        }
    } catch (err) {
        return false;
    }
    return true;
};


HttpHeader.prototype.ReadHeader = function() {
	var index = this.ReadUntil("HTTP/1.1");
	if (index > 0) {
		this.pageCache.readContent = this.pageCache.readContent.substring(index);
	}

	if (index >= 0) {
		index = this.ReadUntil("\r\n\r\n");
        if (index >= 0) 
        {
			this.Content = this.pageCache.readContent.substring(0,index + 4);
			this.pageCache.readContent = this.pageCache.readContent.substring(index+4);

			this.StatusCode = parseInt(this.Content.substring(8));

            if (this.StatusCode === 200) 
            {
				var ContentLengthPattern = /^Content-Length:/gm;
				var ChunkedPattern = /^Transfer-Encoding/gm;

                if (ContentLengthPattern.test(this.Content)) 
                {
					this.ContentLength = parseInt(this.Content.substring(ContentLengthPattern.lastIndex));
				}
                else if (ChunkedPattern.test(this.Content)) 
                {
					this.IsChunked = true;
				}
			}
            else {
                this.ContentLength = -1;
                this.IsChunked = false;
            }
		}
    }
    else
    {
        return false;
    }
    
    return true;
};


HttpHeader.prototype.ReadUntil = function(match){
	var index = this.pageCache.readContent.indexOf(match);
	if (index >= 0) {
		return index;
	}
    this.pageCache.ToMatch = match;
	do {
		if (this.pageCache.ReadBlock(512) <= 0) return -1;
		index = this.pageCache.readContent.indexOf(match);
	} while (index < 0);

	return index;
};

function sleep(milliSeconds) {
    var startTime = new Date().getTime();
    while (new Date().getTime() < startTime + milliSeconds);
}