using System;
using UnityEngine;
using Unity.Networking.Transport;
using Unity.Collections;
using Unity.Jobs;
using Unity.Networking.Transport.Utilities;

namespace Unity.Networking.Transport.Samples
{
    public class SoakServer : IDisposable
    {
        private NetworkDriver m_ServerDriver;
        private NetworkPipeline m_Pipeline;
        private NetworkPipelineStageId m_ReliableStageId;
        private int m_Tick;
        private double m_NextStatsPrint;

        private NativeList<SoakClientCtx> m_Connections;
        private JobHandle m_UpdateHandle;

        private NativeArray<int> m_PendingDisconnects;

        public void Start()
        {
            m_Connections = new NativeList<SoakClientCtx>(1, Allocator.Persistent);
            m_ServerDriver = NetworkDriver.Create();
            //m_Pipeline = m_ServerDriver.CreatePipeline(typeof(UnreliableSequencedPipelineStage));
            m_Pipeline = m_ServerDriver.CreatePipeline(typeof(ReliableSequencedPipelineStage));
            m_ReliableStageId = NetworkPipelineStageCollection.GetStageId(typeof(ReliableSequencedPipelineStage));
            var addr = NetworkEndPoint.AnyIpv4;
            addr.Port = 9000;
            if (m_ServerDriver.Bind(addr) != 0)
                Debug.Log("Failed to bind to port 9000");
            else
                m_ServerDriver.Listen();
        }

        public void Update()
        {
            m_Tick++;
            m_UpdateHandle.Complete();

            if (m_PendingDisconnects.IsCreated)
            {
                m_PendingDisconnects.Dispose();
            }

            var acceptJob = new SoakServerAcceptJob
            {
                now = m_Tick,
                driver = m_ServerDriver,
                connections = m_Connections
            };
            var soakJob = new SoakServerUpdateClientsJob
            {
                driver = m_ServerDriver.ToConcurrent(),
                pipeline = m_Pipeline,
                connections = m_Connections.AsDeferredJobArray()
            };

            /*var time = Time.fixedTime;
            if (time > m_NextStatsPrint)
            {
                PrintStatistics();
                m_NextStatsPrint = time + 10;
            }*/

            m_UpdateHandle = m_ServerDriver.ScheduleUpdate();
            m_UpdateHandle = acceptJob.Schedule(m_UpdateHandle);
            m_UpdateHandle = soakJob.Schedule(m_Connections, 1, m_UpdateHandle);
        }

        public void Dispose()
        {
            m_UpdateHandle.Complete();
            m_ServerDriver.Dispose();
            m_Connections.Dispose();
        }

        void PrintStatistics()
        {
            for (int i = 0; i < m_Connections.Length; i++)
            {
                Debug.Log("Server dumping stats for client " + i);
                Util.DumpReliabilityStatistics(m_ServerDriver, m_Pipeline, m_ReliableStageId, m_Connections[i].Connection);
            }
        }
    }
}
