package com.foxpower.flchatofandroid.util.tool.videoChat;

import android.content.Context;

import com.foxpower.flchatofandroid.util.manager.ClientManager;
import com.foxpower.flchatofandroid.util.manager.SocketManager;
import com.foxpower.flchatofandroid.util.other.FLLog;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.AudioSource;
import org.webrtc.DataChannel;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoCapturerAndroid;
import org.webrtc.VideoSource;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import javax.microedition.khronos.egl.EGLContext;

import io.socket.client.Socket;
import io.socket.emitter.Emitter;

/**
 * Created by fengli on 2018/3/2.
 */

public class VideoChatHelper{


    // 房间ID
    private String room;
    // 回调
    private VideoChatCallBack callBack;
    // 所有的连接ID
//    private JSONArray connectionIdArray;
    private HashMap<String, Peer> peers = new HashMap<>();
    // 所有连接的映射
//    private HashMap<String, PeerConnection> connectionsMap = new HashMap<>();
    // 自己的ID
    private String myId;
    // context
    private Context mContext;
    // factory
    private PeerConnectionFactory factory;


    // 本地音视频流
    private MediaStream localMediaStream;

    private EGLContext mEGLContext;

    private LinkedList<PeerConnection.IceServer> ICEServers;

    public VideoChatHelper(Context mContext, EGLContext mEGLContext, VideoChatCallBack callBack) {
        this.mContext = mContext;
        this.callBack = callBack;
        this.mEGLContext = mEGLContext;
    }

    public void connectRoom(String room) {

        this.room = room;
        addSocketHandles();
        joinRoom(room);
    }


    public void addSocketHandles() {

        final Socket socket = SocketManager.socket;

        socket.on("_peers", new Emitter.Listener() {
            @Override
            public void call(Object... args) {

                JSONObject dataObject = (JSONObject) args[0];

                JSONArray connectionIds = null;
                try {
                    JSONArray connections = dataObject.getJSONArray("connections");
                    connectionIds = connections;

                    myId = dataObject.getString("you");
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                if (myId == null) {

                    FLLog.i("myID 是空的,请检查");
                }

                if (factory == null) {

                    FLLog.i("注意下方this可能错误");
                    PeerConnectionFactory.initializeAndroidGlobals(mContext, true, true, true, mEGLContext);
                    factory = new PeerConnectionFactory();
                }

                if (localMediaStream == null) {

                    createLocalStream();
                }

                if (connectionIds == null) {
                    FLLog.i("错误,connectionIds为空");
                }
                // 创建连接
                createPeerConnections(connectionIds);

                // 连接添加流
                addStream();
                // 生成offer
                createOffers();
            }
        });

        // 接收到新加入的人发了ICE候选,(即经过ICEServer而获取到的地址)
        socket.on("_ice_candidate", new Emitter.Listener() {
            @Override
            public void call(Object... args) {

                JSONObject dataObject = (JSONObject) args[0];
                String socketId = null;
                String sdpMid = null;
                int sdpMLineIndex = -1111;
                String sdp = null;
                try {
                    socketId = dataObject.getString("socketId");
                    sdpMid = dataObject.getString("id");
                    sdpMLineIndex = dataObject.getInt("label");
                    sdp = dataObject.getString("candidate");
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                if (socketId == null || sdpMid == null || sdpMLineIndex == -1111 || sdp == null) {

                    FLLog.i("ice候选出了错");
                } else {


                    //生成远端网络地址对象
                    IceCandidate candidate = new IceCandidate(sdpMid, sdpMLineIndex, sdp);
                    //拿到当前对应的点对点连接
                    Peer peer = peers.get(socketId);
                    PeerConnection peerConnection = peer.pc;
                    //添加到点对点连接中
                    peerConnection.addIceCandidate(candidate);
                }
            }
        });

        // 其他新人加入房间的信息
        socket.on("_new_peer", new Emitter.Listener() {
            @Override
            public void call(Object... args) {


                FLLog.i("_new_peer接收到");
                JSONObject dataObject = (JSONObject) args[0];
                String socketId = null;

                try {
                    socketId = dataObject.getString("socketId");
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                if (socketId == null) {
                    FLLog.i("错误: 新人加入房间获取错误");
                } else {

                    // 创建连接
                    Peer peer = new Peer(socketId);
                    PeerConnection peerConnection = peer.pc;
                    if (localMediaStream == null) {
                        FLLog.i("本地数据流为空");
                    } else {

                        peerConnection.addStream(localMediaStream);
                    }

                    peers.put(socketId, peer);
                }
            }
        });

        // 有人离开房间的事件(暂时处理为单聊,对象离开房间就关闭)
        socket.on("_remove_peer", new Emitter.Listener() {
            @Override
            public void call(Object... args) {

                JSONObject dataObject = (JSONObject) args[0];
                String socketId = null;

                try {
                    socketId = dataObject.getString("socketId");
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                if (socketId == null) {
                    FLLog.i("错误: 有人离开房间获取错误");
                } else {

//                    closePeerConnection(socketId);
                    callBack.onCloseRoom();
                }
            }
        });

        // 新加入的人发送offer
        socket.on("_offer", new Emitter.Listener() {
            @Override
            public void call(Object... args) {

                JSONObject dataObject = (JSONObject) args[0];

                //拿到SDP
                JSONObject sdpDic;
                String sdp = null;
                String type = null;
                String socketId = null;

                try {
                    sdpDic = dataObject.getJSONObject("sdp");
                    sdp = sdpDic.getString("sdp");
                    type = sdpDic.getString("type");
                    socketId = dataObject.getString("socketId");
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                if (sdp == null || type == null || socketId == null) {
                    FLLog.i("检查新人发送offer的错误");
                } else {

                    //拿到这个点对点的连接
                    Peer peer = peers.get(socketId);
                    PeerConnection peerConnection = peer.pc;
                    //根据类型和SDP 生成SDP描述对象
                    SessionDescription remoteSdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), sdp);
                    //设置给这个点对点连接
                    peerConnection.setRemoteDescription(peer, remoteSdp);

                    // 回应answer
//                    JSONObject answer = new JSONObject();
//                    JSONObject sdpa = new JSONObject();
//                    String typea = remoteSdp.type.canonicalForm();
//                    FLLog.i("查看type是否正确answer:" + type);
//                    try {
//                        sdpa.put("type", typea);
//                        sdpa.put("sdp", remoteSdp.description);
//                        FLLog.i("可能错误description");
//                        answer.put("socketId", peer.id);
//                        answer.put("sdp", sdpa);
//
//                        SocketManager.socket.emit("__answer", answer);
//                    } catch (JSONException e) {
//
//                        FLLog.i("错误:offer发送为空");
//                        e.printStackTrace();
//                    }

                    FLLog.i("createAnswer调用");
                    peerConnection.createAnswer(peer, offerOrAnswerConstraint());

                }
            }
        });

        //回应offer
        socket.on("_answer", new Emitter.Listener() {
            @Override
            public void call(Object... args) {

                JSONObject dataObject = (JSONObject) args[0];

                //拿到SDP
                JSONObject sdpDic;
                String sdp = null;
                String type = null;
                String socketId = null;

                try {
                    sdpDic = dataObject.getJSONObject("sdp");
                    sdp = sdpDic.getString("sdp");
                    type = sdpDic.getString("type");
                    socketId = dataObject.getString("socketId");
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                if (sdp == null || type == null || socketId == null) {
                    FLLog.i("检查回应offer的错误");
                } else {

                    //拿到这个点对点的连接
                    Peer peer = peers.get(socketId);
                    PeerConnection peerConnection = peer.pc;
                    //根据类型和SDP 生成SDP描述对象
                    SessionDescription remoteSdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), sdp);
                    //设置给这个点对点连接
                    peerConnection.setRemoteDescription(peer, remoteSdp);
                }
            }
        });

        // 对方拒接了通话
        socket.on("cancelVideoChat", new Emitter.Listener() {
            @Override
            public void call(Object... args) {

                callBack.onCloseRoom();
            }
        });
    }


    /**
     * 加入房间
     *
     * @param room 房间号
     */
    private void joinRoom(String room) {

        Socket socket = SocketManager.socket;
        JSONObject object = new JSONObject();
        try {
            object.put("room", room);
        } catch (JSONException e) {

            FLLog.i("加入房间失败!!!");
            e.printStackTrace();
        }
        socket.emit("__join", object);
    }

    /**
     * 退出房间
     */
    public void exitRoom() {

//        for (Map.Entry<String, Peer> entry : peers.entrySet()){
//
//            closePeerConnection(entry.getKey());
//        }

        localMediaStream = null;


        SocketManager.socket.emit("closeRoom");
        factory = null;
    }



    /**
     * 关闭peerConnection
     *
     * @param connectionId 连接id
     */
    private void closePeerConnection(String connectionId) {

        Peer peer = peers.get(connectionId);
        PeerConnection connection = peer.pc;
        if (connection != null) {
            connection.close();
        }
        peers.remove(connectionId);

        callBack.onCloseWithUserId(connectionId);

    }

    /*
    * 创建本地流,并将流回调
    * */
    private void createLocalStream() {
        localMediaStream = factory.createLocalMediaStream("ARDAMS");

        // 音频
        AudioSource audioSource = factory.createAudioSource(new MediaConstraints());
        localMediaStream.addTrack(factory.createAudioTrack("ARDAMSa0", audioSource));

        // 视频
        String frontCameraDeviceName = VideoCapturerAndroid.getNameOfFrontFacingDevice();
        VideoCapturer capture = VideoCapturerAndroid.create(frontCameraDeviceName);
        VideoSource videoSource = factory.createVideoSource(capture, localVideoConstraints());
        localMediaStream.addTrack(factory.createVideoTrack("ARDAMSv0", videoSource));

        callBack.onSetLocalStream(localMediaStream, myId);
    }

    /*
    * 本地视频流约束
    * */

    private MediaConstraints localVideoConstraints() {

        MediaConstraints videoConstraints = new MediaConstraints();
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxWidth", Integer.toString(640)));
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minWidth", Integer.toString(640)));
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxHeight", Integer.toString(480)));
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minHeight", Integer.toString(480)));
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minFrameRate", Integer.toString(15)));

        return videoConstraints;
    }

    /**
     * 为所有连接创建offer
     */
    private void createOffers() {

        for (Map.Entry<String, Peer> entry : peers.entrySet()) {

            Peer peer = entry.getValue();
            PeerConnection connection = peer.pc;
            FLLog.i("createoffer调用");
            connection.createOffer(peer, offerOrAnswerConstraint());
        }
    }

    /*
    * 为所有连接添加流
    * */
    private void addStream() {

        for (Map.Entry<String, Peer> entry : peers.entrySet()) {

            Peer peer = entry.getValue();
            PeerConnection connection = peer.pc;

            if (localMediaStream == null) {
                FLLog.i("添加本地流时,本地流为空");
            } else {

                connection.addStream(localMediaStream);
            }
        }
    }

    /*
    * 创建所有连接
    * */
    private void createPeerConnections(JSONArray connectionIds) {

        for (int i = 0; i < connectionIds.length(); i++) {

            String connectionId = null;
            try {
                connectionId = connectionIds.getString(i);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            if (connectionId == null) {

                FLLog.i("错误:获取connectionId失败!");
            } else {

                Peer peer = new Peer(connectionId);
                peers.put(connectionId, peer);
            }
        }
    }

    /*
    * 创建点对点连接
    *
    * */
    private PeerConnection createPeerConnection(String connectionId, Peer peer) {

        if (factory == null) {
            FLLog.i("工厂为空");
            return null;
        }
        if (ICEServers == null) {
            ICEServers = new LinkedList<>();
            ICEServers.add(defaultSTUNServer("stun:23.21.150.121"));
            ICEServers.add(defaultSTUNServer("stun:stun.l.google.com:19302"));
        }

        PeerConnection peerConnection = factory.createPeerConnection(ICEServers, peerConnectionConstraints(), peer);
        return peerConnection;
    }



    // ICE服务器地址
    private PeerConnection.IceServer defaultSTUNServer(String stunURL) {

        return new PeerConnection.IceServer(stunURL);
    }


    private MediaConstraints peerConnectionConstraints() {

        MediaConstraints constraints = new MediaConstraints();
        FLLog.i("peerConnection约束是否要加东西????");
        constraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        return constraints;
    }


    /**
     * 设置offer/answer的约束
     */
    private MediaConstraints offerOrAnswerConstraint() {

        MediaConstraints constraints = new MediaConstraints();

        constraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        constraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));

        return constraints;
    }


    private String getKeyFromConnectionDic(PeerConnection peerConnection) {

        String socketId = null;
        for (Map.Entry<String, Peer>entry : peers.entrySet()) {

            if (peerConnection.equals(entry.getValue().pc)){

                socketId = entry.getKey();
            }
        }
        if (socketId == null) {
            FLLog.i("错误:未找到相应的socketId");
        }
        return socketId;
    }




    private class Peer implements SdpObserver, PeerConnection.Observer{

        private PeerConnection pc;
        private String id;

        public Peer(String id) {
            this.id = id;
            PeerConnection connection = createPeerConnection(id, this);
            this.pc = connection;
        }


        // ================================PeerConnection========================

        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {

        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {

        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {

        }

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {

            JSONObject dataObject = new JSONObject();
            try {
                dataObject.put("id", iceCandidate.sdpMid);
                dataObject.put("label", iceCandidate.sdpMLineIndex);
                dataObject.put("candidate", iceCandidate.sdp);
                dataObject.put("socketId", this.id);

                SocketManager.socket.emit("__ice_candidate", dataObject);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {

            callBack.onAddRemoteStream(mediaStream, this.id);
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {

        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {

        }

        @Override
        public void onRenegotiationNeeded() {

        }

        // =================================offer======================

        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {

            this.pc.setLocalDescription(this, sessionDescription);

            // 向服务器发送offer
            JSONObject offer = new JSONObject();
            JSONObject sdp = new JSONObject();
            String type = sessionDescription.type.canonicalForm();
            FLLog.i("查看type是否正确:" + type);
            try {
                sdp.put("type", type);
                sdp.put("sdp", sessionDescription.description);
                FLLog.i("可能错误description");
                offer.put("socketId", this.id);
                offer.put("sdp", sdp);

                String event = "__" + type;
                SocketManager.socket.emit(event, offer);
            } catch (JSONException e) {

                FLLog.i("错误:offer发送为空");
                e.printStackTrace();
            }
        }

        @Override
        public void onSetSuccess() {

        }

        @Override
        public void onCreateFailure(String s) {

        }

        @Override
        public void onSetFailure(String s) {

        }
    }

    public interface VideoChatCallBack {

        void onSetLocalStream(MediaStream localStream, String userId);

        void onCloseWithUserId(String userId);

        void onCloseRoom();

        void onAddRemoteStream(MediaStream remoteStream, String userId);
    }
}