﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using Coderluna.Events;

namespace Coderluna.Scene
{
    using event_kind = System.Enum;

    public partial class SceneComponent : ManagedComponent, ISender, IReceiver
    {
        protected enum channel
        {
            global,
            local,
        }

        protected enum receiver
        {
            none,
            self,
        }

        private EventModule _events;
        private List<binder> _binders;

        private channel _channel_mode;
        private receiver _receiver_mode;

        public bool receive { get; protected set; }

        protected override bool initialize()
        {
            if (base.initialize()) {
                _events = module.component<EventModule>();
                _binders = new List<binder>();

                this.setup_bind(channel.global, receiver.none);

                return true;
            }
            return false;
        }

        protected override void release()
        {
            this.unbind_all();

            _events = null;

            base.release();
        }

        protected new SceneManager manager
        {
            get { return base.manager as SceneManager; }
        }

        public SceneNode node
        {
            get { return this.manager.node; }
        }
        
        protected void setup_bind(channel mode, receiver target)
        {
            _channel_mode = mode;
            _receiver_mode = target;
        }

        protected void bind(event_kind kind, Listener function)
        {
            if (this.find_binder(kind) == null) {
                IChannel channel = this.get_channel_object(_channel_mode);
                var binder = new binder(channel, _receiver_mode, kind, function);

                _binders.Add(binder);

                binder.bind(_events, this);
            }
        }

        protected void unbind(event_kind kind)
        {
            var binder = this.find_binder(kind);

            if (binder != null) {
                binder.unbind(_events, this);
                _binders.Remove(binder);
            }
        }

        protected void unbind_all()
        {
            foreach (var binder in _binders)
                binder.unbind(_events, this);

            _binders.Clear();
        }

        protected void connect(event_kind kind, bool mode)
        {
            var binder = this.find_binder(kind);

            if (binder == null || binder.is_bind == mode)
                return;

            if (mode)
                binder.bind(_events, this);
            else
                binder.unbind(_events, this);
        }

        protected virtual IChannel get_channel_object(channel mode)
        {
            return null;
        }

        private binder find_binder(event_kind kind)
        {
            return _binders.Find(e => e.kind == kind);
        }

        private class binder
        {
            public IChannel channel { get; private set; }
            public receiver target { get; private set; }
            public event_kind kind { get; private set; }
            public Listener function { get; private set; }

            public bool is_bind { get; private set; }

            public binder(IChannel channel, receiver target, event_kind kind, Listener function)
            {
                this.channel = channel;
                this.target = target;
                this.kind = kind;
                this.function = function;
            }

            public void bind(EventModule events, IReceiver target)
            {
                if (this.is_bind)
                    return;

                switch (this.target) {
                    case receiver.none: events.attach(channel, kind, function); break;
                    case receiver.self: events.attach(channel, kind, target, function); break;
                }

                this.is_bind = true;
            }

            public void unbind(EventModule events, IReceiver target)
            {
                if (!this.is_bind)
                    return;

                switch (this.target) {
                    case receiver.none: events.detach(channel, kind, function); break;
                    case receiver.self: events.detach(channel, kind, target, function); break;
                }

                this.is_bind = false;
            }
        }
    }

    public class SceneObject : SceneComponent, IChannel
    {
        protected sealed override IChannel get_channel_object(channel mode)
        {
            return this;
        }
    }

    public class SceneElement : SceneComponent
    {

    }

    public class UIRoot : SceneObject
    {

    }

    public class UIElement : SceneElement
    {

    }
}

