package com.insidesecure.drm.android.reference.services;

import java.io.IOException;
import java.net.URI;
import java.util.*;

import android.app.*;
import android.content.*;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.*;
import android.util.Log;
import android.view.View;
import android.widget.*;
import com.insidesecure.drm.android.reference.*;
import com.insidesecure.drm.android.reference.Loader;
import com.insidesecure.drm.android.reference.xoplayer.R;
import com.insidesecure.drm.android.reference.activities.HomeActivity;
import com.insidesecure.drmagent.*;
import com.insidesecure.drmagent.download.AbstractDownloadEventListener;
import com.insidesecure.drmagent.utils.AndroidVersion;

/**
 * @author Inside Secure
 */
public class DownloadService extends Service implements DRMAgentDelegate.DRMAgentLifecycleListener, Loader.Callback
{
    private Loader mLoader;

    @Override
    public void destroyed()
    {
        mDRMAgent = null;
    }

    public static String TAG = "DownloadService";

    // Binder given to clients
    private final IBinder mBinder = new DownloadServiceBinder();

    private NotificationManager mNM;
    private int NOTIFICATION_SERVICE_STARTED = 1;

    private int NOTIFICATION_SERVICE_DOWNLOADING = 2;

    public DRMAgent mDRMAgent;
    public Notification mNotificationServiceRunning;

    public Notification mNotificationServiceDownloading;
    private PendingIntent mNotificationIntent;
    private RemoteViews mRemoteViews;
    private int mFlags;
    private int mDownloadedPercentage = 0;

    @Override
    public void onCreate()
    {
        super.onCreate();
        mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        showNotification();

        mLoader = new Loader("INSIDE/DownloadServiceLoader");

        try
        {
            DRMAgentDelegate.addDRMAgentLifecycleListener(this);
            DRMAgentDelegate.initialize(DownloadService.this);
            mDRMAgent = DRMAgentDelegate.getDRMAgentInstance(DownloadService.this);

        }

        catch (Exception exception)
        {
            exception.printStackTrace();
            Log.e(TAG, "Unexpected error " + exception.toString());
            stopSelf();
        }
    }

    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Log.i("DownloadService", "Received start id " + startId + ": " + intent);
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    /**
     * Show a notification while this service is running.
     */
    private void showNotification()
    {
        // In this sample, we'll use the same text for the ticker and the expanded notification
        CharSequence text = getText(R.string.dl_service_started);
        // The PendingIntent to launch our activity if the user selects this notification
        mNotificationIntent = PendingIntent.getActivity(this, 0, new Intent(this, HomeActivity.class), 0);

        mNotificationServiceRunning = createNotificationServiceRunning(text, getText(R.string.dl_service_label), "Idle");

        mNotificationServiceRunning.flags = mNotificationServiceRunning.flags | Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
        // Send the notification.
        mNM.notify(NOTIFICATION_SERVICE_STARTED, mNotificationServiceRunning);
    }

    @Override
    public void onDestroy()
    {
        // Cancel all pending downloads now
        if ((mDRMAgent != null) && mDRMAgent.isInitialized() && !mDRMAgent.getDownloadManager().retrieveDownloadingDRMContent().isEmpty())
        {
            mDRMAgent.getDownloadManager().cancelAllPendingDownloads();
        }

        // Cancel the persistent notification.
        mNM.cancel(NOTIFICATION_SERVICE_STARTED);

        // Tell the user we stopped.
        Toast.makeText(this, R.string.dl_service_stopped, Toast.LENGTH_SHORT).show();

        DRMAgentDelegate.removeDRMAgentLifecycleListener(this);

        DRMAgentDelegate.uninitialize();

        mLoader.release();

    }


    public boolean isActivelyDownloading()
    {
        if (mDRMAgent == null || !mDRMAgent.isInitialized())
        {
            return false;
        }
        return mDRMAgent.getDownloadManager().retrieveDownloadingDRMContent().size() > 0;
    }

    public void cancelAllPendingDownloads()
    {
        if (mDRMAgent != null && mDRMAgent.isInitialized())
        {
            if (!mDRMAgent.getDownloadManager().retrieveDownloadingDRMContent().isEmpty())
            {
                // Cancel all pending downloads first
                mDRMAgent.getDownloadManager().cancelAllPendingDownloads();
            }

        }

    }

    public void initiateDownload(final URI originalContentURI, final DRMContentFormat drmContentFormat, final DRMScheme drmScheme, final DRMContent.VideoQualityLevel videoQualityLevel, final List<DRMContent.AudioTrack> audioTracks, final List<DRMContent.SubtitleTrack> subtitleTracks)
    {
        mLoader.startLoading(new Loader.Loadable()
        {
            @Override
            public void cancelLoad()
            {
                // Huh?
            }

            @Override
            public boolean isLoadCanceled()
            {
                return false;
            }

            @Override
            public void load() throws IOException, InterruptedException
            {
                if (mDRMAgent == null || !mDRMAgent.isInitialized())
                {
                    mDRMAgent = DRMAgentDelegate.getDRMAgentInstance(DownloadService.this);
                }

                // Can't download if in offline mode!
                if (mDRMAgent.getDRMAgentConfiguration().isOfflineMode())
                {
                    throw new IOException("Download could not be started - in offline mode");
                }

                final DRMContent drmContent = mDRMAgent.getDRMContent(originalContentURI, drmContentFormat, drmScheme);

                setupDRMContentListener(drmContent);

                final DRMContent.DownloadAndPlayRequest downloadAndPlayRequest = new DRMContent.DownloadAndPlayRequest();
                downloadAndPlayRequest.mVideoQualityLevel = videoQualityLevel;
                downloadAndPlayRequest.mAudioTracks = audioTracks;
                downloadAndPlayRequest.mSubtitleTracks = subtitleTracks;

                try
                {
                    drmContent.enableDownloadAndPlay(downloadAndPlayRequest);

                    final UUID download = mDRMAgent.getDownloadManager().download(drmContent);
                    Log.d(TAG,"Download initiated with download session identifier " + download);
                    mNotificationServiceDownloading = createNotificationServiceDownloading(
                            "Content downloading", getText(R.string.dl_service_label), "Idle");
                    mRemoteViews = new RemoteViews(DownloadService.this.getApplicationContext().getPackageName(), R.layout.cache_progress);
                    mNotificationServiceDownloading.contentView = mRemoteViews;

                    mRemoteViews.setViewVisibility(R.id.cache_status_progress, View.VISIBLE);
                    mRemoteViews.setViewVisibility(R.id.cache_status_text, View.VISIBLE);
                    mRemoteViews.setProgressBar(R.id.cache_status_progress,Integer.MAX_VALUE,0,false);

                    mFlags = mNotificationServiceDownloading.flags;

                    mNM.notify(NOTIFICATION_SERVICE_DOWNLOADING, mNotificationServiceDownloading);

                }
                catch (Exception e)
                {
                    Log.e(TAG,"Error initiating download: " + e.getMessage(), e);
                    throw new IOException("Error initating download: " + e.getMessage(), e);
                }
            }
        }, this);
    }

    private Notification createNotificationServiceDownloading(String tickerText, CharSequence contentTitle, String contentText)
    {
        return new Notification.Builder(DownloadService.this)
                .setSmallIcon(android.R.drawable.ic_menu_save)
                .setTicker(tickerText)
                .setWhen(System.currentTimeMillis())
                .setContentIntent(mNotificationIntent)
                .setContentTitle(contentTitle)
                .setContentText(contentText)
                .build();
    }

    private Notification createNotificationServiceRunning(CharSequence tickerText, CharSequence contentTitle, String contentText)
    {
        if (Build.VERSION.SDK_INT >= AndroidVersion.HONEYCOMB)
        {
            return new Notification.Builder(this)
                    .setSmallIcon(R.drawable.ic_daand_notification)
                    .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.logo_inside_secure_64_64))
                    .setTicker(tickerText)
                    .setContentTitle(contentTitle)
                    .setContentText(contentText)
                    .setContentIntent(mNotificationIntent)
                    .setWhen(System.currentTimeMillis()).build();
        }
        else
        {
            Notification notification = new Notification(R.drawable.logo_inside_secure_64_64, tickerText, System.currentTimeMillis());
            notification.contentIntent = mNotificationIntent;
            return notification;
        }
    }

    private void setupDRMContentListener(final DRMContent drmContent)
    {
        drmContent.setDownloadEventListener(new AbstractDownloadEventListener()
        {

            boolean mComplete = false;

            @Override
            public void complete(final DRMContent drmContent, final int totalNumSegments, final int totalDuration)
            {
                Log.d(TAG, "Download completed: " + totalNumSegments + " (" + totalDuration + ")");

                mComplete = true;
                mNotificationServiceRunning = createNotificationServiceRunning(
                        getText(R.string.dl_service_started), getText(R.string.dl_service_label), "Idle");

                mNM.notify(NOTIFICATION_SERVICE_STARTED, mNotificationServiceRunning);

                mNotificationServiceDownloading.tickerText = "Download complete";
                mNotificationServiceDownloading.flags = mFlags;

                updateDownloadNotification(totalNumSegments, totalNumSegments, "Download completed with ");

                // Finally release the DRMContent instance
                drmContent.release();

            }

            @Override
            public void segmentDownloaded(final DRMContent drmContent, final int segmentNumber, final int totalSegmentsDownloaded, final int totalNumSegments, final int totalNumSecondsAvailableInCache)
            {
                Log.d(TAG, "Segment downloaded: " + segmentNumber + " (" + totalSegmentsDownloaded + "/" + totalNumSegments + " - " + totalNumSecondsAvailableInCache + " seconds)");

                // notification should be updated only when the percentage is different
                if (mDownloadedPercentage < ((totalSegmentsDownloaded * 100) / totalNumSegments))
                {
                    updateDownloadNotification(totalSegmentsDownloaded, totalNumSegments, "");
                }
            }

            @Override
            public void started(final DRMContent drmContent, final int totalSegmentsDownloaded, final int totalNumSegments, final int totalNumSecondsAvailableInCache)
            {
                Log.d(TAG, "Downloaded started: " + totalSegmentsDownloaded + "/" + totalNumSegments + " (" + totalNumSecondsAvailableInCache + " seconds)");

                mNotificationServiceRunning = createNotificationServiceRunning(
                        getText(R.string.dl_service_started), getText(R.string.dl_service_label), "Downloading");
                mNM.notify(NOTIFICATION_SERVICE_STARTED, mNotificationServiceRunning);

                mNotificationServiceDownloading.flags = mNotificationServiceRunning.flags | Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;

                updateDownloadNotification(totalSegmentsDownloaded, totalNumSegments, "Download started with ");
            }

            @Override
            public void stopped(final DRMContent drmContent, final int totalSegmentsDownloaded, final int totalNumSegments, final int totalNumSecondsAvailableInCache)
            {
                Log.d(TAG, "Downloaded stopped: " + totalSegmentsDownloaded + "/" + totalNumSegments + " (" + totalNumSecondsAvailableInCache + " seconds)");


                if (!mComplete)
                {
                    mNotificationServiceRunning = createNotificationServiceRunning(
                            getText(R.string.dl_service_started), getText(R.string.dl_service_label), "Idle");
                    mNM.notify(NOTIFICATION_SERVICE_STARTED, mNotificationServiceRunning);

                    mNotificationServiceDownloading.tickerText = "Download stopped";
                    mNotificationServiceDownloading.flags = mFlags;

                    updateDownloadNotification(totalSegmentsDownloaded, totalNumSegments, "Download stopped with ");
                }
            }

            private void updateDownloadNotification(final int totalSegmentsDownloaded, final int totalNumSegments, final String cacheStatusText)
            {
                mNotificationServiceDownloading.contentView.setProgressBar(R.id.cache_status_progress, totalNumSegments, totalSegmentsDownloaded, false);
                mNotificationServiceDownloading.contentView.setTextViewText(R.id.cache_status_text, cacheStatusText + totalSegmentsDownloaded + " of " + totalNumSegments + " segment(s) retrieved");

                mDownloadedPercentage = ((totalSegmentsDownloaded * 100) / totalNumSegments);
                mNotificationServiceDownloading.contentView.setTextViewText(R.id.cache_percentage_text, mDownloadedPercentage + "%");

                mNM.notify(NOTIFICATION_SERVICE_DOWNLOADING, mNotificationServiceDownloading);
            }

            @Override
            public void error(final DRMContent drmContent, final DRMError error)
            {

                mNotificationServiceRunning = createNotificationServiceRunning(
                        getText(R.string.dl_service_started), getText(R.string.dl_service_label), "Idle");
                mNM.notify(NOTIFICATION_SERVICE_STARTED, mNotificationServiceRunning);

                mNM.cancel(NOTIFICATION_SERVICE_DOWNLOADING);

                mNotificationServiceDownloading.tickerText = "Download failed";
                mNotificationServiceDownloading.contentView.setTextViewText(R.id.cache_status_text, "Download failed with " + error);
                mNotificationServiceDownloading.contentView.setViewVisibility(R.id.cache_percentage_text, View.INVISIBLE);
                mDownloadedPercentage = 0;
                mNotificationServiceDownloading.contentView.setViewVisibility(R.id.cache_status_progress, View.INVISIBLE);

                mNotificationServiceDownloading.flags = mFlags;

                mNM.notify(NOTIFICATION_SERVICE_DOWNLOADING, mNotificationServiceDownloading);
            }
        });
    }

    @Override
    public void onLoadCanceled(final Loader.Loadable loadable)
    {

    }

    @Override
    public void onLoadCompleted(final Loader.Loadable loadable)
    {
        Log.d(TAG,"Succesfully initiated download of DRMContent");
        Toast.makeText(DownloadService.this,"Background download initiated",Toast.LENGTH_LONG).show();

    }

    @Override
    public void onLoadError(final Loader.Loadable loadable, final IOException exception)
    {
        Log.e(TAG,"Error occurred while initiating download of DRMContent: " + exception.getMessage(), exception);
        Toast.makeText(DownloadService.this,"Download could not be started: " + exception.getMessage(),Toast.LENGTH_LONG).show();
    }

    public void downloadDefaultContent(String location, final ContentHandler.OnCDescRetrieveFinishedCallback callback)
    {
        ContentHandler.addCustomContent(Uri.parse(location), new ContentHandler.OnCDescRetrieveFinishedCallback()
        {
            public void onRetrieveFinished(String loadedFilePath)
            {
                ContentHandler.unzipContent(loadedFilePath, callback);
            }

            public void onRetrieveFailed(String errorMessage)
            {
                callback.onRetrieveFailed(errorMessage);
            }
        });
    }


    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class DownloadServiceBinder extends Binder
    {
        public DownloadService getService()
        {
            // Return this instance of LocalService so clients can call public methods
            return DownloadService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent)
    {
        return mBinder;
    }


}

