/*
 * /////////////////////////////////////////////////////////////////////////////////
 * //
 * // Copyright <2015>-<2016> Avaya Inc. All Rights Reserved.
 * //
 * // Usage of this source is bound to the terms described
 * // in assets/Licence.txt
 * //
 * // Avaya - Confidential & Proprietary. Use pursuant to your signed agreement
 * // or Avaya Policy
 * //
 * /////////////////////////////////////////////////////////////////////////////
 */

package com.avaya.odonnell3.AndroidChatTemplate_2_3.services;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import android.widget.Toast;

import com.avaya.odonnell3.AndroidChatTemplate_2_3.R;
import com.avaya.odonnell3.AndroidChatTemplate_2_3.activities.ChatActivity;
import com.avaya.odonnell3.AndroidChatTemplate_2_3.model.ChatMessage;
import com.avaya.odonnell3.AndroidChatTemplate_2_3.classes.MessageParser;
import com.avaya.odonnell3.AndroidChatTemplate_2_3.model.NewMessageNotification;
import com.avaya.odonnell3.AndroidChatTemplate_2_3.model.CustomerJsonWrapper;
import com.avaya.odonnell3.AndroidChatTemplate_2_3.model.NewParticipant;
import com.avaya.odonnell3.AndroidChatTemplate_2_3.model.PagePush;
import com.avaya.odonnell3.AndroidChatTemplate_2_3.model.ParticipantLeave;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;


import java.net.URI;
import java.net.URISyntaxException;

import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;


public class WebSocketService extends Service {

    public final String className = this.getClass().getSimpleName();
    private String uri;
    URI conn;
    private static WebSocketClient wsClient;
    private final IBinder mBinder = new LocalBinder();
    private boolean appVisible = true;
    private boolean hasJoined = false;
    BlockingQueue<String> messageList;

    List<ChatMessage> persistedChatMessageList = new ArrayList<>();


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.v("Info",className+ " onBind()");

        return mBinder;
    }

    public void onConnectWs() {
        Log.v("Info",className+ " onConnectWs()");
        try {
            conn = new URI(uri.trim());
            Log.v("Info", "Connecting to " + uri);
        } catch (URISyntaxException e) {
            e.printStackTrace();
            return;
        }
        wsClient = new WebSocketClient(conn, new Draft_6455()) {
            @Override
            public void onOpen(ServerHandshake handshake) {
                Log.v("Info",className+ " onOpen()");
                messageList = new LinkedBlockingQueue<>();
            }

            @Override
            public void onMessage(String m) {
                Log.v("Info",className+ " onMessage()");
                Log.v("Info", "WebSocket Message: " + m);

                if (m != null) {
                    broadcastMessage(m);
                }
                CustomerJsonWrapper wrapper = MessageParser.jsonStringToJava(m);
                String className = null;
                if (wrapper != null) {
                    className = wrapper.getBody().getClass().getSimpleName();
                } else {
                    Log.e("Error", "Wrapper was null");
                }
                //add message types to this list to get notifications when app is in background
                String[] backgroundClasses = {"NewMessageNotification", "PagePush", "ParticipantLeave", "NewParticipant"};

                for (String backgroundClass : backgroundClasses) {
                    //when the chat activity is not visible show notificationsS

                    Log.v("Info", "app is visible in service? " + appVisible);
                    if (className.equals(backgroundClass) && !appVisible)  {
                        Log.v("Info", "setting Notification");
                        showCustomNotifications(m);
                    }
                }
            }



            @Override
            public void onClose(int code, String reason, boolean remote) {
                Log.v("Info",className+ " onClose(), reason: " + reason);
            }

            @Override
            public void onError(Exception ex) {
                Log.e("Info", className+" Exception: " + ex.toString());
            }
        };
        if(conn.toString().contains("wss")) {
            SSLContext Cur_SSL_Context = null;
            try
            {
                Cur_SSL_Context = SSLContext.getInstance("TLS");
                Cur_SSL_Context.init(null, new TrustManager[]{new X509TrustManager(){
                    public java.security.cert.X509Certificate[] getAcceptedIssuers(){
                        return new java.security.cert.X509Certificate[]{};
                    }
                    public void checkClientTrusted(    X509Certificate[] chain,    String authType) throws CertificateException {
                    }
                    public void checkServerTrusted(    X509Certificate[] chain,    String authType) throws CertificateException {
                    }
                }
                }, new SecureRandom());
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            wsClient.setSocketFactory(Cur_SSL_Context.getSocketFactory());

        }
    }

    public WebSocketClient getWsClient() {
        return wsClient;
    }

//build and show notifications if app in background
    public void showCustomNotifications(String message) {
        Log.v("Info",className+ " showCustomNotifications()");
        long[] pattern = {500, 500, 500};
        Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

        CustomerJsonWrapper wrapper = MessageParser.jsonStringToJava(message);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        builder.setSmallIcon(R.drawable.ic_chat_white)
                .setColor(getResources().getColor(R.color.colorPrimary))
                .setVibrate(pattern).setLights(Color.RED, 500, 500)
                .setSound(alarmSound);


        if (wrapper != null) {
            switch (wrapper.getBody().getClass().getSimpleName()) {
                case "ParticipantLeave":
                    ParticipantLeave pl = (ParticipantLeave) wrapper.getBody();
                    if(pl.getLeaveReason().equalsIgnoreCase("transfer")) {
                        builder.setContentTitle(pl.getAgentId())
                                .setContentText("Transferring to another service. Please remain in the chat");
                    } if (pl.getLeaveReason().equalsIgnoreCase("transfer_to_user")) {
                    builder.setContentTitle(pl.getAgentId())
                            .setContentText("Transferring to User. Please remain in the chat");
                    } else {
                        builder.setContentTitle(pl.getAgentId())
                                .setContentText("Agent has left the chat");
                    }
                    showAgentChatNotification(builder);
                    break;
                case "NewMessageNotification":
                    Log.v("Info", "entering Agent to Customer Message");

                    NewMessageNotification am = (NewMessageNotification) wrapper.getBody();
                    builder.setContentTitle(am.getDisplayName())
                            .setContentText(am.getMessage());
                    ChatMessage chatMessage = new ChatMessage(am.getDisplayName(), am.getMessage(), 1);
                    persistedChatMessageList.add(chatMessage);

                    break;
                case "PagePush":
                    Log.v("Info", "entering Agent Page Push");
                    PagePush pagePush = (PagePush) wrapper.getBody();
                    builder.setContentTitle(pagePush.getDisplayName())
                            .setContentText("Agent has sent you a Link");

                    ChatMessage cm = new ChatMessage(pagePush.getDisplayName(), pagePush.getPagePushURL(), 3);
                    persistedChatMessageList.add(cm);


                    break;
                case "NewParticipant":
                    Log.v("Info", "entering New Participant");

                    NewParticipant np = (NewParticipant) wrapper.getBody();
                    String role = np.getRole().replace("_", " ");
                    builder.setContentTitle(np.getDisplayName())
                            .setContentText(role + ": " + np.getDisplayName() + " has joined the Chat");
                    hasJoined = true;
                default:
                    break;
            }
        }

        showAgentChatNotification(builder);

    }

    public void showAgentChatNotification(NotificationCompat.Builder builder) {
        Log.v("Info",className+ " showAgentChatNotification()");
        Intent intent = new Intent(this, ChatActivity.class);
        intent.putExtra("visible", true);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addNextIntent(intent);

        PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(pendingIntent).setAutoCancel(true);
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0, builder.build());
    }


    @Override
    public void onCreate() {

        Log.v("Info",className+ " showCustomNotifications()");
    }

    @Override
    public void onDestroy() {

        Log.v("Info",className+ " showCustomNotifications()");
    }

//send a broadcast of received messages
    public void broadcastMessage(String message) {
        Log.v("Info",className+ " broadcastMessage()");
        Intent intent = new Intent(getApplicationContext().getPackageName());
        intent.putExtra("message", message);
        Log.v("Info", "Broadcasting: " + message);
        sendBroadcast(intent);
    }

    public List<ChatMessage> getPersistedChatMessageList() {
        return persistedChatMessageList;
    }

    public void setPersistedChatMessageList(List<ChatMessage> persistedChatMessageList) {
        this.persistedChatMessageList = persistedChatMessageList;
    }

    public void setAppVisible(boolean appVisible, String activity) {
        Log.v("Info",className+ " setAppVisible: " + appVisible + "\nActivity: " + activity);
        this.appVisible = appVisible;

        setWaitVisible(appVisible);
        removeNotifications(appVisible);
    }
    public void removeNotifications(Boolean isVisible){

        String ns = Context.NOTIFICATION_SERVICE;
        NotificationManager nm = (NotificationManager) getApplicationContext().getSystemService(ns);

        if(isVisible){
            nm.cancelAll();
        }
    }

    public void setUri(String uri) {
        this.uri = uri;
    }
//handles notification visibility when waitMessageActivity is in background
    public void setWaitVisible(boolean waitVisible) {
        Log.v("Info",className+ " setWaitVisible()");
        if (waitVisible && hasJoined) {
            Intent intent = new Intent(this, ChatActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
            hasJoined = false;

        }
    }
    public class LocalBinder extends Binder {

        public WebSocketService getService() {
            return WebSocketService.this;
        }

    }


}
