/****************************************************************************
* SKCD  > skBasket                                                          *
*---------------------------------------------------------------------------*
* skBasket's goal is to store on the user machine a part or a whole         *
* document of any type (text or not). This module can create and manage     *
* a lot of baskets at the same time. Physically, a basket is a file. It can *
* also be renamed, removed, emptied. Its elements are easily accessible and *
* they can be renamed or removed.                                           *
*---------------------------------------------------------------------------*
* (c) IDM. 2003                                                             *
* Project  : sk                                                             *
* Language : javascript                                                     *
* Created  : MikaelLebrun, 13/02/2003                                       *
****************************************************************************/

/*XPCOM Object used*/
const L_File = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
const MyProfile = Components.classes["@mozilla.org/profile/manager;1"].createInstance(Components.interfaces.nsIProfileInternal);
const I_File = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
const I_Stream = Components.classes['@mozilla.org/scriptableinputstream;1'].createInstance(Components.interfaces.nsIScriptableInputStream);
const O_File = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
const nsiArray = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);

//prio.h
const MODE_CREATE = 0x08;
const MODE_WRONLY = 0x02;
const MODE_APPEND = 0x10;
const MODE_TRUNCATE = 0x20;

const cParsePart = String.fromCharCode(10);//Character to separate the title and each element
const cParseField = String.fromCharCode(9); //Character to separate the query and the wording of the element


function skBasketManager() {
    this.hasBasket = true;						//Allows to know so at least a basket exists.
    this.BasketContentArray = nsiArray.clone();
    this.BasketArray = nsiArray.clone();

    var xoPathProfile = MyProfile.getProfileDir(MyProfile.currentProfile);
    L_File.initWithFile(xoPathProfile);
    L_File.appendRelativePath("panier");
    //The basket directory doesn't exists
    if (!L_File.exists()) {
        this.hasBasket = false;
        return;
    }
    this.BasketDirectory = L_File;			//Objet nsiFIle : Directory path where all the basket are saved
    this.SetupBasket();
}

skBasketManager.prototype = {

    QueryInterface: function(iid) {
        if (!iid.equals(Components.interfaces.skIBasket)
            &&!iid.equals(Components.interfaces.nsISupports)) {
            throw Components.results.NS_ERROR_NO_INTERFACE;
        }
        return this;
    },

    /*********************************************************************************************
    FUNCTION : CreateNewBasketFile()
    This function generates a file name and verify if it doesn't exist in Directory basket,
    while the file name exists.
    ****INPUT: NOTHING
    ****OUPUT: an nsiFile XPCOM object [a basket file]
    ********************************************************************************************/
    CreateNewBasketFile:function() {
        var xiDirectory = this.BasketDirectory.clone().QueryInterface(Components.interfaces.nsILocalFile);
        var FilesList = xiDirectory.directoryEntries;
        var iIndice = 1;
        var FileName = "panier1.txt";
        if (FilesList.hasMoreElements()) {
            while(true) {
                FileName = "panier" + iIndice + ".txt";
                xiDirectory.appendRelativePath(FileName);
                if (!xiDirectory.exists())
                    break;
                xiDirectory = this.BasketDirectory.clone().QueryInterface(Components.interfaces.nsILocalFile);
                iIndice += 1;
            }
        }
        xiDirectory = this.BasketDirectory.clone().QueryInterface(Components.interfaces.nsILocalFile);
        xiDirectory.appendRelativePath(FileName);
        xiDirectory.create(xiDirectory.NORMAL_FILE_TYPE, 0700);
        return xiDirectory;
    },

    SetWindow:function(pxiWindow) {
        this.window = pxiWindow;
    },

    AddBasket:function(psBasketTitle) {
        //The basket isn't created yet
        if (!this.hasBasket) {
            var xoPathProfile = MyProfile.getProfileDir(MyProfile.currentProfile);
            L_File.initWithFile(xoPathProfile);
            L_File.appendRelativePath("panier"); //Create the basket directory in user profile
            L_File.create(L_File.DIRECTORY_TYPE, 0700);
            this.hasBasket = true;
            this.BasketDirectory = L_File;
        }
        var xoMyNewFile = this.CreateNewBasketFile();
        O_File.init(xoMyNewFile, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, 0664, 0);//Open the file to write (Truncate the content)
        var sBasketContent = psBasketTitle + cParsePart;
        O_File.write(sBasketContent, sBasketContent.length)//Write title
        O_File.flush()
        O_File.close()
        this.SetupBasket();
    },


    /*********************************************************************************************
    FUNCTION : ReturnBasketFileContent()
    This function return a basket file content.
    ****INPUT: an nsiFile XPCOM object [pxiBasketFile - a basket file]
    ****OUPUT: a string [ the basket file content]
    ********************************************************************************************/
    ReturnBasketFileContent:function(pxiBasketFile) {
        I_File.init(pxiBasketFile, 1, 0, true)
        I_Stream.init(I_File);
        var iFileSize = I_Stream.available();
        var sFileContents = I_Stream.read(iFileSize);
        I_Stream.close();
        I_File.close();
        return sFileContents;
    },


    /*********************************************************************************************
    FUNCTION : ReturnBasketTitle()
    This function split a basket file content the and return its title.
    ****INPUT: a string [psContent - a basket file content]
    ****OUPUT: a string [the basket title]
    ********************************************************************************************/
    ReturnBasketTitle:function(psContent) {
        var aContent = psContent.split(cParsePart);
        return aContent[0];
    },

    /*********************************************************************************************
    FUNCTION : SetupBasket()
    This function is used to set all baskets.
    ****INPUT: NOTHING
    ****OUPUT: NOTHING
    ********************************************************************************************/
    SetupBasket:function() {
        var xoFiles = this.BasketDirectory.directoryEntries;
        var xiFile;
        var i=0;
        this.BasketContentArray = nsiArray.clone();
        this.BasketArray = nsiArray.clone();
        while(xoFiles.hasMoreElements()) {
            xiFile = xoFiles.getNext().QueryInterface(Components.interfaces.nsIFile);
            if (xiFile.isFile()) {
                var sFileContents = this.ReturnBasketFileContent(xiFile);
                this.LoadBasketArray(this.ReturnBasketTitle(sFileContents), xiFile)
                this.LoadBasketContentArray(sFileContents)
                i+=1;
            }
        }
    },

    /*********************************************************************************************
    FUNCTION : LoadBasketArray()
    This function load a basket in the basket list (i.e its title and its corresponding file)
    ****INPUT: 1/ a string [psBasketTitle - the basket title]
               2/ an nsiFile XPCOM object [pxiBasketFile - a basket file]
    ****OUPUT: NOTHING
    ********************************************************************************************/
    LoadBasketArray:function(psBasketTitle, pxiBasketFile) {
        var aContent = nsiArray.clone();
        aContent.AppendElement(this.CreateXPCOMstring(psBasketTitle));
        aContent.AppendElement(pxiBasketFile);
        this.BasketArray.AppendElement(aContent); //append the new array to the XPCOM array (nsiSupportArray)
    },

    /*********************************************************************************************
    FUNCTION : LoadBasketContentArray()
    This function split the a basket file content, split it in order to retrieve its elements and
    put them in the basket list.
    ****INPUT: a string [psContent - a basket file content]
    ****OUPUT: NOTHING
    ********************************************************************************************/
    LoadBasketContentArray:function(psContent) {
        var xiArrayContent = nsiArray.clone();
        var xiArrayId = nsiArray.clone();
        var xiArrayTitle = nsiArray.clone();
        var aContent = psContent.split(cParsePart);
        var aLine;
        for (var i=1; i<aContent.length-1; i++) {
            aLine = aContent[i].split(cParseField);
            xiArrayId.AppendElement(this.CreateXPCOMstring(aLine[0])); //KEY (query, primary key...)
            xiArrayTitle.AppendElement(this.CreateXPCOMstring(aLine[1])); //Wording
        }
        xiArrayContent.AppendElement(xiArrayId);
        xiArrayContent.AppendElement(xiArrayTitle);
        this.BasketContentArray.AppendElement(xiArrayContent); //append the new array to the XPCOM array (nsiSupportArray)
    },

    RenameBasket:function(psBasketTitle, piBasketIndex) {
        var xiArrayTitle = this.BasketArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        xiArrayTitle.SetElementAt(0, this.CreateXPCOMstring(psBasketTitle));
        this.WriteInFile(piBasketIndex);
    },

    RemoveBasket:function(piBasketIndex) {
        this.BasketFileAt(piBasketIndex).remove(true);
        this.SetupBasket();
    },

    AddContentInBasket:function(psKey, psName, piBasketIndex) {
        var xiArrayContent = this.BasketContentArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        var xiArrayKey = xiArrayContent.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsArray);
        var xiArrayName = xiArrayContent.GetElementAt(1).QueryInterface(Components.interfaces.nsISupportsArray);
        xiArrayKey.AppendElement(this.CreateXPCOMstring(psKey));
        xiArrayName.AppendElement(this.CreateXPCOMstring(this.window.btoa(encodeURIComponent(psName?psName:' ')) ));
        this.WriteInFile(piBasketIndex)
    },

    EmptyBasket:function(piBasketIndex) {
        var sBasketontent = this.BasketTitleAt(piBasketIndex) + cParsePart;
        //Ouvrir Le fichier en ecriture
        O_File.init(this.BasketFileAt(piBasketIndex), MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, 0664, 0);
        O_File.write(sBasketontent, sBasketontent.length);
        O_File.flush();
        O_File.close();
        this.SetupBasket();
    },

    RenameElement:function(psNewName, piBasketIndex, piElementIndex) {
        var xiArrayContent = this.BasketContentArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        var xiArrayName = xiArrayContent.GetElementAt(1).QueryInterface(Components.interfaces.nsISupportsArray);
        xiArrayName.SetElementAt(piElementIndex, this.CreateXPCOMstring( this.window.btoa(encodeURIComponent(psNewName?psNewName:' '))));
        this.WriteInFile(piBasketIndex)
    },

    RemoveElement:function(piBasketIndex, piElementIndex) {
        var xiArrayContent = this.BasketContentArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        var xiArrayKey = xiArrayContent.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsArray);
        var xiArrayName = xiArrayContent.GetElementAt(1).QueryInterface(Components.interfaces.nsISupportsArray);
        xiArrayKey.DeleteElementAt(piElementIndex);
        xiArrayName.DeleteElementAt(piElementIndex);
        this.WriteInFile(piBasketIndex)
    },

    /*********************************************************************************************
    FUNCTION : WriteInFile()
    This function writes a basket into the basket file.
    ****INPUT: an integer [piBasketIndex - the basket position in the basket list]
    ****OUPUT: NOTHING
    ********************************************************************************************/
    WriteInFile:function(piBasketIndex) {
        O_File.init(this.BasketFileAt(piBasketIndex), MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, 0664, 0);
        var sBasketContent = this.BasketTitleAt(piBasketIndex) + cParsePart;
        O_File.write(sBasketContent, sBasketContent.length)
        for (var i=0; i < this.BasketContentCount(piBasketIndex); i++) {
            sBasketContent = this.ReadKeyAt(piBasketIndex, i, false) + cParseField + this.ReadNameAt(piBasketIndex, i, true) + cParsePart;
            O_File.write(sBasketContent, sBasketContent.length);
        }
        O_File.flush();
        O_File.close();
    },

    /*********************************************************************************************
    FUNCTION : BasketFileAt()
    This function return the nsiFile XPCOM object corresponding to the desired basket
    ****INPUT: an integer [piBasketIndex - the basket position in the basket list]
    ****OUPUT: an nsiFile XPCOM object [the basket file]
    ********************************************************************************************/
    BasketFileAt:function(piBasketIndex) {
        var xiArrayTitle = this.BasketArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        return xiArrayTitle.GetElementAt(1).QueryInterface(Components.interfaces.nsIFile);
    },

    /*********************************************************************************************
    FUNCTION : GetBasketIndexByName()
    This function return the index of a basket by its title
    ****INPUT: piBasketName the basket name
    ****OUPUT: the basket index
    ********************************************************************************************/
    GetBasketIndexByName:function(psBasketName) {
        //dump(this.BasketFileCount());
        for (var i=0; i<this.BasketFileCount(); i++) {
            //dump(this.BasketTitleAt(i));
            if (this.BasketTitleAt(i) == psBasketName) {
                return i;
            }
        }
        return -1;
    },


    /*********************************************************************************************
    FUNCTION : BasketTitleAt()
    This function return a basket title
    ****INPUT: an integer [piBasketIndex - the basket position in the basket list]
    ****OUPUT: a string [the title of the basket]
    ********************************************************************************************/
    BasketTitleAt:function(piBasketIndex) {
        if (piBasketIndex > this.BasketFileCount()-1)
            return null;
        var xiArrayTitle = this.BasketArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        return xiArrayTitle.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsString).data;
    },

    /*********************************************************************************************
    FUNCTION : BasketFileCount()
    This function return the number of registered baskets
    ****INPUT: NOTHING
    ****OUPUT: NOTHING
    ********************************************************************************************/
    BasketFileCount:function() {
        return this.BasketArray.Count();
    },

    /*********************************************************************************************
    FUNCTION : ReadKeyAt()
    This function return an element key knowing its basket index and its element index
    ****INPUT: 1/ an integer [piBasketIndex - the basket position in the basket list]
               2/ an integer [piElementIndex - the element position in the element list]
    ****OUPUT: a string [the element key]
    ********************************************************************************************/
    ReadKeyAt:function(piBasketIndex, piElementIndex) {
        var xiArrayContent = this.BasketContentArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        var xiArrayKey = xiArrayContent.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsArray);
        var sName = xiArrayKey.GetElementAt(piElementIndex).QueryInterface(Components.interfaces.nsISupportsString).data;
        return sName;
    },

    /*********************************************************************************************
    FUNCTION : ReadNameAt()
    This function return an element name knowing its basket index and its element index
    ****INPUT: 1/ an integer [piBasketIndex - the basket position in the basket list]
               2/ an integer [piElementIndex - the element position in the element list]
               3/ a boolean [pbNoDecode - not decode the name to return]
    ****OUPUT: a string [the element name]
    ********************************************************************************************/
    ReadNameAt:function(piBasketIndex, piElementIndex, pbNoDecode) {
        var xiArrayContent = this.BasketContentArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        var xiArrayName = xiArrayContent.GetElementAt(1).QueryInterface(Components.interfaces.nsISupportsArray);
        var sName = xiArrayName.GetElementAt(piElementIndex).QueryInterface(Components.interfaces.nsISupportsString).data;
        if(!pbNoDecode) {
            sName = /*skIBase64.Decode*/ decodeURIComponent(this.window.atob(sName));
        }
        return sName;
    },

    /*********************************************************************************************
    FUNCTION : BasketContentCount()
    This function return the number of elements in a basket
    ****INPUT: an integer [piBasketIndex - the basket position in the basket list]
    ****OUPUT: an integer [the number of elements in a basket]
    ********************************************************************************************/
    BasketContentCount:function(piBasketIndex) {
        var xiArrayContent = this.BasketContentArray.GetElementAt(piBasketIndex).QueryInterface(Components.interfaces.nsISupportsArray);
        var xiArrayId = xiArrayContent.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsArray);
        return xiArrayId.Count();
    },

    /*********************************************************************************************
    FUNCTION : CreateXPCOMstring()
    This function creatre, initialize and return xpcom object in order to insert it in a XPCOM
    nsiSupportArray object.
    ****INPUT: a string [piBasketIndex - the basket position in the basket list]
    ****OUPUT: an XPCOM object [an interface nsISupportsString]
    ********************************************************************************************/
    CreateXPCOMstring:function(psValue) {
        var xiString = Components.classes["@mozilla.org/supports-string;1"].createInstance().QueryInterface(Components.interfaces.nsISupportsString);
        xiString.data = psValue;
        return xiString;
    }
}

var skBasketManagerModule = {

    registerSelf: function (compMgr, fileSpec, location, type) {
        compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(this.myCID,
                                        "Sk Basket Manager Component",
                                        this.myProgID,
                                        fileSpec,
                                        location,
                                        type);
    },

    getClassObject: function (compMgr, cid, iid) {
        if (!cid.equals(this.myCID))
            throw Components.results.NS_ERROR_NO_INTERFACE;
        if (!iid.equals(Components.interfaces.nsIFactory))
            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
        return this.myFactory;
    },

    myCID: Components.ID("e2472c81-e0d5-4cd4-b513-2a987ac8831a"),

    myProgID: "@idm.fr/sk_basket;1",

    myFactory: {
        createInstance: function (outer, iid) {
            if (outer != null)
                throw Components.results.NS_ERROR_NO_AGGREGATION;
            return (new skBasketManager()).QueryInterface(iid);
        }
    },

    canUnload: function(compMgr) {
        return true;
    }
};

function NSGetModule(compMgr, fileSpec) {
    return skBasketManagerModule;
}
