using System.Linq;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System;
using System.IO;
using UnityEngine.UI;
public enum RemoteAssistCMD { videoCallRequest, callStop, audioCallRequest, messageSend, imageSend, SMOPP_TASK_INFO };
public enum ImageCMD { receiveImage, streamStart, streamStop, receiveVideo, pause, unpause };
public class Client : MonoBehaviour
{
    const byte PING_PACKET = 30;      
    
    Texture2D textureReceived;
    public GameObject UserInfo;
    public GameObject UserCameraImage;
    public GameObject RemoteAssistCanvas;

    public static Client instance;
    public const int socketPort = 87;
    public const int webglPort = 86;
    public uint account_id = 0; //id акка
    public uint company_id;
    string ACCOUNT_NAME = "";

    bool login_sent;
    bool entered = false;
    bool dc = false;
    public bool connected = false;
    int lag = 0;
    double last_ping_time = 0;
    static string[] Servers = { "dev.prmsys.net", "localhost", "corp.prmsys.net" };
    static bool ShowPacketsSent = true;
    static bool printPacketsShort = true;
    static bool ShowPackets = true; //ставится только в редакторе
    double initial_timer;
    int internal_timer = 0;

    public double timer
    {
        get
        {
            return DateTime.Now.Ticks / 10000000.0;    //секунды
        }
    }
    public static string ServerName;
    public static bool StartConnectFlag = false;
#if UNITY_WEBGL
    public static WebSocket Mysocket;
#else
    public static Socket Mysocket;
#endif
    Dictionary<int, int> PacketLength = new Dictionary<int, int>();
    public delegate void OnReceive(byte[] bytedata);
    public static OnReceive[] packets = new OnReceive[255];
    public string _dataPath = "";
    GameObject drone;
    public static ClientType clientType;
    public enum ClientType
    {
        Desktop,
        Web
    }

    public static void SendEnqueue(byte[] bytedata)
    {
        SendQueue.Enqueue(bytedata);
    }

    private void Awake()
    {
        textureReceived = new Texture2D(2, 2);
        instance = this;
        ServerName = PlayerPrefs.GetString("server");
        if (ServerName == "")
            ServerName = Servers[0];

        Register(1, Disconnect, 5);
        Register(2, Login, 9);
        Register(3, User.CoordinatesReceive);
        Register(30, Myping, 3);
        Register(47, User.UsersReceive);
        Register(48, Beacon.ReceiveBeacons);

        Register(51, Wall.ReceiveWalls);
        Register(52, Zone.ReceiveZones);
        Register(54, Location.ReceiveLocations);
        Register(55, ImageReceive);
        Register(57, Location.TextureGetURL);
        Register(58, User.LogInOut, 6);
        Register(59, RemoteAssistCallCallback);

        //set data path
        //if (Application.platform == RuntimePlatform.WindowsWebPlayer ||
        //    Application.platform == RuntimePlatform.OSXWebPlayer)
        if (Application.platform == RuntimePlatform.WebGLPlayer)
        {
            _dataPath = Application.dataPath + "/StreamingAssets";
            clientType = ClientType.Web;
        }
        else if (Application.platform == RuntimePlatform.Android)
        {
            _dataPath = "jar:file://" + Application.dataPath + "!/assets";
            clientType = ClientType.Web;
        }
        else
        {
            _dataPath = "file://" + Application.dataPath + "/StreamingAssets";
            clientType = ClientType.Desktop;
        }
        _dataPath = Application.streamingAssetsPath;
        //Debug.Log("_dataPath " + _dataPath);
        remoteScript_ = FindObjectOfType<RemoteClickAction>();
    }

    public int LagCount
    {
        get
        {
            return lagpoints.Count;
        }
    }

    int totallag
    {
        get
        {
            int q = 0;
            foreach (var g in lagpoints)
            {
                q += g;
            }
            return q;
        }
    }

    Queue<int> lagpoints = new Queue<int>();

    public int Averagelag
    {
        get
        {
            if (lagpoints.Count > 0)
                return totallag / lagpoints.Count;
            return 0;
        }
    }


    //private Button exitButton;
    private Button loginButton;
    private Button cancelButton;



    //private Text sysInfoTxt;
    private Text loginInputText;
    private Text passInputText;

    private InputField passInputField;

    private Animator loginCanvasAnim;
    private RemoteClickAction remoteScript_;

    /// <summary>
    /// Videocall accept from mobile client
    /// </summary>
    void RemoteAssistCallCallback(byte[] bytedata)
    {
        var cmd = (RemoteAssistCMD)bytedata[5];
        uint fromId = BitConverter.ToUInt32(bytedata, 6);
        RemoteAssistCanvas.SetActive(true);
        Debug.Log(cmd);
        switch (cmd)
        {
            case RemoteAssistCMD.videoCallRequest:
                //Сделать активной кнопку звонка!!!
                //RemoteAssistCanvas.set
                remoteScript_.StartSession(fromId);
                break;
            case RemoteAssistCMD.SMOPP_TASK_INFO:
                uint taskId = BitConverter.ToUInt32(bytedata, 10);
                
                int nameLen = BitConverter.ToInt32(bytedata, 14);
                string name = Encoding.UTF8.GetString(bytedata, 18, nameLen);
                Debug.Log("taskId " + taskId + " Команда "+name +" Команда " + name + " name "+name);

                var user = User.Find(remoteScript_.SessionCallerId);
                if (user != null)
                    remoteScript_.callerName_.text = $"Звонок от [{ user.id }] { user.name_user} Проблема с командой: [{taskId}] {name}";
                else
                    remoteScript_.callerName_.text = $"Звонок от сотрудника  [ID: { remoteScript_.SessionCallerId }] Проблема с командой: [{taskId}]  {name}";
                break;
        }
        
    }

    public void Myping(byte[] bytedata)
    {
        last_ping_time = timer;
        int lag = BitConverter.ToUInt16(bytedata, 1);
        //print("Myping " + lag);
        this.lag = lag;
        if (lagpoints.Count > 5)
            lagpoints.Dequeue();

        lagpoints.Enqueue(lag);

        List<byte> list = new List<byte>();
        list.Add(30);
        list.Add(1);
        SendEnqueue(list.ToArray());
    }

    public void Disconnect(byte[] bytedata)
    {
        uint bid = BitConverter.ToUInt32(bytedata, 1);
        if (bid == account_id)
            Exit();
        else
        {
            print("disc id " + bid);
        }
    }


    public void Exit()
    {
        print("Client Exit");
        connected = false;
        login_sent = false;
        if (Mysocket != null)
        {
            Mysocket.Close();
            Mysocket = null;
        }
        connect_started = new DateTime();
        totalbytes = 0;
        account_id = 0;
        ACCOUNT_NAME = "";
        //connected = false;
        sendDone = false;
        receiveDone = false;
        connectDone = false;
        StartConnectFlag = false;
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }

    //пакет с 1 параметром = 1
    public static void SendOneBytePacket(byte num)
    {
        SendOneByteParamPacket(num, 1);
    }

    public static void SendOneByteTwoParamsPacket(byte num, byte param1, byte param2)
    {
        byte[] data = { num, param1, param2 };
        SendEnqueue(data);
    }

    public static void SendThreeParamsIntPacket(byte num, int param1, int param2, int param3)
    {
        List<byte> list = new List<byte>();
        list.Add(num);
        list.AddRange(BitConverter.GetBytes(param1));
        list.AddRange(BitConverter.GetBytes(param2));
        list.AddRange(BitConverter.GetBytes(param3));
        SendEnqueue(list.ToArray());
    }

    //пакет с 1 1-байтовым параметром
    public static void SendOneByteParamPacket(byte num, byte param)
    {
        byte[] data = { num, param };
        byteSend(data);
    }

    //пакет с 1 4-байтовым параметром
    public static void SendTwoByteParamPacket(byte num, ushort param)
    {
        List<byte> list = new List<byte>();
        list.Add(num);
        list.AddRange(BitConverter.GetBytes(param));
        SendEnqueue(list.ToArray());
    }

    //пакет с 1 4-байтовым параметром
    public static void SendFourByteParamPacket(byte num, uint param)
    {
        List<byte> list = new List<byte>();
        list.Add(num);
        list.AddRange(BitConverter.GetBytes(param));
        SendEnqueue(list.ToArray());
    }

    public static DateTime connect_started;
    public void Login(byte[] bytedata)
    {
        uint accid = BitConverter.ToUInt32(bytedata, 1);
        if (accid == 0xFFFFFFFF)
        {
            Debug.LogError("Login or password incorrect");
            AuthorizationController.error = true;
            return;
        }
        else if (accid == 0xFFFFFFFE)
        {
            Debug.LogError("Account was already connected");
            return;
        }
        else if (accid == 0xFFFFFFFD)
        {
            Debug.LogError("Incorrect client version");
            return;
        }
        company_id = BitConverter.ToUInt32(bytedata, 5);
        AuthorizationController.success = true;
        account_id = accid;
        //Location.LocationsRequest();
    }

    #region system functions

    public void Register(int packnum, OnReceive func)
    {
        packets[packnum] = func;
    }

    private void Register(int packnum, OnReceive func, int len)
    {
        packets[packnum] = func;
        if (len > 1)
            PacketLength[packnum] = len;
    }

    public int GetLength(byte[] data, int num, int offset)
    {
        int leng;
        if (!PacketLength.ContainsKey(num))
        {
            var rest = data.Length - offset;
            //Debug.Log("GetLength rest " + rest+" offset "+ offset);
            //printBytes(data);
            //packet not full
            if (rest < 5)
                return 1;
            leng = BitConverter.ToInt32(data, offset + 1);
        }
        else
            leng = PacketLength[num];
        return leng;
    }

    int maximumPacketsPerUpdate = 50;
    public string tempflag;

    public static void printBytes(byte[] bytedata, bool sent = false)
    {
        var num = bytedata[0];
        if (num == PING_PACKET)// || num==55)
            return;        

        var func = packets[num];
        string callBackName = "Unknown packet ";
        if (func != null)
            callBackName = packets[num].Method.Name + " ";

        string prefix;
        if (!sent)
        {
            if (!ShowPackets)
                return;
            prefix = "received ";
        }
        else
        {
            if (!ShowPacketsSent)
                return;
            prefix = "sent ";
        }
        byte[] newbuf = new byte[bytedata.Length];
        Array.Copy(bytedata, newbuf, bytedata.Length);

        string mygg = prefix + $"{callBackName}[{num}] length " + bytedata.Length;       //Печатаем отправленные байты!
        if (!printPacketsShort)
        {
            mygg += " bytes: ";
            foreach (byte b in newbuf)
            {
                mygg += b + " ";
            }
        }
        Debug.Log(mygg);
    }

    public void Update()
    {
#if UNITY_WEBGL
        Receive();
#endif
        if (instance == null || !entered)    //вход не нажат, ждем
        {
            return;
        }
        else
        {
            if (!StartConnectFlag)
            {
                StartCoroutine(Connect());
                StartConnectFlag = true;
            }
        }

        int from_connect = DateTime.Now.Subtract(connect_started).Seconds;

        if (from_connect > 7 && from_connect != DateTime.Now.Second && account_id == 0)
        {
            Exit();
        }
        var processedCount = 0;
        while (PacketQueue.Count > 0 && processedCount < maximumPacketsPerUpdate)   //если длина очереди с пакетами ==0
        {
            byte[] packetbytes;
            lock (packetqueuelock)
            {
                packetbytes = PacketQueue.Dequeue();
            }
            int offset = 0;
            int totallen = 0;
            int num = packetbytes[0];
            int leng = GetLength(packetbytes, packetbytes[0], 0);

            if (leng <= packetbytes.Length)
            {
                while (offset < packetbytes.Length)
                {
                    num = packetbytes[offset];
                    leng = GetLength(packetbytes, num, offset);
                    totallen += leng;                    
                    byte[] newpack = new byte[leng];
                    Array.Copy(packetbytes, offset, newpack, 0, leng);
                    offset += leng;
                    printBytes(newpack);
                    processedCount++;
                    packets[num](newpack); //запустить OnReceive функцию
                }
            }
        }
    }

    public void Start()
    {
        tempflag = "";
        entered = true;
        CreateCameraImage();        
    }

    void CreateCameraImage()
    {
        UserInfo = new GameObject();
        var canvas = GameObject.Find("Canvas");
        UserCameraImage = new GameObject();
        UserCameraImage.transform.parent = canvas.transform;        
        UserCameraImage.name = "UserCameraImage";
        UserCameraImage.transform.localScale = new Vector3(1, 1, 1);
        UserCameraImage.transform.localPosition = new Vector3(0, 0, 0);
        UserCameraImage.transform.localEulerAngles = new Vector3(0, 0, 0);
        var rawimg = UserCameraImage.AddComponent<RawImage>();

        rawimg.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, 0f, 640f);
        rawimg.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0f, 480f);
        var button = UserCameraImage.AddComponent<Button>();
        button.onClick.AddListener(CloseInfo);

        UserCameraImage.SetActive(false);
    }

    void SetResolution(float width, float height)
    {
        if (UserCameraImage != null)
        {
            UserCameraImage.transform.localEulerAngles = new Vector3(0, 0, 0);
            var rawimg = UserCameraImage.GetComponent<RawImage>();
            if (rawimg != null)
            {
                var proportions = width / height;                
                    width = 480f * proportions;
                    height = 480f;
                
                //    RawImage.transform.localScale = new Vector3(1, proportions, 1);
                //else RawImage.transform.localScale = new Vector3(targets[select_target].x / targets[select_target].y, 1, 1);
                rawimg.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, 0f, width);
                rawimg.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0f, height);


            }
        }
    }

    void CloseInfo()
    {
        if (UserCameraImage != null)
        {
            UserCameraImage.SetActive(false);
            var player = PlayerController.instance;
            player.UserInfo.SetActive(false);
        }
    }

    public void LateUpdate()
    {
        while (SendQueue.Count > 0)
        {
            //print("SendQueue.Count " + SendQueue.Count);
            byte[] sendstring = SendQueue.Dequeue();
            //print("sendstring len " + sendstring.Length);
            byteSend(sendstring);
        }
    }

    public class StateObject
    {
        // Client socket.

        // Size of receive buffer.
        public const int BufferSize = 128000;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.
    }

    public static Queue<byte[]> PacketQueue = new Queue<byte[]>();
    static Queue<byte[]> SendQueue = new Queue<byte[]>();
    public static bool receiveDone, connectDone, sendDone = false;

    public static StateObject state = new StateObject();
    private IEnumerator Connect()
    {
        Debug.Log("Connect");
        // Connect to a remote server.
#if UNITY_WEBGL
        var uristring = $"ws://{ServerName}:{webglPort}/ws";
        Debug.Log($"WebSocket tryconnect: {uristring}");
        WebSocket client = new WebSocket(new Uri(uristring));
        Mysocket = client;
        yield return StartCoroutine(Mysocket.Connect());
        connectDone = (Mysocket.error == null);
        if (!connectDone)
        {
            Debug.Log("WebSocket error: " + Mysocket.error);
            yield break;
        }
#else
        // Establish the remote endpoint for the socket.
        /*IPAddress ip = */
        new IPAddress(new byte[] { 127, 0, 0, 1 });
        IPHostEntry ipHostInfo = Dns.GetHostEntry(ServerName);
        //string localHost = Dns.GetHostName();
        //print("localHost " + ipHostInfo.AddressList[0]);
        //IPHostEntry ipHostInfo = Dns.GetHostEntry(localHost);

        IPAddress[] ipv4Addresses = Array.FindAll(ipHostInfo.AddressList, a => a.AddressFamily == AddressFamily.InterNetwork);
        //print("localHost v4 " + ipv4Addresses[0]);
        // IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPAddress ipAddress = ipv4Addresses[0];

        //IPEndPoint remoteEP = new IPEndPoint(ip, socketPort);
        IPEndPoint remoteEP = new IPEndPoint(ipAddress, socketPort);

        // Create a TCP/IP socket.
        Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //Socket client = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        print("remoteEP.AddressFamily " + AddressFamily.InterNetwork);
        print("client " + client.AddressFamily);
        Mysocket = client;
        // Connect to the remote endpoint.
        try
        {
            client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
        }
        catch (Exception e)
        {
            print(e.Message);
        }
        int num = 0;
        while (!connectDone && num < 10)
        {
            num++;
            yield return new WaitForSeconds(0.2f);
        }

        if (!connectDone)
        {
            print(socketPort + " port failed");
            yield break;
        }
#endif
        print("connected");
        connected = true;
        while (true)
        {
            if (!entered)
                break;

            while (!sendDone && connected)
            {
                yield return 1;
            }

            Receive();

            while (!receiveDone && connected)
            {
                yield return 1;
            }
            receiveDone = false;
            break;
        }
        yield break;
    }

    private static void ConnectCallback(IAsyncResult ar)
    {
        // Retrieve the socket from the state object.
        Socket client = (Socket)ar.AsyncState;
        connect_started = DateTime.Now;
        // Complete the connection.
        try
        {
            client.EndConnect(ar);
        }
        catch (Exception e)
        {
            print(e.Message);
        }

        // Signal that the connection has been made.
        connectDone = true;
    }

    private void Receive()
    {
#if UNITY_WEBGL
        CheckWebSocket();

        if (Mysocket == null)
            return;

        byte[] packet = null;
        do
        {
            packet = Mysocket.Recv();
            if (packet != null)
            {
                lock (packetqueuelock)
                {
                    PacketQueue.Enqueue(packet);
                }
                receiveDone = true;
                totalbytes += packet.Length;
            }
        }
        while (packet != null);
#else
        if (Mysocket != null)
        {
            if (state != null)
                Mysocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
        }
#endif
    }

    public static object packetqueuelock = new object();
    public static Queue<byte[]> tempqueue = new Queue<byte[]>();

    public bool CheckLength(byte[] packetbytes)
    {
        if (packetbytes.Length > 0)   //если длина очереди с пакетами ==0
        {
            int offset = 0;
            //TODO проверка на мусор
            int num = packetbytes[0];
            int leng = GetLength(packetbytes, packetbytes[0], 0);

            if (leng <= packetbytes.Length)
            {
                while (offset < packetbytes.Length)
                {
                    num = packetbytes[offset];
                    leng = GetLength(packetbytes, num, offset);
                    if (leng == 1)
                    {
                        print("Error: CheckLength: packet not complete");
                        return false;
                    }
                    //print("Checking Length of " + num + " len " + leng);
                    if (leng == 0)
                    {
                        print("CheckLength2 break! length error " + num);
                        dc = true;
                        break;
                    }
                    offset += leng;
                }
            }
            if (offset == packetbytes.Length)
                return true;
            return false;
        }
        return false;
    }

    public static int totalbytes = 0;

#if !UNITY_WEBGL
    private void ReceiveCallback(IAsyncResult ar)    //TODO если приходят данные больше длины буфера, пакеты режутся на части и складываются в обратном порядке - fix, пока что увеличим буфер
    {
        // Retrieve the state object and the client socket 
        // from the asynchronous state object.
        StateObject state = (StateObject)ar.AsyncState;

        // Read data from the remote device.
        int bytesRead = 0;
        bytesRead = Mysocket.EndReceive(ar);

        if (bytesRead > 0)
        {
            // All the data has arrived; put it in response.
            byte[] newbuf = new Byte[bytesRead];
            Array.Copy(state.buffer, newbuf, bytesRead);

            //if (ShowPackets)
            //{
            //    if (newbuf[0] != 30)    //не пакет пинга
            //    {
            //        string mygg = "";       //Печатаем принятые байты!
            //        foreach (byte b in newbuf)
            //        {
            //            mygg += b + " ";
            //        }
            //        print(newbuf.Length + " bytes: " + mygg);
            //    }
            //    totalbytes += bytesRead;
            //}

            byte[] tocheck = newbuf;
            if (tempqueue.Count > 0)
            {
                Queue<byte[]> myqueue = new Queue<byte[]>(tempqueue);
                List<byte> list = new List<byte>();
                while (myqueue.Count > 0)
                {
                    list.AddRange(myqueue.Dequeue());
                }
                list.AddRange(newbuf);
                tocheck = list.ToArray();
            }
            if (!CheckLength(tocheck))
            {
                tempqueue.Clear();
                tempqueue.Enqueue(tocheck);
            }
            else
            {
                tempqueue.Clear();
                lock (packetqueuelock)
                {
                    PacketQueue.Enqueue(tocheck);
                }
            }
            receiveDone = true;
            Receive();
        }
    }
#endif

#if UNITY_WEBGL
    private static void CheckWebSocket()
    {
        if (Mysocket != null && Mysocket.error != null)
        {
            Debug.LogError(Mysocket.error);
            instance.Exit();
        }
    }
#endif

    private static void byteSend(byte[] tosend)
    {
        if (tosend == null)
        {
            Debug.LogError("tosend null");
            return;
        }
        printBytes(tosend, true);

#if UNITY_WEBGL
        byte[] webglbuf = new Byte[tosend.Length];
        Array.Copy(tosend, webglbuf, tosend.Length);
        CheckWebSocket();
        if (Mysocket != null)
        {
            Mysocket.Send(webglbuf);
            sendDone = true;
            Debug.Log("websocket sendDone " + webglbuf.Length);
        }
#else
        try
        {
            //print("buffer size " + Mysocket.SendBufferSize + " tosend.Length " + tosend.Length);            
            Mysocket.BeginSend(tosend, 0, tosend.Length, 0, SendCallback, Mysocket);
        }
        catch (Exception e)
        {
            if (instance != null)
            {
                print(e.Message);
                instance.Exit();
            }
        }
#endif
    }

#if !UNITY_WEBGL
    private static void SendCallback(IAsyncResult ar)
    {
        // Retrieve the socket from the state object.
        Socket client = (Socket)ar.AsyncState;

        // Complete sending the data to the remote device.
        /*int bytesSent = */
        client.EndSend(ar);

        //print("Sent " + bytesSent + " bytes to server.");

        // Signal that all bytes have been sent.

        sendDone = true;
    }
#endif

    #endregion

    public static string GetMD5Hash(string input)
    {
        System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
        byte[] bs = Encoding.UTF8.GetBytes(input);
        bs = x.ComputeHash(bs);
        StringBuilder s = new StringBuilder();
        foreach (byte b in bs)
        {
            s.Append(b.ToString("x2").ToLower());
        }
        string password = s.ToString();
        return password;
    }

    /// <summary>
    /// Запрос координат с сервера, timeStart, timeEnd == DateTime.Ticks
    /// </summary>
    public void CoordinatesRequest(long timeStart, long timeEnd, byte resolution, uint locationID, uint account_id)
    {
        login_sent = true;
        Debug.Log("SendRequestCoordinates");
        List<byte> list = new List<byte>();
        list.Add(3);
        list.AddRange(BitConverter.GetBytes(account_id));
        list.AddRange(BitConverter.GetBytes(timeStart));
        list.AddRange(BitConverter.GetBytes(timeEnd));
        list.Add(resolution);
        list.AddRange(BitConverter.GetBytes(locationID));
        SendEnqueue(list.ToArray());
    }

    public static byte[] ConstructVariablePacket(byte num, byte[] vardatabytes)
    {
        List<byte> bytedata = new List<byte>();
        bytedata.Add(num);
        int len = vardatabytes.Length + 5;
        bytedata.AddRange(BitConverter.GetBytes(len));
        bytedata.AddRange(vardatabytes);
        return bytedata.ToArray();
    }

    public void MessageReceiver(string data)
    {
        var expl = data.Split(' ');

        var accname = expl[0];
        //account_id = Convert.ToUInt32(expl[1]);
        //company_id = Convert.ToUInt32(expl[2]);
        SendLogin(accname, "333666999");

        //tempflag = data + " " + accname;
    }

    /// <summary>
    /// Sender: current account, receiver account: target
    /// </summary>
    /// <param name="target">receiver account</param>
    public void RemoteAssistRequestVideoCall(uint targetId, RemoteAssistCMD cmd)
    {
        MemoryStream ms = new MemoryStream();
        ms.WriteByte((byte)cmd);
        BinaryWriter bw = new BinaryWriter(ms);        
        bw.Write(targetId);
        var data = ConstructVariablePacket(59, ms.ToArray());        
        SendEnqueue(data);
    }

    public void RemoteAssistRequestAudioCall(User target)
    {

    }

    public void RemoteAssistSendChatMessage(User target)
    {

    }

    public void RemoteAssistScreenshotReceive()
    {

    }

    public void RemoteAssistScreenshotSend()
    {

    }

    public void SendLogin(string login, string passwordToEdit)
    {
        if (!connected)
        {
            //Debug.LogError("Couldn't connect");
            //Exit();
            return;
        }
        if (!login_sent)
        {
            login_sent = true;
            string pass = GetMD5Hash(passwordToEdit);
            byte[] bpass = Encoding.UTF8.GetBytes(pass);
            byte[] blogin = Encoding.UTF8.GetBytes(login);
            List<byte> list = new List<byte>();
            byte loginFlag = 1;
            int leng = (bpass.Length + blogin.Length + 6);   //+имя+длина        
            list.Add(2);
            list.AddRange(BitConverter.GetBytes(leng));
            list.Add(loginFlag);
            list.AddRange(bpass);
            list.AddRange(blogin);
            SendEnqueue(list.ToArray());
        }
    }

    #region test functions
    public void ImageReceive(byte[] bytedata)
    {
        var cmd = (ImageCMD)bytedata[5];
        switch (cmd)
        {
            case ImageCMD.receiveImage:
                var fromId = BitConverter.ToUInt32(bytedata, 6);  //кому пришло
                User from = User.Find(fromId);
                if (from != null)
                {
                    var imglen = bytedata.Length - 10;
                    byte[] rawdata = new byte[imglen];

                    Array.Copy(bytedata, 10, rawdata, 0, imglen);
                    textureReceived.LoadImage(rawdata);

                    if (remoteScript_.SessionCallerId == fromId)
                    {
                        //UserCameraImage == remoteassist image
                        remoteScript_.remoteCamImage.texture = textureReceived;
                    }                    
                    else if (UserCameraImage != null)
                    {
                        SetResolution(textureReceived.width, textureReceived.height);

                        UserCameraImage.SetActive(true);
                        UserCameraImage.GetComponent<RawImage>().texture = textureReceived;
                        var player = PlayerController.instance;
                        UserCameraImage.transform.SetParent(player.UserInfo.transform);
                        player.UserInfo.SetActive(player.broadcast);
                    }
                }
                else
                {
                    Debug.LogError("Image sender not found, id: " + fromId);
                    User.SendGetUser(fromId);
                }
                break;
            case ImageCMD.streamStart: break;
            case ImageCMD.streamStop: break;
        }
    }
    public void ImageStreamStartSend(uint targetAccountId)
    {
        MemoryStream ms = new MemoryStream();
        ms.WriteByte((byte)ImageCMD.streamStart);
        BinaryWriter bw = new BinaryWriter(ms);
        bw.Write(targetAccountId);

        //ms.Write(bdata, 0, bdata.Length);
        //Debug.Log("imagesend bytes "+ms.ToArray().Length);
        var data = ConstructVariablePacket(55, ms.ToArray());
        SendEnqueue(data);
    }

    public void ImageStreamStopSend(uint accid)
    {
        MemoryStream ms = new MemoryStream();
        ms.WriteByte((byte)ImageCMD.streamStop);
        BinaryWriter bw = new BinaryWriter(ms);
        bw.Write(accid);
        //ms.Write(bdata, 0, bdata.Length);
        //Debug.Log("imagesend bytes "+ms.ToArray().Length);
        var data = ConstructVariablePacket(55, ms.ToArray());
        SendEnqueue(data);
    }

    public static void ImageStreamControlCommandSend(uint accid, ImageCMD cmd)
    {
        MemoryStream ms = new MemoryStream();
        ms.WriteByte((byte) cmd);
        BinaryWriter bw = new BinaryWriter(ms);
        bw.Write(accid);
        //ms.Write(bdata, 0, bdata.Length);
        //Debug.Log("imagesend bytes "+ms.ToArray().Length);
        var data = ConstructVariablePacket(55, ms.ToArray());
        SendEnqueue(data);
    }

    public void ImageSend(byte[] bdata, uint targetAccountId)
    {
        print("ImageSend " + targetAccountId);
        MemoryStream ms = new MemoryStream();              
        ms.WriteByte((byte)ImageCMD.receiveImage);
        ms.Write(BitConverter.GetBytes(targetAccountId), 0, 4);
        ms.Write(bdata, 0, bdata.Length);
        var data = ConstructVariablePacket(55, ms.ToArray());
        SendEnqueue(data);
    }

    public void OnGUI()
    {
        //if (GUI.Button(new Rect(250, 350, 160, 64), "Test VideoCall!"))
        //{
        //    RemoteAssistRequestVideoCall(4);
        //}
        //if (GUI.Button(new Rect(250, 280, 160, 64), "Test"))
        //{
        //    var fbytes = File.ReadAllBytes("Assets/Resources/Image/photo_2020-04-15_15-40-54.jpg");
        //    print("fbytes " + fbytes.Length);
        //    ImageSend(fbytes);
        //}

        //if (GUI.Button(new Rect(250, 350, 160, 64), "Test Login!"))
        //{
        //    SendLogin("Admin12", "333666999");
        //    account_id = 1;
        //    company_id = 2;

        //    AuthorizationController.success = true;
        //}

        //if (tempflag != "")
        //{
        //    if (GUI.Button(new Rect(250, 350, 160, 64), "Stop Stream Image!"))
        //    {
        //        ImageStreamStopSend(4);
        //    }
        //}

        //if (tempflag != "")
        //{
        //    if (GUI.Button(new Rect(250, 280, 160, 64), "Start Stream Image!"))
        //    {
        //        var fbytes = File.ReadAllBytes("Assets/Resources/Image/photo_2020-04-15_15-40-54.jpg");
        //        print("fbytes " + fbytes.Length);
        //        ImageSend(fbytes);
        //    }
        //}
    }
    #endregion
}