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

import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.*;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import com.insidesecure.drm.android.reference.*;
import com.insidesecure.drm.android.reference.entitlements.CallbackEnabledSamplePlayReadyEntitlementDelegate;
import com.insidesecure.drmagent.*;
import com.insidesecure.drm.android.reference.xoplayer.R;

import java.net.URL;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LicenseAcquisitionActivity extends AbstractDRMActivity
{
    /**
     * Defines the identifiers of the various dialogs used by this activity.
     */
    public static final int NO_LICENSE = 101;
    public static final int RETRIEVING_LICENSE = 102;
    public static final int LICENSE_RETRIEVED = 103;
    public static final int ERROR_RETRIEVING_LICENSE = 104;
    public static final int CANCELLING_RETRIEVING_LICENSE = 105;
    public static final int TIMEOUT_RETRIEVING_LICENSE = 106;
    public static final int UNTRUSTED_TIME = 107;
    public static final int POLICY_CHECK_FAILED = 108;
    private static final int GENERAL_DRM_ERROR = 112;
    private static final int STARTING_VIDEO = 113;
    private static final int CONTENT_RETRIEVAL_ERROR = 114;
    private static final int STREAM_BLACKED_OUT = 115;
    private static final int IO_HTTP_ERROR_NOT_FOUND = 116;
    private static final int IO_HTTP_ERROR = 117;
    private static final int PLAYER_VERIFICATION_FAILED = 118;
    private static final int NOT_SUPPORTED = 119;
    private static final int DOWNLOAD_PLAY_DATA_MISMATCH = 120;
    private static final int DATA_UNAVAILABLE = 121;
    private static final int API_USAGE_EXPIRED = 122;
    private static final int DEVICE_NOT_TIED = 123;
    private static final int UNKNOWN_RIGHTS = 124;
    private static final int MEDIA_PLAYER_INTERNAL_ERROR = 125;
    private static final int INVALID_OPERATION = 126;
    private static final int IO_ERROR = 127;
    private static final int IO_HTTP_READ_ERROR = 128;



    public static final int PLAY_MEDIA_REQUEST_CODE = 1;
    public static final int RESULT_DRM_ERROR = 2;

    /**
     * Defines the maximum amount of time for which to wait for a license to be installed (in seconds).
     * This controls how long the application will display the 'Retrieving License' dialog.
     */
    // public static final int LICENSE_ACQUISITION_MAX_WAIT = 30;

    public static final String TAG = "LicenseAcquisition";

    public static final Handler mHandler = new Handler();

    private Intent mResultData;
    private boolean mDRMContentInstanceCreated = false;

    private ProgressDialog mRetrievingLicenseDialog;
    private int mLastDialogId = -1;

    @Override
    protected void onDRMContentInstanceCreatedInternal(DRMContent drmContent)
    {
        mDRMContentInstanceCreated = true;

        dismissLoadingDialog();

        // Check if activity is not finishing to avoid starting playback after calling finish() in onActivityResult()
        // There could be a case when LicenseAcquisitionActivity was destroyed and recreated again after getting result from playback activity
        if(!isFinishing())
        {
            handleIntent(mResultData == null ? getIntent() : mResultData, drmContent);
        }
    }


    private void handleIntent(final Intent lastIntent, final DRMContent drmContent)
    {
        Log.d(TAG, "Handling intent: " + lastIntent);

        final Bundle extras = lastIntent.getExtras();

        dismissLoadingDialog();

        if (drmContent == null)
        {
            Log.e(TAG, "Error while opening content");
            generalDRMError();
        }
        else
        {
            final DRMError drmError = (DRMError) lastIntent.getExtras().get("DRM_ERROR");

            if (drmError != null)
            {
                Log.d(TAG, "Intent contained error, will handle it accordingly: " + drmError);
                // Something went wrong during playback
                switch (drmError)
                {
                    case NO_RIGHTS:
                        noLicenseDetected();
                        break;
                    case UNTRUSTED_TIME:
                        untrustedTimeDetected();
                        break;
                    case POLICY_CHECK_FAILURE:
                        policyCheckFailed();
                        break;
                    case UNEXPECTED_CONTENT_ERROR:
                        errorRetrievingContent();
                        break;
                    case STREAM_BLACKOUT:
                        streamBlackedOut();
                        break;
                    case IO_HTTP_ERROR_NOT_FOUND:
                        remoteResourceNotFound();
                        break;
                    case IO_HTTP_ERROR:
                        remoteResourceError();
                        break;
                    case PLAYER_VERIFICATION_FAILED:
                        playerVerificationFailed();
                        break;
                    case API_USAGE_EXPIRED:
                        apiUsageExpired();
                        break;
                    case DEVICE_NOT_TIED:
                        deviceNotTied();
                        break;
                    case NOT_SUPPORTED:
                        playerUnsupportedOperationError();
                        break;
                    case CONTENT_DOWNLOAD_PLAY_TRACKS_SIZE_MISMATCH:
                    case CONTENT_DOWNLOAD_PLAY_BITRATE_MISMATCH:
                    case CONTENT_DOWNLOAD_PLAY_SUBTITLE_TRACK_MISMATCH:
                        downloadAndPlayDataMismatch();
                        break;
                    case IO_HTTP_OFFLINE_MODE_ERROR:
                        dataUnavailable();
                        break;
                    case MEDIA_PLAYER_INTERNAL_ERROR:
                        mediaPlayerInternalError();
                        break;
                    case DRM_INVALID_OPERATION:
                        invalidOperation();
                        break;
                    case IO_ERROR:
                        ioError();
                        break;
                    case IO_HTTP_READ_ERROR:
                        ioHttpReadError();
                        break;
                    case GENERAL_DRM_ERROR:
                    default:
                        Log.d(TAG, "DRM error: " + drmError);
                        generalDRMError();
                        break;
                }
            }
            else
            {
                if (drmContent.isProtected())
                {
                    // This checks whether or not the Agent has rights for the content in question.  This check will
                    // verify both if the content is clear-text or protected & rights exists
                    final DRMRights.DRMRightsType drmRightsType = drmContent.getDRMRights().getDRMRightsType();
                    if (drmRightsType == DRMRights.DRMRightsType.VALID)
                    {
                        Log.d(TAG, "Rights available ... starting player");
                        startPlayer();
                    }
                    else if (drmRightsType == DRMRights.DRMRightsType.RIGHTS_ON_DEMAND)
                    {
                        Log.d(TAG, "Rights availability is actually not known (for this live stream), the rights should have being delivered... starting player");
                        startPlayer();
                    }
                    else
                    {
                        Log.d(TAG, "No rights available: " + drmRightsType);

                        switch (drmRightsType)
                        {
                            case UNTRUSTED_TIME:
                                untrustedTimeDetected();
                                break;
                            case NO_RIGHTS:
                            case RIGHTS_IN_FUTURE:
                            case EXPIRED:
                                // No license available - display the dialog or simply initiate license acquisition
                                noLicenseDetected();
                                break;
                            default:
                                unknownRightsDetected();
                                break;
                        }
                    }
                }
                else
                {
                    Log.d(TAG, "Content is clear-text... starting player");
                    startPlayer();
                }
            }
        }
    }

    private void playerUnsupportedOperationError()
    {
        showDialog(LicenseAcquisitionActivity.NOT_SUPPORTED);
    }

    private void dataUnavailable()
    {
        showDialog(LicenseAcquisitionActivity.DATA_UNAVAILABLE);
    }

    private void downloadAndPlayDataMismatch()
    {
        showDialog(LicenseAcquisitionActivity.DOWNLOAD_PLAY_DATA_MISMATCH);
    }

    private void remoteResourceError()
    {
        showDialog(LicenseAcquisitionActivity.IO_HTTP_ERROR);
    }

    private void remoteResourceNotFound()
    {
        showDialog(LicenseAcquisitionActivity.IO_HTTP_ERROR_NOT_FOUND);
    }

    private void playerVerificationFailed()
    {
        showDialog(LicenseAcquisitionActivity.PLAYER_VERIFICATION_FAILED);
    }

    private void apiUsageExpired()
    {
        showDialog(LicenseAcquisitionActivity.API_USAGE_EXPIRED);
    }

    private void deviceNotTied()
    {
        showDialog(LicenseAcquisitionActivity.DEVICE_NOT_TIED);
    }

    private void mediaPlayerInternalError()
    {
        showDialog(LicenseAcquisitionActivity.MEDIA_PLAYER_INTERNAL_ERROR);
    }

    private void invalidOperation()
    {
        showDialog(LicenseAcquisitionActivity.INVALID_OPERATION);
    }

    private void generalDRMError()
    {
        showDialog(LicenseAcquisitionActivity.GENERAL_DRM_ERROR);
    }

    private void ioError()
    {
        showDialog(LicenseAcquisitionActivity.IO_ERROR);
    }

    private void ioHttpReadError()
    {
        showDialog(LicenseAcquisitionActivity.IO_HTTP_READ_ERROR);
    }

    private void streamBlackedOut()
    {
        showDialog(LicenseAcquisitionActivity.STREAM_BLACKED_OUT);
    }

    private void policyCheckFailed()
    {
        showDialog(LicenseAcquisitionActivity.POLICY_CHECK_FAILED);
    }

    private void errorRetrievingContent()
    {
        showDialog(LicenseAcquisitionActivity.CONTENT_RETRIEVAL_ERROR);
    }

    protected void noLicenseDetected()
    {
        showDialog(LicenseAcquisitionActivity.NO_LICENSE);
    }

    protected void untrustedTimeDetected()
    {
        showDialog(LicenseAcquisitionActivity.UNTRUSTED_TIME);
    }

    protected void unknownRightsDetected()
    {
        showDialog(LicenseAcquisitionActivity.UNKNOWN_RIGHTS);
    }

    protected void startPlayer()
    {
        showDialog(LicenseAcquisitionActivity.STARTING_VIDEO);
        final Intent intent = new Intent(getBaseContext(), getPlayMediaActivityClass());

        // Copy all the extras from the original intent - safe to do so
        intent.putExtras(getIntent().getExtras());

        startActivityForResult(intent, PLAY_MEDIA_REQUEST_CODE);
    }

    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data)
    {
        Log.d(TAG, "Activity result received: " + requestCode + " result: " + resultCode + " intent: " + data);

        if (requestCode == PLAY_MEDIA_REQUEST_CODE)
        {
            Log.d(TAG, "Received correct request code: " + resultCode);
            if (resultCode == RESULT_DRM_ERROR)
            {
                Log.d(TAG, "DRM error occurred during playback");
                // Some error!
                if (data != null)
                {
                    mResultData = data;

                    if(mDRMContentInstanceCreated)
                    {
                        handleIntent(data, getDRMContent());
                    }
                }
                else
                {
                    Log.e(TAG, "DRM error occurred, but received no intent info, please validate the code");
                }
            }
            else
            {
                Log.d(TAG, "Finishing due to successful completion");
                finish();
            }
        }
    }

    protected Class getPlayMediaActivityClass()
    {
        return DRMFusionAgentReferenceApplication.sPlayMediaActivityClass;
    }

    private void dismissLastDialog(int newDialogId)
    {
        try
        {
            if (mLastDialogId > -1)
                removeDialog(mLastDialogId);
        }
        catch (Exception e)
        {
            Log.i(TAG, "Could not dismiss dialog with ID " + mLastDialogId);
        }
        mLastDialogId = newDialogId;
    }

    /**
     * Method responsible for initiating delegated license acquisition within the sample application.  The actual
     * acquisition is spun out to a separate thread doing the HTTP Post as well as into one waiting & controlling
     * the UI
     */
    protected void initiateRightsIssuerLicenseAcquisition()
    {
        removeDialog(LicenseAcquisitionActivity.NO_LICENSE);
        showDialog(LicenseAcquisitionActivity.RETRIEVING_LICENSE);

        final DRMContent drmContent = getDRMContent();
        drmContent.setEntitlementDelegate(new CallbackEnabledSamplePlayReadyEntitlementDelegate(LicenseAcquisitionActivity.this, getPlaybackInfo().mLicenseAcquisitionURL, new CallbackEnabledSamplePlayReadyEntitlementDelegate.Callback()
        {
            @Override
            public void actionInitiated(final Action action, final DRMContent drmContent)
            {
                Log.d(TAG,"Action initiated: " + action);
                switch (action)
                {
                    case ACKNOWLEDGE_LICENSE:
                        mRetrievingLicenseDialog.setMessage(getDialogTextAcknowledgingLicense());
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void actionCompleted(final Action action, final DRMContent drmContent, final DRMError result)
            {
                Log.d(TAG,"Action completed: " + action + " result: " + result);
                switch (action)
                {
                    case ACQUIRE_LICENSE:
                        if (result == DRMError.SUCCESS)
                        {
                            mRetrievingLicenseDialog.setMessage(getDialogTextInstallingLicense());
                        }
                        break;
                    default:
                        break;
                }
            }
        }, LicenseAcquisitionActivity.this.getMainLooper()));

        final Future<DRMError> drmErrorFuture = drmContent.acquireRightsAsync(new DRMContent.AcquireRightsCallback()
        {
            @Override
            public void acquireRightsComplete(final DRMError drmError)
            {
                switch (drmError)
                {
                    case SUCCESS:
                        mRetrievingLicenseDialog.setMessage(getDialogTextLicenseRetrieved());
                        break;
                    default:
                        // Something went wrong!
                        mRetrievingLicenseDialog.setMessage(getDialogTextErrorRetrievingLicense());
                }

                removeDialog(LicenseAcquisitionActivity.RETRIEVING_LICENSE);
                showDialog(drmError == DRMError.SUCCESS ? LicenseAcquisitionActivity.LICENSE_RETRIEVED : LicenseAcquisitionActivity.ERROR_RETRIEVING_LICENSE);
            }
        });

    }

    protected void setupDialogsRetrievingLicense()
    {
        removeDialog(LicenseAcquisitionActivity.NO_LICENSE);
        showDialog(LicenseAcquisitionActivity.RETRIEVING_LICENSE);
    }

    protected int resolveDialogForLicenseAcquisitionResult(final DRMError drmError)
    {
        switch (drmError)
        {
            case SUCCESS:
                return LicenseAcquisitionActivity.LICENSE_RETRIEVED;
            default:
                return LicenseAcquisitionActivity.ERROR_RETRIEVING_LICENSE;
        }
    }

    protected CharSequence getDialogTextCancellingLicenseRequest()
    {
        return getText(R.string.dialog_text_cancelling_license_request);
    }

    protected CharSequence getDialogButtonNo()
    {
        return getText(R.string.no);
    }

    protected CharSequence getDialogButtonYes()
    {
        return getText(R.string.yes);
    }

    protected CharSequence getDialogTextRetrieveLicenseQuestion()
    {
        return getText(R.string.dialog_text_retrieve_license_question);
    }

    protected CharSequence getDialogTextRequestingLicense()
    {
        return getText(R.string.dialog_text_requesting_license);
    }

    protected CharSequence getDialogTextInstallingLicense()
    {
        return getText(R.string.dialog_text_installing_license);
    }

    protected CharSequence getDialogTextAcknowledgingLicense()
    {
        return getText(R.string.dialog_text_acknowledging_license);
    }

    protected CharSequence getDialogButtonCancel()
    {
        return getText(R.string.dialog_button_cancel);
    }

    protected CharSequence getDialogTitle()
    {
        return getText(R.string.dialog_title);
    }

    protected CharSequence getDialogTextErrorRetrievingLicense()
    {
        return getText(R.string.dialog_text_error_retrieving_license);
    }

    protected CharSequence getDialogButtonOK()
    {
        return getText(R.string.dialog_button_ok);
    }

    protected CharSequence getDialogTextTimeoutRetrievingLicense()
    {
        return getText(R.string.dialog_text_timeout_retrieving_license);
    }

    protected CharSequence getDialogButtonPlay()
    {
        return getText(R.string.dialog_button_play);
    }

    protected CharSequence getDialogButtonBackHome()
    {
        return getText(R.string.dialog_button_back_home);
    }

    protected CharSequence getDialogTextLicenseRetrieved()
    {
        return getText(R.string.dialog_text_license_retrieved);
    }

    @Override
    protected Dialog onCreateDialog(int id)
    {
        Dialog dialog;
        Log.d(TAG, "onCreateDialog: " + id);
        dismissLastDialog(id);

        switch (id)
        {
            case NO_LICENSE:
                dialog = createAskRetrieveLicenseDialog();
                break;
            case STARTING_VIDEO:
                dialog = Tools.createSimpleProgressDialog(this, getText(R.string.dialog_title), getText(R.string.dialog_text_starting_video));
                break;
            case RETRIEVING_LICENSE:
                dialog = createRetrievingLicenseDialog();
                break;
            case LICENSE_RETRIEVED:
                dialog = createLicenseRetrievedDialog();
                break;
            case ERROR_RETRIEVING_LICENSE:
                dialog = createErrorRetrievingLicenseDialog();
                break;
            case TIMEOUT_RETRIEVING_LICENSE:
                dialog = createRetrievingLicenseTimeOutDialog();
                break;
            case CANCELLING_RETRIEVING_LICENSE:
                dialog = createCancellingLicenseDialog();
                break;
            case UNTRUSTED_TIME:
                dialog = getDialog(getText(R.string.dialog_text_untrusted_time));
                break;
            case POLICY_CHECK_FAILED:
                dialog = getDialog(getText(R.string.dialog_text_policy_check_failed));
                break;
            case CONTENT_RETRIEVAL_ERROR:
                dialog = getDialog(getText(R.string.dialog_text_content_retrieval_error));
                break;
            case STREAM_BLACKED_OUT:
                dialog = getDialog(getText(R.string.dialog_text_stream_blacked_out));
                break;
            case IO_HTTP_ERROR_NOT_FOUND:
                dialog = getDialog(getText(R.string.dialog_text_remote_resource_not_found));
                break;
            case IO_HTTP_ERROR:
                dialog = getDialog(getText(R.string.dialog_text_remote_resource_error));
                break;
            case PLAYER_VERIFICATION_FAILED:
                dialog = getDialog(getText(R.string.dialog_text_player_verification_failed));
                break;
            case NOT_SUPPORTED:
                dialog = getDialog( getText(R.string.dialog_text_unsupported_operation));
                break;
            case DOWNLOAD_PLAY_DATA_MISMATCH:
                dialog = getDialog(getText(R.string.dialog_text_download_play_data_mismatch));
                break;
            case DATA_UNAVAILABLE:
                dialog = getDialog(getText(R.string.dialog_text_data_unavailable));
                break;
            case API_USAGE_EXPIRED:
                dialog = getDialog(getText(R.string.dialog_text_api_usage_expired));
                break;
            case DEVICE_NOT_TIED:
                dialog = getDialog(getText(R.string.dialog_text_device_not_tied));
                break;
            case UNKNOWN_RIGHTS:
                dialog = getDialog(getText(R.string.dialog_text_unknown_rights));
                break;
            case MEDIA_PLAYER_INTERNAL_ERROR:
                dialog = getDialog(getText(R.string.dialog_text_media_player_internal_error));
                break;
            case INVALID_OPERATION:
                dialog = getDialog(getText(R.string.dialog_text_invalid_operation));
                break;
            case IO_ERROR:
                dialog = getDialog(getText(R.string.dialog_text_io_error));
                break;
            case IO_HTTP_READ_ERROR:
                dialog = getDialog(getText(R.string.dialog_text_io_http_read_error));
                break;
            case GENERAL_DRM_ERROR:
            default:
                dialog = getDialog(getText(R.string.dialog_text_general_drm_error));
                break;
        }
        return dialog;
    }

    private Dialog getDialog(CharSequence message)
    {
        final DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                finish();
            }
        };
        return Tools.createAlertDialog(this, getDialogTitle(), message, false, getDialogButtonOK(), positiveListener, null, null);
    }

    protected Dialog createAskRetrieveLicenseDialog()
    {
        final DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int id)
            {
                Log.d(TAG, "Open up connection and such!");
                initiateRightsIssuerLicenseAcquisition();
            }
        };
        final DialogInterface.OnClickListener negativeListener = new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int id)
            {
                dialog.cancel();
                finish();
            }
        };
        return Tools.createAlertDialog(this, getDialogTitle(), getDialogTextRetrieveLicenseQuestion(), false, getDialogButtonYes(), positiveListener, getDialogButtonNo(), negativeListener);
    }

    protected Dialog createRetrievingLicenseDialog()
    {
        mRetrievingLicenseDialog = Tools.createSimpleProgressDialog(this, getDialogTitle(),getDialogTextRequestingLicense());
        return mRetrievingLicenseDialog;
    }

    protected Dialog createCancellingLicenseDialog()
    {
        return Tools.createSimpleProgressDialog(this, getDialogTitle(),getDialogTextCancellingLicenseRequest());
    }

    protected Dialog createErrorRetrievingLicenseDialog()
    {
        final DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int id)
            {
                dialog.cancel();
                finish();
            }
        };
        return Tools.createAlertDialog(this, getDialogTitle(), getDialogTextErrorRetrievingLicense(), false, getDialogButtonOK(), positiveListener, null, null);
    }

    protected Dialog createRetrievingLicenseTimeOutDialog()
    {
        final DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int id)
            {
                dialog.cancel();
                finish();
            }
        };
        return Tools.createAlertDialog(this, getDialogTitle(), getDialogTextTimeoutRetrievingLicense(), false, getDialogButtonOK(), positiveListener, null, null);
    }

    protected Dialog createLicenseRetrievedDialog()
    {
        final DialogInterface.OnClickListener negativeListener = new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int which)
            {
                dialog.dismiss();
                LicenseAcquisitionActivity.this.finish();
            }
        };

        final DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int id)
            {
                dialog.dismiss();
                startPlayer();
            }
        };
        return Tools.createAlertDialog(this, getDialogTitle(), getDialogTextLicenseRetrieved(), false, getDialogButtonPlay(), positiveListener, getDialogButtonBackHome(), negativeListener);

    }
}
