package com.tsi.navi.websocket;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;

import androidx.annotation.Nullable;

import com.tsi.navi.preferences.PrefManager;

import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.security.cert.CertificateException;

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

public class NaviWebsocketClientService extends Service {

    private static final String TAG = NaviWebsocketClientService.class.getSimpleName();

    private NaviWebsocketClient client;
    private NaviWebsocketClientBinder binder = new NaviWebsocketClientBinder();
    private Handler handler = new Handler();
    private static final long HEART_BEAT_RATE = 10 * 1000;
    PowerManager.WakeLock wakeLock;

    public NaviWebsocketClient getNaviWebsocketClient() {
        return this.client;
    }
    private final Runnable heartBeatRunnable = new Runnable() {
        @Override
        public void run() {
            if (client != null) {
                if (client.isClosed()) {
                    reConnect();
                }
            }
            else {
                client = null;
                initSocketClient();
            }
            handler.postDelayed(this, HEART_BEAT_RATE);
        }
    };

    public NaviWebsocketClientService() {
    }

    @SuppressLint({"InvalidWakeLockTag", "WakelockTimeout"})
    private void acquireWakeLock() {
        if (null == wakeLock)
        {
            PowerManager pm = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
            wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "PostLocationService");
            if (null != wakeLock) {
                wakeLock.acquire();
            }
        }
    }

    public class NaviWebsocketClientBinder extends Binder {
        public NaviWebsocketClientService getService() {
            return NaviWebsocketClientService.this;
        }
    }

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

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @SuppressLint("ObsoleteSdkInt")
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        initSocketClient();

        handler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);

        acquireWakeLock();

        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        closeConnect();
        super.onDestroy();
    }

    private void initSocketClient() {
        String url = PrefManager.getWebsocketUrl(this);
        URI uri = URI.create(url);

        client = new NaviWebsocketClient(uri) {
            @Override
            public void onMessage(String message) {
                //Log.e(TAG, "onMessage: " + message.length() + " bytes.");

                Intent intent = new Intent();
                intent.setAction("com.websocket.content");
                intent.putExtra("message", message);
                sendBroadcast(intent);
            }

            @Override
            public void onOpen(ServerHandshake handshake) {
                super.onOpen(handshake);
                //Log.e(TAG, "onOpen: " + handshake.getHttpStatusMessage());
                Intent intent = new Intent();
                intent.setAction("com.websocket.event");
                intent.putExtra("event", "open");
                sendBroadcast(intent);
            }

            @Override
            public void onClose(int code, String reason, boolean remote) {
                super.onClose(code, reason, remote);
                //Log.e(TAG, "onClose: " + reason);
                Intent intent = new Intent();
                intent.setAction("com.websocket.event");
                intent.putExtra("event", "close");
                sendBroadcast(intent);
            }

            @Override
            public void onError(Exception ex) {
                super.onError(ex);
                //Log.e(TAG, "onError: " + ex.getMessage());
                Intent intent = new Intent();
                intent.setAction("com.websocket.event");
                intent.putExtra("event", "error");
                sendBroadcast(intent);
            }
        };
        client.setTcpNoDelay(true);
        client.setReuseAddr(true);

        if (uri.getScheme().contains("wss")) {
            try {
                // Create a trust manager that does not validate certificate chains
                final TrustManager[] trustAllCerts = new TrustManager[] {
                        new X509TrustManager() {
                            @Override
                            public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                            }

                            @Override
                            public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                            }

                            @Override
                            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                                return new java.security.cert.X509Certificate[]{};
                            }
                        }
                };
                // Install the all-trusting trust manager
                final SSLContext sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
                // Create an ssl socket factory with our all-trusting manager
                final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

                client.setSocketFactory(sslSocketFactory);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        connect();
    }

    private void connect() {
        new Thread() {
            @Override
            public void run() {
                try {
                    client.connectBlocking();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    private void reConnect() {
        handler.removeCallbacks(heartBeatRunnable);
        new Thread() {
            @Override
            public void run() {
                try {
                    //Log.e(TAG, "reConnect: reconnectBlocking, start");
                    client.reconnectBlocking();
                    //Log.e(TAG, "reConnect: reconnectBlocking, end");
                }
                catch (InterruptedException e) {
                    Log.e(TAG, "reConnect: " + e.getMessage());
                }
            }
        }.start();
    }

    public void sendMsg(String msg) {
        if (null != client) {
            client.send(msg);
            //Log.e(TAG, "sendMsg：" + msg);
        }
    }

    private void closeConnect() {
        try {
            if (null != client) {
                client.close();
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "closeConnect: " + e.getMessage());
        }
        finally {
            client = null;
        }
    }

}
