package com.avaya.sdksampleapp.commpackage;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.util.Log;
import android.util.SparseArray;

import com.avaya.clientservices.call.Call;
import com.avaya.clientservices.call.CallCompletionHandler;
import com.avaya.clientservices.call.CallCreationInfo;
import com.avaya.clientservices.call.CallEndReason;
import com.avaya.clientservices.call.CallException;
import com.avaya.clientservices.call.CallListener;
import com.avaya.clientservices.call.CallPrecedenceLevel;
import com.avaya.clientservices.call.CallPreemptionReason;
import com.avaya.clientservices.call.CallService;
import com.avaya.clientservices.call.CallServiceListener;
import com.avaya.clientservices.call.AllowedVideoDirection;
import com.avaya.clientservices.call.MediaDirection;
import com.avaya.clientservices.call.VerificationStatus;
import com.avaya.clientservices.call.VideoChannel;
import com.avaya.clientservices.call.VideoMode;
import com.avaya.clientservices.client.Client;
import com.avaya.clientservices.client.ClientConfiguration;
import com.avaya.clientservices.client.ClientListener;
import com.avaya.clientservices.client.CreateUserCompletionHandler;
import com.avaya.clientservices.client.UserCreatedException;
import com.avaya.clientservices.common.MessageBodyPart;
import com.avaya.clientservices.common.SignalingServer;
import com.avaya.clientservices.credentials.CredentialProvider;
import com.avaya.clientservices.media.MediaServicesInstance;
import com.avaya.clientservices.media.VoIPConfigurationAudio;
import com.avaya.clientservices.media.VoIPConfigurationVideo;
import com.avaya.clientservices.media.capture.VideoCamera;
import com.avaya.clientservices.media.capture.VideoCaptureController;
import com.avaya.clientservices.media.capture.VideoCaptureException;
import com.avaya.clientservices.provider.conference.ConferenceConfiguration;
import com.avaya.clientservices.provider.http.HTTPUserConfiguration;
import com.avaya.clientservices.provider.media.MediaConfiguration;
import com.avaya.clientservices.provider.unifiedportal.UnifiedPortalConfiguration;
import com.avaya.clientservices.unifiedportal.RequestToJoinMeetingCompletionHandler;
import com.avaya.clientservices.unifiedportal.UnifiedPortalMeetingInfo;
import com.avaya.clientservices.unifiedportal.UnifiedPortalService;
import com.avaya.clientservices.user.User;
import com.avaya.clientservices.user.UserConfiguration;
import com.avaya.clientservices.user.UserRegistrationListener;
import com.avaya.clientservices.call.CallRecordingState;

import java.util.List;
import java.util.UUID;
import java.util.Map;

/**
 * SDKManager class is created to handle all library API calls related to Client and User management
 */
public class SDKManager implements UserRegistrationListener, ClientListener, CallServiceListener, CallListener {

    private static final String LOG_TAG = SDKManager.class.getSimpleName();

    public static final String CLIENTSDK_TEST_APP_PREFS = "com.avaya.android.prefs";
    public static final String ACTIVE_CALL_FRAGMENT_TAG = "com.avaya.sdksampleapp.activeCallFragment";

    public static final String CALL_EVENTS_RECEIVER = "callEventsReceiver";
    public static final String MESSAGE_RECEIVER = "messageReceiver";

    public static final String CALL_EVENT_TAG = "callEvent";
    public static final String TOAST_TAG = "toastMessage";
    public static final String EXCEPTION_TAG = "exceptionString";
    public static final String START_LOCAL_VIDEO_TAG = "startLocalVideo";
    public static final String START_REMOTE_VIDEO_TAG = "startRemoteVideo";
    public static final String STOP_VIDEO_TAG = "stopVideo";
    public static final String CHANNEL_ID_TAG = "videoChannelID";
    public static final String CONFERENCE_TAG = "isConferenceCall";
    public static final String MUTE_TAG = "isMuted";

    public static final String CONFERENCE_URL = "confereneURL";
    public static final String USER_DISPLAY_NAME = "userDisplayName";
    public static final String LOGIN_AS_GUEST = "loginAsGuest";
    public static final String USER_NAME = "userName";
    public static final String PASSWORD = "password";

    public static final String CALL_ID = "callId";
    public static final String IS_VIDEO_CALL = "isVideoCall";

    public static final String CALL_EVENT_STARTED = "onCallStarted";
    public static final String CALL_EVENT_RINGING = "onCallRemoteAlerting";
    public static final String CALL_EVENT_ESTABLISHED = "onCallEstablished";
    public static final String CALL_EVENT_ENDED = "onCallEnded";
    public static final String CALL_EVENT_FAILED = "onCallFailed";
    public static final String CALL_EVENT_CAPABILITIES_CHANGED = "onCallCapabilitiesChanged";
    public static final String CALL_EVENT_REMOTE_ADDRESS_CHANGED = "onCallRemoteAddressChanged";
    public static final String CALL_EVENT_REDIRECTED = "onCallRedirected";
    public static final String CALL_EVENT_QUEUED = "onCallQueued";
    public static final String CALL_EVENT_HELD = "onCallHeld";
    public static final String CALL_EVENT_UNHELD = "onCallUnheld";
    public static final String CALL_EVENT_HELD_REMOTELY = "onCallHeldRemotely";
    public static final String CALL_EVENT_UNHELD_REMOTELY = "onCallUnheldRemotely";
    public static final String CALL_EVENT_JOINED = "onCallJoined";
    public static final String CALL_EVENT_DENIED = "onCallDenied";
    public static final String CALL_EVENT_IGNORED = "onCallIgnored";
    public static final String CALL_EVENT_SPEAKER_STATUS_CHNAGED = "onCallSpeakerSilenceStatusChanged";
    public static final String CALL_EVENT_AUDIO_MUTE_STATUS_CHANGED = "onCallAudioMuteStatusChanged";
    public static final String CALL_EVENT_VIDEO_CHANNELS_UPDATED = "onCallVideoChannelsUpdated";
    public static final String CALL_EVENT_INCOMING_VIDEO_REQUEST_RECEIVED = "onCallIncomingVideoAddRequestReceived";
    public static final String CALL_EVENT_INCOMING_VIDEO_REQUEST_ACCEPTED = "onCallIncomingVideoAddRequestAccepted";
    public static final String CALL_EVENT_INCOMING_VIDEO_REQUEST_DENIED = "onCallIncomingVideoAddRequestDenied";
    public static final String CALL_EVENT_INCOMING_VIDEO_REQUEST_TIMEDOUT = "onCallIncomingVideoAddRequestTimedout";
    public static final String CALL_EVENT_CONFERENCE_STATUS_CHANGED = "onCallConferenceStatusChanged";
    public static final String CALL_EVENT_SERVICE_AVAILABLE = "onCallServiceAvailable";
    public static final String CALL_EVENT_SERVICE_UNAVAILABLE = "onCallServiceUnavailable";
    public static final String CALL_EVENT_PARTICIPANT_MATCHED_CONTACTS_CHANGED = "onCallParticipantMatchedContactsChanged";
    public static final String CALL_DIGIT_COLLECTION_PLAY_DIAL_TONE = "onCallDigitCollectionPlayDialTone";
    public static final String CALL_DIGIT_COLLECTION_COMPLETED = "onCallDigitCollectionCompleted";
    public static final String CALL_PRECEDENCE_LEVEL_CHANGED = "onCallPrecedenceLevelChanged";
    public static final String CALL_PREEMPTED = "onCallPreempted";
    public static final String CALL_ALLOWED_VIDEO_DIRECTION_CHANGED = "onCallAllowedVideoDirectionChanged";
    public static final String CALL_EXTRA_PROPERTIES_CHANGED = "onCallExtraPropertiesChanged";

    // Singleton instance of SDKManager
    private static volatile SDKManager instance;

    private final Activity activity;

    private UserConfiguration userConfiguration;
    private static volatile VideoCaptureController videoCaptureController;
    private Client mClient;
    private User mUser;

    public static VideoCamera currentCamera;

    private static int activeVideoChannel = -1;

    // Store all active calls with callId key
    private final SparseArray<CallWrapper> callsMap;

    private boolean isUserLoggedIn = false;


    private SDKManager(Activity activity) {
        this.activity = activity;
        callsMap = new SparseArray<>();
    }

    public static SDKManager getInstance(Activity activity) {
        if (instance == null) {
            synchronized (SDKManager.class) {
                if (instance == null) {
                    instance = new SDKManager(activity);
                }
            }
        }
        return instance;
    }

    public static void removeInstance() {
        synchronized (SDKManager.class) {
            instance = null;
        }
    }

    // Configure and create mClient
    public void setupClientConfiguration(Application application) {

        Resources resources = activity.getResources();
        // Create client configuration

        String dataDirectory = resources.getString(R.string.dataDirectory);
        ClientConfiguration clientConfiguration = new ClientConfiguration(application.getApplicationContext(), dataDirectory);

        // A unique instance id of the user agent defined in RFC 5626.
        // For the real applications please generate unique value once (e.g. UUID [RFC4122]) and
        // then save this in persistent storage for all future use.
        clientConfiguration.setUserAgentInstanceId(GenerateUserAgentInstanceId(application));

        // Set media configuration
        final MediaConfiguration mediaConfiguration = new MediaConfiguration();
        mediaConfiguration.setVoIPConfigurationAudio(new VoIPConfigurationAudio());
        mediaConfiguration.setVoIPConfigurationVideo(new VoIPConfigurationVideo());
        clientConfiguration.setMediaConfiguration(mediaConfiguration);
        // Create Client
        mClient = new Client(clientConfiguration, application, this);
        Client.setLogLevel(Client.LogLevel.DEBUG);
    }

    // Generates unique UserAgentInstanceId value
    private static String GenerateUserAgentInstanceId(Context context) {
        String androidID = android.provider.Settings.Secure.getString(
                context.getContentResolver(),
                android.provider.Settings.Secure.ANDROID_ID);
        if (androidID.length() > 0) {
            //deterministic generation (based on ANDROID_ID)
            return UUID.nameUUIDFromBytes(androidID.getBytes()).toString();
        } else {
            //random generation (if ANDROID_ID isn't available)
            return UUID.randomUUID().toString();
        }
    }

    // Configure and create mUser
    public void setupUserConfiguration() {

        userConfiguration = new UserConfiguration();

        // Enable HTTPUA to make OTT calls
        HTTPUserConfiguration httpUserConfiguration = userConfiguration.getHTTPUserConfiguration();
        httpUserConfiguration.setEnabled(true);
        userConfiguration.setHTTPUserConfiguration(httpUserConfiguration);

        // Turn on enhanced conference
        ConferenceConfiguration conferenceConfiguration = userConfiguration.getConferenceConfiguration();
        conferenceConfiguration.setUCCPEnabled(true);
        conferenceConfiguration.setEnhancedConferencingEnabled(true);
        conferenceConfiguration.setUCCPAdditionalFeaturesEnabled(true);
        userConfiguration.setConferenceConfiguration(conferenceConfiguration);

        // Finally create and login a user
        register();
    }


    private void register() {
        Log.d(LOG_TAG, "Register user");
        if (mUser != null) {
            // Login if user already exist
            mUser.start();
        } else {
            // Create user if not created yet
            mClient.createUser(userConfiguration, new CreateUserCompletionHandler() {
                @Override
                public void onSuccess(User user) {
                    Log.d(LOG_TAG, "createUser onSuccess");
                    // Initialize class member mUser if we created user successfully
                    mUser = user;
                    Log.d(LOG_TAG, "User Id = " + mUser.getUserId());
                    mUser.addRegistrationListener(SDKManager.this);

                    CallService callService = mUser.getCallService();
                    if (callService != null) {
                        Log.d(LOG_TAG, "CallService is ready to use");
                        // Subscribe to CallService events for incoming call handling
                        callService.addListener(getInstance(activity));
                    }

                    // And login
                    mUser.start();
                }

                @Override
                public void onError(UserCreatedException e) {
                    Log.e(LOG_TAG, "createUser onError " + e.getFailureReason());

                    //Send broadcast to notify BaseActivity to show message to the user
                    activity.sendBroadcast(new Intent(MESSAGE_RECEIVER).putExtra(TOAST_TAG,
                            "ERROR: " + e.getFailureReason().toString()));
                }
            });
        }
    }

    public User getUser() {
        return mUser;
    }

    public MediaServicesInstance getMediaServiceInstance() {
        return mClient.getMediaEngine();
    }

    public static VideoCaptureController getVideoCaptureController() {
        if (videoCaptureController == null) {
            synchronized (VideoCaptureController.class) {
                if (videoCaptureController == null) {
                    videoCaptureController = new VideoCaptureController();
                }
            }
        }

        return videoCaptureController;
    }

    public void shutdownClient() {
        Log.d(LOG_TAG, "Shutdown client");

        //Remove call service listener as we are not going to receive calls anymore
        if (mUser != null) {
            CallService callService = mUser.getCallService();
            if (callService != null) {
                callService.removeListener(getInstance(activity));
            }
            mUser.stop();
        }

        // gracefulShutdown true will try to disconnect the user from servers
        if (mClient != null) {
            mClient.shutdown(true);
        }
    }

    public void delete(boolean loginStatus) {
        Log.d(LOG_TAG, "Delete user");
        if (mUser != null) {
            Log.d(LOG_TAG, "User exist. Deleting...");

            isUserLoggedIn = false;

            mClient.removeUser(mUser, loginStatus);
            mUser = null;
        }
    }

    public boolean isUserLoggedIn() {
        return isUserLoggedIn;
    }


    /*
     * Call management section
     */

    public static int getActiveVideoChannel() {
        return activeVideoChannel;
    }

    public static void setActiveVideoChannel(int activeVideoChannel) {
        SDKManager.activeVideoChannel = activeVideoChannel;
    }

    // Return the call with specified call id if it is not removed yet
    public CallWrapper getCallWrapperByCallId(int callId) {
        return callsMap.get(callId);
    }


    public void sendRequestToJoinMeeting(final MeetingInfo meetingInfo, RequestToJoinMeetingCompletionHandler completionHandler) {

        // Create Unified Portal configuration object
        final UnifiedPortalConfiguration unifiedPortalConfiguration =
                new UnifiedPortalConfiguration();

        // Specify Unified Portal URL (mandatory)
        unifiedPortalConfiguration.setServerURL(meetingInfo.portalURL);

        // Optionally, specify credential provider to sign in portal
        // and join the meeting as signed in user.
        CredentialProvider credentialProvider = null;
        if (!meetingInfo.loginAsGuest) {
            credentialProvider =  new UserCredentialProvider(meetingInfo.userName, meetingInfo.password);
        }
        unifiedPortalConfiguration.setCredentialProvider(credentialProvider);

        // Retrieve the token and other meeting details
        UnifiedPortalService unifiedPortalService = mUser.getUnifiedPortalService();
        unifiedPortalService.requestToJoinMeeting(
                unifiedPortalConfiguration,
                meetingInfo.conferenceID,
                meetingInfo.userDisplayName,
                false,
                "",
                "",
                completionHandler);
    }

    // Create call object with meeting info (), add to call map and return call object
    public CallWrapper createHTTPCall(MeetingInfo meetingInfo, UnifiedPortalMeetingInfo unifiedPortalMeetingInfo) {

        // Create call
        CallCreationInfo callCreationInfo = new CallCreationInfo(
                meetingInfo.conferenceID,
                meetingInfo.conferencePasscode,
                unifiedPortalMeetingInfo.getPortalToken(),
                unifiedPortalMeetingInfo.getUCCPURL(),
                unifiedPortalMeetingInfo.getServiceGatewayURL(),
                meetingInfo.portalURL,
                meetingInfo.userDisplayName,
                meetingInfo.conferenceID,
                false);
        Call call = mUser.getCallService().createCall(callCreationInfo);
        if (call == null) {
            return null;
        }
        else {
            call.setRemoteAddress(meetingInfo.conferenceID);

            // Get unique call id specified for created call
            int callId = call.getCallId();

            CallWrapper callWrapper = new CallWrapper(call);

            // Add the call to call Map
            callsMap.put(callId, callWrapper);
            return callWrapper;
        }
    }


    public void startCall(CallWrapper callWrapper) {

        Call call = callWrapper.getCall();
        // Subscribe to call state events
        call.addListener(this);

        // Add video to the call
        if (callWrapper.isVideoCall()) {
            addVideo(call);
        }

        if (call.isIncoming()) {
            Log.d(LOG_TAG, "Incoming call accepted");
            // Accept the call if it is incoming call
            call.accept();
        } else {
            Log.d(LOG_TAG, "Outgoing call started");
            // Start the call if it is outgoing call
            call.start();
        }
    }

    // Create video chanel and set it for the call
    private void addVideo(Call call) {
        // Check if video supported
        if (!call.getUpdateVideoModeCapability().isAllowed()) {
            Log.d(LOG_TAG, "Don't add video. Video isn't supported");
            return;
        } else if (call.isIncoming() && call.getIncomingVideoStatus()
                != Call.IncomingVideoStatus.SUPPORTED) {
            Log.d(LOG_TAG, "Don't add video. Far-end didn't send video information");
            return;
        }

        // Set video mode for the call depending on camera device
        call.setVideoMode(setupCamera(), new CallCompletionHandler() {
            @Override
            public void onSuccess() {
                Log.d(LOG_TAG, "Video mode has been set");
            }

            @Override
            public void onError(CallException e) {
                Log.e(LOG_TAG, "Video mode can't be set. Exception: " + e.getError());
            }
        });
    }

    // Set the camera and return video mode for initializing video
    private VideoMode setupCamera() {
        // Check if device has camera
        VideoCaptureController videoCaptureController = getVideoCaptureController();
        try {
            if (videoCaptureController.hasVideoCamera(VideoCamera.Front)) {
                currentCamera = VideoCamera.Front;
                return VideoMode.SEND_RECEIVE;
            } else if (videoCaptureController.hasVideoCamera(VideoCamera.Back)) {
                currentCamera = VideoCamera.Back;
                return VideoMode.SEND_RECEIVE;
            }
        } catch (VideoCaptureException e) {
            Log.e(LOG_TAG, "Camera can't be set. Exception: " + e.getLocalizedMessage());
            return VideoMode.RECEIVE_ONLY;
        }
        // No cameras found
        return VideoMode.RECEIVE_ONLY;
    }

    /*
     * UserRegistrationListener section
     */
    @Override
    public void onUserRegistrationInProgress(User user, SignalingServer signalingServer) {
        Log.d(LOG_TAG, "onUserRegistrationInProgress");
    }


    // onUserRegistrationSuccessful is called when signaling server respond that provided user
    // credentials are fine and user successfully registered on the server.
    @Override
    public void onUserRegistrationSuccessful(User user, SignalingServer signalingServer) {
        Log.d(LOG_TAG, "onUserRegistrationSuccessful");
        isUserLoggedIn = true;

        // Send broadcast to notify BaseActivity to show message to the user
        activity.sendBroadcast(new Intent(MESSAGE_RECEIVER).putExtra(TOAST_TAG,
                "User has been successfully started"));
    }

    // onUserRegistrationFailed is called when connection error occurred or signaling server
    // respond that provided user credentials are incorrect.
    @Override
    public void onUserRegistrationFailed(User user, SignalingServer signalingServer, Exception e) {
        Log.d(LOG_TAG, "onUserRegistrationFailed " + e.toString());
        isUserLoggedIn = false;

        // Send broadcast to notify BaseActivity to show message to the user
        activity.sendBroadcast(new Intent(MESSAGE_RECEIVER).putExtra(TOAST_TAG, "Failed to start user: "
                + e.getLocalizedMessage()));
    }

    @Override
    public void onUserAllRegistrationsSuccessful(User user) {
        Log.d(LOG_TAG, "onUserAllRegistrationsSuccessful");
    }

    @Override
    public void onUserAllRegistrationsFailed(User user, boolean bWillRetry) {
        Log.d(LOG_TAG, "onUserRegistrationFailed ");
    }

    @Override
    public void onUserUnregistrationInProgress(User user, SignalingServer signalingServer) {
        Log.d(LOG_TAG, "onUserUnregistrationInProgress");
    }

    @Override
    public void onUserUnregistrationSuccessful(User user, SignalingServer signalingServer) {
        Log.d(LOG_TAG, "onUserUnregistrationSuccessful");
    }

    @Override
    public void onUserUnregistrationFailed(User user, SignalingServer signalingServer,
                                           Exception e) {
        Log.d(LOG_TAG, "onUserUnregistrationFailed " + e.toString());
    }

    // onUserUnregistrationComplete is called when server respond that user successfully
    // unregistered.
    @Override
    public void onUserUnregistrationComplete(User user) {
        Log.d(LOG_TAG, "onUserUnregistrationComplete");
        isUserLoggedIn = false;

        // Send broadcast to notify BaseActivity to show message to the user
        activity.sendBroadcast(new Intent(MESSAGE_RECEIVER).putExtra(TOAST_TAG,
                "User has been successfully stopped"));
    }

    @Override
    public void onRegistrationResponsePayloadReceived(User var1, List<MessageBodyPart> var2, SignalingServer var3) {
        Log.d(LOG_TAG, "onRegistrationResponsePayloadReceived");
    }

    /*
     * ClientListener section
     */
    @Override
    public void onClientShutdown(Client client) {
        Log.d(LOG_TAG, "onClientShutdown");
    }

    @Override
    public void onClientUserCreated(Client client, User user) {
        Log.d(LOG_TAG, "onClientUserCreated");
    }

    // onClientUserRemoved executed when Client.removeUser() is called and successfully completed
    @Override
    public void onClientUserRemoved(Client client, User user) {
        Log.d(LOG_TAG, "onClientUserRemoved");
        // User was deleted due to settings update. Let's create new user with updated
        // configuration.
        setupUserConfiguration();
    }

    @Override
    public void onIdentityCertificateEnrollmentFailed(Client client, int errorCode, String errorType, String message) {
        Log.d(LOG_TAG, "onIdentityCertificateEnrollmentFailed");
    }

    /*
     * CallServiceListener listener section
     */
    @Override
    public void onIncomingCallReceived(CallService callService, Call call) {
        Log.d(LOG_TAG, "onIncomingCall");
        // We can't receive incoming HTTPUA call
    }

    @Override
    public void onCallCreated(CallService callService, Call call) {
        Log.d(LOG_TAG, "onCallCreated");
    }

    @Override
    public void onIncomingCallUndelivered(CallService callService, Call call) {
        Log.d(LOG_TAG, "onIncomingCallUndelivered");
    }

    @Override
    public void onActiveCallChanged(CallService callService, Call call) {
        Log.d(LOG_TAG, "onActiveCallChanged");
    }

    @Override
    public void onCallRemoved(CallService callService, Call call) {
        Log.d(LOG_TAG, "onCallRemoved");

        // Remove the call from call map for prevent any actions with it
        int callId = call.getCallId();
        callsMap.remove(callId);

        // Unsubscribe from call state events
        call.removeListener(this);
    }

    @Override
    public void onCallServiceCapabilityChanged(CallService callService) {
        Log.d(LOG_TAG, "onCallServiceCapabilityChanged");
    }

    @Override
    public void onAcceptCallRequestReceived(CallService callService, Call call, VideoMode videoMode) {
        Log.d(LOG_TAG, "onAcceptCallRequestReceived");
    }

    @Override
    public void onStartCallRequestReceived(CallService callService, Call call, VideoMode videoMode) {
        Log.d(LOG_TAG, "onStartCallRequestReceived");
    }

    /*
     * CallListener section. This section is handling events that are received for calls.
     */
    @Override
    public void onCallStarted(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_STARTED);
    }

    @Override
    public void onCallRemoteAlerting(Call call, boolean b) {
        Log.d(LOG_TAG, CALL_EVENT_RINGING);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_RINGING));
    }

    @Override
    public void onCallEstablished(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_ESTABLISHED);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_ESTABLISHED));
    }


    @Override
    public void onCallFailed(Call call, CallException e) {
        Log.d(LOG_TAG, CALL_EVENT_FAILED);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_FAILED)
                .putExtra(EXCEPTION_TAG, e.getLocalizedMessage()));

    }

    @Override
    public void onCallCapabilitiesChanged(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_CAPABILITIES_CHANGED);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_CAPABILITIES_CHANGED));
    }

    @Override
    public void onCallRemoteAddressChanged(Call call, String s, String s1) {
        Log.d(LOG_TAG, CALL_EVENT_REMOTE_ADDRESS_CHANGED);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_REMOTE_ADDRESS_CHANGED));

    }

    @Override
    public void onCallRedirected(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_REDIRECTED);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_REDIRECTED));
    }

    @Override
    public void onCallQueued(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_QUEUED);
    }

    @Override
    public void onCallHeld(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_HELD);
    }

    @Override
    public void onCallUnheld(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_UNHELD);
    }

    @Override
    public void onCallHeldRemotely(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_HELD_REMOTELY);
    }

    @Override
    public void onCallUnheldRemotely(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_UNHELD_REMOTELY);
    }

    @Override
    public void onCallJoined(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_JOINED);
    }

    @Override
    public void onCallEnded(Call call, CallEndReason callEndReason) {
        Log.d(LOG_TAG, CALL_EVENT_ENDED);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_ENDED));
    }

    @Override
    public void onCallDenied(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_DENIED);
    }

    @Override
    public void onCallIgnored(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_IGNORED);
    }

    @Override
    public void onCallAudioMuteStatusChanged(Call call, boolean isMuted) {
        Log.d(LOG_TAG, "onCallAudioMuteStatusChanged isMuted: " + isMuted);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_AUDIO_MUTE_STATUS_CHANGED)
                .putExtra(MUTE_TAG, isMuted));
    }

    @Override
    public void onCallSpeakerSilenceStatusChanged(Call call, boolean b) {
        Log.d(LOG_TAG, CALL_EVENT_SPEAKER_STATUS_CHNAGED);
    }

    @Override
    public void onCallVideoChannelsUpdated(Call call, List<VideoChannel> list) {
        Log.d(LOG_TAG, CALL_EVENT_VIDEO_CHANNELS_UPDATED);
        // Get call id
        int callId = call.getCallId();

        CallWrapper callWrapper = getCallWrapperByCallId(callId);

        if (!list.isEmpty()) {
            // Get video channel id
            int channelId = list.get(0).getChannelId();
            setActiveVideoChannel(channelId);

            // Get negotiated media direction
            MediaDirection mediaDirection = list.get(0).getNegotiatedDirection();
            Log.d(LOG_TAG, "Negotiated media direction: " + mediaDirection);
            if (mediaDirection == MediaDirection.SEND_RECEIVE
                    || mediaDirection == MediaDirection.SEND_ONLY) {
                Log.d(LOG_TAG, START_LOCAL_VIDEO_TAG);
                activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                        .putExtra(CALL_EVENT_TAG, CALL_EVENT_VIDEO_CHANNELS_UPDATED)
                        .putExtra(START_LOCAL_VIDEO_TAG, true)
                        .putExtra(CHANNEL_ID_TAG, channelId));
                callWrapper.setLocalVideoActive(true);
            }
            if (mediaDirection == MediaDirection.SEND_RECEIVE
                    || mediaDirection == MediaDirection.RECEIVE_ONLY) {
                Log.d(LOG_TAG, START_REMOTE_VIDEO_TAG);
                activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                        .putExtra(CALL_EVENT_TAG, CALL_EVENT_VIDEO_CHANNELS_UPDATED)
                        .putExtra(START_REMOTE_VIDEO_TAG, true)
                        .putExtra(CHANNEL_ID_TAG, channelId));
                callWrapper.setRemoteVideoActive(true);
            }
        } else {
            Log.d(LOG_TAG, STOP_VIDEO_TAG);
            activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                    .putExtra(CALL_EVENT_TAG, CALL_EVENT_VIDEO_CHANNELS_UPDATED)
                    .putExtra(STOP_VIDEO_TAG, true));
            callWrapper.setLocalVideoActive(false);
            callWrapper.setRemoteVideoActive(false);
            setActiveVideoChannel(-1);
        }
    }

    @Override
    public void onCallIncomingVideoAddRequestReceived(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_INCOMING_VIDEO_REQUEST_RECEIVED);
    }

    @Override
    public void onCallIncomingVideoAddRequestAccepted(Call call, VideoChannel videoChannel) {
        Log.d(LOG_TAG, CALL_EVENT_INCOMING_VIDEO_REQUEST_ACCEPTED);
    }

    @Override
    public void onCallIncomingVideoAddRequestDenied(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_INCOMING_VIDEO_REQUEST_DENIED);
    }

    @Override
    public void onCallIncomingVideoAddRequestTimedOut(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_INCOMING_VIDEO_REQUEST_TIMEDOUT);
    }

    @Override
    public void onCallRemoteControlVideoModeRequested(Call call, VideoMode videoMode) {
        Log.d(LOG_TAG, "onCallRemoteControlVideoModeRequested: videoMode = " + videoMode);
    }

    @Override
    public void onCallConferenceStatusChanged(Call call, boolean isConference) {
        Log.d(LOG_TAG, CALL_EVENT_CONFERENCE_STATUS_CHANGED);
        activity.sendBroadcast(new Intent(CALL_EVENTS_RECEIVER)
                .putExtra(CALL_EVENT_TAG, CALL_EVENT_CONFERENCE_STATUS_CHANGED)
                .putExtra(CONFERENCE_TAG, isConference));
    }

    @Override
    public void onCallServiceAvailable(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_SERVICE_AVAILABLE);
    }

    @Override
    public void onCallServiceUnavailable(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_SERVICE_UNAVAILABLE);
    }

    @Override
    public void onCallParticipantMatchedContactsChanged(Call call) {
        Log.d(LOG_TAG, CALL_EVENT_PARTICIPANT_MATCHED_CONTACTS_CHANGED);
    }

    @Override
    public void onCallDigitCollectionPlayDialTone(Call call) {
        Log.d(LOG_TAG, CALL_DIGIT_COLLECTION_PLAY_DIAL_TONE);
    }

    @Override
    public void onCallDigitCollectionCompleted(Call call) {
        Log.d(LOG_TAG, CALL_DIGIT_COLLECTION_COMPLETED);
    }

    @Override
    public void onCallPrecedenceLevelChanged(Call var1, CallPrecedenceLevel newPrecedenceLevel) {
        Log.d(LOG_TAG, CALL_PRECEDENCE_LEVEL_CHANGED);
    }

    @Override
    public void onCallPreempted(Call call, CallPreemptionReason preemptionReason, boolean isPreemptionCompletionRequiredByClient) {
        Log.d(LOG_TAG, CALL_PREEMPTED);
    }

    @Override
    public void  onCallAllowedVideoDirectionChanged(Call call, AllowedVideoDirection allowedVideoDirection) {
        Log.d(LOG_TAG, CALL_ALLOWED_VIDEO_DIRECTION_CHANGED + allowedVideoDirection);
    }

    @Override
    public void onCallExtraPropertiesChanged(Call call, Map<String, String> extraProperties) {
        Log.d(LOG_TAG, CALL_EXTRA_PROPERTIES_CHANGED );
    }

    @Override
    public void onCallLongHoldTimeExpired(Call call) {
        Log.d(LOG_TAG, "onCallLongHoldTimeExpired");
    }

    @Override
    public void onCallRecordingStateChanged(Call call, CallRecordingState recordingState) {
        Log.d(LOG_TAG, "onCallRecordingStateChanged: " + recordingState);
    }

    @Override
    public void onCallSecureIndicationChanged(Call call, boolean isSecure) {
        Log.d(LOG_TAG, "onCallSecureIndicationChanged: isSecure = " + isSecure);
    }

    @Override
    public void onCallVerificationStatusChanged(Call call, VerificationStatus verificationStatus) {
        Log.d(LOG_TAG, "onCallVerificationStatusChanged: " + verificationStatus);
    }
}
