using System;
using ComponentFramework.Modules;
using ComponentFramework;
using MTV3D65;

namespace OctreeRendering
{
    class CameraState : Service, ICameraState
    {
        public event Action ViewChanged = ActionHelper.NullAction;
        public event Action ProjectionChanged = ActionHelper.NullAction;

        public CameraState(ICore core) : base(core) { }

        public BoundingFrustum Frustum { get; private set; }

        public override void Initialize()
        {
            Frustum = new BoundingFrustum();
            Camera = CameraFactory.GetCamera(0);

            OnViewChanged();
            OnProjectionChanged();
        }

        Matrix oldView;
        public void OnViewChanged()
        {
            View = Camera.GetViewMatrix();
            if (oldView != View)
            {
                oldView = View;
                Position = Camera.GetPosition();

                UpdateFrustum();
                ViewChanged();
            }
        }

        Matrix oldProjection;
        public void OnProjectionChanged()
        {
            Projection = Camera.GetProjectionMatrix();
            if (oldProjection != Projection)
            {
                oldProjection = Projection;

                float fov = 0, near = 0, far = 0;
                Camera.GetViewFrustum(ref fov, ref far, ref near);
                NearPlane = near;
                FarPlane = far;
                FieldOfView = fov;

                UpdateFrustum();
                ProjectionChanged();
            }
        }

        void UpdateFrustum()
        {
            ViewProjection = View * Projection;
            Camera.GetFrustumPlanes(ref Frustum.Planes[0]);
        }

        public TVCamera Camera { get; private set; }

        public Matrix View { get; private set; }
        public Matrix Projection { get; private set; }
        public Matrix ViewProjection { get; private set; }

        public float NearPlane { get; private set; }
        public float FarPlane { get; private set; }
        public float FieldOfView { get; private set; }

        public Vector3f Position { get; private set; }
    }

    interface ICameraState : IService
    {
        event Action ViewChanged;
        void OnViewChanged();

        event Action ProjectionChanged;
        void OnProjectionChanged();

        TVCamera Camera { get; }

        BoundingFrustum Frustum { get; }

        float NearPlane { get; }
        float FarPlane { get; }
        float FieldOfView { get; }

        Vector3f Position { get; }

        Matrix View { get; }
        Matrix Projection { get; }
        Matrix ViewProjection { get; }
    }
}
