package net.openmob.mobileimsdk.android.core;

import android.os.Handler;
import android.util.Log;

import com.renrenliao.im.utils.MDateUtils;

import net.openmob.mobileimsdk.android.ClientCoreSDK;
import net.openmob.mobileimsdk.android.utils.MBSimpleTimer;
import net.openmob.mobileimsdk.android.utils.MBThreadPoolExecutor;

import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.atomic.AtomicLong;


public class KeepAliveDaemon {
    private static final String TAG = KeepAliveDaemon.class.getSimpleName();

    private static KeepAliveDaemon instance = null;

    public static int NETWORK_CONNECTION_TIME_OUT = 16000;

    public static int KEEP_ALIVE_INTERVAL = 3000;

    public static int NETWORK_CONNECTION_TIME_OUT_CHECK_INTERVAL = 2000;

    private boolean keepAliveRunning = false;

    private final AtomicLong lastGetKeepAliveResponseFromServerTimstamp = new AtomicLong(0L);

    private Observer networkConnectionLostObserver = null;

    private Handler keepAliveHandler = null;

    private Runnable keepAliveRunnable = null;

    private volatile boolean keepAliveTaskExcuting = false;

    private boolean keepAliveWillStop = false;

    private MBSimpleTimer keepAliveTimeoutTimer = null;

    private boolean init = false;

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

    private KeepAliveDaemon() {
        init();
    }

    private void init() {
        if (!this.init) {
            this.keepAliveHandler = new Handler();
            this.keepAliveRunnable = (() -> {
                if (!this.keepAliveTaskExcuting) {
                    MBThreadPoolExecutor.runInBackground(() -> {
                        int code = this.doKeepAlive();
                        MBThreadPoolExecutor.runOnMainThread(()->{
                             this.onKeepAlive(code);
                        });
                    });
                }
            });
            this.keepAliveTimeoutTimer = new MBSimpleTimer(NETWORK_CONNECTION_TIME_OUT_CHECK_INTERVAL) {
                protected void doAction() {
                    if (ClientCoreSDK.DEBUG) {
                        Log.i(KeepAliveDaemon.TAG, "【IMCORE-TCP】心跳[超时检查]线程执行中...");
                    }
                    KeepAliveDaemon.this.doTimeoutCheck();
                }
            };
            this.keepAliveTimeoutTimer.init();
            this.init = true;
        }
    }

    private int doKeepAlive() {
        this.keepAliveTaskExcuting = true;
        if (ClientCoreSDK.DEBUG) {
            Log.i(TAG, "【IMCORE-UDP】心跳包[发送]线程执行中...");
        }
        int code = LocalDataSender.getInstance().sendKeepAlive();
        Log.i(TAG, "【IMCORE-UDP】心跳包[发送] code:"+code);
        return code;

    }

    private void onKeepAlive(int code) {
        boolean isInitialedForKeepAlive = this.isInitialedForKeepAlive();
        if (isInitialedForKeepAlive) {
            this.lastGetKeepAliveResponseFromServerTimstamp.set(MDateUtils.currentTimMillis());
        }

        this.keepAliveTaskExcuting = false;
        if (!this.keepAliveWillStop) {
            this.keepAliveHandler.postDelayed(this.keepAliveRunnable, (long)KEEP_ALIVE_INTERVAL);
        }
    }

    private void doTimeoutCheck() {
        boolean isInitialedForKeepAlive = this.isInitialedForKeepAlive();
        if (!isInitialedForKeepAlive) {
            long now = MDateUtils.currentTimMillis();
            if (ClientCoreSDK.DEBUG) {
                Log.i(TAG, ">>>> t1=" + now + ", t2=" + this.lastGetKeepAliveResponseFromServerTimstamp + " -> 差：" + (now - this.lastGetKeepAliveResponseFromServerTimstamp.longValue()));
            }

            if (now - this.lastGetKeepAliveResponseFromServerTimstamp.longValue() >= (long)NETWORK_CONNECTION_TIME_OUT) {
                if (ClientCoreSDK.DEBUG) {
                    Log.i(TAG, "【IMCORE-TCP】心跳机制已判定网络断开，将进入断网通知和重连处理逻辑 ...");
                }

                this.notifyConnectionLost();
                this.keepAliveWillStop = true;
            }
        }

    }

    private boolean isInitialedForKeepAlive() {
        return this.lastGetKeepAliveResponseFromServerTimstamp.longValue() == 0L;
    }

    public void notifyConnectionLost() {
        this.stop();
        if (this.networkConnectionLostObserver != null) {
            this.networkConnectionLostObserver.update((Observable)null, (Object)null);
        }
    }

    public void stop() {
        this.keepAliveTimeoutTimer.stop();
        this.keepAliveHandler.removeCallbacks(this.keepAliveRunnable);
        this.keepAliveRunning = false;
        this.keepAliveWillStop = false;
        this.lastGetKeepAliveResponseFromServerTimstamp.set(0L);
    }

    public void start(boolean immediately) {
        this.stop();
        this.keepAliveHandler.postDelayed(this.keepAliveRunnable, immediately ? 0L : (long)KEEP_ALIVE_INTERVAL);
        this.keepAliveRunning = true;
        this.keepAliveWillStop = false;
        this.keepAliveTimeoutTimer.start(immediately);
    }

    public boolean isKeepAliveRunning() {
        return this.keepAliveRunning;
    }

    public boolean isInit() {
        return this.init;
    }

    public void updateGetKeepAliveResponseFromServerTimstamp() {
        this.lastGetKeepAliveResponseFromServerTimstamp.set(MDateUtils.currentTimMillis());
    }

    public void setNetworkConnectionLostObserver(Observer networkConnectionLostObserver) {
        this.networkConnectionLostObserver = networkConnectionLostObserver;
    }


}
