using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace Passer.Control.Core {

    public class SiteServer : Participant {

        public SiteServer(int port = 7681) {
            this.ipAddress = "0.0.0.0";
            this.port = port;
            this.endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), 0); // for sending, but should not be used really...
            this.udpClient = new UdpClient(); // for receiving
            this.udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port));
            this.udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null);
            this.name = "Site Server";

            Register<TemperatureSensor>((byte)Thing.Type.TemperatureSensor);
        }

        public override void Update(ulong currentTimeMs) {
            Thing.UpdateAll(currentTimeMs);
        }

        protected override void Process(Participant sender, ClientMsg msg) {
            if (msg.networkId == 0) {
                Console.WriteLine($"{this.name} received New Client -> {sender.networkId}");
                sender.Send(new NetworkIdMsg(sender.networkId));
            }
        }

        protected override void Process(Participant sender, NetworkIdMsg msg) {
        }


        delegate Thing ThingConstructor(byte networkId, byte thingId);
        readonly Dictionary<byte, ThingConstructor> thingMsgProcessors = new();

        public void Register<ThingClass>(byte thingType) where ThingClass : Thing {
            thingMsgProcessors[thingType] = (byte networkId, byte thingId) => {
                if (Activator.CreateInstance(typeof(Thing), networkId, thingId) is not ThingClass instance)
                    throw new InvalidOperationException($"Could not created an instance of {(typeof(ThingClass))}.");
                return instance;
            };
            Console.WriteLine($"Registering {typeof(ThingClass)} for thing type {thingType}");
        }

        protected override void Process(ThingMsg msg) {
            Thing thing = Thing.Get(msg.networkId, msg.thingId);
            if (thing == null) {
                if (thingMsgProcessors.TryGetValue(msg.thingType, out ThingConstructor thingConstructor)) {
                    thingConstructor(msg.networkId, msg.thingId);
                }
                else {
                    new Thing(this, msg.networkId, msg.thingId, msg.thingType);
                }
            }
        }

    }
}