//
// 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 FLOCK_PHOTO_API_MANAGER_CID               = Components.ID('{18e27f2e-c99c-4915-9907-1a9fc0780ed6}');

const nsISupports                   = Components.interfaces.nsISupports;
const nsIClassInfo                  = Components.interfaces.nsIClassInfo;
const nsIFactory                    = Components.interfaces.nsIFactory;
const nsIProperties                 = Components.interfaces.nsIProperties;
const nsILocalFile                  = Components.interfaces.nsILocalFile;
const nsIFile                       = Components.interfaces.nsIFile;
const nsIIOService                  = Components.interfaces.nsIIOService;
const nsIFileProtocolHandler        = Components.interfaces.nsIFileProtocolHandler;
const nsIPreferenceService       = Components.interfaces.nsIPrefBranch;
const flockIPhotoAPIManager      = Components.interfaces.flockIPhotoAPIManager;
const flockIPhotoAPI                = Components.interfaces.flockIPhotoAPI;
const flockIPhotoPeopleService      = Components.interfaces.flockIPhotoPeopleService;

const FLOCK_PHOTO_API_MANAGER_CONTRACTID        = '@flock.com/photo-api-manager;1?';
const DIRECTORY_SERVICE_CONTRACTID  = '@mozilla.org/file/directory_service;1';
const LOCAL_FILE_CONTRACTID         = '@mozilla.org/file/local;1';
const PREFERENCES_CONTRACTID        = '@mozilla.org/preferences-service;1';
const IO_SERVICE_CONTRACTID         = '@mozilla.org/network/io-service;1';

const PHOTO_PEOPLE_RDF              = 'flock_people_photo.rdf';


function flockPhotoAPIManager() {
    this.mName2ServiceMap = {};
    this.mService2NameMap = {};
    this.mPreferredServices = {};
    this.mPrefService = Components.classes[PREFERENCES_CONTRACTID].getService(nsIPreferenceService);
    
    var prefSvcs = this.getPreferredServices();
    for (var i=0; i < prefSvcs.length; i++)
        this.mPreferredServices[prefSvcs[i]] = true;
    
    var catmgr = Components.classes["@mozilla.org/categorymanager;1"]
        .getService (Components.interfaces.nsICategoryManager);
    var enum = catmgr.enumerateCategory("flockIPhotoAPI");
    while(enum.hasMoreElements()) {
        try {
            var obj = enum.getNext();
            var supportsString = obj.QueryInterface(Components.interfaces.nsISupportsCString);
            var shortName = supportsString.toString();
            var cid = catmgr.getCategoryEntry("flockIPhotoAPI", shortName); 
            var svc = Components.classes[cid].getService(Components.interfaces.flockIPhotoAPI);
            this.registerAPI(svc, shortName);
        }
        catch(e) {
        }
    }
    var inst = this;
    var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
    var timerFunc = {
        notify: function(aTimer) {
            inst.updateStates();
        },
    }
    timer.initWithCallback(timerFunc, 5 * 1000, 1);
}
//////////////////////////////////////////////////////////////////////////////
// Helpers
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Implementation
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

flockPhotoAPIManager.prototype.mStates = {};
flockPhotoAPIManager.prototype.mListeners = new Array();

flockPhotoAPIManager.prototype.addListener = function(aListener) {
    if (aListener == null) {
        throw  "Listener is null!!";
    }
    this.mListeners.push(aListener);
}

flockPhotoAPIManager.prototype.updateStates = function() {
    for(var p in this.mStates) {
        var shortName = p;
        var state = this.mStates[p];
        var api = this.getAPIFromShortname(p);
        var newState = api.authState;
        if(newState != state) {
            this.notify(api);
        }
        this.mStates[p] = newState;
    }
}

flockPhotoAPIManager.prototype.notify = function(aService) {
    for(var i=0;i<this.mListeners.length;++i) {
            this.mListeners[i].onAPIStateChange(aService);
    }
}

flockPhotoAPIManager.prototype.doLogin = function(aShortname) {
    var inst = this;
    var myListener = {
        onAuth: function() {
            inst.notify(service);
            inst.setDefaultService(service.shortName);
            inst.addPreferredService(service.shortName);
        },
        onError: function(aError) {
           for (var i=0; i < inst.mListeners.length; i++) {
               aError.QueryInterface(Components.interfaces.flockIError);
               try {
                    var theError = {};
                    theError.errorCode = aError.errorCode;
                    theError.serviceErrorCode = aError.serviceErrorCode;
                    theError.serviceErrorString = aError.serviceErrorString;
                    theError.errorString = aError.errorString;
                    inst.mListeners[i].onError(theError);
               } catch (ex) {
                    dump('flockPhotoAPIManager.js: ' + ex + '\n');
               }
            }
            
        },
    }
    var service = this.getAPIFromShortname(aShortname);
    service.login(myListener);
    this.notify(service);
}

flockPhotoAPIManager.prototype.doLogout = function(aShortname) {
    var service = this.getAPIFromShortname(aShortname);
    service.logout();
    if (this.getDefaultService() && this.getDefaultService().shortName == aShortname) {
        var preferredSvcs = this.getPreferredServices();
        this.setDefaultService('');
        for (var i=0; i < preferredSvcs.length; i++) {
            var svc = this.mName2ServiceMap[preferredSvcs[i]];
            if (svc && svc.isLoggedIn) {
                this.setDefaultService(svc.shortName);
                break;
            }
        }
    }
    
    this.notify(service);
}


flockPhotoAPIManager.prototype.removeListener = function(aListener) {
    for(var i=0;i<this.mListeners.length;++i) {
        if(aListener==this.mListeners[i]) {
            this.mListeners.splice(i,1);
            break;
        }
    }
}

flockPhotoAPIManager.prototype.registerAPI = function(aService, aShortname) {
    
    if (this.mPrefService.getPrefType('flock.photo.service.' + aShortname + '.isDisabled')) {
        if (this.mPrefService.getBoolPref('flock.photo.service.' + aShortname + '.isDisabled') && !this.shouldForceApiRegistration(aShortname)) {
            return true;
        }
        this.mPrefService.setBoolPref('flock.photo.service.' + aShortname + '.isDisabled', false);
    }        
    this.mName2ServiceMap[aShortname] = aService;
    this.mService2NameMap[aService] = aShortname;
    this.mStates[aShortname] = aService.authState;
}

flockPhotoAPIManager.prototype.shouldForceApiRegistration = function (aShortname) {
    var force = this.isPreferredService(aShortname);
    if (force)
        return true;
        
    var peopleRdf = Components.classes["@mozilla.org/file/directory_service;1"]
                             .getService(Components.interfaces.nsIProperties)
                             .get("ProfD", Components.interfaces.nsIFile);     
    peopleRdf.append(PHOTO_PEOPLE_RDF);
    if (peopleRdf.exists()) {
        var data = "";
        var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                                .createInstance(Components.interfaces.nsIFileInputStream);
        var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
                                .createInstance(Components.interfaces.nsIScriptableInputStream);
        fstream.init(peopleRdf, 1, 0, false);
        sstream.init(fstream); 

        var str = sstream.read(4096);
        while (str.length > 0) {
          data += str;
          str = sstream.read(4096);
        }

        sstream.close();
        fstream.close();
        
        force = (data.match(':' + aShortname + ':').length) > 0;
          
    }
    return force;  
}

flockPhotoAPIManager.prototype.isPreferredService = function (aShortname) {
    return this.mPreferredServices[aShortname];
}

flockPhotoAPIManager.prototype.getAPIFromShortname = function(aShortname) {
    return this.mName2ServiceMap[aShortname];
}

flockPhotoAPIManager.prototype.__defineGetter__('services', function () { 
    var ar = new Array();
    for(var p in this.mName2ServiceMap) {
        ar.push(this.mName2ServiceMap[p]);
    }
    var rval = {
        getNext: function() {
            var rval = ar.shift();
            return rval;
        },
        hasMoreElements: function() {
            return (ar.length>0);
        },
    }
    return rval;
})

flockPhotoAPIManager.prototype.getPreferredServices = function() {
    try {
        var pref = this.mPrefService.getCharPref("flock.photo.preferredServices");
        var ar = pref.split(",");
        var rval = new Array();
        for(var i=0;i<ar.length;++i) {
            if(ar[i].length) rval.push(ar[i]); 
        }
        return rval;
    } catch(e) {
        debug(e);
        return new Array();
    }
}

flockPhotoAPIManager.prototype.setPreferredServices = function(aArray) {
    var val = aArray.join(",");
    var pref = this.mPrefService.setCharPref("flock.photo.preferredServices", val);
}

flockPhotoAPIManager.prototype.getDefaultService = function() {
    try {
        var pref = this.mPrefService.getCharPref("flock.photo.lastService");
        if (pref && pref.length)
            return this.getAPIFromShortname(pref);
    } catch(e) {
        return null;
    }
}

flockPhotoAPIManager.prototype.setDefaultService = function(aShortname) {
    try {
        this.mPrefService.setCharPref("flock.photo.lastService", aShortname);
    } catch(e) {
        debug(e);
    }
}

flockPhotoAPIManager.prototype.removePreferredService = function(aShortName) {
    var preferredServices = this.getPreferredServices();
    var newArray = new Array();
    for(var i=0;i<preferredServices.length;++i) {
        if(preferredServices[i] != aShortName) {
            newArray.push(preferredServices[i]); 
        }
    }
    this.setPreferredServices(newArray);

    var ppsvc = Components.classes['@mozilla.org/rdf/datasource;1?name=flock-people-photo'].getService(Components.interfaces.flockIPhotoPeopleService);
    var enum = ppsvc.getListEnumerator(Components.interfaces.flockIPhotoPeopleService.LIST_ACCOUNTS);
    var toRemove = new Array();
    while(enum.hasMoreElements()) {
        var person = enum.getNext();
        if(person && person.shortName==aShortName) {
            toRemove.push(person);
        }
    }
    while(toRemove.length) {
        var person = toRemove.pop();
        ppsvc.removeFromList(Components.interfaces.flockIPhotoPeopleService.LIST_ACCOUNTS, person);
    }
}

flockPhotoAPIManager.prototype.addPreferredService = function(aShortName) {
    this.removePreferredService(aShortName);
    var preferredServices = this.getPreferredServices();
    preferredServices.push(aShortName);

    this.setPreferredServices(preferredServices);

    var ppsvc = Components.classes['@mozilla.org/rdf/datasource;1?name=flock-people-photo'].getService(Components.interfaces.flockIPhotoPeopleService);
    var api = this.getAPIFromShortname(aShortName);
    var person = api.getAuthPerson();
    ppsvc.addToList(Components.interfaces.flockIPhotoPeopleService.LIST_ACCOUNTS, person);
}

flockPhotoAPIManager.prototype.__defineGetter__('preferredServices', function () { 
try {
    var inst = this;
    var ar = this.getPreferredServices();
    var rval = {
        getNext: function() {
            var shortName = ar.shift();
            var api = inst.getAPIFromShortname(shortName);
            return api;
        },
        hasMoreElements: function() {
            return (ar.length>0);
        },
    }
    return rval;
    } catch(e) {
        debug(e);
    }
})

flockPhotoAPIManager.prototype.flags = nsIClassInfo.SINGLETON;
flockPhotoAPIManager.prototype.classDescription = "Flock Photo People Service";
flockPhotoAPIManager.prototype.getInterfaces = function (count) {
    var interfaceList = [flockIPhotoAPIManager, nsIClassInfo];
    count.value = interfaceList.length;
    return interfaceList;
}

flockPhotoAPIManager.prototype.getHelperForLanguage = function (count) {return null;}/*}}}*/

flockPhotoAPIManager.prototype.QueryInterface =
function (iid) {
    if (!iid.equals(flockIPhotoAPIManager) && 
        !iid.equals(nsIClassInfo) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
}/*}}}*/

// Module implementation/*{{{*/
var PhotoPeopleModule = new Object();

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

    compMgr.registerFactoryLocation(FLOCK_PHOTO_API_MANAGER_CID, 
                                    "Flock Photo API Manager JS Component",
                                    FLOCK_PHOTO_API_MANAGER_CONTRACTID, 
                                    fileSpec, 
                                    location,
                                    type);
}

PhotoPeopleModule.getClassObject =
function (compMgr, cid, iid) {
    if (!cid.equals(FLOCK_PHOTO_API_MANAGER_CID))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    
    if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    
    return PhotoAPIManagerFactory;
}

PhotoPeopleModule.canUnload =
function(compMgr)
{
    debug("Unloading photo api manager component.\n");
    return true;
}/*}}}*/
    
/* factory object *//*{{{*/
var PhotoAPIManagerFactory = new Object();

PhotoAPIManagerFactory.createInstance =
function (outer, iid) {
    debug("CI: " + iid + "\n");
    if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;

    return (new flockPhotoAPIManager()).QueryInterface(iid);
}/*}}}*/

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

