package kr.co.icontrols.V40IFCCD;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.Vibrator;
import android.util.Log;
import android.view.Surface;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Date;

import kr.co.icontrols.ilp300_700.global.DataClasses;
import milsip.stack.sip.BSSVideo;
import milsip.stack.sip.CMCore;
import milsip.stack.sip.SIPStack;
import milsip.stack.sip.videoEnc;
import cm.sip.hns.SIPController;
import cm.sip.hns.SIPSound;
import cm.sip.hns.SIPStun;
import kr.co.icontrols.ilp300_700.MainActivity;
import kr.co.icontrols.ilp300_700.call.CallManager.CALLEVENTTYPE;
import kr.co.icontrols.ilp300_700.call.CallManager.CALLTYPE;

public class V40IFCCD {

    Context mContext;
    protected static Context callContext = null;

    private static final String TAG = "V40IFCCD";

    static {
        System.loadLibrary("jniV40if_ccd");
    }

    public V40IFCCD (Context context) {
        try {
            Log.d(TAG, "[V40IFCCD] ========== START ==========");
            mContext = context;
            if (!SIPStack.bActive) {
                // SIPStun서비스가 active 상태일때 서비스 생성을 중복수행않음.
                doBindService();
                callContext = mContext;
                CMCore.bDialerActive = true;
            }
        } catch (Exception e) {
            Log.e(TAG, "[Exception] V40IFCCD(Context context)");
            e.printStackTrace();
        }
    }

    public void releaseV40IFCCD() {
        try {
            Log.d(TAG, "[releaseV40IFCCD] ========== START ==========");
            doUnbindService();
        } catch (Exception e) {
            Log.e(TAG, "[Exception] releaseV40IFCCD()");
            e.printStackTrace();
        }
    }

    // JNI
    public native void setCameraSelect();

    public native int setCameraSurface(Surface surface, int width, int height, int bitrate, int framerate);

    public native int encoderStartOperation();

    public native int encoderStopOperation();

    public native int decoderStartOperation();

    public native int decoderStopOperation();

    public native void sendDataToDecoder(byte[] data, int headerLen, int frameLen, int timeStamp);

    public native int getYVU420pFrame(byte[] frameBuffer, int len, int[] width, int[] height);

    private static boolean bCheckIFrame = false;

    public void setCheckIFrame(boolean bSet) {
        try {
            Log.d(TAG, "[setCheckIFrame] bSet [" + bSet + "]");
            bCheckIFrame = bSet;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] setCheckIFrame(boolean bSet)");
            e.printStackTrace();
        }
    }

    public static int recvFramecallback(byte[] encodedFrame, int picType, int headerLen, int frameLen, int timeStamp) {
        try {
            //Log.d(TAG, "[recvFrame_callback] !! picType [" + picType + "], headerLen [" + headerLen + "], frameLen [" + frameLen + "], timeStamp [" + timeStamp + "]");
            if (frameLen > 50000) {
                Log.e(TAG, "[recvFrame_callback] !! Buffer Overflow !! picType [" + picType + "], headerLen [" + headerLen + "], frameLen [" + frameLen + "], timeStamp [" + timeStamp + "]");
                return 0;
            }

            String frameType = "";
            switch (picType) {
                case 0:   // I-Frame
                    frameType = "I-Frame";
                    bCheckIFrame = false;
                    break;

                case 1:   // P-Frame
                    frameType = "P-Frame";
                    break;

                case 2:   // B-Frame
                    frameType = "B-Frame";
                    break;

                case 3:   // Invalid-Frame
                default:
                    break;
            }

            if (bCheckIFrame) {
                Log.d(TAG, "[recvFrame_callback] bCheckIFrame [" + bCheckIFrame + "]");
            }

//            if (!bCheckIFrame) {
                if (videoRtpManager != null && videoRtpManager.videoenc.bReceiveRunning) {
                    //Log.d(TAG, "===== [recvFrame_callback.writeHNSRTPPacket] =====");
                    videoRtpManager.writeHNSRTPPacket(encodedFrame, frameLen, frameLen, timeStamp, (picType == 0));
                }
//            }
        } catch (Exception e) {
            Log.e(TAG, "[Exception] recvFrame_callback(byte[] encodedFrame, int picType, int headerLen, int frameLen, int timeStamp)");
            e.printStackTrace();
        }
        return 0;
    }

    public int[] convertYVU420PtoRGB8888(byte[] ahData, int nWidth, int nHeight) {
        try {
            int nSize = nWidth * nHeight;
            int nOffSet = nSize;
            int[] anPixels = new int[nSize];
            int u, v, y1, y2, y3, y4;
            Log.d(TAG, "[convertYUV420PtoRGB888] nSize: " + nSize);

            for (int i = 0, k = 0; i < nSize; i += 2, k += 1) {
                y1 = ahData[i] & 0xFF;
                y2 = ahData[i + 1] & 0xFF;
                y3 = ahData[nWidth + i] & 0xFF;
                y4 = ahData[nWidth + i + 1] & 0xFF;

//                v = ahData[nOffSet + k] & 0xFF;
//                u = ahData[nOffSet + k + nSize / 4] & 0xFF;
                // V40 HW Decoder 영상 포맷에서 U와 V 영역이 swap 되어있음 (다담 윤덕배 팀장)
                v = ahData[nOffSet + k + nSize / 4] & 0xFF;
                u = ahData[nOffSet + k] & 0xFF;
                u = u - 128;
                v = v - 128;

                anPixels[i] = convertYUVtoRGB(y1, u, v);
                anPixels[i + 1] = convertYUVtoRGB(y2, u, v);
                anPixels[nWidth + i] = convertYUVtoRGB(y3, u, v);
                anPixels[nWidth + i + 1] = convertYUVtoRGB(y4, u, v);

                if (i != 0 && (i + 2) % nWidth == 0) i += nWidth;
            }
            return anPixels;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] convertYVU420PtoRGB8888(byte[] ahData, int nWidth, int nHeight)");
            e.printStackTrace();
            return null;
        }
    }

    private int convertYUVtoRGB(int Y, int U, int V) {
        try {
            int R, G, B;

            R = Y + (int) 1.402f * V;
            G = Y - (int) (0.344f * U + 0.714f * V);
            B = Y + (int) 1.772f * U;
            R = R > 255 ? 255 : R < 0 ? 0 : R;
            G = G > 255 ? 255 : G < 0 ? 0 : G;
            B = B > 255 ? 255 : B < 0 ? 0 : B;

//            Log.d(TAG, "[convertYUVtoRGB] R: " + R + ", G: " + G + ", B: " + B);
            return 0xFF000000 | (B << 16) | (G << 8) | R;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] convertYUVtoRGB(int Y, int U, int V)");
            e.printStackTrace();
            return 0;
        }
    }


    // RTP 전송
    public interface SIPEventListener {
        public void onSIPEvent(CALLEVENTTYPE eEventType, CALLTYPE eCallType, String strRemoteIP, String strRemoteSIPID, String strMessage);

        public void onVideoSet(boolean bSet, String strRemoteSIPID);

        public void onShowRemoteVideo(boolean bShow);
    }

    private SIPEventListener mSIPEventListener;

    public void setSIPEventListener(SIPEventListener SIPEV) {
        mSIPEventListener = SIPEV;
    }

    final Messenger mMessenger = new Messenger(new CmPiper());
    boolean bIsBound = false;
    Messenger mService = null;

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            Log.d(TAG, "Messenger ServiceConnection Attached!");
            try {
                Message msg = Message.obtain(null, CMCore.BSSSIPStun_ICONTROLS_REGISTER);
                msg.replyTo = mMessenger;
                mService.send(msg);
            } catch (Exception e) {
                Log.e(TAG, "[Exception] onServiceConnected(ComponentName className, IBinder service)");
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mService = null;
            Log.d(TAG, "Messenger ServiceConnection Disconnected!");
        }
    };

    void doBindService() {
        mContext.bindService(new Intent(mContext, SIPStun.class), mConnection, Context.BIND_AUTO_CREATE);
        bIsBound = true;
        Log.d(TAG, "===== SERVICE BIND NEWLY =====");
    }

    void doUnbindService() {
        bIsBound = true;
        if (bIsBound) {
            // If we have received the service, and hence registered with it, then now is the time to unregister.
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null, CMCore.BSSSIPStun_ICONTROLS_UNREGISTER);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                } catch (RemoteException e) {
                    Log.e(TAG, "[RemoteException] doUnbindService(): " + e);
                    e.printStackTrace();
                }
            }
            // Detach our existing connection.
            mContext.unbindService(mConnection);
            bIsBound = false;
            Log.d(TAG, "===== Service Unbinding =====");
        }
    }

    private boolean bEncoderStarted = false;
    private boolean bDecoderStarted = false;

    static VideoRtpManager videoRtpManager = null;

    protected static int RTP_PORT = 3002;

    class VideoRtpManager extends Thread {
        protected DatagramSocket theSocket = null;
        protected boolean bOpenCover = false;
        videoEnc videoenc = new videoEnc();
        private DatagramSocket runningSocket = null;
        private byte[] sps;

        @Override
        public void run() {
            Log.d(TAG, "[VideoRtpManager] VideoRtpManager started!");
            try {
                if(MainActivity.m_bCallType == 1)
                    RTP_PORT = 50061;
                else if(MainActivity.m_bCallType == 2)
                    RTP_PORT = 50063;

                //RTP_PORT = SIPController.sipCall.sdp.audioM.mediaPort + 2;
                Log.d(TAG, "[VideoRtpManager] Video Port [" + RTP_PORT + "]");
                theSocket = new DatagramSocket(RTP_PORT);
                //theSocket.setSoTimeout(10); 10 ms이내에 데이터가 없으면 소켓을 닫음.
                theSocket.setReuseAddress(true);
                SIPController.dp = new DatagramPacket(videoenc.buffer, videoenc.buffer.length);

            } catch (SocketException se) {
                System.err.println(se);
                return;
            } catch (NullPointerException ne) {
                System.err.println(ne);
                return;
            }

            videoenc.init();
            videoenc.bReceiveRunning = true;
            runningSocket = theSocket;
            videoenc.bService = true;
            bOpenCover = false;
            Log.d(TAG, "[VideoRtpManager] VideoRtpManager While");
            while (videoenc.bReceiveRunning && runningSocket != null) {
//                if (videoRtpManager == null) break;
//                if (videoRtpManager.isInterrupted()) break;
//                try {
//                    if (videoenc.buffer == null) break;
//                    if (SIPController.dp == null) break;
//
//                    SIPController.dp.setData(videoenc.buffer);
//                    SIPController.dp.setLength(videoenc.RTP_VIDEO_SIZE);
//                    runningSocket.receive(SIPController.dp);
//                    if (SIPController.dp != null && SIPController.dp.getLength() > 0) {
//                        if (!videoenc.parseHNSRTPHeader()) continue;
//                        if (videoenc.PARSEpayload != SIPStack.SIP_CODEC_H264) {
//                            continue;
//                        }
//
//                        if (SIPController.forward.bForward) {
//                            if (videoenc.PARSEmark == 1 && SIPController.sipCall != null && SIPController.sipCall.flag && !SIPController.sipCall.bForwardRequest) {
//                                // recording forward picture
//                                byte[] field = new byte[4];
//                                System.arraycopy(videoenc.buffer, 13, field, 0, 4);
//                                int headerLen = getIntByByteArr(field);
//                                System.arraycopy(videoenc.buffer, 17, field, 0, 4);
//                                int frameLen = getIntByByteArr(field);
//                                System.arraycopy(videoenc.buffer, 21, field, 0, 4);
//                                int timeStamp = getIntByByteArr(field);
//                                int frameSize = (frameLen > 0) ? frameLen : headerLen;
//                                if (SIPController.dp.getLength() - 25 >= frameSize && bDecoderStarted) {
//                                    System.arraycopy(videoenc.buffer, 25, videoenc.data, 0, frameSize);
//                                    forwardNotifyToStun();
//                                }
//                            }
//                            continue;
//                        }
//
//                        if (!bOpenCover) {
//                            if (!videoenc.bIframeset) {
//                                if (videoenc.PARSEmark == 1) {
//                                    videoenc.bIframeset = true;
//                                    if (BSSVideo.bProgressvideooffer) {
//                                        bOpenCover = true;
//                                        BSSVideo.incurtenLength = 3000;   //Milli seconds
//                                        BSSVideo.outcurtenLength = 100;   //Milli seconds
//                                    }
//                                }
//                            } else if (!videoenc.bIframedoubleset) {
//                                if (videoenc.PARSEmark == 1) {
//                                    videoenc.bIframedoubleset = true;
//                                    bOpenCover = true;
//                                    BSSVideo.incurtenLength = 100;   //Milli seconds
//                                    BSSVideo.outcurtenLength = 100;   //Milli seconds
//                                }
//                            }
//                        }
//
//                        byte[] field = new byte[4];
//                        System.arraycopy(videoenc.buffer, 13, field, 0, 4);
//                        int headerLen = getIntByByteArr(field);
//                        System.arraycopy(videoenc.buffer, 17, field, 0, 4);
//                        int frameLen = getIntByByteArr(field);
//                        System.arraycopy(videoenc.buffer, 21, field, 0, 4);
//                        int timeStamp = getIntByByteArr(field);
//                        int frameSize = (frameLen > 0) ? frameLen : headerLen;
//                        if (SIPController.dp.getLength() - 25 >= frameSize && bDecoderStarted) {
//                            System.arraycopy(videoenc.buffer, 25, videoenc.data, 0, frameSize);
//                            MainActivity.mCallController.mVideoJNI.sendDataToDecoder(videoenc.data, headerLen, frameLen, timeStamp);
//
//                            if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_IN) {
//                                if (SIPController.sipCall.hnsOriginatorServiceType == BSSVideo.HNS_SERVICE_NEIGHBOR) {
//                                    if (!videoenc.bView) {
//                                        if (!BSSVideo.bCover) {
//                                            BSSVideo.bRemoteview = false;
//                                            BSSVideo.dateVideoincoming = new Date();
//                                            attachIncomingcover();
//                                            BSSVideo.bCover = true;
//                                        }
//                                        continue;
//                                    } else if (bOpenCover && BSSVideo.bCover) {
//                                        Log.e(TAG, "========== 1 ==========");
//                                        removeIncomingcover();
//                                    }
//                                }
//                            } else {
//                                // SIP_CALLDIRECTION_OUT
//                                if (SIPController.sipCall.hnsReceiverServiceType == BSSVideo.HNS_SERVICE_NEIGHBOR) {
//                                    if (!videoenc.bView) {
//                                        if (!BSSVideo.bCover) {
//                                            BSSVideo.bRemoteview = false;
//                                            BSSVideo.dateVideoincoming = new Date();
//                                            attachIncomingcover();
//                                            BSSVideo.bCover = true;
//                                        }
//                                        continue;
//                                    } else if (bOpenCover && BSSVideo.bCover) {
//                                        Log.e(TAG, "========== 2 ==========");
//                                        removeIncomingcover();
//                                    }
//                                }
//                            }
//
//                            if (!BSSVideo.bRemoteview && bOpenCover) {
//                                BSSVideo.bRemoteview = true;
//                                BSSVideo.dateVideoincoming = new Date();
//                            }
//
//                            if (BSSVideo.bCover) {
//                                if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_IN) {
//                                    if (bOpenCover && ((new Date()).getTime() - BSSVideo.dateVideoincoming.getTime()) > BSSVideo.incurtenLength) {
//                                        Log.e(TAG, "========== 3 ==========");
//                                        removeIncomingcover();
//                                    }
//                                } else {
//                                    if (bOpenCover && ((new Date()).getTime() - BSSVideo.dateVideoincoming.getTime()) > BSSVideo.outcurtenLength) {
//                                        Log.e(TAG, "========== 4 ==========");
//                                        removeIncomingcover();
//                                    }
//                                }
//                            }
//                        }
//                    }
//                } catch (SocketException se) {
//                    Log.e(TAG, "[SocketException] VideoRtpManager().run()");
//                    se.printStackTrace();
//                    break;
//
//
//                } catch (Exception e) {
//                    Log.e(TAG, "[Exception] VideoRtpManager().run()");
//                    e.printStackTrace();
//                    break;
//                }
            } //while

            Log.d(TAG, "RTP Thread terminated.");
            if (runningSocket != null) {
                if (runningSocket == theSocket) theSocket = null;
                runningSocket.close();
                runningSocket = null;
                theSocket = null;
            }
            videoenc.bReceiveRunning = false;
            videoRtpManager = null;
            theSocket = null;
            videoenc.bService = false;
        }

        private int getIntByByteArr(final byte[] byteArr) {
            int byte_0 = (((int) byteArr[0]) << 24) & 0xFF000000;
            int byte_1 = (((int) byteArr[1]) << 16) & 0x00FF0000;
            int byte_2 = (((int) byteArr[2]) << 8) & 0x0000FF00;
            int byte_3 = (((int) byteArr[3]) << 0) & 0x000000FF;

            return (byte_0 | byte_1 | byte_2 | byte_3);
        }

        public byte[] getByteArrByInt (int intVal) {
            byte[] arrByte = new byte[4];

            arrByte[0] = (byte) ((intVal >>> 24) & 0xFF);
            arrByte[1] = (byte) ((intVal >>> 16) & 0xFF);
            arrByte[2] = (byte) ((intVal >>> 8) & 0xFF);
            arrByte[3] = (byte) ((intVal >>> 0) & 0xFF);

            return arrByte;
        }

        public int writeHNSRTPPacket(final byte[] encodedFrame, final int headerLen, final int frameLen, final int timeStamp, final boolean bIframe) {

            Log.d(TAG, "writeHNSRTPPacket [start]");
//            if (SIPController.sipCall == null || !SIPController.sipCall.flag) return 0;
//            else {
//                //세대기이면서 상대가 세대기가 아니면 영상제공차단
//                if (BSSVideo.HNS_SERVICE_ORIGINATOR_SELECTED == BSSVideo.HNS_SERVICE_NEIGHBOR) {
//                    if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_IN) {
//                        if (SIPController.sipCall.hnsOriginatorServiceType != BSSVideo.HNS_SERVICE_NEIGHBOR)
//                            return 0;
//                    } else if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_OUT) {
//                        if (SIPController.sipCall.hnsReceiverServiceType != BSSVideo.HNS_SERVICE_NEIGHBOR)
//                            return 0;
//                    }
//                }
//            }

//            if (SIPController.audioRTPManager.rtpCore == null) return 0;

            //videoenc.remoteIp = SIPController.audioRTPManager.rtpCore.remoteMediaIp;
            videoenc.remoteIp = DataClasses.RemoteIDInfo.strIP;
            //videoenc.remotePort = SIPController.audioRTPManager.rtpCore.remoteMediaPort + 2;
            if(MainActivity.m_bCallType == 1)
                videoenc.remotePort = 50061;
            else if(MainActivity.m_bCallType == 2)
                videoenc.remotePort = 50063;

            int sent = 0;
            Log.d(TAG, "writeHNSRTPPacket [start] 1 videoenc.remoteIp[" + videoenc.remoteIp + "] ");
            if (videoenc.remoteIp == null || videoenc.remoteIp.length() <= 0) return 0;
            if (encodedFrame == null || frameLen <= 0) return 0;
            if (videoenc.remotePort <= 0) return 0;
            if (runningSocket == null) return 0;
            try {
                Log.d(TAG, "writeHNSRTPPacket [send] 1");
                InetAddress ia = InetAddress.getByName(videoenc.remoteIp);
                int dataSize = 13;
                if (ia != null) {
                    int frameSize = (frameLen > 0) ? frameLen : headerLen;
                    byte[] headerByte = getByteArrByInt(headerLen);
                    byte[] frameByte = getByteArrByInt(frameLen);
                    byte[] timeByte = getByteArrByInt(timeStamp);

                    System.arraycopy(headerByte, 0, videoenc.rtpVideoBuffer, dataSize, 4);
                    dataSize += 4;
                    System.arraycopy(frameByte, 0, videoenc.rtpVideoBuffer, dataSize, 4);
                    dataSize += 4;
                    System.arraycopy(timeByte, 0, videoenc.rtpVideoBuffer, dataSize, 4);
                    dataSize += 4;
                    System.arraycopy(encodedFrame, 0, videoenc.rtpVideoBuffer, dataSize, frameSize);
                    dataSize += frameSize;

                    int packetsize = 0;

                    try {
                        packetsize = dataSize;
                        videoenc.RTPtimestamp = timeStamp;
                        if (videoenc.rtpVideoBuffer != null && packetsize > 0) {
                            if (bIframe) videoenc.RTPmark = 1;
                            else videoenc.RTPmark = 0;
                            videoenc.RTPsequence++;
                            if (BSSVideo.HNS_SERVICE_ORIGINATOR_SELECTED == BSSVideo.HNS_SERVICE_GUARD || BSSVideo.HNS_SERVICE_ORIGINATOR_SELECTED == BSSVideo.HNS_SERVICE_LOBBY) {
                                videoenc.encodeHNSRtpHeader(SIPStack.SIP_CODEC_H264, true);
                            } else {
                                videoenc.encodeHNSRtpHeader(SIPStack.SIP_CODEC_H264, BSSVideo.bViewme);
                            }
                        }
                    } catch (Exception e) {
                        System.err.println("I/O problems: " + e);
                        SIPStack.exceptionCountAtCurrentCall++;
                        return 0;
                    }
                    Log.d(TAG, "writeHNSRTPPacket [send] 2");

                    byte[] byteEncData = new byte[28];
                    byte[] bframe = new byte[encodedFrame.length+byteEncData.length];

                    int iDataSize = encodedFrame.length+16;
                    int iDataSize1 = encodedFrame.length;
                    //Log.d("AvcEncoder", "[writeHNSRTPPacket] iDataSize " + iDataSize + " iDataSize1 " +  iDataSize1);

                    byte[] byteDataSize = new byte[4];

                    byteDataSize[0] = (byte)(iDataSize - 256);
                    byteDataSize[1] = (byte)(iDataSize / 256);

                    byteDataSize[2] = (byte)(iDataSize1 - 256);
                    byteDataSize[3] = (byte)(iDataSize1 / 256);

                    byteEncData[12] = byteDataSize[0];
                    byteEncData[13] = byteDataSize[1];
                    byteEncData[20] = byteDataSize[2];
                    byteEncData[21] = byteDataSize[3];



                    for(int i = 0; i < 12; i++)
                    {

                        byteEncData[i] = videoenc.rtpVideoBuffer[i];
                        if(i == 0)
                            byteEncData[i] = (byte)0x80;
//                        if(i < 12)
//                            Dialer.m_byteTest[i] = 0x00;
                        //Log.d("Dialer_KSJ", i + " " + String.format("%02x", byteEncData[i]&0xff));
                    }

                    System.arraycopy(byteEncData, 0, bframe, 0, byteEncData.length);
                    //System.arraycopy(sps, 0, bframe, byteEncData.length, sps.length);
                    System.arraycopy(encodedFrame, 0, bframe, byteEncData.length, encodedFrame.length);

                    DatagramPacket dp = new DatagramPacket(bframe, bframe.length, ia, videoenc.remotePort);
                    if (dp != null) {
                        runningSocket.send(dp);
                    }
                }
                sent = dataSize;
            } catch (NullPointerException ne) {
                System.err.println(ne);
            } catch (UnknownHostException uhe) {
                System.err.println(uhe);
            } catch (IOException ie) {
                System.err.println(ie);
            }
            return sent;
        }

        public void closeSocket() {
            try {
                Log.d(TAG, "[closeSocket] ===== UDP Client Closed =====");
                if (runningSocket != null) {
                    if (runningSocket == theSocket) theSocket = null;
                    runningSocket.close();
                    runningSocket = null;
                }
                videoenc.bReceiveRunning = false;
            } catch (Exception e) {
                Log.e(TAG, "[Exception] closeSocket()");
                e.printStackTrace();
            }
        }
    }

    public void videoSet(final boolean bSet) {
        Log.d(TAG, "[videoSet] bSet [" + bSet + "]");
        if (BSSVideo.bVideoMute) {
            Log.d(TAG, "[videoSet] BSSVideo.bVideoMute [" + BSSVideo.bVideoMute + "]");
            return;
        }

        try {
            Runnable runner = new Runnable() {
                public void run() {
                    if (!bSet && BSSVideo.bService) {
                        // video off
                        BSSVideo.bService = false;
                        BSSVideo.bRemoteview = false;

                        if (videoRtpManager != null && videoRtpManager.videoenc.bReceiveRunning) {
                            videoRtpManager.videoenc.bReceiveRunning = false;
                            videoRtpManager.interrupt();
                            videoRtpManager.closeSocket();
                            videoRtpManager = null;
                        }

                        if (bDecoderStarted) {
                            bDecoderStarted = false;
                            MainActivity.mCallController.mVideoJNI.decoderStopOperation();
                        }

                        if (bEncoderStarted) {
                            bEncoderStarted = false;
                            MainActivity.mCallController.mVideoJNI.encoderStopOperation();
                        }

                        BSSVideo.bViewme = false;
                    }
                    else if (bSet && !BSSVideo.bService) {
                        // video on
                        BSSVideo.bCover = true;
                        BSSVideo.bRemoteview = false;
                        BSSVideo.bService = true;
                        BSSVideo.dateVideoincoming = new Date();

                        if (!bEncoderStarted) {
                            videoRtpManager = new VideoRtpManager();
                            videoRtpManager.start();
                            MainActivity.mCallController.mVideoJNI.setCameraSurface(null, 640, 480, 0, 0);
                            MainActivity.mCallController.mVideoJNI.encoderStartOperation();
                            bEncoderStarted = true;

                        }

                        if (!bDecoderStarted) {
                            // Decode start
                            MainActivity.mCallController.mVideoJNI.decoderStartOperation();
                            bDecoderStarted = true;
                        }
                    }
                }
            };
            if (runner != null) ((MainActivity) mContext).runOnUiThread(runner);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] videoSet(final boolean bSet) : " + e);
            e.printStackTrace();
            return;
        }
    }

    protected static String padNumber = "";
    protected static int officeNumber = 0 ;   // 1:정문, 2:후문, 3:관할, 4:관리사무소

    class CmPiper extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case CMCore.BSSSIPStun_ICONTROLS_CM_UPDATESTATUS_TRUE: {
                    String value = msg.getData().getString("UPDATESTATUS");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_UPDATESTATUS_TRUE [" + value + "]");
                    if (value.contains("Call forwarded."))
                        sendSIPEvent(CALLEVENTTYPE.SIP_FORWARD_CALL, value);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_UPDATESTATUS_FALSE: {
                    String value = msg.getData().getString("UPDATESTATUS");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_UPDATESTATUS_FALSE [" + value + "]");
                    if (value.equals("Call disconnecting."))
                        sendSIPEvent(CALLEVENTTYPE.SIP_TERMINATE_CALL, value);
                    if (value.equals("Call cancelling."))
                        sendSIPEvent(CALLEVENTTYPE.SIP_CANCEL_CALL, value);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_VIDEOSET_TRUE: {
                    String value = msg.getData().getString("VIDEOSET");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_VIDEOSET_TRUE [" + value + "]");
                    videoSet(true);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_VIDEOSET_FALSE: {
                    String value = msg.getData().getString("VIDEOSET");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_VIDEOSET_FALSE [" + value + "]");
                    videoSet(false);
                    try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
                    ((MainActivity) mContext).ctrlVideoSurface(false);
                    ((MainActivity) mContext).checkMemoryUsage();
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_INCOMINGCALL: {
                    String value = msg.getData().getString("INCOMINGCALL");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_INCOMINGCALL [" + value + "]");
                    try {
                        String dn = "";
                        String uri = "";
                        int pos = value.indexOf(";");
                        if (pos >= 0) {
                            dn = value.substring(0, pos);
                            uri = value.substring(pos + 1);
                            Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_INCOMINGCALL [" + dn + " uri : " + uri + "]");
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] CmPiper -> BSSSIPStun_ICONTROLS_CM_INCOMINGCALL");
                        e.printStackTrace();
                    }
                    sendSIPEvent(CALLEVENTTYPE.SIP_RECEIVE_CALL, value);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_INCOMINGMULTICALL: {
                    String value = msg.getData().getString("INCOMINGMULTICALL");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_INCOMINGMULTICALL [" + value + "]");
                    SIPController.pendingCall.bRejectRequest = true;
                    SIPController.bMulticallBusyResponse = true;
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_CALLCONNECTED: {
                    String value = msg.getData().getString("CALLCONNECTED");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CALLCONNECTED [" + value + "]");
                    try {
                        String dn = "";
                        String uri = "";
                        int pos = value.indexOf(";");
                        if (pos >= 0) {
                            dn = value.substring(0, pos);
                            uri = value.substring(pos + 1);
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] CmPiper -> BSSSIPStun_ICONTROLS_CM_CALLCONNECTED");
                        e.printStackTrace();
                    }
                    sendSIPEvent(CALLEVENTTYPE.SIP_CONNECT_CALL, value);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_BACKGROUNDINCOMINGCALL: {
                    String value = msg.getData().getString("BACKGROUNDINCOMINGCALL");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_BACKGROUNDINCOMINGCALL [" + value + "]");
                    sendSIPEvent(CALLEVENTTYPE.SIP_RECEIVE_CALL, value);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_SHUTDOWNAPPLICATION: {
                    String value = msg.getData().getString("SHUTDOWNAPPLICATION");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_SHUTDOWNAPPLICATION [" + value + "]");
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_VIBRATOR_TRUE: {
                    String value = msg.getData().getString("VIBRATOR");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_VIBRATOR_TRUE [" + value + "]");
                    vibrator(true);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_VIBRATOR_FALSE: {
                    String value = msg.getData().getString("VIBRATOR");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_VIBRATOR_FALSE [" + value + "]");
                    vibrator(false);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_REGISTSTATUS_TRUE: {
                    String value = msg.getData().getString("REGISTSTATUS");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_REGISTSTATUS_TRUE [" + value + "]");
                    registerStatus(true);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_REGISTSTATUS_FALSE: {
                    String value = msg.getData().getString("REGISTSTATUS");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_REGISTSTATUS_FALSE [" + value + "]");
                    registerStatus(false);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_RINGBACKTONE_TRUE: {
                    String value = msg.getData().getString("RINGBACKTONE");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_RINGBACKTONE_TRUE [" + value + "]");
                    sendSIPEvent(CALLEVENTTYPE.SIP_RECEIVE_CALL_ACK, value);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_RINGBACKTONE_FALSE: {
                    String value = msg.getData().getString("RINGBACKTONE");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_RINGBACKTONE_FALSE [" + value + "]");
                    sendSIPEvent(CALLEVENTTYPE.SIP_RECEIVE_CALL_ACK, value);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_CALLEND_TRUE: {
                    String value = msg.getData().getString("CALLEND");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CALLEND_TRUE [" + value + "]");
                    try {
                        String status = "";
                        String dn = "";
                        String uri = "";
                        int pos = value.indexOf(";");
                        if (pos >= 0) {
                            status = value.substring(0, pos);
                            int posE = value.indexOf(";", pos + 1);
                            if (posE > 0) {
                                dn = value.substring(pos + 1, posE);
                                uri = value.substring(posE + 1);
                                notifyCallEnd(status, dn, uri, true);
                                Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CALLEND_TRUE [status : " + status + ", dn : " + dn + ", uri : " + uri + "]");
                            }
                        } else {
                            notifyCallEnd(status, dn, uri, true);
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] CmPiper -> BSSSIPStun_ICONTROLS_CM_CALLEND_TRUE");
                        e.printStackTrace();
                    }
                    sendSIPEvent(CALLEVENTTYPE.SIP_TERMINATE_CALL, value);
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_CALLEND_FALSE: {
                    String value = msg.getData().getString("CALLEND");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CALLEND_FALSE [" + value + "]");
                    String status = "";
                    String dn = "";
                    String uri = "";
                    try {
                        int pos = value.indexOf(";");
                        if (pos >= 0) {
                            status = value.substring(0, pos);
                            int posE = value.indexOf(";", pos + 1);
                            if (posE > 0) {
                                dn = value.substring(pos + 1, posE);
                                uri = value.substring(posE + 1);
                                notifyCallEnd(status, dn, uri, false);
                                Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CALLEND_FALSE [status :  " + status + ", dn : " + dn + ", uri : " + uri + "]");
                            }
                        } else {
                            notifyCallEnd(status, dn, uri, false);
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] CmPiper -> BSSSIPStun_ICONTROLS_CM_CALLEND_FALSE");
                        e.printStackTrace();
                    }

                    if (status.contains("Call rejected by remoter")) {
                        sendSIPEvent(CALLEVENTTYPE.SIP_REJECT_CALL, value);
                    } else if (status.contains("Ready.")) {
                    } else {
                        sendSIPEvent(CALLEVENTTYPE.SIP_TERMINATE_CALL, value);
                    }
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_OUTBANDDTMF: {
                    String value = msg.getData().getString("OUTBANDDTMF");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_OUTBANDDTMF [" + value + "]");
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_PADNUMBER: {
                    String value = msg.getData().getString("PADNUMBER");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_PADNUMBER [" + value + "]");
                    padNumber = value;
                    if (SIPSound.amAudioManager != null) {
                        Log.d(TAG, "[CmPiper] SIPSound.amAudioManager is created!!!");
                        int curVolume = 0;
                        if (SIPSound.amAudioManager != null) {
                            curVolume = SIPSound.amAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
                        }
                    } else {
                        Log.d(TAG, "[CmPiper] SIPSound.amAudioManager is null.");
                    }
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_PADTYPE: {
                    String value = msg.getData().getString("PADTYPE");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_PADTYPE [" + value + "]");
                    int pos = value.indexOf('.');
                    if (pos >= 0) {
                        SIPController.latestMenu = Integer.parseInt(value.substring(0, pos));
                        officeNumber = Integer.parseInt(value.substring(pos + 1));
                    }
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_CALLNEIGHBOR: {
                    String value = msg.getData().getString("CALLNEIGHBOR");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CALLNEIGHBOR [" + value + "]");
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_CALLGUARD: {
                    String value = msg.getData().getString("CALLGUARD");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CALLGUARD [" + value + "]");
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_CALLTIME: {
                    String value = msg.getData().getString("CALLTIME");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CALLTIME [" + value + "]");
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_CANCELMULTICALL: {
                    String value = msg.getData().getString("CANCELMULTICALL");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_CANCELMULTICALL [" + value + "]");
                    break;
                }
                case CMCore.BSSSIPStun_ICONTROLS_CM_OUTCALLFAIL: {
                    String value = msg.getData().getString("CALLFAIL");
                    Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_OUTCALLFAIL [" + value + "]");
                    int pos = value.indexOf('|');
                    if (pos >= 0) {
                        int finalCode = Integer.parseInt(value.substring(0, pos));
                        String remoteip = value.substring(pos + 1);
                        String desc = "";
                        desc = CMCore.getCallfailReason(finalCode);
                        Log.d(TAG, "[CmPiper] BSSSIPStun_ICONTROLS_CM_OUTCALLFAIL [desc : " + desc + ", code : " + finalCode + ", ip : " + remoteip + "]");
                        if (finalCode == 486) {
                            sendSIPEvent(CALLEVENTTYPE.SIP_REJECT_BUSY_CALL, value);
                        } else if (finalCode == 487) {
                            sendSIPEvent(CALLEVENTTYPE.SIP_CANCEL_CALL, value);
                        } else if (finalCode == 488 || finalCode == 480) {
                            sendSIPEvent(CALLEVENTTYPE.SIP_REJECT_CALL, value);
                        }
                    }
                    break;
                }
                default: {
                    Log.d(TAG, "[CmPiper] default [" + msg.toString() + "]");
                    super.handleMessage(msg);
                    break;
                }
            }
        }
    }

    private void sendSIPEvent(CALLEVENTTYPE eEventType, String strMessage) {
        try {
            Log.d(TAG, "[sendSIPEvent] SIPController.sipCall.flag [" + SIPController.sipCall.flag + "]");
            Log.d(TAG, "[sendSIPEvent] eEventType [" + eEventType + "], strMessage [" + strMessage + "]");
            Log.d(TAG, "[sendSIPEvent] BSSVideo.HNS_SERVICE_RECEIVER_SELECTED [" + BSSVideo.HNS_SERVICE_RECEIVER_SELECTED + "]");
            Log.d(TAG, "[sendSIPEvent] SIPController.sipCall.remoteIp : " + SIPController.sipCall.remoteIp);
            Log.d(TAG, "[sendSIPEvent] SIPController.sipCall.number : " + SIPController.sipCall.number);
            Log.d(TAG, "[sendSIPEvent] SIPController.sipCall.cid : " + SIPController.sipCall.cid);
            Log.d(TAG, "[sendSIPEvent] SIPController.sipCall.dnis : " + SIPController.sipCall.dnis);
            Log.d(TAG, "[sendSIPEvent] SIPController.sipCall.dong_number : " + SIPController.sipCall.dong_number);
            Log.d(TAG, "[sendSIPEvent] SIPController.sipCall.ho_number : " + SIPController.sipCall.ho_number);
            Log.d(TAG, "[sendSIPEvent] SIPController.sipCall.id : " + SIPController.sipCall.id);

            CALLTYPE eCallType = CALLTYPE.NONE;

            if (SIPController.sipCall.dnis != null) {
                if (SIPController.sipCall.dnis.contains("_Resi_")) {
                    eCallType = CALLTYPE.RESIDENCE;
                } else if (SIPController.sipCall.dnis.contains("_Lobby_")) {
                    eCallType = CALLTYPE.LOBBY;
                } else if (SIPController.sipCall.dnis.contains("_Guard_")) {
                    eCallType = CALLTYPE.GUARD;
                }
            } else {
                if (BSSVideo.HNS_SERVICE_RECEIVER_SELECTED == BSSVideo.HNS_SERVICE_NEIGHBOR) {
                    eCallType = CALLTYPE.RESIDENCE;
                } else if (BSSVideo.HNS_SERVICE_RECEIVER_SELECTED == BSSVideo.HNS_SERVICE_LOBBY) {
                    eCallType = CALLTYPE.LOBBY;
                } else if (BSSVideo.HNS_SERVICE_RECEIVER_SELECTED == BSSVideo.HNS_SERVICE_GUARD) {
                    eCallType = CALLTYPE.GUARD;
                }
            }

            if (eCallType == CALLTYPE.NONE) {
                if (strMessage.contains("_Resi_")) {
                    eCallType = CALLTYPE.RESIDENCE;
                } else if (strMessage.contains("_Lobby_")) {
                    eCallType = CALLTYPE.LOBBY;
                } else if (strMessage.contains("_Guard_")) {
                    eCallType = CALLTYPE.GUARD;
                }
            }

            if (eCallType == CALLTYPE.NONE) {
                if (!SIPController.sipCall.dong_number.equals("") && !SIPController.sipCall.ho_number.equals("")) {
                    if (SIPController.sipCall.ho_number.length() > 2)
                        eCallType = CALLTYPE.RESIDENCE;
                }
            }
            Log.d(TAG, "[sendSIPEvent] eType [" + eCallType + "]");
            mSIPEventListener.onSIPEvent(eEventType, eCallType, (String) SIPController.sipCall.remoteIp, (String) SIPController.sipCall.dnis, strMessage);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendSIPEvent(eEvent, strMessage) : " + e);
            e.printStackTrace();
        }
    }

    public final void notifyCallEnd(final String status, final String dn, final String uri, final boolean bDialreset) {
        try {
            Runnable runner = new Runnable() {
                public void run() {
                    SIPStack.userDial.dial = "";
                    SIPStack.userDial.dong = "";
                    SIPStack.userDial.ho = "";
                    BSSVideo.bService = false;
                    BSSVideo.bRemoteview = false;
                    BSSVideo.HNS_SERVICE_RECEIVER_SELECTED = BSSVideo.HNS_SERVICE_NONE;
                }
            };
            if (runner != null) ((MainActivity)mContext).runOnUiThread(runner);
        } catch(Exception e) {
            Log.e(TAG, "[Exception] notifyCallEnd(final String status, final String dn, final String uri, final boolean bDialreset) : " + e);
            e.printStackTrace();
            return;
        }
    }

    private void forwardNotifyToStun() {
        String contact = "";
        String ip = "";
        String redirectName = "";
        if (!SIPController.forward.bForward) return;
        if (SIPController.forward.iForwardTarget == 0) {
            // 세대기
            String ip_field1 = "";
            String ip_field2 = "";
            String ip_field3 = "";
            String ip_field4 = "";

            if (SIPController.forward.dong.length() > 3) {
                ip_field1 = SIPController.forward.dong.substring(0, 3);
                ip_field2 = SIPController.forward.dong.substring(3);
            }

            if (SIPController.forward.ho.length() > 3) {
                int start = SIPController.forward.ho.length() - 3;
                ip_field3 = SIPController.forward.ho.substring(0, start);
                ip_field4 = SIPController.forward.ho.substring(start);
            }

            if (ip_field1.length() > 0 && ip_field2.length() > 0 && ip_field3.length() > 0 && ip_field4.length() > 0) {
                ip = Integer.parseInt(ip_field1) + "." + Integer.parseInt(ip_field2) + "." + Integer.parseInt(ip_field3) + "." + Integer.parseInt(ip_field4);
            }
            redirectName = "이웃세대기 " + SIPController.forward.dong + "동 " + SIPController.forward.ho + "호";
        } else {
            // 경비실
            SIPController.forward.dong = "office";
            if (Integer.parseInt(SIPController.forward.ho) == 1) {
                // 정문
                // ip = 경비실 정문 아이피주소 설정;
                redirectName = "정문 경비실";
            } else if (Integer.parseInt(SIPController.forward.ho) == 2) {
                // 후문
                // ip = 경비실 후문 아이피주소 설정;
                redirectName = "후문 경비실";
            } else if (Integer.parseInt(SIPController.forward.ho) == 3) {
                // 관할
                // ip = 경비실 관할 아이피주소 설정;
                redirectName = "관할 경비실";
            } else if (Integer.parseInt(SIPController.forward.ho) == 4) {
                // 관리
                // ip = 경비실 관리소 아이피주소 설정;
                redirectName = "관리사무소";
            }
        }

        if (ip.length() == 0) {
            // 취소
            if (SIPController.sipCall != null) {
                SIPController.sipCall.bRejectRequest = true;
            }
        } else {
            contact = "sip:" + SIPController.forward.dong + "*" + SIPController.forward.ho + "@" + ip + ":5060";
            if (bIsBound) {
                if (mService != null) {
                    try {
                        Bundle bundle = new Bundle();
                        Message msg = null;
                        bundle.putString("FORWARD", contact);
                        msg = Message.obtain(null, CMCore.BSSSIPStun_ICONTROLS_DIALER_FORWARD);
                        msg.setData(bundle);
                        mService.send(msg);
                        Log.d(TAG, "[forwardNotifyToStun] Call Forward -> " + redirectName + "Call forwarded!!!");
                    } catch (RemoteException e) {
                        Log.e(TAG, "[RemoteException] forwardNotifyToStun()");
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    protected static boolean bIncomingcover_lock = false;

    public final int getIncomingcoverCase() {
        if (SIPController.sipCall == null || !SIPController.sipCall.flag) return 99;
        else {
            if (BSSVideo.HNS_SERVICE_ORIGINATOR_SELECTED == BSSVideo.HNS_SERVICE_NEIGHBOR) {
                if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_IN) {
                    if (SIPController.sipCall.hnsOriginatorServiceType == BSSVideo.HNS_SERVICE_LOBBY) {
                        return 0;
                    } else if (SIPController.sipCall.hnsOriginatorServiceType == BSSVideo.HNS_SERVICE_GUARD) {
                        return 1;
                    } else if (SIPController.sipCall.hnsOriginatorServiceType == BSSVideo.HNS_SERVICE_NEIGHBOR) {
                        return 2;
                    }
                } else if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_OUT) {
                    if (SIPController.sipCall.hnsReceiverServiceType == BSSVideo.HNS_SERVICE_NEIGHBOR) {
                        return 3;
                    } else if (SIPController.sipCall.hnsReceiverServiceType == BSSVideo.HNS_SERVICE_GUARD) {
                        return 4;
                    }
                }
            } else if (BSSVideo.HNS_SERVICE_ORIGINATOR_SELECTED == BSSVideo.HNS_SERVICE_GUARD) {
                if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_IN) {
                    if (SIPController.sipCall.hnsOriginatorServiceType == BSSVideo.HNS_SERVICE_LOBBY) {
                        return 5;
                    } else if (SIPController.sipCall.hnsOriginatorServiceType == BSSVideo.HNS_SERVICE_GUARD) {
                        return 6;
                    } else if (SIPController.sipCall.hnsOriginatorServiceType == BSSVideo.HNS_SERVICE_NEIGHBOR) {
                        return 7;
                    }
                } else if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_OUT) {
                    if (SIPController.sipCall.hnsReceiverServiceType == BSSVideo.HNS_SERVICE_NEIGHBOR) {
                        return 8;
                    } else if (SIPController.sipCall.hnsReceiverServiceType == BSSVideo.HNS_SERVICE_GUARD) {
                        return 9;
                    }
                }
            } else if (BSSVideo.HNS_SERVICE_ORIGINATOR_SELECTED == BSSVideo.HNS_SERVICE_LOBBY) {
                if (SIPController.sipCall.callDirection == SIPStack.SIP_CALLDIRECTION_OUT) {
                    if (SIPController.sipCall.hnsReceiverServiceType == BSSVideo.HNS_SERVICE_NEIGHBOR) {
                        return 10;
                    } else if (SIPController.sipCall.hnsReceiverServiceType == BSSVideo.HNS_SERVICE_GUARD) {
                        return 11;
                    }
                }
            }
        }
        return 99;
    }

    public void attachIncomingcover() {
        if (bIncomingcover_lock) return;
        Log.d(TAG, ">>>> attachIncomingcover");
        bIncomingcover_lock = true;
        try {
            Runnable runner = new Runnable() {
                public void run() {
                    if (SIPController.sipCall.callState == SIPStack.SIP_CALLSTATE_CONNECTED) {
                        try {
                            BSSVideo.bCover = true;
                            BSSVideo.dateVideoincoming = new Date();
                            //SIPSound.resetBufferInfo();
                        } catch (Exception e) {
                            Log.e(TAG, "[Exception] attachIncomingcover() 1");
                            e.printStackTrace();
                        }
                    }
                    bIncomingcover_lock = false;
                    return;
                }
            };

            if (runner != null) ((MainActivity) mContext).runOnUiThread(runner);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] attachIncomingcover() 2");
            bIncomingcover_lock = false;
            return;
        }
    }

    public void removeIncomingcover() {
        if (bIncomingcover_lock) return;
        Log.d(TAG, ">>>> removeIncomingcover");
        bIncomingcover_lock = true;
        try {
            Runnable runner = new Runnable() {
                public void run() {
                    try {
                        int index = getIncomingcoverCase();
                        Log.d(TAG, "[removeIncomingcover] index = " + index);
                        switch (index) {

                            case 0: //neighbor<-, neighbor<-lobby
                                if (SIPController.sipCall.callState == SIPStack.SIP_CALLSTATE_CONNECTED) {
                                    BSSVideo.bCover = false;
                                    BSSVideo.dateVideoincoming = new Date();
                                } else if (BSSVideo.bProgressvideooffer) {
                                    BSSVideo.bCover = false;
                                    BSSVideo.dateVideoincoming = new Date();
                                }
                                break;

                            case 1:   //neighbor<-, neighbor<-guard
                                if (SIPController.sipCall.callState == SIPStack.SIP_CALLSTATE_CONNECTED) {
                                    BSSVideo.bCover = false;
                                    BSSVideo.dateVideoincoming = new Date();
                                } else if (BSSVideo.bProgressvideooffer) {
                                    BSSVideo.bCover = false;
                                    BSSVideo.dateVideoincoming = new Date();
                                }
                                break;

                            case 2:   //neighbor<-, neighbor<-neighbor
                                if (SIPController.sipCall.callState == SIPStack.SIP_CALLSTATE_CONNECTED) {
                                    BSSVideo.bCover = false;
                                    BSSVideo.dateVideoincoming = new Date();
                                }
                                break;

                            case 3:   //neighbor->, neighbor->neighbor
                                if (SIPController.sipCall.callState == SIPStack.SIP_CALLSTATE_CONNECTED) {
                                    BSSVideo.bCover = false;
                                    BSSVideo.dateVideoincoming = new Date();
                                }
                                break;

                            case 4:   //neighbor->, neighbor->guard
                                if (SIPController.sipCall.callState == SIPStack.SIP_CALLSTATE_CONNECTED) {
                                    BSSVideo.bCover = false;
                                    BSSVideo.dateVideoincoming = new Date();
                                } else if (BSSVideo.bProgressvideooffer) {
                                    BSSVideo.bCover = false;
                                    BSSVideo.dateVideoincoming = new Date();
                                }
                                break;

                            case 5:
                                BSSVideo.bCover = false;
                                BSSVideo.dateVideoincoming = new Date();
                                break;

                            case 10://lobby->, lobby->neighbor
                                BSSVideo.bCover = false;
                                BSSVideo.dateVideoincoming = new Date();
                                break;

                            case 11://lobby->, lobby->guard
                                BSSVideo.bCover = false;
                                BSSVideo.dateVideoincoming = new Date();
                                break;

                            default:
                                break;
                        }

                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] removeIncomingcover() 1");
                        e.printStackTrace();
                    }
                    bIncomingcover_lock = false;
                    return;
                }
            };

            if (runner != null) ((MainActivity) mContext).runOnUiThread(runner);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] removeIncomingcover() 2");
            bIncomingcover_lock = false;
            return;
        }
    }

    public final void registerStatus(final boolean bActive) {
        try {
            Runnable runner = new Runnable() {
                public void run() {
                    if (SIPStack.bNetworkActive == bActive) {
                        if (bActive) {
                        }
                    } else {
                        SIPStack.bNetworkActive = bActive;
                    }
                }
            };

            if (runner != null) ((MainActivity) mContext).runOnUiThread(runner);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] registerStatus(final boolean bActive) : " + e);
            e.printStackTrace();
        }
    }

    public void vibrator(boolean bSet) {
        try {
            if (SIPStack.bVibrator) {
                if (bSet && !SIPStack.bVibrating) {
                    SIPController.vibe = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
                    long[] pattern = {1000, 200, 1000, 2000, 1200};
                    SIPController.vibe.vibrate(pattern, SIPStack.iVibrator);
                    SIPStack.bVibrating = true;
                } else if (SIPStack.bVibrating && SIPController.vibe != null) {
                    SIPController.vibe.cancel();
                    SIPStack.bVibrating = false;
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "[Exception] vibrator(boolean bSet) : " + e);
            e.printStackTrace();
        }
    }
}

