/* Cross-browser wrapper for Video Manager */
DMWrapper.prototype = {
    /*CONSTANTS*/
    TYPE_NA: 0,// not available
    TYPE_IE: 1,// Internet Explorer - ActiveX
    TYPE_FF: 2,// Mozilla Firefox - XPCOM
    TYPE_PL: 3,// Plugin for Firefox, Chrome, Opera (Gecko)
    DOM_ID_DM: 'ax',// as the component programmer wanted
    MIME_TYPE: 'application/firedlmgrgate',// Plugin mime type
    TEST_REG_NAME: 'inexistent',// Registry test entry name
    ERR_CODE_DOL: 'DOL',
    ERR_CODE_LAC: 'LAC',
    RET_CODE_SUCCESS: 0,// addDown returned success
    RET_CODE_NOTFOUND: 1,// addDown returned vodItem not found
    RET_CODE_PARSER: 2,// addDown returned parser error
    RET_CODE_NOMOVIEURL: 3,// addDown returned no movie URL
    RET_CODE_NOMOVIETITLE: 4,// addDown returned no movie title
    RET_CODE_UNKNOWN: 10,// addDown returned unkown error
    RET_CODE_NOVTIID: 101,// addDown returned no vti_id
    RET_CODE_DATE: 110,// addDown returned writing date error
    STATUS_TESTING: 'testing',// VM is testing the download
    STATUS_DOWNLOADING: 'downloading',// the movie is downloading
    STATUS_ERROR: /error\:(\d+)/i,// error while downloading
    STATUS_IDLE: 'idle',// not in the downloading state (stopped or queued)
    STATUS_NOTFOUND: 'not_found',// vtiId not in VM list
    /*VARIABLES*/
    /* the underlaying ActiveX / XPCOM / Plugin */
    dmObj: null,
    /* PluginHelper object */
    phObj: null,
    /* object type (browser dependant) */
    type: null,
    /* MS class ID */
    classid: null,
    /* ActiveX ID */
    progid: null,
    /* Status checker timer instance */
    checkTimer: null,
    /* The vtiIds that need checks */
    checkIds: [],
    /* The VOD data sent to VM indexed by vtiId */
    vodItems: [],
    /* vtiIds that have errors, indexed by ERR_CODE */
    errIds: {},
    /* Checker callback */
    checkCallback: null,
    /* XPCOM support */
    supportXPCOM: true,
    /*METHODS*/
    /* test if DM is already instantiated on IE */
    testInstantiationIE: function () {
        try {
            var isAX = !!(window.ActiveXObject || (this.phObj && this.phObj.attName == 'CLASSID'));
            var check = typeof(this.dmObj.release);
            return isAX && check != 'unknown' && check != 'undefined';
        } catch(ex) {
            //alert(ex.message || ex);
            return false;
        }
    },
    /* test if VM is already instantiated with plugin */
    testInstantiationPL: function () {
        try {
            var isPL = !!(this.phObj && this.phObj.attName == 'TYPE');
            var check = typeof(this.dmObj.value(this.TEST_REG_NAME));
            return isPL && check != 'unknown' && check != 'undefined';
        } catch(ex) {
            //alert(ex.message || ex);
            return false;
        }
    },
    /* test if VM is already instantiated on FF */
    testInstantiationFF: function () {
        try {
            var x = this.dmObj.get('release');
            return true;
        } catch(ex) {
            return false;
        }
    },
    /* test IE installation */
    testInstallationIE: function () {
        // already instatiated ?
        if (this.testInstantiationIE())
            return true;
        try {
            this.initIE();
            return this.testInstantiationIE();
        } catch(ex) {
            //alert(ex.message || ex);
            return false;
        }
    },
    /* test FF installation */
    testInstallationFF: function () {
        // XPCOM compatible ?
        if (typeof(netscape) == 'undefined' && typeof(Components) == 'undefined')
            return false;
        // already instatiated ?
        if (this.testInstantiationFF())
            return true;
        try {
            // VM installed ?
            this.initFF();
            return this.testInstantiationFF();
        } catch(ex) {
            return false;
        }
    },
    /* test Plugin installation */
    testInstallationPL: function () {
        // already instatiated ?
        if (this.testInstantiationPL())
            return true;
        try {
            // VM installed ?
            this.initPL();
            return this.testInstantiationPL();
        } catch(ex) {
            return false;
        }
    },
    /* test with which object type the browser is compatible
     * ! the order of checks is important !
     */
    testInstall: function () {
        // if type already decided stick to it
        switch (this.type) {
            case this.TYPE_PL:
                if (this.testInstallationPL())
                    return this.type;
                break;
            case this.TYPE_FF:
                if (this.supportXPCOM && this.testInstallationFF())
                    return this.type;
                break;
            case this.TYPE_IE:
                if (this.testInstallationIE())
                    return this.type;
                break;
            default:
        }
        // type not set yet or not correctly initialized
        if (this.testInstallationPL()) {
            this.type = this.TYPE_PL;
        } else
        if (this.supportXPCOM && this.testInstallationFF()) {
            this.type = this.TYPE_FF;
        } else
        if (this.testInstallationIE()) {
            this.type = this.TYPE_IE;
        } else {
            this.type = this.TYPE_NA;
        }
        return this.type;
    },
    /* test if the object is installed */
    isInstalled: function () {
        return this.testInstall() != this.TYPE_NA;
    },
    /* clear IE object instances */
    cleanupIE: function () {
        if ( window.ActiveXObject ) {
            this.dmObj = null;
        } else {
            this.cleanupPL();
        }
    },
    /* clear plugin instances */
    cleanupPL: function () {
        while (document.getElementById(this.DOM_ID_DM)) {
            try {
                this.phObj.release();
            } catch(ex) {}
        }
    },
    /* IE initialization */
    initIE: function () {
        this.cleanupIE();
        if ( window.ActiveXObject ) {
            this.dmObj = new ActiveXObject(this.progid);
        } else {/*PluginHelper can be used to instatiate the ActiveX*/
            this.phObj = new PluginHelper('CLASSID','CLSID:'+this.classid);
            this.dmObj = this.phObj.objDOM;
        }
    },
    /* FF initialization */
    initFF: function () {
        try {
            this.dmObj = new Dlmgr();
            this.dmObj.init(this.classid);
        } catch (ex) {
            //alert(ex.message || ex);
        }
    },
    /* Plugin initialization */
    initPL: function () {
        this.cleanupPL();
        try {
            if (!ConfigTest.hasPlugin(this.MIME_TYPE))
                return;
            this.phObj = new PluginHelper('TYPE',this.MIME_TYPE);
            this.dmObj = this.phObj.objDOM;
            this.dmObj.init('{' + this.classid + '}');
        } catch (ex) {
            //alert(ex.message || ex);
        }
    },
    /* initialization method */
    init: function () {
        // if not already tested, make install
        if (this.type == this.TYPE_NA) {
            this.type = this.testInstall();
            return;
        }
        // installation tested, check for instance
        switch (this.type) {
            case this.TYPE_PL:
                if (!this.testInstantiationPL())
                    this.initPL();
                break;
            case this.TYPE_IE:
                if (!this.testInstantiationIE())
                    this.initIE();
                break;
            case this.TYPE_FF:
                if (this.supportXPCOM && !this.testInstantiationFF())
                    this.initFF();
                break;
            default:
        }
    },
    /* check if COPP is supported */
    isCOPPSupported: function () {
        return (/^(0|3\d+)$/g.test(''+this.getCOPP()));
    },
    /* check the status of a vtiId */
    isStatusOK: function (/*BSTR*/vtiId) {
        var sts = this.getStatus( vtiId );
        return (sts != this.STATUS_NOTFOUND);
    },
    /* Call the error callback */
    callErrCallback: function () {
        var info = {};
        var hasErrors = false;
        for (var errCode in this.errIds) {
            var ids = this.errIds[errCode];
            if (!ids || ids.length <= 0) {
                continue;
            }
            info[errCode] = [];
            for (var x=0; x<ids.length; x++) {
                info[errCode].push(this.vodItems[ids[x]].vtiTitle);
                hasErrors = true;
            }
        }
        if (hasErrors)
            this.checkCallback(info);
    },
    /* Checker function called by timer */
    checker: function () {
        for (var x=0;x<this.checkIds.length;x++) {
            var sts = this.getStatus(this.checkIds[x]);
            if (sts == this.STATUS_TESTING) {
                continue;
            }
            if (sts == this.STATUS_NOTFOUND) {
                this.errIds[this.ERR_CODE_DOL].push(this.checkIds[x]);
            } else
            if (this.STATUS_ERROR.test(sts)) {
                //TODO: save the error
                //var errCode = this.STATUS_ERROR.exec(sts)[1];
                this.errIds[this.ERR_CODE_DOL].push(this.checkIds[x]);
            }
            // no more checking for this vtiId
            this.checkIds.splice(x,1);
        }
        if (this.checkIds.length==0) {// finished checking
            clearInterval(this.checkTimer);
            this.checkTimer = null;
            this.callErrCallback();
            this.checkCallback = null;
        }
    },
    /* Check the addDown status of a vtiId */
    checkStatus: function (/*String*/vtiId,/*String*/retCode) {
        // version < 3.3
        if (typeof(retCode) == 'undefined') {
            if (!this.isStatusOK(vtiId)) {
                this.errIds[this.ERR_CODE_DOL].push(vtiId);
            }
        } else {
            if (retCode != this.RET_CODE_SUCCESS) {// adding unsuccessful
                this.errIds[this.ERR_CODE_DOL].push(vtiId);//TODO: save the retCode
            } else {
                // check for status
                this.checkIds.push(vtiId);
            }
        }
        if (!this.checkTimer) {
            var objThis = this;
            this.checkTimer = setInterval(function(){try{objThis.checker();}catch(ex){/*alert(ex.message||ex);*/}},500);
        }
    },

    /*******************
        Wrapped methods
    *******************/
    /* add a movie to download list - returns SHORT :
        0	– The download was added successfully.
        1	– No vod_item found in xml structure.
        2	– Parser error.
        3	– No movie url.
        4	– No movie title.
        10	– Unknown error.
        101	– No vti_id.
        110	– Error writing date.
     */
    addDown: function (/*BSTR*/ vti_id, /*BSTR*/ vhi_id, /*BSTR*/ xmldata, /*BOOL*/ telech) {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    return this.dmObj.addDown(vti_id, vhi_id, xmldata, telech);
                case this.TYPE_FF:
                    try {// version > 3.3
                        return this.dmObj.get('addDown', vti_id, vhi_id, xmldata, telech);
                    } catch (ex) {// back-compatibility
                        this.dmObj.call('addDown', vti_id, vhi_id, xmldata, telech);
                    }
                    break;
                default:
            }
        } catch(ex) {
            //alert(ex.message || ex);
        }
    },
    /* show window method */
    showWindow: function (/*BOOL*/ show) {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    this.dmObj.showWindow(show);
                    break;
                case this.TYPE_FF:
                    this.dmObj.call('showWindow', show);
                    break;
                default:
            }
        } catch(ex) {
            //alert(ex.message || ex);
        }

    },
    /* show window with adult tab */
    showAdultWindow: function (/*BOOL*/ show) {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    this.dmObj.showAdultWindow(show);
                    break;
                case this.TYPE_FF:
                    this.dmObj.call('showAdultWindow', show);
                    break;
                default:
            }
        } catch(ex) {
            //alert(ex.message || ex);
        }

    },
    /*******************************
        Wrapped properties (get/set)
    *******************************/
    /*
        Get the status for a vti_id - returns BSTR :
        “not_found”     - The download with this vti_id was not found.
        “testing”       - The download with this vti_id is being tested
        “downloading”   - The download with this vti_id is downloading
        “idle”          - The download with this vti_id is idle (not downloading)
        “error:{nr}”    - The download with this vti_id has a problem, {nr} is the error number
    */
    getStatus: function (/*BSTR*/ vti_id) {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    return this.dmObj.status(vti_id);
                case this.TYPE_FF:
                    return this.dmObj.get('status', vti_id);
                default:
                    return null;
            }
        } catch(ex) {
            //alert(ex.message || ex);
            return null;
        }

    },
    /* get a registry value - returns BSTR */
    getValue: function (/*BSTR*/ name) {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    return this.dmObj.value(name);
                case this.TYPE_FF:
                    return this.dmObj.get('value', name);
                default:
                    return null;
            }
        } catch(ex) {
            //alert(ex.message || ex);
            return null;
        }

    },
    /* set a registry value */
    setValue: function (/*BSTR*/ name, /*BOOL*/ value) {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                    this.dmObj.value(name, value);
                    break;
                case this.TYPE_IE:
                    this.dmObj.value(name) = value;
                    break;
                case this.TYPE_FF:
                    this.dmObj.set('value', name, value);
                    break;
                default:
            }
        } catch(ex) {
            //alert(ex.message || ex);
        }

    },
    /* obtain a license for a vtiId, given the URL - returns BOOL */
    getLicenseFromUrl: function (/*BSTR*/ vti_id, /*BSTR*/ url) {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    return this.dmObj.getLicenseFromUrl(vti_id, url);
                case this.TYPE_FF:
                    return this.dmObj.get('getLicenseFromUrl', vti_id, url);
                default:
                    return null;
            }
        } catch(ex) {
            //alert(ex.message || ex);
            return null;
        }

    },
    /* obtains the release version - returns BSTR */
    getRelease: function () {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    return this.dmObj.release;
                case this.TYPE_FF:
                    return this.dmObj.get('release');
                default:
                    return null;
            }
        } catch(ex) {
            //alert(ex.message || ex);
            return null;
        }

    },
    /* obtains the COPP availability - returns LONG
    	0 - SUPPORTED
		1 - NOT SUPPORTED
		2 - CANNOT DETERMINE SUPPORT
		3XXXXXXX -
    */
    getCOPP: function () {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    return this.dmObj.COPP;
                case this.TYPE_FF:
                    return this.dmObj.get('COPP');
                default:
                    return null;
            }
        } catch(ex) {
            //alert(ex.message || ex);
            return null;
        }

    },
    /* obtains a diagnostic string with DRM info */
    getDiagnostic: function () {
        try {
            this.init();
            switch (this.type) {
                case this.TYPE_PL:
                case this.TYPE_IE:
                    return this.dmObj.diagnostic;
                case this.TYPE_FF:
                    return this.dmObj.get('diagnostic');
                default:
                    return null;
            }
        } catch(ex) {
            //alert(ex.message || ex);
            return null;
        }
    },

    /*******************************
        Own public methods
    *******************************/
    /* call addDown for several vod items */
    addDownAll: function (/*Array of PurchaseTitleData*/ items,/*Function*/ errCallback) {
        if (!items || !items.length)
            return;
        this.checkCallback = errCallback;
        this.errIds[this.ERR_CODE_LAC] = [];
        this.errIds[this.ERR_CODE_DOL] = [];
        for (var x=0; x<items.length; x++) {
            this.vodItems[items[x].vtiId] = items[x];
            // call getLicense only if we have an URL
            if (items[x].licenseURL) {
                if (!this.getLicenseFromUrl(items[x].vtiId, items[x].licenseURL)) {
                    this.errIds[this.ERR_CODE_LAC].push(items[x].vtiId);
                }
            }
            var retCode = this.addDown(
                items[x].vtiId,
                items[x].vhiId,
                items[x].xmldata,
                items[x].download
            );
            this.checkStatus(items[x].vtiId,retCode);
        }
    }
};

function checkDM() {
	var dmw = new DMWrapper();
	return dmw.isInstalled();
}