/**
 * $URL$
 * DAAND
 * Copyright (C) 2004 - 2013, INSIDE Secure BV.  All rights reserved.
 */
package com.insidesecure.drm.android.reference;


import android.content.Context;
import android.net.Uri;
import android.os.*;
import android.util.Log;
import com.insidesecure.drm.android.reference.customdata.CustomDataProvider;
import com.insidesecure.drm.android.reference.customdata.file.*;
import com.insidesecure.drm.android.reference.customdata.inline.InlineCustomDataProvider;
import com.insidesecure.drmagent.*;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.*;
import java.lang.reflect.*;
import java.lang.reflect.Proxy;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ContentHandler
{
    public static final String TAG = "ContentHandler";

    private static XmlPullParserFactory mParserFactory;

    private static Set<String> mLastLoadedListFileNames = new HashSet<String>();
    private static List<DRMContentInfo> mLastLoadedDRMContentInfos = new ArrayList<DRMContentInfo>();

    public interface OnCDescRetrieveFinishedCallback
    {
        void onRetrieveFinished(String loadedFilePath);

        void onRetrieveFailed(String errorMessage);
    }

    /**
     *
     */
    public static void initialize()
    {
        try
        {
            final File f = new File(Constants.CONFIGURATION_CDESC_PATH);

            if (!f.exists())
            {
                if (!f.mkdir())
                {
                    Log.w(TAG, "Could not create content directory: " + Constants.CONFIGURATION_CDESC_PATH);
                }
                else
                {
                    Log.d(TAG, "Created content directory: " + Constants.CONFIGURATION_CDESC_PATH);
                }
            }
            else
            {
                // Verfify it is a directory!
                if (!f.isDirectory())
                {
                    Log.e(TAG, "Content directory is actually a file!");
                }
            }

        }
        catch (Exception e)
        {
            Log.e(TAG, e.getMessage(), e);
        }
    }

    /**
     *
     * @param uri
     */
    public static void addCustomContent(final Uri uri, final OnCDescRetrieveFinishedCallback callback)
    {
        final String fileName = uri.getLastPathSegment();
        if(fileName != null)
        {
            new Thread(new Runnable()
            {

                public void run()
                {
                    try
                    {
                        Log.d(TAG, "Opening url: " + uri);
                        FileOutputStream outputStream = null;
                        InputStream inputStream = null;
                        try
                        {
                            inputStream = new URL(uri.toString()).openStream();
                            outputStream = new FileOutputStream(Constants.CONFIGURATION_CDESC_PATH + fileName);
                            Tools.copyStream(inputStream, outputStream);
                            callback.onRetrieveFinished(Constants.CONFIGURATION_CDESC_PATH + fileName);
                        }
                        catch (MalformedURLException e)
                        {
                            Log.e(TAG, "Error while opening uri: " + uri, e);
                            callback.onRetrieveFailed("Error occurred while opening a URL");
                        }
                        catch (IOException e1)
                        {
                            Log.e(TAG, "Error while opening uri: " + uri, e1);
                            callback.onRetrieveFailed("Error occurred while loading content");
                        }
                        finally
                        {
                            if (inputStream != null)
                                inputStream.close();
                            if (outputStream != null)
                            {
                                outputStream.flush();
                                outputStream.close();
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Log.e(TAG, "Error while retrieving data from uri: " + e.getMessage(), e);
                        callback.onRetrieveFailed("Error occurred while loading content");
                    }
                }

            }).start();
        }
        else
        {
            Log.e(TAG, "Error while retrieving data from uri: " + uri);
            callback.onRetrieveFailed("Error occurred while opening a URL");
        }
    }

    public static void unzipContent(final String fileName, OnCDescRetrieveFinishedCallback callback)
    {
        try
        {
            ZipInputStream zis = null;
            ZipEntry zipEntry;

            try
            {
                zis = new ZipInputStream(new FileInputStream(fileName));
                zipEntry = zis.getNextEntry();
                while (zipEntry != null)
                {
                    String file = zipEntry.getName();
                    File newFile = new File(file);
                    FileOutputStream fos = new FileOutputStream(Constants.CONFIGURATION_CDESC_PATH + newFile);
                    Tools.copyStream(zis, fos);
                    fos.close();
                    zipEntry = zis.getNextEntry();
                }
                callback.onRetrieveFinished(null);
            }
            catch (Exception e)
            {
                Log.e(TAG, e.getMessage());
                callback.onRetrieveFailed("Error occurred while unzipping content");
            }
            finally
            {
                if(zis != null)
                {
                    zis.close();
                }
            }
        }
        catch (Exception e)
        {
            Log.e(TAG, e.getMessage());
            callback.onRetrieveFailed("Error occurred while unzipping content");
        }
        File file = new File(fileName);
        file.delete();
    }


    public static void clearCache()
    {
        mLastLoadedListFileNames.clear();
        mLastLoadedDRMContentInfos.clear();
    }

    public static void clearCachedContent(final Context context, final DRMContentInfo drmContentInfo)
    {
        drmContentInfo.getDRMContent(context).clearCachedData();
    }

    public static void performIntegrityCheck(final Context context, final DRMContentInfo drmContentInfo)
    {
        drmContentInfo.getDRMContent(context).performCacheIntegrityCheck(true);
    }

    /** Listener API which is called as valid descriptor files are found.
     */
    public static interface ContentRetrieverListener
    {
        /** Method invoked whenever a valid content descriptor has been found.
         *
         * @param drmContentInfo A valid content descriptor
         */
        public void drmContentInfoRetrieved(final DRMContentInfo drmContentInfo);

        /** Method invoked whenever a error occured while processing a descriptor
         *
         * @param name The name of the file being processed
         * @param url The URL (may be null if could not be read) of the content.
         */
        public void drmContentInfoRetrievalFailed(final File name, final URL url);

        public void drmContentRetrievalFinished(List<DRMContentInfo> drmContentInfos);

        public void checkProtection(DRMContentInfo drmContentInfo);

    }

    private static ExecutorService CONTENT_DESCRIPTOR_LOADER = Executors.newFixedThreadPool(4);

    /** Retrieves the list of content information found.  This will attempt to load all the descriptors available and
     * may impose some network I/O.
     *
     * @param context The context to use for resolving information as need be
     * @param validateLocalFiles Whether the content descriptors found should be validated if pointing to local media files
     * @param contentRetrieverListener Listener which is invoked for each valid descriptor found.  Allows for progressive
     * updates of content lists for example
     * @return The fully loaded list of valid content descriptors
     */
    public static void retrieveDRMContentInfos(final Context context, final boolean validateLocalFiles, final boolean loadDRMInformation, final ContentRetrieverListener contentRetrieverListener)
    {
        initialize();

        CONTENT_DESCRIPTOR_LOADER.submit(new Runnable()
        {
            @Override
            public void run()
            {
                Log.d(TAG, "Retrieving content");

                final Handler handler = new Handler(Looper.getMainLooper());
                final File[] files = retrieveContentDescriptorFiles();
                final Set<String> loadedFileNames = convertToNameSet(files);

                if (loadedFileNames.size() == mLastLoadedListFileNames.size() && mLastLoadedListFileNames.containsAll(loadedFileNames))
                {
                    Log.d(TAG, "No changes in descriptor set: will use cached content info");

                    if (contentRetrieverListener != null) {

                        for (final DRMContentInfo drmContentInfo : mLastLoadedDRMContentInfos) {
                            try {
                                if (drmContentInfo.getDRMContent(context) != null) {
                                    // Don't do this on the main UI - only the fact we have the completely loaded DRMContentInfo
                                    // available will be done on the UI thread.
                                    contentRetrieverListener.checkProtection(drmContentInfo);
                                    handler.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            contentRetrieverListener.drmContentInfoRetrieved(drmContentInfo);
                                        }
                                    });
                                }
                            } catch (DRMAgentException e) {
                                Log.e(TAG, "Error loading content descriptor: " + e.getMessage(), e);
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        contentRetrieverListener.drmContentInfoRetrievalFailed(new File(drmContentInfo.mContentDescriptorLocation), null);
                                    }
                                });
                            }
                        }

                        // Finalize the load
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                contentRetrieverListener.drmContentRetrievalFinished(mLastLoadedDRMContentInfos);
                            }
                        });
                    }
                    return;
                }

                // Else - we schule N tasks to load the data
                if (files != null && files.length > 0)
                {
                    Log.d(TAG, "Changes in descriptor set: cannot use cached content info");
                    loadedFileNames.clear();

                    final CountDownLatch countDownLatch = new CountDownLatch(files.length);
                    final List<Future<DRMContentInfo>> loadingInstances = new ArrayList<Future<DRMContentInfo>>();

                    for (final File file : files)
                    {
                        Future<DRMContentInfo> drmContentInfoFuture = CONTENT_DESCRIPTOR_LOADER.submit(new Callable<DRMContentInfo>()
                        {
                            @Override
                            public DRMContentInfo call() throws Exception
                            {
                                try
                                {
                                    final DRMContentInfo drmContentInfo = processContentDescriptor(context, file, validateLocalFiles, loadDRMInformation);
                                    if (drmContentInfo != null)
                                    {
                                        if (contentRetrieverListener != null)
                                        {
                                            handler.post(new Runnable()
                                            {
                                                @Override
                                                public void run()
                                                {
                                                    contentRetrieverListener.checkProtection(drmContentInfo);
                                                    contentRetrieverListener.drmContentInfoRetrieved(drmContentInfo);
                                                }
                                            });
                                        }
                                        loadedFileNames.add(file.getAbsolutePath());
                                    }
                                    else
                                    {
                                        // Error loading the descriptor in question
                                        if (contentRetrieverListener != null)
                                        {
                                            handler.post(new Runnable()
                                            {
                                                @Override
                                                public void run()
                                                {
                                                    contentRetrieverListener.drmContentInfoRetrievalFailed(file, null);
                                                }
                                            });
                                        }
                                    }
                                    return drmContentInfo;
                                }
                                catch (Exception e)
                                {
                                    Log.e(TAG, "Error loading content descriptor: " + e.getMessage(), e);
                                    // Error loading the descriptor in question
                                    if (contentRetrieverListener != null)
                                    {
                                        handler.post(new Runnable()
                                        {
                                            @Override
                                            public void run()
                                            {
                                                contentRetrieverListener.drmContentInfoRetrievalFailed(file, null);
                                            }
                                        });
                                    }
                                    return null;
                                }
                                finally
                                {
                                    countDownLatch.countDown();
                                }
                            }
                        });
                        loadingInstances.add(drmContentInfoFuture);
                    }

                    try
                    {
                        // Wait for loading to complete
                        countDownLatch.await();
                    }
                    catch (InterruptedException e)
                    {
                        // Opps
                    }

                    final List<DRMContentInfo> drmContentInfos = new ArrayList<DRMContentInfo>();

                    for (Future<DRMContentInfo> loadingInstance : loadingInstances)
                    {
                        try
                        {
                            DRMContentInfo drmContentInfo = loadingInstance.get();
                            if (drmContentInfo != null)
                            {
                                drmContentInfos.add(drmContentInfo);
                            }
                        }
                        catch (Exception e)
                        {
                            // Ignore for now
                        }
                    }

                    mLastLoadedDRMContentInfos = drmContentInfos;
                    mLastLoadedListFileNames = loadedFileNames;

                    handler.post(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            contentRetrieverListener.drmContentRetrievalFinished(mLastLoadedDRMContentInfos);
                        }
                    });

                }


            }
        });

    }

    private static Set<String> convertToNameSet(final File[] files)
    {
        final Set<String> names = new TreeSet<String>();
        if (files != null)
        {
            for (File file : files)
            {
                names.add(file.getAbsolutePath());
            }
        }
        return names;
    }

    private static File[] retrieveContentDescriptorFiles()
    {
        final File baseDir = new File(Constants.CONFIGURATION_CDESC_PATH);
        return baseDir.listFiles(new FileFilter()
        {
            public boolean accept(final File file)
            {
                return (file.isFile() && file.getName().endsWith(".cdesc"));
            }
        });
    }

    private static DRMContentInfo processContentDescriptor(final Context context, final File file, final boolean validateLocalFiles, final boolean loadDRMInformation)
    {
        try
        {
            Log.d(TAG,"Processing descriptor: " + file);
            // First try to parse it as XML
            DRMContentInfo drmContentInfo = parseAsXML(context, file,validateLocalFiles, loadDRMInformation);

            if (validateLocalFiles && drmContentInfo != null)
            {
                // Perform some sanity checking of the content - this will check if the content is actually
                // DRM protected (if the descriptors indicate they are).  Prevents errors further down the
                // road
                try
                {
                    if (drmContentInfo.getDRMContent(context).isProtected())
                    {
                        // calling the ContentHandler wrapper here wouldn't detect inconsistencies
                        drmContentInfo.getDRMContent(context).getDRMRights();
                    }
                }
                catch (DRMAgentException e)
                {
                    // If this happens in the above call, there is some inconsistency with the content!
                    Log.d(TAG, "Peeking at content caused error: " + e.getMessage());
                    Log.d(TAG, "This indicates the content located at " +
                            "'" + drmContentInfo.mContentLocation + "' " +
                            "is marked as DRM protected and the DRM Agent detected the content to be CLEARTEXT.  " +
                            "Please verify your content descriptor");
                    throw new DescriptorLoadException(file, drmContentInfo.mContentLocation.toURL(), e.getMessage());
                }
            }

            // The try property
            return drmContentInfo;
        }
        catch (DescriptorLoadException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            Log.e(TAG, "Error while parsing XML: " + e.getMessage(), e);
            throw new DescriptorLoadException(file, null, e.getMessage());
        }
    }

    private static URI performValidationAndRewrite(final boolean validateLocalFiles, final String uriStr, final boolean useContentFromUS) throws URISyntaxException
    {
        String uriToUse;
        if (uriStr == null)
        {
            throw new NullPointerException("No URI specified");
        }

        Log.d(TAG,"Resolving URI: " + uriStr);

        if(useContentFromUS && (uriStr.startsWith(Constants.CONFIGURATION_EU_HOST_NAME) || uriStr.startsWith(Constants.CONFIGURATION_DEV_HOST_NAME)))
        {
            uriToUse = uriStr.replace(Constants.CONFIGURATION_EU_HOST_NAME, Constants.CONFIGURATION_US_HOST_NAME);
            uriToUse = uriToUse.replace(Constants.CONFIGURATION_DEV_HOST_NAME, Constants.CONFIGURATION_US_HOST_NAME);
            Log.d(TAG,"Updating host-name to stream content from US, new url: " + uriToUse);
        }
        else
        {
            uriToUse = uriStr;
        }

        URI uri;
        if (uriToUse.startsWith("file://") || uriToUse.startsWith("http://")|| uriToUse.startsWith("https://") )
        {
            //encode white spaces
            uri = new URI(uriToUse.replaceAll(" ", "%20"));
        }
        else
        {
            File f = new File(Constants.CONFIGURATION_CDESC_PATH,uriToUse);
            uri = f.toURI();
        }

        Log.d(TAG,"URI resolved to: " + uri);

        if (validateLocalFiles && (uri.getScheme() == null || "file".equals(uri.getScheme())))
        {
            Log.d(TAG,"Verifying local URI: " + uri );
            // Verify the file exists as well!
            File f = new File(uri.getPath());
            if (!f.exists())
            {
                throw new RuntimeException("Local file specified by '" + uriToUse + "' could not be found");
            }
        }
        return uri;
    }

    public static DRMContentInfo parseAsXML(final Context context, final File file, final boolean validateLocalFiles, final boolean attemptLoadDRMInformation)
    {
        boolean useContentFromUS = context.getSharedPreferences(Constants.PREFERENCES_FILENAME, Context.MODE_PRIVATE).getBoolean(Constants.PREFERENCES_USE_CONTENT_FROM_US, false);

        try
        {
            try
            {
                DRMContentInfo drmContentInfo = parseXMLFile(file, validateLocalFiles, useContentFromUS);


                if (drmContentInfo != null)
                {
                    Log.d(TAG, "Have sufficient information from parsing the XML, will return DRM content info");
                    if (attemptLoadDRMInformation)
                    {
                        drmContentInfo.setDRMContent(DRMAgentDelegate.getDRMContent(context, drmContentInfo.mContentLocation, drmContentInfo.mDRMContentFormat, drmContentInfo.mDRMScheme), drmContentInfo.mLaURLOverride);
                    }
                    drmContentInfo.mContentDescriptorLocation = file.getAbsolutePath();

                    return drmContentInfo;
                }
                else
                {
                    Log.d(TAG, "Not enough information retrieved during parsing");
                    return null;
                }
            }
            finally
            {

            }
        }
        catch (DescriptorLoadException e)
        {
            Log.e(TAG, "Error while parsing XML descriptor '" + file.getAbsolutePath() + "' : " + e.getMessage(), e);
            throw e;
        }
        catch (Exception e)
        {
            Log.e(TAG, "Error while parsing XML descriptor '" + file.getAbsolutePath() + "' : " + e.getMessage(), e);
            throw new DescriptorLoadException(file,null, e.getMessage());
        }
    }


    private static DRMContentInfo parseXMLFile(File file, boolean validateLocalFiles, boolean useContentFromUS) throws Exception
    {
        Log.d(TAG, "Attempting to parse " + file + " as XML content descriptor");

        URL contentLocation = null;
        BufferedReader br = new BufferedReader(new FileReader(file), 8192);
        try
        {
            long t1 = System.currentTimeMillis();

            if (mParserFactory == null)
            {
                mParserFactory = XmlPullParserFactory.newInstance();
            }

            XmlPullParser parser = mParserFactory.newPullParser();
            parser.setInput(br);

            long t2 = System.currentTimeMillis();

            Log.v(TAG, "Parser setup and initialized in " + (t2 - t1) + " millisecond(s)");


            t1 = System.currentTimeMillis();

            int parserEvent = parser.getEventType();
            String tag = null;
            DRMContentInfo drmContentInfo = null;
            DRMScheme drmScheme = null;
            DRMContentFormat drmContentFormat = null;

            while (parserEvent != XmlPullParser.END_DOCUMENT)
            {
                switch (parserEvent)
                {
                    case XmlPullParser.START_TAG:
                        tag = parser.getName();
                        if ("Descriptor".equals(tag))
                        {
                            drmContentInfo = new DRMContentInfo();
                        }
                        else if ("CustomDataParameters".equals(tag))
                        {
                            // read the contents of this tag, including the XML tags
                            final StringBuffer buffer = new StringBuffer("<" + tag + ">");
                            parserEvent = parser.next();
                            boolean copy = true;
                            while (copy)
                            {
                                String innerTag;
                                switch (parserEvent)
                                {
                                    case XmlPullParser.START_TAG:
                                        innerTag = parser.getName();
                                        buffer.append("<" + innerTag + ">");
                                        break;
                                    case XmlPullParser.END_TAG:
                                        innerTag = parser.getName();
                                        buffer.append("</" + innerTag + ">");
                                        if (innerTag == tag)
                                        {
                                            copy = false;
                                        }
                                        break;
                                    case XmlPullParser.CDSECT:
                                        final String cdata = parser.getText();
                                        if (cdata.trim().length() != 0)
                                        {
                                            buffer.append(cdata);
                                        }
                                        break;
                                    case XmlPullParser.TEXT:
                                        final String innerText = parser.getText();
                                        if (innerText.trim().length() != 0)
                                        {
                                            buffer.append(innerText);
                                        }
                                        break;
                                }

                                parserEvent = parser.nextToken();
                            }
                            drmContentInfo.mCustomDataParameters = buffer.toString();
                        }
                        else if (tag.equals("ProtectionInfo"))
                        {
                            /*
                                <ProtectionInfo>
                                    <Protection type="WIDEVINE">
                                        <LA_URL_OVERRIDE>http://essdrmproducts-eu.insidesecure.com/test-portal/device/portal?technology=WIDEVINE&amp;PlayRight=1</LA_URL_OVERRIDE>
                                    </Protection>
                                </ProtectionInfo>
                             */
                            int depth = 1;
                            while (depth > 0)
                            {
                                parserEvent = parser.next();
                                switch (parserEvent)
                                {
                                    case XmlPullParser.END_TAG:
                                        tag = parser.getName();
                                        depth--;
                                        break;
                                    case XmlPullParser.START_TAG:
                                    {
                                        tag = parser.getName();
                                        if (depth == 1)
                                        {
                                            // Expect a Protection tag
                                            if ("Protection".equals(tag))
                                            {
                                                // All good - we are looking for the type attribute now
                                                final String type = parser.getAttributeValue(null, "type");
                                                drmScheme = DRMScheme.valueOf(type);
                                            }
                                        }
                                        else if (depth == 2)
                                        {
                                            // Expect LA OVERRIDE
                                        }
                                        depth++;
                                        break;
                                    }
                                    case XmlPullParser.TEXT:
                                        String text = parser.getText();
                                        if (text.trim().length() == 0) break;
                                        if ("LA_URL_OVERRIDE".equals(tag) && depth == 2)
                                        {
                                            // Capture the LA URL Override
                                            drmContentInfo.mLaURLOverride = verifyAndCreateURL(text);

                                        }
                                        // so?

                                }
                                // drmScheme = DRMScheme.valueOf(text);
                            }
                            continue;
                        }
                        break;
                    case XmlPullParser.END_TAG:
                        break;
                    case XmlPullParser.TEXT:
                        String text = parser.getText();
                        if (text.trim().length() == 0)
                        {
                            break;
                        }
                        if (drmContentInfo != null)
                        {
                            if (text.trim().length() == 0) break;
                            else if (tag.compareTo("Title") == 0)
                            {
                                drmContentInfo.mTitle = text;
                            }
                            else if (tag.compareTo("URL") == 0)
                            {
                                drmContentInfo.mContentLocation = performValidationAndRewrite(validateLocalFiles, text, useContentFromUS);
                                contentLocation = drmContentInfo.mContentLocation.toURL();
                            }
                            else if (tag.compareTo("LA_URL_OVERRIDE") == 0)
                            {
                                drmContentInfo.mLaURLOverride = verifyAndCreateURL(text);
                            }
                            else if (tag.equals("SubtitleFileLocation"))
                            {
                                drmContentInfo.mSubtitleFileLocation = performValidationAndRewrite(validateLocalFiles, text, useContentFromUS);
                            }
                            else if (tag.equals("WebInitiatorURL"))
                            {
                                drmContentInfo.mWebInitiatorURL = verifyAndCreateURL(text);
                            }
                            else if (tag.compareTo("Protection") == 0)
                            {
                                drmScheme = DRMScheme.valueOf(text);
                            }
                            else if (tag.compareTo("LocalFile_ServerPath") == 0)
                            {
                                // What now?
                                drmContentInfo.mLocalFileServerPath = text;
                            }
                            else if (tag.compareTo("StreamType") == 0)
                            {
                                drmContentFormat = DRMContentFormat.valueOf(text);
                                drmContentInfo.mContentFormatFromCdesc = text;
                            }
                            else if (tag.compareTo("StreamLive") == 0)
                            {
                                drmContentInfo.mStreamLive = ("1".equals(text));
                            }
                            else if (tag.compareTo("Author") == 0)
                            {
                                drmContentInfo.mAuthor = text;
                            }
                            else if (tag.compareTo("Genres") == 0)
                            {
                                drmContentInfo.mGenres = text;
                            }
                            else if (tag.compareTo("Language") == 0)
                            {
                                drmContentInfo.mLanguage = text;
                            }
                            else if (tag.compareTo("ShortDescription") == 0)
                            {
                                drmContentInfo.mShortDescription = text;
                            }
                            else if (tag.compareTo("LongDescription") == 0)
                            {
                                drmContentInfo.mLongDescription = text;
                            }
                            else if (tag.compareTo("Container") == 0)
                            {
                                drmContentInfo.mContainer = text;
                            }
                            else if (tag.compareTo("AudioCodec") == 0)
                            {
                                drmContentInfo.mAudioCodec = text;
                            }
                            else if (tag.compareTo("AudioChannels") == 0)
                            {
                                drmContentInfo.mAudioChannels = text;
                            }
                            else if (tag.compareTo("VideoCodec") == 0)
                            {
                                drmContentInfo.mVideoCodec = text;
                            }
                            else if (tag.compareTo("Width") == 0)
                            {
                                drmContentInfo.mWidth = text;
                            }
                            else if (tag.compareTo("Height") == 0)
                            {
                                drmContentInfo.mHeight = text;
                            }
                            else if (tag.compareTo("BitDepth") == 0)
                            {
                                drmContentInfo.mBitDepth = text;
                            }
                            else if (tag.compareTo("BitRate") == 0)
                            {
                                drmContentInfo.mBitRate = text;
                            }
                            else if (tag.compareTo("Framerate") == 0)
                            {
                                drmContentInfo.mFramerate = text;
                            }
                            else if (tag.compareTo("Runtime") == 0)
                            {
                                drmContentInfo.mRuntime = text;
                            }
                            else if (tag.compareTo("Picture") == 0)
                            {
                                drmContentInfo.mPicture = text;
                            }
                            else if (tag.compareTo("Thumbnail") == 0)
                            {
                                drmContentInfo.mThumbnail = text;
                            }
                            else if (tag.compareTo("CustomDataType") == 0)
                            {
                                drmContentInfo.mCustomDataType = text;
                            }
                            break;
                        }
                        break;
                }
                parserEvent = parser.next();
            }
            t2 = System.currentTimeMillis();
            Log.d(TAG, "Content descriptor parsed in " + (t2 - t1) + " millisecond(s)");

            Log.d(TAG, "DRMContentInfo: " + drmContentInfo);
            Log.d(TAG, "DRMScheme: " + drmScheme);
            Log.d(TAG, "DRM Content Format: " + drmContentFormat);
            drmContentInfo.mDRMScheme = drmScheme;
            drmContentInfo.mDRMContentFormat = drmContentFormat;
            return drmContentInfo;
        }
        catch (Exception e)
        {
            throw new DescriptorLoadException(file,contentLocation, e.getMessage());
        }
        finally
        {
            br.close();
        }

    }

    private static URL verifyAndCreateURL(final String urlStr)
    {
        try
        {
            return new URL(urlStr);
        }
        catch (MalformedURLException e)
        {
            Log.e(TAG,"Error while validating URL: " + urlStr);
            throw new RuntimeException("Error while validating URL: " + urlStr, e);
        }
    }

    public static void deleteContent(DRMContentInfo drmContentInfo, boolean removeFromList)
    {
        initialize();
        final File f = new File(drmContentInfo.mContentDescriptorLocation);
        Log.d(TAG, "Deleting " + f);
        if (!f.delete())
        {
            Log.d(TAG, "Deleted file, but for some reason it was not properly deleted");
        }
        //delete local file (if needed)
        if(drmContentInfo.mContentLocation.getScheme().compareToIgnoreCase("file")==0)
        {
            //delete local file
            final File localFile = new File(drmContentInfo.mContentLocation);
            Log.d(TAG, "Deleting local file " + localFile);
            if (!localFile.delete())
            {
                Log.d(TAG, "Deleted local file, but for some reason it was not properly deleted");
            }
        }

        if(removeFromList)
        {
            mLastLoadedDRMContentInfos.remove(drmContentInfo);
            mLastLoadedListFileNames.remove(f.getAbsolutePath());
        }
    }
    public static void deleteContent(DRMContentInfo drmContentInfo)
    {
        deleteContent(drmContentInfo,  true);
    }

    public static void deleteAllContent()
    {
        initialize();

        Log.d(TAG, "Deleting all content (including local files)");

        if (!mLastLoadedDRMContentInfos.isEmpty())
        {
            for (DRMContentInfo drmContentInfo : mLastLoadedDRMContentInfos)
            {
                deleteContent(drmContentInfo, false);
            }
        }

        // All files should now be gone - just make sure
        final File[] files = retrieveContentDescriptorFiles();
        if (files != null && files.length > 0)
        {
            // We still have files!
            for (File file : files)
            {
                try
                {
                    final DRMContentInfo drmContentInfo = parseXMLFile(file, false, false);
                    switch (drmContentInfo.mDRMContentFormat)
                    {
                        case PIFF:
                            // Could be local file!
                            if (drmContentInfo.mContentLocation.getScheme() == null || !drmContentInfo.mContentLocation.getScheme().startsWith("http"))
                            {
                                // Local file, delete that too
                                File f = new File(drmContentInfo.mContentLocation);
                                if (f.exists())
                                {
                                    if (!f.delete())
                                    {
                                        f.deleteOnExit();
                                    }
                                }
                            }
                            if (!file.delete())
                            {
                                file.deleteOnExit();
                            }
                            // FALL THROUGH
                        default:
                            if (!file.delete())
                            {
                                file.deleteOnExit();
                            }
                            break;

                    }
                }
                catch (Exception e)
                {
                    // Ignore for now
                }
            }
        }

        mLastLoadedDRMContentInfos.clear();
        mLastLoadedListFileNames.clear();
    }

    private static class DescriptorLoadException extends RuntimeException
    {
        File mDescriptorFile;
        URL mURl;
        String mErrorMessage;

        private DescriptorLoadException(final File mDescriptorFile, final URL mURl, final String mErrorMessage)
        {
            super("Error loading descriptor at " + mDescriptorFile + " (" + (mURl == null ? "NULL" : mURl) + ") with error message: " + mErrorMessage);
            this.mDescriptorFile = mDescriptorFile;
            this.mURl = mURl;
            this.mErrorMessage = mErrorMessage;
        }
    }

    public static String getCustomData(DRMContentInfo drmContentInfo)
    {
        final CustomDataProvider customDataProvider;

        final String customDataProviderType = drmContentInfo.mCustomDataType;
        final String customDataParameters = drmContentInfo.mCustomDataParameters;

        if (customDataProviderType == null || "file".equalsIgnoreCase(customDataProviderType))
        {
            customDataProvider = new FileCustomDataProvider();
        }
        else if ("inline".equalsIgnoreCase(customDataProviderType))
        {
            customDataProvider = new InlineCustomDataProvider();
        }
        else
        {
            // Type now signifies a class
            try
            {
                final Class<?> customDataProviderClass = Class.forName(drmContentInfo.mCustomDataType);
                final Object customDataProviderInstance = customDataProviderClass.newInstance();
                final Method getCustomDataMethod = customDataProviderClass.getMethod("getCustomData", String.class);
                final InvocationHandler invocationHandler = new InvocationHandler()
                {
                    @Override
                    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
                    {
                        return getCustomDataMethod.invoke(customDataProviderInstance,args);
                    }
                };
                customDataProvider = (CustomDataProvider)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[] { CustomDataProvider.class},invocationHandler);
            }
            catch (Exception e)
            {
                throw new RuntimeException("Error while preparing custom data provider: " + e.getMessage(), e);
            }
        }

        try
        {
            // All well hopefully :)
            final String customData = customDataProvider.getCustomData(customDataParameters);
            if (customData != null && customData.trim().length() > 0)
            {
                return customData;
            }
            return null;
        }
        catch (Exception e)
        {
            Log.e(TAG,"Error while retrieving  custom data: " + e.getMessage(), e);
            return null;
        }
    }


}
