/**
 * s1tony.CM.nsPreferences - a wrapper around nsIPrefService. Provides built in
 * exception handling to make preferences access simpler.
 * Based on chrome://global/content/nsUserSettings.js
 **/
s1tony.CM.nsPreferences =
{
    _branch: null,

    get mPrefService()
    {
        if (this._branch != undefined)
        {
            return Components.classes["@mozilla.org/preferences-service;1"]
                .getService(Components.interfaces.nsIPrefBranch)
                .getBranch(this._branch);
        }
        else
        {
            return Components.classes["@mozilla.org/preferences-service;1"]
                .getService(Components.interfaces.nsIPrefBranch);
        }
    },

    set mBranch(aExtension)
    {
        this._branch = "extensions." + aExtension + ".";
    },

    // Boolean preferences
    setBoolPref: function (aPrefName, aPrefValue)
    {
        try
        {
            // This looks weird, but otherwise, setBoolPref
            // for aPrefValue = true does not work for me
            if (aPrefValue)
            {
                this.mPrefService.setBoolPref(aPrefName, true);
            }
            else
            {
                this.mPrefService.setBoolPref(aPrefName, false);
            }
        }
        catch(e)
        {
        }
    },

    getBoolPref: function (aPrefName, aDefVal)
    {
        try
        {
            return this.mPrefService.getBoolPref(aPrefName);
        }
        catch(e)
        {
            return aDefVal != undefined ? aDefVal : null;
        }
        return null;                              // quiet warnings
    },
    
    // String preferences
    setStringPref: function (aPrefName, aPrefValue)
    {
        try
        {
        	this.mPrefService.setCharPref(aPrefName, aPrefValue);
        }
        catch(e)
        {
        }
    },

    setAppendToStringPref: function (aPrefName, aPrefValue)
    {
    	var stringPref = null;
    	
        try
        {
            // Get the string preference, add to it and
        	// write back to preferences
			stringPref = this.getStringPref(aPrefName, false);
			
			if ((stringPref == null) || (stringPref.length == 0))
			{
				this.setStringPref(aPrefName, aPrefValue);
			}
			else
			{
				this.setStringPref(aPrefName, stringPref + '|' + aPrefValue);				
			}
        }
        catch(e)
        {
        }
    },

    setRemoveFromStringPref: function (aPrefName, aPrefValue)
    {
    	var stringPrefArray = null;
    	
        try
        {
            // Get the string preference array, check for aPrefValue,
        	// remove it from array and write back to preferences
			stringPrefArray = this.getStringPrefArray(aPrefName);

            for (var i = 0; i < stringPrefArray.length; i++)
            {
	        	var tempItem = stringPrefArray[i].split(';');
	        	
            	if (tempItem[0] === aPrefValue)
            	{
					stringPrefArray.splice(i, 1);
					break;
            	}
            }
            
			this.setStringPrefArray(aPrefName, stringPrefArray);
        }
        catch(e)
        {
        }
    },
    
    setStringPrefArray: function (aPrefName, aPrefValue)
    {
    	var stringPref = null;
    	
    	try
        {
            // Convert aPrefValue into a string,
        	// with array values separated by a '|'
			stringPref = aPrefValue.join('|');
			
			this.setStringPref(aPrefName, stringPref);
        }
        catch(e)
        {
        }
    },
    
    getStringPref: function (aPrefName, aDefVal)
    {
        try
        {
            return this.mPrefService.getCharPref(aPrefName);
        }
        catch(e)
        {
            return aDefVal != undefined ? aDefVal : null;
        }
        return null;
    },   

    getStringPrefArray: function (aPrefName)
    {
    	var stringPref = null;
    	var retValue = new Array();
    	
        try
        {
			stringPref = this.getStringPref(aPrefName, false);
			
			if ((stringPref != null) && (stringPref != ""))
			{
				retValue = stringPref.split('|');
			}
        }
        catch(e)
        {
        }
        return retValue;
    },

    // Special Int Preference for Global Cookies
    setCookiePrefInt: function()
    {
        try
        {
        	this.mPrefService.setCharPref(aPrefName, aPrefValue);
        }
        catch(e)
        {
        }   
    },
    
    // Clear User preference
    clearUserPref: function (aPrefName)
    {
        try
        {
        	this.mPrefService.clearUserPref(aPrefName);
        }
        catch(e)
        {
        }
    }
};

// Observer to notify when preferences are changed
s1tony.CM.prefObserver =
{
    _branchExt: null,
    _branchNetworkCookie: null,
    _menuBundle: null,

    register: function()
    {
        var prefService = Components.classes["@mozilla.org/preferences-service;1"]
            .getService(Components.interfaces.nsIPrefService);
        this._branchExt = prefService.getBranch("extensions.cookiemonster.");
        this._branchExt.QueryInterface(Components.interfaces.nsIPrefBranch2);
        this._branchExt.addObserver("", this, false);

        this._branchNetworkCookie = prefService.getBranch("network.cookie.");
        this._branchNetworkCookie.QueryInterface(Components.interfaces.nsIPrefBranch2);
        this._branchNetworkCookie.addObserver("", this, false);
        
        // Create a reference to all the menu elements
        this._menuBundle = document.getElementById("cookie-menu-labels");
    },

    unregister: function()
    {
        if(this._branchExt)
        {
            this._branchExt.removeObserver("", this);
        }
        if(this._branchNetworkCookie)
        {
            this._branchNetworkCookie.removeObserver("", this);
        }
    },

    observe: function(aSubject, aTopic, aData)
    {
        if(aTopic != "nsPref:changed") return;
        // aSubject is the nsIPrefBranch we're observing (after appropriate QI)
        // aData is the name of the pref that's been changed (relative to aSubject)

        s1tony.CM.cookieMonster.extUpdateFromPreferences(aData);
        //    switch (aData) {
        //      case "secondlevelurl":
        //        cookieMonster.extUpdateFromPreferences(aData);
        //        break;
        //      case "deletecookiesondeny":
        //        // extensions.myextension.pref2 was changed
        //        break;
        //    }
    },

    // Return array representing user's current
    // choices for cookie preferences: cookieBehavior and lifetimePolicy
    getCookiePrefArray: function()
    {
        var resultPolicy = new Array(-1, -1);

        var prefService = Components.classes["@mozilla.org/preferences-service;1"]
            .getService(Components.interfaces.nsIPrefBranch)
            .getBranch("network.cookie.");

        resultPolicy[CM_PREF_COOKIE_BEHAVIOR] = prefService.getIntPref("cookieBehavior");
        resultPolicy[CM_PREF_LIFETIME_POLICY] = prefService.getIntPref("lifetimePolicy");
        
        return resultPolicy;
    },
    
    // Return string representing user's current
    // choices for cookie preferences
    getCookiePrefString: function()
    {
        var resultPolicy = null;

        var prefService = Components.classes["@mozilla.org/preferences-service;1"]
            .getService(Components.interfaces.nsIPrefBranch)
            .getBranch("network.cookie.");

        var cookieBehavior = prefService.getIntPref("cookieBehavior");
        var lifetimePolicy = prefService.getIntPref("lifetimePolicy");

        if (cookieBehavior != 2)
        {
            switch (lifetimePolicy)
            {
                case 0:                           //Server sets time
                    resultPolicy = this._menuBundle.getString("accepted");
                    break;
                case 1:                           // The user is prompted
                    resultPolicy = this._menuBundle.getString("userprompted");
                    break;
                case 2:                           // Session
                    resultPolicy = this._menuBundle.getString("acceptedsession");
                    break;
                default:
                    resultPolicy = this._menuBundle.getString("accepted");
                    break;
            }

            if (cookieBehavior == 1)
            {
                // Originator only
                resultPolicy += " [" + this._menuBundle.getString("originatingsiteonly") + "]";
            }
        }
        else
        {
        	resultPolicy = this._menuBundle.getString("rejected");
            //resultPolicy = "Rejected";
        }

        return resultPolicy;
    },

    // Set user's current
    // choices for cookie preferences: cookieBehavior and lifetimePolicy
    // aGlobalCookiePref is an array of type (int, int)
    setCookiePref: function(aGlobalCookiePref)
    {
        var resultPolicy = new Array(-1, -1);

        var prefService = Components.classes["@mozilla.org/preferences-service;1"]
            .getService(Components.interfaces.nsIPrefBranch)
            .getBranch("network.cookie.");

        try
        {
        	prefService.setIntPref("cookieBehavior", aGlobalCookiePref[CM_PREF_COOKIE_BEHAVIOR]);
        	prefService.setIntPref("lifetimePolicy", aGlobalCookiePref[CM_PREF_LIFETIME_POLICY]);
        }
        catch(e)
        {
        	alert("Error in setCookiePref: " + e);
        }
    }
};

// Observer to notify when cookie permissions have changed and
// when the browser is shutting down
s1tony.CM.observerService =
{
    _permService: null,
    _nsIHttpChannel: Components.interfaces.nsIHttpChannel,

    // Ensure the observers are only added once
    register: function()
    {
    	if (this._permService == null)
    	{
    		this._permService = Components.classes["@mozilla.org/observer-service;1"]
	            .getService(Components.interfaces.nsIObserverService);
	        this._permService.addObserver(this, "perm-changed", false);
	        this._permService.addObserver(this, "quit-application-requested", false);
	        this._permService.addObserver(this, "http-on-examine-response", false);
	        this._permService.addObserver(this, "http-on-examine-cached-response", false);   
        }
    },

    unregister: function()
    {
        if (this._permService)
        {
            this._permService.removeObserver(this, "perm-changed");
            this._permService.removeObserver(this, "quit-application-requested");
        	this._permService.removeObserver(this, "http-on-examine-response");
        	this._permService.removeObserver(this, "http-on-examine-cached-response");        	
        }
    },

    observe: function(aSubject, aTopic, aData)
    {
        if (aTopic == "perm-changed")
        {
            var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);

            if (permission.type == "cookie")
            {
            	//alert("Perm Observer  Host:  " + permission.host + "  Topic:  " + aTopic + "  Data:  " + aData);
                s1tony.CM.cookieMonster.extUpdateFromPermissions(permission.host);
            }
        }     
        else if (aTopic == "quit-application-requested")
        {
        	// Delete all cookies from web sites listed
        	// in the CM_PREFERENCE_TYPE_COOKIE_TEMP
        	// preference string and then remove
        	// the sites from the preference
        	// Using quit-application-requested is a little
        	// risky because the quit-application
        	// process can still be aborted at this point,
        	// but I have to in order to catch the browser
        	// being closed by clicking the close [x] button
        	// for the actual window
        	//alert("quit-application-requested " + "  Topic:  " + aTopic + "  Data:  " + aData);
        	s1tony.CM.cookieMonster.extResetTempCookies(true);
        	
        	// Also restore the user's current global cookie
        	// permission preferences if override global is used
        	s1tony.CM.cookieMonster.extResetGlobalCookiePref();
        }
        else if (aTopic == "http-on-examine-response" || aTopic == "http-on-examine-cached-response")
        {
        	var httpChannel = aSubject.QueryInterface(this._nsIHttpChannel); 
        	
        	if (httpChannel.visitResponseHeaders)
        	{
        		httpChannel.visitResponseHeaders(s1tony.CM.headerVisitor);       		
        	}
        	
         	if (s1tony.CM.headerVisitor.isCookieRequest())
         	{
     			httpChannel.visitRequestHeaders(s1tony.CM.headerVisitor);     		        			  		 
         		var browser = s1tony.CM.utils.getBrowserFromChannel(httpChannel);
       		             		
 			    if (browser)
		    	{
		    		s1tony.CM.cookieRequest.addCookieRequest(s1tony.CM.headerVisitor.getHost(),
			    		browser, s1tony.CM.headerVisitor.isDomainCookie());
		    	}
			    else
			    {
			    	//alert("browser null: cookieRequests:  " +  cookieRequests + "  Request Name:  " + requestObject.name + "  url:  " + url);		    	
		    	}            		
	    	}
             	
         	s1tony.CM.headerVisitor.reset();
        }
    }
};

s1tony.CM.headerVisitor =
{
	_isCookieRequest: false,
	_isDomainCookie: false,
	_host: null,
	_referer: null,
	
	visitHeader: function (aHeader, aValue)
	{
 		if (aHeader.indexOf("Set-Cookie") !== -1)
		{  
			this._isCookieRequest = true;
            this._isDomainCookie = (aValue.toLowerCase().indexOf("domain=") !== -1);
		}
     	
     	if (aHeader.indexOf("Host") !== -1)
     	{
     		this._host = aValue;
     	}

     	if (aHeader.indexOf("Referer") !== -1)
     	{
     		this._referer = aValue;
     	}
 	},
	
	isCookieRequest: function ()
	{
		return this._isCookieRequest;
	},
	
	isDomainCookie: function ()
	{
		return this._isDomainCookie;
	},
	
	getHost: function ()
	{
		return this._host;
	},

	getReferer: function ()
	{
		var retValue = "None";
		
		if (this._referer)
		{
			retValue = this._referer;
		}
		
		return retValue;
	},
	
	reset: function ()
	{
		this._isCookieRequest = false;
		this._isDomainCookie = false;
		this._host = null;
		this._referer = null;
	}
};
