package com.ntels.kneet.v3.common.websocket;

import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.http.AsyncHttpClient;
import com.koushikdutta.async.http.WebSocket;
import com.ntels.kneet.v3.R;
import com.ntels.kneet.v3.common.AppConfig;
import com.ntels.kneet.v3.common.Constants;
import com.ntels.kneet.v3.common.util.LogUtil;
import com.ntels.kneet.v3.common.util.Util;

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 java.util.ArrayList;
import java.util.HashMap;

/**
 * <pre>
 * 1. ClassName :   WebSocketService
 * 2. FileName  :   WebSocketService.java
 * 3. Package   :   com.ntels.kneet.v3.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_AUTH = 1;

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

    private static final int TIMEOUT = 20;
    private AppConfig appConfig;
    private WebSocket webSocket = null;
    private ArrayList<OnWebSocketResponse> webSocketResponses = new ArrayList<OnWebSocketResponse>();
    private final IBinder binder = new WebSocketBinder();
    private Util util = new Util();
    private int iTempApiId = 0;
    private HashMap<String, Object> hmTempParam;
    private boolean bFromUser = false;

    public WebSocketService() {
    }

    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_AUTH:
                    send(Constants.WEBSOCKET_SEND_AUTH);
                    break;
            }
            return false;
        }
    });

    @Override
    public void onCreate() {
        super.onCreate();
        appConfig = (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() && !appConfig.getSelect_Homegrp_Id().equals("")) {
            webSocketConnect();
        }
        return binder;
    }

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

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

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

        if (isWebSocketConnected()) {
            webSocketDisconnect();
        }

        String url = appConfig.getServicehubWebsocketUrl() + "/" + sServicehubAppId + "/" + sHomeGrpId;
        LogUtil.i(TAG, "WebSocket URL : " + url);

        AsyncHttpClient.getDefaultInstance().websocket(url, null, new AsyncHttpClient.WebSocketConnectCallback() {
            @Override
            public void onCompleted(Exception ex, WebSocket ws) {
                if (ex != null) {
                    ex.printStackTrace();
                    appConfig.setbWebSocketConn(false);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (webSocketResponses.size() > 0) {
                                for (OnWebSocketResponse wsr : webSocketResponses) {
                                    wsr.onError(Constants.WEBSOCKET_ERROR_TIMEOUT, util.getString(getApplicationContext(), R.string.WS_ERR_100));
                                }
                            }
                        }
                    });
                    return;
                }
                LogUtil.i(TAG, "onCompleted");
                appConfig.setbWebSocketConn(true);
                ws.setStringCallback(new WebSocket.StringCallback() {
                    @Override
                    public void onStringAvailable(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);
                                    }
                                }
                            }
                        });
                    }
                });
                ws.setClosedCallback(new CompletedCallback() {
                    @Override
                    public void onCompleted(Exception ex) {
                        LogUtil.i(TAG, "Websocket closed!!");
                        if (!bFromUser) {
                            webSocketConnect();
                        }
                        bFromUser = false;
                    }
                });
                webSocket = ws;
                // WebSocket Client 인증 수행
                handler.sendEmptyMessage(WEBSOCKET_CLIENT_AUTH);
            }
        });
    }

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

    /**
     * Websocket 연결 해제.
     */
    public void webSocketDisconnect() {
        if (webSocket != null) {
            bFromUser = true;
            webSocket.close();
            webSocket = null;
            appConfig.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.WEBSOCKET_KEY_MESSAGE_TYPE).equals(Constants.WEBSOCKET_TYPE_CONN)) {
                appConfig.setsSelect_Homegrp_Homehub_Conn_State(util.null2string(jsonObject.get(Constants.WEBSOCKET_KEY_DEVICE_CONN)));
                return true;
            } else if (jsonObject.get(Constants.WEBSOCKET_KEY_MESSAGE_TYPE).equals(Constants.WEBSOCKET_TYPE_AUTH)) {
                appConfig.setbWebSocketAuth(util.null2string(jsonObject.get(Constants.WEBSOCKET_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.WEBSOCKET_KEY_MESSAGE_TYPE).equals(Constants.WEBSOCKET_TYPE_DEVICE_CONTENT) ) {
                String onlineStatus = ((JSONObject) jsonObject.get(Constants.WEBSOCKET_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;
    }

    /**
     * WebSocket 연결 상태 체크.
     *
     * @return : true - 연결됨, false - 연결안됨
     */
    public boolean isWebSocketConnected() {
        if (webSocket != null) {
            LogUtil.i(TAG, "webSocket.isOpen() : " + webSocket.isOpen());
            return webSocket.isOpen();
        }
        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_AUTH:
                webSocketSender.sendClientAuth();
                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");
                }
            }
            return;
        }

        WebSocketSender webSocketSender = new WebSocketSender(webSocket, handler);
        switch (apiId) {
            case Constants.WEBSOCKET_SEND_AUTH:
                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_AUTH:
                break;
        }
    }

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

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