package com.ntels.onecable.common.websocket;

import com.ntels.onecable.R;
import com.ntels.onecable.common.AppConfig;
import com.ntels.onecable.common.Constants;
import com.ntels.onecable.common.util.LogUtil;
import com.ntels.onecable.common.util.Util;

import org.java_websocket.WebSocket;
import org.java_websocket.client.DefaultSSLWebSocketClientFactory;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_10;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import java.net.URI;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

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

/**
 * <pre>
 * 1. ClassName :   WebSocketService
 * 2. FileName  :   WebSocketService.java
 * 3. Package   :   com.ntels.onecable.common.websocket
 * 4. Date      :   2016-12-06
 * 5. Author    :   aaron
 * 6. Comment   :   Websocket request service
 * </pre>
 */
public class WebSocketService extends Service {
    public static final int WEBSOCKET_CONNECT_TIMEOUT = 0;
    public static final int WEBSOCKET_CLIENT_CONNECT_AUTH = 1;
    private static final String TAG = WebSocketService.class.getSimpleName();
    private static final int TIMEOUT = 20;
    private final IBinder binder = new WebSocketBinder();
    private AppConfig mAppConfig;
    private WebSocketClient webSocket = null;
    private ArrayList<OnWebSocketResponse> webSocketResponses = new ArrayList<OnWebSocketResponse>();
    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case WEBSOCKET_CONNECT_TIMEOUT:
                    LogUtil.i(TAG, "WebSocket Time out");
                    break;
                case WEBSOCKET_CLIENT_CONNECT_AUTH:
                    send(Constants.WEBSOCKET_SEND_CONNECT_AUTH);
                    break;
            }
            return false;
        }
    });
    private Util util = new Util();
    private int iTempApiId = 0;
    private HashMap<String, Object> hmTempParam;
    private boolean bFromUser = false;

    public WebSocketService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mAppConfig = (AppConfig) this.getApplicationContext();
    }

    /**
     * WebSocket Listener 추가.
     *
     * @param webSocketResponse WebSocket 데이터를 전달받을 리스너 객체
     */
    public void addWebSocketResponse(OnWebSocketResponse webSocketResponse) {
        this.webSocketResponses.add(webSocketResponse);
    }

    /**
     * WebSocket Listener 삭제.
     *
     * @param webSocketResponse 삭제할 Websocket 리스너 객체
     */
    public void removeWebSocketResponse(OnWebSocketResponse webSocketResponse) {
        this.webSocketResponses.remove(webSocketResponse);
    }

    @Override
    public IBinder onBind(Intent intent) {
        LogUtil.i(TAG, "onBind!!");
//        if (!isWebSocketConnected() && !mAppConfig.getCtrtGroupId().equals("")) {
        if (!isWebSocketConnected()) {
            LogUtil.i(TAG, "onBind1!!");
            webSocketConnect();
            LogUtil.i(TAG, "onBind2!!");
        }
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        LogUtil.i(TAG, "onUnbind()!!");
        return super.onUnbind(intent);
    }

    /**
     * WebSocket 연결.
     */
    public void webSocketConnect() {
//        String sServicehubAppId = mAppConfig.getsSelect_Servicehub_App_Id();
//        String sHomeGrpId = mAppConfig.getCtrtGroupId();

//        if (sServicehubAppId.isEmpty() || sHomeGrpId.isEmpty()) {
//        if (sServicehubAppId.isEmpty()) {
//            return;
//        }

        if (isWebSocketConnected()) {
            webSocketDisconnect();
        }
//        java.security.cert.X509Certificate
        URI uri;
        try {
//            String url = mAppConfig.getServicehubWebsocketUrl() + "/" + sServicehubAppId + "/" + sHomeGrpId;
            String url = mAppConfig.getServicehubWebsocketUrl() + util.getString(getApplicationContext(), R.string.API_VERSION);
            uri = new URI(url);
        } catch (URISyntaxException e) {
            e.printStackTrace();
            return;
        }

//        String url = Constants.URL_SERVICE_HUB_WEBSOCKET_V3;
        LogUtil.i(TAG, "WebSocket URL : " + uri);

        webSocket = new WebSocketClient(uri, new Draft_10(), getHeaders(), 0) {
            @Override
            public void onOpen(ServerHandshake serverHandshake) {
                Log.i("Websocket", "Opened");

                LogUtil.i(TAG, "jino1");
                LogUtil.i(TAG, "onCompleted");
                mAppConfig.setbWebSocketConn(true);
                // WebSocket Client 인증 수행
                LogUtil.i(TAG, "jino4");
                // 이미 Authorization token이 있는경우 auth를 진행하지않는다.
                if ("".equals(mAppConfig.getAuthorization())) {
                    // 로그인 성공시. 또는 자동 로그인시만  토큰 유지 관련 처리
                    if (mAppConfig.getLogin() || (!mAppConfig.getLogin() && mAppConfig.getAutoLogin())) {
                        handler.sendEmptyMessage(WEBSOCKET_CLIENT_CONNECT_AUTH);
                    }
                }

            }

            @Override
            public void onMessage(final String message) {
                LogUtil.i(TAG, "WebSocket Response : " + message);
                if (checkBasicStatus(message)) {
                    return;
                }

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (webSocketResponses.size() > 0) {
                            for (OnWebSocketResponse wsr : webSocketResponses) {
                                wsr.onResponse(message);
                            }
                        }
                    }
                });

            }

            @Override
            public void onClose(int i, String s, boolean b) {
                Log.i("Websocket", "Closed " + s);
                LogUtil.i(TAG, "Websocket closed!!");
                if (!bFromUser) {
                    LogUtil.i(TAG, "Websocket closed!!1");
                    webSocketConnect();
                }
                bFromUser = false;
            }

            @Override
            public void onError(Exception ex) {
                Log.i("Websocket", "Error " + ex.getMessage());
                LogUtil.i(TAG, "jino2");
                ex.printStackTrace();
                mAppConfig.setbWebSocketConn(false);
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        LogUtil.i(TAG, "jino3");
                        if (webSocketResponses.size() > 0) {
                            for (OnWebSocketResponse wsr : webSocketResponses) {
                                wsr.onError(Constants.WEBSOCKET_ERROR_TIMEOUT, util.getString(getApplicationContext(), R.string
                                        .WS_ERR_100));
                            }
                        }
                    }
                });
                return;
            }
        };

        if (uri.getScheme().equals("wss")) {
            try {
//                SSLContext sslContext = SSLContext.getDefault();
                TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }

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

                    @Override
                    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert
                            .CertificateException {
                    }
                }
                };
                SSLContext sc = SSLContext.getInstance("TLS");
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
                webSocket.setWebSocketFactory(new DefaultSSLWebSocketClientFactory(sc));
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
//        webSocket.connectBlocking();
            webSocket.connect();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Websocket 종료.
     */
    public void webSocketClose() {
        if (webSocket != null) {
            bFromUser = true;
            webSocket.close();
            webSocket = null;
            webSocketResponses.clear();
            mAppConfig.setbWebSocketConn(false);
            stopSelf();
        }
    }

    /**
     * Websocket 연결 해제.
     */
    public void webSocketDisconnect() {
        if (webSocket != null) {
            bFromUser = true;
            webSocket.close();
            webSocket = null;
            mAppConfig.setbWebSocketConn(false);
        }
    }

    /**
     * device online/connect 상태 및 client 인증 결과는 리스너로 전달 없이 바로 값 저장.
     *
     * @param message WebSocket으로 전달받은 json 데이터 스트링
     *
     * @return 리스너로 결과 전달할지 말지 여부
     */
    private boolean checkBasicStatus(String message) {
        try {
            JSONObject jsonObject = new JSONObject(message);
            if (jsonObject.get(Constants.KEY_MESSAGE_TYPE).equals(Constants.WEBSOCKET_TYPE_CONN)) {
                mAppConfig.setsSelect_Homegrp_Homehub_Conn_State(util.null2string(jsonObject.get(Constants.KEY_DEVICE_CONN)));
                return true;
            } else if (jsonObject.get(Constants.KEY_MESSAGE_TYPE).equals(Constants.WEBSOCKET_TYPE_AUTH)) {
                mAppConfig.setbWebSocketAuth(util.null2string(jsonObject.get(Constants.KEY_RESULT_CODE)).equals("00") ? true : false);
                if (webSocketResponses.size() > 0) {
                    for (OnWebSocketResponse wsr : webSocketResponses) {
                        wsr.onResponse(message);
                    }
                }

                /*if ( iTempApiId > 0 && hmTempParam != null ) {
                    send(iTempApiId, hmTempParam);
                }*/
                return true;
            } /*else if ( jsonObject.get(Constants.KEY_MESSAGE_TYPE).equals(Constants.WEBSOCKET_TYPE_DEVICE_CONTENT) ) {
                String onlineStatus = ((JSONObject) jsonObject.get(Constants.KEY_CONTENT_VALUE)).getString(Constants
                .WEBSOCKET_KEY_ONLINE);
                if ( onlineStatus.equalsIgnoreCase("ON") ) {
                    mAppConfig.setsSelect_Homegrp_Homehub_Dongle_State("ON");
                    mAppConfig.setsSelect_Homegrp_Homehub_Conn_State("connect");
                } else {
                    mAppConfig.setsSelect_Homegrp_Homehub_Dongle_State("OFF");
                    mAppConfig.setsSelect_Homegrp_Homehub_Conn_State("connect");
                }
                return true;
            }*/
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return false;
    }

    private Map<String, String> getHeaders() {

        if (null != mAppConfig.getAuthorization()) {
            HashMap<String, String> headers = new HashMap<>();
            headers.put(Constants.KEY_AUTHORIZATION, mAppConfig.getAuthorization());
            return headers;
        }
        return null;
    }

    /**
     * WebSocket 연결 상태 체크.
     *
     * @return : true - 연결됨, false - 연결안됨
     */
    public boolean isWebSocketConnected() {
        if (webSocket != null) {
            // NOT_YET_CONNECTED, CONNECTING, OPEN, CLOSING, CLOSED;
            LogUtil.v(TAG, "webSocket.connect() : " + webSocket.getReadyState());
            if (WebSocket.READYSTATE.OPEN.equals(webSocket.getReadyState())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Websocket request send.
     *
     * @param apiId Request type id
     */
    public void send(int apiId) {
        if (!isWebSocketConnected()) {
            if (webSocketResponses.size() > 0) {
                for (OnWebSocketResponse wsr : webSocketResponses) {
                    wsr.onError(Constants.WEBSOCKET_ERROR_NOT_CONNECTED, "error");
                }
            }
            return;
        }

        WebSocketSender webSocketSender = new WebSocketSender(webSocket, handler);
        switch (apiId) {
            case Constants.WEBSOCKET_SEND_CONNECT_AUTH:
                webSocketSender.sendClientConnectAuth();
//                webSocketSender.sendWssTest();
                break;
        }
    }

    /**
     * Websocket request send.
     *
     * @param apiId    Request type id
     * @param hmParams Request parameter
     */
    public void send(int apiId, HashMap<String, Object> hmParams) {
        if (!isWebSocketConnected()) {
            webSocketConnect();
            if (webSocketResponses.size() > 0) {
                for (OnWebSocketResponse wsr : webSocketResponses) {
                    wsr.onError(Constants.WEBSOCKET_ERROR_NOT_CONNECTED, "error");
                }
            }
            LogUtil.d("jino", "aaaaa1");
            return;
        }
        LogUtil.d("jino", "aaaaa1");

        WebSocketSender webSocketSender = new WebSocketSender(webSocket, handler);
        switch (apiId) {
            case Constants.WEBSOCKET_SEND_AUTH:
                // 임시
                webSocketSender.sendClientAuth(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_AUTH_AUTO:
                // 임시
                webSocketSender.sendClientAuthAuto(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_DEVICE_REGISTER:
                webSocketSender.sendDeviceRegister(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_DEVICE_DELETE:
                webSocketSender.sendDeviceDelete(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_NODE_COMMAND:
                webSocketSender.sendNodeCommand(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_DEVICE_COMMAND:
                webSocketSender.sendDeviceCommand(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_NODE_RELOAD:
                webSocketSender.sendNodeReload(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_NODE_REGISTER:
                webSocketSender.sendNodeRegister(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_NODE_DELETE:
                webSocketSender.sendNodeDelete(hmParams);
                break;
            case Constants.WEBSOCKET_SEND_DEVICE_ALIVE:
                webSocketSender.sendDeviceAlive(hmParams);
                break;
        }

        if (iTempApiId != 0) {
            iTempApiId = 0;
            hmTempParam = null;
        }
    }

    /**
     * Websocket request send.
     *
     * @param apiId      Request type id
     * @param sJsonParam Request parameter
     */
    public void send(int apiId, String sJsonParam) {
        if (!isWebSocketConnected()) {
            if (webSocketResponses.size() > 0) {
                for (OnWebSocketResponse wsr : webSocketResponses) {
                    wsr.onError(Constants.WEBSOCKET_ERROR_NOT_CONNECTED, "error");
                }
            }
            return;
        }

        WebSocketSender webSocketSender = new WebSocketSender(webSocket, handler);
        switch (apiId) {
            case Constants.WEBSOCKET_SEND_CONNECT_AUTH:
                webSocketSender.sendClientConnectAuth();
//                webSocketSender.sendWssTest();
                break;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.i(TAG, "onStartCommand!!");
        if (!isWebSocketConnected() && !mAppConfig.getCtrtGroupId().equals("")) {
            webSocketConnect();
        }
        return START_STICKY;
    }

    /**
     * Websocket binder class.
     *
     * @see #binder
     */
    public class WebSocketBinder extends Binder {
        public WebSocketService getService() {
            return WebSocketService.this;
        }
    }
}
