// uses the first available transport for server and client.
// example: to use Apathy if on Windows/Mac/Linux and fall back to Telepathy
//          otherwise.
using System;
using UnityEngine;

namespace Mirror
{
    // Deprecated 2021-05-13
    [HelpURL("https://mirror-networking.gitbook.io/docs/transports/fallback-transport")]
    [DisallowMultipleComponent]
    [Obsolete("Fallback Transport will be retired. It was only needed for Apathy/Libuv. Use kcp or Telepathy instead, those run everywhere.")]
    public class FallbackTransport : Transport
    {
        public Transport[] transports;

        // the first transport that is available on this platform
        Transport available;

        public void Awake()
        {
            if (transports == null || transports.Length == 0)
            {
                throw new Exception("FallbackTransport requires at least 1 underlying transport");
            }
            available = GetAvailableTransport();
            Debug.Log("FallbackTransport available: " + available.GetType());
        }

        void OnEnable()
        {
            available.enabled = true;
        }

        void OnDisable()
        {
            available.enabled = false;
        }

        // The client just uses the first transport available
        Transport GetAvailableTransport()
        {
            foreach (Transport transport in transports)
            {
                if (transport.Available())
                {
                    return transport;
                }
            }
            throw new Exception("No transport suitable for this platform");
        }

        public override bool Available()
        {
            return available.Available();
        }

        public override void ClientConnect(string address)
        {
            available.OnClientConnected = OnClientConnected;
            available.OnClientDataReceived = OnClientDataReceived;
            available.OnClientError = OnClientError;
            available.OnClientDisconnected = OnClientDisconnected;
            available.ClientConnect(address);
        }

        public override void ClientConnect(Uri uri)
        {
            foreach (Transport transport in transports)
            {
                if (transport.Available())
                {
                    try
                    {
                        transport.ClientConnect(uri);
                        available = transport;
                    }
                    catch (ArgumentException)
                    {
                        // transport does not support the schema, just move on to the next one
                    }
                }
            }
            throw new Exception("No transport suitable for this platform");
        }

        public override bool ClientConnected()
        {
            return available.ClientConnected();
        }

        public override void ClientDisconnect()
        {
            available.ClientDisconnect();
        }

        public override void ClientSend(ArraySegment<byte> segment, int channelId)
        {
            available.ClientSend(segment, channelId);
        }

        // right now this just returns the first available uri,
        // should we return the list of all available uri?
        public override Uri ServerUri() => available.ServerUri();

        public override bool ServerActive()
        {
            return available.ServerActive();
        }

        public override string ServerGetClientAddress(int connectionId)
        {
            return available.ServerGetClientAddress(connectionId);
        }

        public override void ServerDisconnect(int connectionId)
        {
            available.ServerDisconnect(connectionId);
        }

        public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId)
        {
            available.ServerSend(connectionId, segment, channelId);
        }

        public override void ServerStart()
        {
            available.OnServerConnected = OnServerConnected;
            available.OnServerDataReceived = OnServerDataReceived;
            available.OnServerError = OnServerError;
            available.OnServerDisconnected = OnServerDisconnected;
            available.ServerStart();
        }

        public override void ServerStop()
        {
            available.ServerStop();
        }

        public override void ClientEarlyUpdate() => available.ClientEarlyUpdate();
        public override void ServerEarlyUpdate() => available.ServerEarlyUpdate();
        public override void ClientLateUpdate() => available.ClientLateUpdate();
        public override void ServerLateUpdate() => available.ServerLateUpdate();

        public override void Shutdown()
        {
            available.Shutdown();
        }

        public override int GetMaxPacketSize(int channelId = 0)
        {
            // finding the max packet size in a fallback environment has to be
            // done very carefully:
            // * servers and clients might run different transports depending on
            //   which platform they are on.
            // * there should only ever be ONE true max packet size for everyone,
            //   otherwise a spawn message might be sent to all tcp sockets, but
            //   be too big for some udp sockets. that would be a debugging
            //   nightmare and allow for possible exploits and players on
            //   different platforms seeing a different game state.
            // => the safest solution is to use the smallest max size for all
            //    transports. that will never fail.
            int mininumAllowedSize = int.MaxValue;
            foreach (Transport transport in transports)
            {
                int size = transport.GetMaxPacketSize(channelId);
                mininumAllowedSize = Mathf.Min(size, mininumAllowedSize);
            }
            return mininumAllowedSize;
        }

        public override string ToString()
        {
            return available.ToString();
        }

    }
}
