package com.ntels.onecable.view.things;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.nable.alllink.AllLinkCallIncomingListener;
import com.nable.alllink.AllLinkCallStateListener;
import com.nable.alllink.AllLinkClientSdk;
import com.nable.alllink.AllLinkConfiguration;
import com.nable.alllink.AllLinkDataListener;
import com.nable.alllink.AllLinkRecvMessageListener;
import com.nable.alllink.AllLinkRegisterStateListener;
import com.nable.alllink.AllLinkSendMessageResultListener;
import com.nable.nme.NableMediaEngine_Video;
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 java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.HashMap;

/**
 * <pre>
 * 1. ClassName :   ThingsCameraDetail
 * 2. FileName  :   ThingsCameraDetail.java
 * 3. Package   :   com.ntels.onecable.view.things
 * 4. Date      :   2016-11-28
 * 5. Author    :   aaron
 * 6. Comment   :   Initialize
 * </pre>
 */
public class ThingsCameraDetail extends Activity implements View.OnClickListener,
    View.OnTouchListener,
    AllLinkCallIncomingListener,
    AllLinkCallStateListener,
    AllLinkDataListener,
    AllLinkRecvMessageListener,
    AllLinkSendMessageResultListener,
    AllLinkRegisterStateListener {

  private static final String TAG = ThingsCameraDetail.class.getSimpleName();
  private static final String BUNDLE_PARTICIPANT = "participant";

  private Activity act;
  private AppConfig appConfig;
  private LinearLayout remoteLayout = null;
  private SurfaceView remoteSurfaceView = null;
  private ImageButton btnCameraUp = null;
  private ImageButton btnCameraLeft = null;
  private ImageButton btnCameraDown = null;
  private ImageButton btnCameraRight = null;
  private ImageButton btnClose = null;
  private RelativeLayout cameraControlLayout = null;

  public static String kAppLogTag = "AllLinkClientSDK";
  private static String kScheme = "sip";
  private static String kDomain = "";
  private static String kUserPassword = "";
  private static String kTurnServerIp = "";
  private static int kTurnServerPort = 0;
  private static String kSignalServerIp = "";
  private static String sUserNumber = "";
  private static String sCameraNumber = "";
  private static String sUseTls = "";

  public static CallbackHandler callbackHandler = null;
  public static ControlHandler controlHandler = null;
  private static AudioManager audioManager = null;
  private static int txCount = 0;
  private static int rxCount = 0;
  private int callId = -1;
  private int connectionId = -1;
  private boolean controlEnable = false;
  private boolean isDev = true;

  private static final int MSG_ON_LOG = 0;
  private static final int MSG_ON_CALL = 1;
  private static final int MSG_ON_INCOMING_CALL = 2;
  private static final int MSG_ON_CONNECTING = 3;
  private static final int MSG_ON_ACCEPTING = 4;
  private static final int MSG_ON_CONNECTED = 5;
  private static final int MSG_ON_DISCONNECTED = 6;
  private static final int MSG_ON_MEDIA_START = 7;
  private static final int MSG_ON_MEDIA_STOP = 8;
  private static final int MSG_ON_ERROR = 12;
  private static final int MSG_ON_RSSI = 13;
  private static final int MSG_SEND_DATA = 21;
  private static final int MSG_SEND_LOCALDESC = 22;
  private static final int MSG_ON_HANGUP = 33;
  private static final int MSG_ON_STOP = 34;
  private static final int MSG_ON_EXIT = 35;
  private static final int MSG_ON_CONTROL_HIDE = 40;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    act = ThingsCameraDetail.this;
    appConfig = (AppConfig) act.getApplicationContext();
    callbackHandler = new CallbackHandler();
    controlHandler = new ControlHandler();
    audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);

    View viewToLoad = LayoutInflater.from(act).inflate(R.layout.things_camera_detail, null);
    this.setContentView(viewToLoad);

    if (getIntent() != null) {
      Intent intent = getIntent();
      sUserNumber = intent.getStringExtra(Constants.EXTRA_CAMERA_USER_NUMBER);
      sCameraNumber = intent.getStringExtra(Constants.EXTRA_CAMERA_CAMERA_NUMBER);
    }

    init();
    viewInit();
    start();
  }

  @Override
  public void onBackPressed() {
    if (callId > -1) {
      hangup();
    } else {
      stop();
      super.onBackPressed();
    }
  }

  /**
   * IP Camera configuration data init.
   */
  private void init() {
    kSignalServerIp = appConfig.getsSipUrl();
    kTurnServerIp = appConfig.getsTurnUrl();
    kTurnServerPort = appConfig.getiTurnPort();
    kDomain = appConfig.getsVsaasDomain();
    kUserPassword = appConfig.getsSipPassword();
    sUseTls = kDomain.equals(Constants.VSAAS_SIP_DOMAIN_KNEET_V3_DEV) ? "0" : "1";
    LogUtil.i(TAG, "kSignalServerIp : " + kSignalServerIp);
    LogUtil.i(TAG, "kTurnServerIp : " + kTurnServerIp);
    LogUtil.i(TAG, "kTurnServerPort : " + kTurnServerPort);
    LogUtil.i(TAG, "kDomain : " + kDomain);
    LogUtil.i(TAG, "kUserPassword : " + kUserPassword);
    LogUtil.i(TAG, "isDev : " + isDev);
  }

  /**
   * 화면에 사용되는 View 초기화.
   */
  private void viewInit() {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

    btnCameraUp = (ImageButton) findViewById(R.id.btnCameraUp);
    btnCameraLeft = (ImageButton) findViewById(R.id.btnCameraLeft);
    btnCameraDown = (ImageButton) findViewById(R.id.btnCameraDown);
    btnCameraRight = (ImageButton) findViewById(R.id.btnCameraRight);
    btnClose = (ImageButton) findViewById(R.id.btnClose);
    btnCameraUp.setOnTouchListener(this);
    btnCameraLeft.setOnTouchListener(this);
    btnCameraDown.setOnTouchListener(this);
    btnCameraRight.setOnTouchListener(this);
    btnClose.setOnClickListener(this);
    remoteLayout = (LinearLayout) findViewById(R.id.llRemote);
    ViewGroup.LayoutParams params = remoteLayout.getLayoutParams();
    int width = displayMetrics.widthPixels;
    int height = displayMetrics.heightPixels;
    params.height = height;
    params.width = (height / 3) * 4;
    LogUtil.i(TAG, "width : " + width + ", height : " + height);
    LogUtil.i(TAG, "params.width : " + params.width + ", params.height : " + params.height);
    remoteLayout.setLayoutParams(params);
    remoteSurfaceView = (SurfaceView) NableMediaEngine_Video.getSingleton()
        .createRemoteView(this.getApplicationContext());
    remoteLayout.addView(remoteSurfaceView);
    remoteLayout.setOnClickListener(this);
    cameraControlLayout = (RelativeLayout) findViewById(R.id.rlCameraControl);
  }

  /**
   * 카메라 컨트롤 버튼들 Visibility.
   */
  private void displayController(boolean isDisplay) {
    Animation animation = null;
    if (isDisplay) {
      animation = new AlphaAnimation(0, 1);
      animation.setDuration(1000);
      cameraControlLayout.setVisibility(View.VISIBLE);
      btnClose.setVisibility(View.VISIBLE);
      cameraControlLayout.setAnimation(animation);
      btnClose.setAnimation(animation);
    } else {
      animation = new AlphaAnimation(1, 0);
      animation.setDuration(1000);
      cameraControlLayout.setVisibility(View.GONE);
      btnClose.setVisibility(View.GONE);
      cameraControlLayout.setAnimation(animation);
      btnClose.setAnimation(animation);
    }
  }

  /**
   * IP Camera start.
   */
  private boolean start() {

    addLog("start...");
    AllLinkClientSdk.getSingleton().setLogger(AllLinkClientSdk.LogLevel.Info, "");
    AllLinkClientSdk.getSingleton().setTurnServerAddr(kTurnServerIp, kTurnServerPort);
    AllLinkClientSdk.getSingleton().setStunServerAddr(kTurnServerIp, kTurnServerPort + 1);

    String userId = kScheme + ":" + sUserNumber + "@" + kDomain;
    LogUtil.i(TAG, "userId : " + userId);
    String registrarUri = kScheme + ":" + kDomain;

    HashMap<String, String> map = new HashMap<String, String>();
    map.put(AllLinkConfiguration.KeySipOutboundProxy, kSignalServerIp);
    map.put(AllLinkConfiguration.KeySipPublicUri, userId);
    map.put(AllLinkConfiguration.KeySipPrivateUri, sUserNumber);
    map.put(AllLinkConfiguration.KeySipPassword, kUserPassword);
    map.put(AllLinkConfiguration.KeySipHomeDomain, kDomain);
    map.put(AllLinkConfiguration.KeySipRegistrarUri, registrarUri);
    map.put(AllLinkConfiguration.KeySipUseTls, sUseTls);
    map.put(AllLinkConfiguration.KeySipMtu, "3000");
    map.put(AllLinkConfiguration.KeySipUserAgent, "nXerAllLinkClientSDK");
    map.put(AllLinkConfiguration.KeyLoggerLevel, "DEBUG");
    map.put(AllLinkConfiguration.KeyLoggerEnable, "1");
    map.put(AllLinkConfiguration.KeyLoggerEnableDebugWindow, "1");
    map.put(AllLinkConfiguration.KeySipLocalPort, "5080");

    AllLinkClientSdk.getSingleton().start(getLocalIpAddress());
    AllLinkClientSdk.getSingleton().setIMSConfiguration(map);
    AllLinkClientSdk.getSingleton().setCallIncomingListener((AllLinkCallIncomingListener) act);
    AllLinkClientSdk.getSingleton().setCallStateListener((AllLinkCallStateListener) act);
    AllLinkClientSdk.getSingleton().setRecvMessageListener((AllLinkRecvMessageListener) act);
    AllLinkClientSdk.getSingleton().doRegister((AllLinkRegisterStateListener) act);

    return true;
  }

  /**
   * IP Camera에 call 연결.
   */
  private void call() {
    if (callId != -1) {
      return;
    }
    if (sCameraNumber.length() == 0) {
      return;
    }
    String toId = kScheme + ":" + sCameraNumber + "@" + kDomain;
    LogUtil.i(TAG, "toId : " + toId);

    callId = AllLinkClientSdk.getSingleton().createCall();
    AllLinkClientSdk.getSingleton().setDataListener(callId, (AllLinkDataListener) act);

    //AllLinkClientSdk.getSingleton().setDataListener(callId, (AllLinkDataListener)mThis);
    if (!AllLinkClientSdk.getSingleton().dialUp(callId, toId,
        AllLinkClientSdk.CallType.AudioVideo)) {
      Log.e(kAppLogTag, "[TESTER] call fail");
      return;
    }
  }

  /**
   * IP Camera와 call 종료.
   */
  private void hangup() {
    if (callId > -1) {
      AllLinkClientSdk.getSingleton().hangUp(callId);
    }

    callId = -1;
  }

  /**
   * IP Camera 종료.
   */
  private void stop() {

    addLog("stop...");

    AllLinkClientSdk.getSingleton().deRegister();
    AllLinkClientSdk.getSingleton().stop();
  }

  /**
   * 전화 수신 리스너.
   */
  @Override
  public void onCallIncoming(String participantUri, int callId,
                             AllLinkClientSdk.CallType callType) {
  }

  /**
   * 통화 상태 리스너.
   */
  @Override
  public void onCallState(String participantUri, int callId, CallState callState,
                          AllLinkClientSdk.ErrorCode errorCode) {
    String participant = "Call1:";
    participant = participant + participantUri;
    LogUtil.i(TAG, "participant : " + participant);

    Message msg = callbackHandler.obtainMessage();
    Bundle args = new Bundle();
    args.putString(BUNDLE_PARTICIPANT, participant);

    switch (callState) {
      case Connected:
        LogUtil.i(TAG, "onCallState::Connected");
        audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
        msg.what = MSG_ON_CONNECTED;
        msg.setData(args);
        callbackHandler.sendMessage(msg);
        break;
      case Disconnected:
        LogUtil.i(TAG, "onCallState::Disconnected");
        msg.what = MSG_ON_DISCONNECTED;
        msg.setData(args);
        callbackHandler.sendMessage(msg);
        break;
      case MediaStart:
        LogUtil.i(TAG, "onCallState::MediaStart");
        msg.what = MSG_ON_MEDIA_START;
        msg.setData(args);
        callbackHandler.sendMessage(msg);
        break;
      case MediaStop:
        LogUtil.i(TAG, "onCallState::MediaStop");
        msg.what = MSG_ON_MEDIA_STOP;
        msg.setData(args);
        callbackHandler.sendMessage(msg);
        break;
      default:
        break;
    }
  }

  /**
   * 데이터 송.수신 리스너
   */
  @Override
  public void onData(int callId, AllLinkClientSdk.MediaDirection mediaDirection,
                     AllLinkClientSdk.MediaChannel mediaChannel, byte[] data, int length) {
    if (mediaDirection == AllLinkClientSdk.MediaDirection.Tx) {
      if (txCount >= 500) {
        String log = "";
        log = "onData(TxCount 500)";

        addLog(log);
        txCount = 0;
      } else {
        txCount++;
      }
    } else if (mediaDirection == AllLinkClientSdk.MediaDirection.Rx) {
      if (rxCount >= 500) {
        String log = "";
        log = "onData(RxCount 500)";

        addLog(log);
        rxCount = 0;
      } else {
        rxCount++;
      }
    }
  }

  /**
   * 메세지 리스너.
   */
  @Override
  public void onRecvMessage(String participantUri, String message) {

  }

  /**
   * 계정 등록 상태 리스너.
   */
  @Override
  public void onRegisterState(RegisterState registerState) {
    switch (registerState) {
      case Registered:
        LogUtil.i(TAG, "onRegisterState::Registered");
        callbackHandler.sendEmptyMessage(MSG_ON_CALL);
        break;
      case Deregistered:
        LogUtil.i(TAG, "onRegisterState::Deregistered");
        break;
      case DeregisteredAuthFail:
        LogUtil.i(TAG, "onRegisterState::DeregisteredAuthFail");
        break;
      case DeregisteredTimeout:
        LogUtil.i(TAG, "onRegisterState::DeregisteredTimeout");
        break;
      default:
        break;
    }
  }

  @Override
  public void onSendMessageResult(String participantUri, String message,
                                  AllLinkClientSdk.ErrorCode errorCode) {

  }

  private static final String BUNDLE_LOG = "log";

  /**
   * Log message 출력.
   *
   * @param log Log 출력 할 메세지
   */
  public void addLog(String log) {
    Message msg = callbackHandler.obtainMessage();
    Bundle args = new Bundle();
    args.putString(BUNDLE_LOG, log + "\n");
    msg.what = MSG_ON_LOG;
    msg.setData(args);
    callbackHandler.sendMessage(msg);
  }

  /**
   * 로컬 IP 가져오기.
   *
   * @return 로컬 IP
   */
  private String getLocalIpAddress() {
    try {
      Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
      while (en.hasMoreElements()) {
        NetworkInterface interf = en.nextElement();
        Enumeration<InetAddress> ips = interf.getInetAddresses();
        while (ips.hasMoreElements()) {
          InetAddress inetAddress = ips.nextElement();
          if (!inetAddress.isLoopbackAddress()) {
            String temp = inetAddress.getHostAddress();
            if (!temp.contains(":")) {
              if (!temp.contains("28.") && !temp.contains("49.") && !temp.contains("102.")) {
                return inetAddress.getHostAddress();
              }
            }
          }
        }
      }
    } catch (SocketException ex) {
      Log.e("Error", ex.toString());
    }

    return null;
  }

  @Override
  public void onClick(View view) {
    switch (view.getId()) {
      case R.id.btnClose:
        onBackPressed();
        break;
      case R.id.llRemote:
        displayController(true);
        controlEnable = false;
        controlHandler.sendEmptyMessageDelayed(MSG_ON_CONTROL_HIDE, 3000);
        break;
      default:
        break;
    }
  }

  @Override
  public boolean onTouch(View view, MotionEvent event) {
    int action = event.getAction();

    switch (view.getId()) {
      case R.id.btnCameraUp:
        if (callId <= -1) {
          return false;
        }

        if (action == MotionEvent.ACTION_DOWN) {
          String controlData = String.format("PTZ_C_START0.0f;0.5f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraUp.setImageResource(R.drawable.img_btn_camera_arrow_up_press);
          callbackHandler.removeCallbacksAndMessages(null);
          controlEnable = true;
        } else if (action == MotionEvent.ACTION_UP) {
          String controlData = String.format("PTZ_C_STOP0.0f;0.0f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraUp.setImageResource(R.drawable.img_btn_camera_arrow_up);
          controlEnable = false;
        }
        break;
      case R.id.btnCameraLeft:
        if (callId <= -1) {
          return false;
        }

        if (action == MotionEvent.ACTION_UP) {
          // PTZ Relative mode
          String controlData = String.format("PTZ_R-0.5f;0.0f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraLeft.setImageResource(R.drawable.img_btn_camera_arrow_left);
          controlEnable = false;
        }
        if (action == MotionEvent.ACTION_DOWN) {
          // PTZ Continuous mode start
          String controlData = String.format("PTZ_C_START-0.5f;0.0f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraLeft.setImageResource(R.drawable.img_btn_camera_arrow_left_press);
          controlEnable = true;
        } else if (action == MotionEvent.ACTION_UP) {
          // PTZ Continuous mode stop
          String controlData = String.format("PTZ_C_STOP0.0f;0.0f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraLeft.setImageResource(R.drawable.img_btn_camera_arrow_left);
          controlEnable = false;
        }
        break;
      case R.id.btnCameraDown:
        if (callId <= -1) {
          return false;
        }

        if (action == MotionEvent.ACTION_DOWN) {
          String controlData = String.format("PTZ_C_START0.0f;-0.5f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraDown.setImageResource(R.drawable.img_btn_camera_arrow_down_press);
          controlEnable = true;
        } else if (action == MotionEvent.ACTION_UP) {
          String controlData = String.format("PTZ_C_STOP0.0f;0.0f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraDown.setImageResource(R.drawable.img_btn_camera_arrow_down);
          controlEnable = false;
        }
        break;
      case R.id.btnCameraRight:
        if (callId <= -1) {
          return false;
        }

        if (action == MotionEvent.ACTION_DOWN) {
          String controlData = String.format("PTZ_C_START0.5f;0.0f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraRight.setImageResource(R.drawable.img_btn_camera_arrow_right_press);
          controlEnable = true;
        } else if (action == MotionEvent.ACTION_UP) {
          String controlData = String.format("PTZ_C_STOP0.0f;0.0f;0.0f;");
          AllLinkClientSdk.getSingleton().putData(callId, AllLinkClientSdk.MediaChannel.CustomData,
              controlData.getBytes());
          btnCameraRight.setImageResource(R.drawable.img_btn_camera_arrow_right);
          controlEnable = false;
        }
        break;
      default:
        break;
    }
    return true;
  }

  /**
   * <pre>
   * 1. ClassName :   CallbackHandler
   * 2. FileName  :   ThingsCameraDetail.java
   * 3. Package   :   com.ntels.onecable.view.things
   * 4. Date      :   2016-11-28
   * 5. Author    :   aaron
   * 6. Comment   :   Initialize
   * </pre>
   */
  class CallbackHandler extends Handler {
    public void handleMessage(Message msg) {
      super.handleMessage(msg);

      Bundle args = msg.getData();
      switch (msg.what) {
        case MSG_ON_RSSI: {
          //mTextViewRssi.setText(args.getString(BUNDLE_RSSI));
        }
        break;
        case MSG_ON_LOG: {
          LogUtil.i(TAG, "LOG : " + args.getString(BUNDLE_LOG));
        }
        break;
        case MSG_ON_CALL:
          call();
          break;
        case MSG_ON_INCOMING_CALL: {
        }
        break;
        case MSG_ON_CONNECTING: {

        }
        break;
        case MSG_ON_ACCEPTING: {

        }
        break;
        case MSG_ON_CONNECTED: {
          //mScrollViewLog.setVisibility(View.GONE);
        }
        break;
        case MSG_ON_DISCONNECTED: {
        }
        break;
        case MSG_ON_MEDIA_START: {
          AllLinkClientSdk.getSingleton().mediaStart(callId, true, true);
          displayController(true);
          controlHandler.sendEmptyMessageDelayed(MSG_ON_CONTROL_HIDE, 3000);
        }
        break;
        case MSG_ON_MEDIA_STOP: {
          AllLinkClientSdk.getSingleton().mediaStop(callId);
          audioManager.setMode(AudioManager.MODE_NORMAL);
          stop();
          finish();
        }
        break;
        case MSG_ON_ERROR:
          break;
        case MSG_SEND_DATA: {
          if (connectionId != -1) {
            // TODO: 2016-11-28 : 에러 처리?
          }
        }
        break;
        case MSG_SEND_LOCALDESC: {

        }
        break;
        case MSG_ON_HANGUP:
          hangup();
          break;
        case MSG_ON_STOP:
          stop();
          finish();
          break;
        case MSG_ON_EXIT:
          finish();
          break;
        default:
          break;
      }
    }
  }

  /**
   * <pre>
   * 1. ClassName :   ControlHandler
   * 2. FileName  :   ThingsCameraDetail.java
   * 3. Package   :   com.ntels.onecable.view.things
   * 4. Date      :   2016-11-28
   * 5. Author    :   aaron
   * 6. Comment   :   Initialize
   * </pre>
   */
  class ControlHandler extends Handler {
    public void handleMessage(Message msg) {
      super.handleMessage(msg);

      Bundle args = msg.getData();
      switch (msg.what) {
        case MSG_ON_CONTROL_HIDE:
          if (!controlEnable) {
            displayController(false);
          } else {
            sendEmptyMessageDelayed(MSG_ON_CONTROL_HIDE, 3000);
          }
          break;
        default:
          break;
      }
    }
  }
}
