// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using UnityEngine;

namespace HoloToolkit.Sharing.Utilities
{
    /// <summary>
    /// This class enables users to pair with a remote client directly.
    /// One side should use the Receiver role, the other side should use the Connector role.
    /// RemoteAddress and RemotePort are used by the Connector role, LocalPort is used by the Receiver.
    /// </summary>
    public class DirectPairing : MonoBehaviour
    {
        public enum Role
        {
            Connector,
            Receiver
        }

        public Role PairingRole = Role.Connector;
        public string RemoteAddress = "localhost";
        public ushort RemotePort = 0x507B;
        public ushort LocalPort = 0x507B;
        public bool AutoReconnect = true;

        private SharingManager sharingMgr;
        private PairMaker pairMaker;
        private PairingAdapter pairingAdapter;
        private NetworkConnectionAdapter connectionAdapter;

        private void Start()
        {
            if (PairingRole == Role.Connector)
            {
                pairMaker = new DirectPairConnector(RemoteAddress, RemotePort);
            }
            else
            {
                pairMaker = new DirectPairReceiver(LocalPort);
            }

            pairingAdapter = new PairingAdapter();
            pairingAdapter.SuccessEvent += OnPairingConnectionSucceeded;
            pairingAdapter.FailureEvent += OnPairingConnectionFailed;

            // Register to listen for disconnections, so we can reconnect automatically
            if (SharingStage.IsInitialized)
            {
                sharingMgr = SharingStage.Instance.Manager;

                if (sharingMgr != null)
                {
                    connectionAdapter = new NetworkConnectionAdapter();
                    connectionAdapter.DisconnectedCallback += OnDisconnected;

                    NetworkConnection pairedConnection = sharingMgr.GetPairedConnection();
                    pairedConnection.AddListener((byte)MessageID.StatusOnly, connectionAdapter);
                }
            }

            StartPairing();
        }

        private void OnDestroy()
        {
            // SharingStage's OnDestroy() might execute first in the script order. Therefore we should check if
            // SharingStage.Instance still exists. Without the instance check, the execution of GetPairingManager
            // on a disposed sharing manager will crash the Unity Editor and application.
            if (SharingStage.IsInitialized && sharingMgr != null)
            {
                PairingManager pairingMgr = sharingMgr.GetPairingManager();
                pairingMgr.CancelPairing(); // Safe to call, even if no pairing is in progress.  Will not cause a disconnect

                // Remove our listener from the paired connection
                NetworkConnection pairedConnection = sharingMgr.GetPairedConnection();
                pairedConnection.RemoveListener((byte)MessageID.StatusOnly, connectionAdapter);
            }
        }

        private void OnPairingConnectionSucceeded()
        {
            if (SharingStage.Instance.ShowDetailedLogs)
            {
                Debug.Log("Direct Pairing Succeeded");
            }
        }

        private void OnPairingConnectionFailed(PairingResult result)
        {
            Debug.LogWarning("Direct pairing failed: " + result);

            if (AutoReconnect)
            {
                Debug.LogWarning("Attempting to reconnect...");
                StartPairing();
            }
        }

        private void OnDisconnected(NetworkConnection connection)
        {
            Debug.LogWarning("Remote client disconnected");

            if (AutoReconnect)
            {
                StartPairing();
            }
        }

        private void StartPairing()
        {
            if (sharingMgr != null)
            {
                PairingManager pairingMgr = sharingMgr.GetPairingManager();

                PairingResult result = pairingMgr.BeginPairing(pairMaker, pairingAdapter);
                if (result != PairingResult.Ok)
                {
                    Debug.LogError("Failed to start pairing");
                }
            }
        }
    }
}