using System;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using Grpc.Core;
using Aurora.RemoteImpl;
using Aurora.Proto.Events;
using Aurora.Proto.Party;
using Aurora.Proto.Playback;
using Aurora.Proto.Sync;


namespace Aurora.Services
{
    public class ServerService : BaseService<ServerService>
    {
        private int _port = SettingsService.Instance.DefaultPort;
        private string _hostname;
        private Grpc.Core.Server _server;

        //Implementation class declarations
        private RemotePartyServiceImpl _remotePartyServiceImpl;
        private RemoteEventServiceImpl _remoteEventImpl;
        private RemotePlaybackServiceImpl _remotePlaybackImpl;
        private RemoteSyncServiceImpl _remoteSyncImpl;

        /// <summary>
        /// Constructor. Registers GRPC service implementations.
        /// </summary>
        public ServerService()
        {
            string host = GetLocalIPAddress();

            if (string.IsNullOrWhiteSpace(host))
            {
                throw new Exception("This device must have a valid IP address");
            }

            _hostname = host;

            _server = new Grpc.Core.Server
            {
                Ports = { new ServerPort(_hostname, _port, ServerCredentials.Insecure) }
            };
        }

        public int Port
        {
            get { return _port; }
        }

        public string Hostname
        {
            get { return _hostname; }
        }

        public bool Initialized
        {
            get
            {
                return (_remoteEventImpl != null &&
                    _remotePartyServiceImpl != null &&
                    _server != null);
            }
        }


        /// <summary>
        /// Start Server
        /// </summary>
        public void Start()
        {
            try
            {

                Console.WriteLine(string.Format("Starting gRPC server at hostname: {0}, port: {1}", _hostname, _port));

                if (!Initialized)
                {
                    //Construct implementations
                    _remotePartyServiceImpl = new RemotePartyServiceImpl();
                    _remoteEventImpl = new RemoteEventServiceImpl();
                    _remotePlaybackImpl = new RemotePlaybackServiceImpl();
                    _remoteSyncImpl = new RemoteSyncServiceImpl();

                    // Register grpc RemoteService with singleton server service
                    RegisterService(RemotePartyService.BindService(_remotePartyServiceImpl));
                    RegisterService(RemoteEventService.BindService(_remoteEventImpl));
                    RegisterService(RemotePlaybackService.BindService(_remotePlaybackImpl));
                    RegisterService(RemoteSyncService.BindService(_remoteSyncImpl));
                }
                _server.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("Error starting gRPC server: {0}", ex.Message));
            }
        }

        /// <summary>
        /// Shutdown server async.
        /// </summary>
        /// <returns>Task</returns>
        public async Task Stop()
        {
            await _server.ShutdownAsync();
        }

        public async Task Reset()
        {
            await Stop();
            _server = new Grpc.Core.Server
            {
                Ports = { new ServerPort("localhost", _port, ServerCredentials.Insecure) }
            };
        }

        private void RegisterService(ServerServiceDefinition definition)
        {
            _server.Services.Add(definition);
        }

        public static string GetLocalIPAddress()
        {
            string returnIp = "";
            var host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (var ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    returnIp = ip.ToString();
                }
            }
            return returnIp;
        }
    }
}