//
// BEGIN FLOCK GPL
// 
// Copyright Flock Inc. 2005-2006
// http://flock.com
// 
// This file may be used under the terms of of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
// 
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
// 
// END FLOCK GPL
//

const DEBUG = false; /* set to false to suppress debug messages */

const PROTOCOL_BASE_CONTRACTID        = '@mozilla.org/network/protocol;1?name=';

const FLOCK_FEED_PROTOCOL_SCHEME      = 'feed';
const FLOCK_FEED_PROTOCOL_CONTRACTID  = PROTOCOL_BASE_CONTRACTID +
                                        FLOCK_FEED_PROTOCOL_SCHEME;
const FLOCK_FEED_PROTOCOL_CID         = Components.ID('{9405bc3e-6543-45e8-a0f3-d92197c31f2b}');

const FLOCK_FEEDS_PROTOCOL_SCHEME     = 'feeds';
const FLOCK_FEEDS_PROTOCOL_CONTRACTID = PROTOCOL_BASE_CONTRACTID +
                                        FLOCK_FEEDS_PROTOCOL_SCHEME;
const FLOCK_FEEDS_PROTOCOL_CID        = Components.ID('{3376dbaa-a72a-4728-a8dd-e29f09970eb2}');

const nsIRequest                      = Components.interfaces.nsIRequest;
const nsIChannel                      = Components.interfaces.nsIChannel;
const nsISupports                     = Components.interfaces.nsISupports;
const nsIProtocolHandler              = Components.interfaces.nsIProtocolHandler;
const nsIURI                          = Components.interfaces.nsIURI;
const nsITimer                        = Components.interfaces.nsITimer;
const nsITimerCallback                = Components.interfaces.nsITimerCallback;

const SIMPLEURI_CONTRACTID            = '@mozilla.org/network/simple-uri;1';
const TIMER_CONTRACTID                = '@mozilla.org/timer;1';

const flockIFeedService               = Components.interfaces.flockIFeedService;
const flockIFeedChannel               = Components.interfaces.flockIFeedChannel;

const FEED_SERVICE_CONTRACTID         = '@flock.com/feed-service;1';

// Possible flags for loadFlags property...
const LOAD_NORMAL =                Components.interfaces.nsIRequest.LOAD_NORMAL;
const LOAD_BACKGROUND =            Components.interfaces.nsIRequest.LOAD_BACKGROUND;
const INHIBIT_CACHING =            Components.interfaces.nsIRequest.INHIBIT_CACHING;
const INHIBIT_PERSISTENT_CACHING = Components.interfaces.nsIRequest.INHIBIT_PERSISTENT_CACHING;
const LOAD_BYPASS_CACHE =          Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
const LOAD_FROM_CACHE =            Components.interfaces.nsIRequest.LOAD_FROM_CACHE;
const VALIDATE_ALWAYS =            Components.interfaces.nsIRequest.VALIDATE_ALWAYS;
const VALIDATE_NEVER =             Components.interfaces.nsIRequest.VALIDATE_NEVER;
const VALIDATE_ONCE_PER_SESSION =  Components.interfaces.nsIRequest.VALIDATE_ONCE_PER_SESSION;


// Service implementation
function flockFeedProtocol()
{
    debug("Constructed feed protocol");
}

// XXX: Fix bug #762, or handle ports and auth properly
flockFeedProtocol.prototype = {
    scheme: FLOCK_FEED_PROTOCOL_SCHEME,
    defaultPort: -1,
    protocolFlags: nsIProtocolHandler.URI_NORELATIVE |
                   nsIProtocolHandler.URI_NOAUTH
}

function flockFeedsProtocol()
{
    debug("Constructed feeds protocol");
}

flockFeedsProtocol.prototype = {
    scheme: FLOCK_FEEDS_PROTOCOL_SCHEME,
    defaultPort: -1,
    protocolFlags: nsIProtocolHandler.URI_NORELATIVE |
                   nsIProtocolHandler.URI_NOAUTH
}

flockFeedProtocol.prototype.newURI =
flockFeedsProtocol.prototype.newURI =
function (spec, charset, baseURI)
{
    var url = Components.classes[SIMPLEURI_CONTRACTID].createInstance(nsIURI);

    try {
        url.spec = spec;
    } catch (e) {
        try {
            url.spec = this.scheme + ':' + spec;
        } catch (e) {
            url.spec = 'javascript:void(0)';
        }
    }

    return url;
}

flockFeedProtocol.prototype.newChannel =
flockFeedsProtocol.prototype.newChannel =
function (uri)
{
    return new FeedChannel(uri);
}

flockFeedProtocol.prototype.allowPort =
flockFeedsProtocol.prototype.allowPort =
function (port, scheme)
{
    return false;
}

// the nsISupports implementation
flockFeedProtocol.prototype.QueryInterface =
flockFeedsProtocol.prototype.QueryInterface =
function (iid)
{
    if (!iid.equals(nsIProtocolHandler) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
}

// Module implementation
var protocolModule = new Object();

protocolModule.registerSelf =
function (compMgr, fileSpec, location, type)
{
    compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);

    compMgr.registerFactoryLocation(FLOCK_FEED_PROTOCOL_CID,
                                    "Flock Feed Protocol JS Component",
                                     FLOCK_FEED_PROTOCOL_CONTRACTID,
                                     fileSpec,
                                     location,
                                     type);

    compMgr.registerFactoryLocation(FLOCK_FEEDS_PROTOCOL_CID,
                                    "Flock Feeds Protocol JS Component",
                                     FLOCK_FEEDS_PROTOCOL_CONTRACTID,
                                     fileSpec,
                                     location,
                                     type);
}

protocolModule.getClassObject =
function (compMgr, cid, iid)
{
    if (cid.equals(FLOCK_FEED_PROTOCOL_CID))
        return feedProtocolFactory;

    if (cid.equals(FLOCK_FEEDS_PROTOCOL_CID))
        return feedsProtocolFactory;

    if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

    return Components.results.NS_ERROR_NO_INTERFACE;
}

protocolModule.canUnload =
function(compMgr)
{
    debug("Unloading feed protocol component.");
    return true;
}

/* entrypoint */
function NSGetModule(compMgr, fileSpec)
{
    return protocolModule;
}

var feedProtocolFactory = new Object();

feedProtocolFactory.createInstance =
function (outer, iid)
{
    debug("CI: " + iid);

    if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;

    if (!iid.equals(nsIProtocolHandler) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    
    return new flockFeedProtocol();
}

var feedsProtocolFactory = new Object();

feedsProtocolFactory.createInstance =
function (outer, iid)
{
    debug("CI: " + iid);

    if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;

    if (!iid.equals(nsIProtocolHandler) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    
    return new flockFeedsProtocol();
}

if (DEBUG) {
    var builtin_debug = debug;
    var debug = function (s) {
        builtin_debug("-*- flockFeedProtocol: " + s + "\n");
    }
} else {
    var debug = function (s) {}
}

/* data channel implementation */
function FeedChannel(uri)
{
    debug("Constructed feed channel");

    this.URI = uri;
    this.originalURI = uri;

    this.pending = true;
}

FeedChannel.prototype = {
    contentCharset: 'utf-8',
    contentLength: -1,
    contentType: 'text/html',
    loadAttributes: null,
    loadGroup: null,
    notificationCallbacks: null,
    owner: null,
    securityInfo: null,
    shouldCache: false,
    bypassCache: false,
    loadFlags: null
}


FeedChannel.prototype.open =
function ()
{
    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}

FeedChannel.prototype.asyncOpen =
function (listener, context)
{
    this.listener = listener;
    this.context  = context;

    if (this.loadFlags & LOAD_BYPASS_CACHE ||
        this.loadFlags & VALIDATE_ALWAYS) {
        // They pressed reload or shift+reload.
        this.bypassCache = true;
    } else {
        this.bypassCache = false;
    }
    if (this.loadFlags & INHIBIT_CACHING ||
        this.loadFlags & INHIBIT_PERSISTENT_CACHING) {
        // For privacy reasons we shouldn't cache this (eg it is HTTPS).
        // XXX we do not currently honor this.
        this.shouldCache = false;
    } else {
        this.shouldCache = true;
    }

    if (this.loadGroup)
        this.loadGroup.addRequest(this, null);

    this.timer = Components.classes[TIMER_CONTRACTID].getService(nsITimer);
    this.timer.initWithCallback(this, 0, nsITimer.TYPE_ONE_SHOT);
}

FeedChannel.prototype.notify =
function (timer)
{
    this.listener.onStartRequest(this, this.context);

    var feedsrv = Components.classes[FEED_SERVICE_CONTRACTID]
        .getService(flockIFeedService);
    feedsrv.loadFeedView(this.URI.spec, this);
}

FeedChannel.prototype.sendStream =
function (stream, offset, length)
{
    if (!this.pending)
        return;

    this.listener.onDataAvailable(this, this.context,
                                  stream, offset, length);
}

FeedChannel.prototype.finish =
function ()
{
    this.cancel(Components.results.NS_OK);
}

FeedChannel.prototype.isPending =
function ()
{
    return this.pending;
}

FeedChannel.prototype.status = Components.results.NS_OK;

FeedChannel.prototype.cancel =
function (status)
{
    if (this.pending) {
        this.pending = false;

        this.listener.onStopRequest(this, this.context, status);

        if (this.loadGroup)
            this.loadGroup.removeRequest(this, null, status);
    }

    this.status = status;
}

FeedChannel.prototype.suspend =
FeedChannel.prototype.resume =
function ()
{
    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}

FeedChannel.prototype.QueryInterface =
function (iid)
{
    if (!iid.equals(flockIFeedChannel) &&
        !iid.equals(nsIChannel) &&
        !iid.equals(nsIRequest) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
}
