/**********************************************************************************
* Blueprint Reality Inc. CONFIDENTIAL
* 2023 Blueprint Reality Inc.
* All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains, the property of
* Blueprint Reality Inc. and its suppliers, if any.  The intellectual and
* technical concepts contained herein are proprietary to Blueprint Reality Inc.
* and its suppliers and may be covered by Patents, pending patents, and are
* protected by trade secret or copyright law.
*
* Dissemination of this information or reproduction of this material is strictly
* forbidden unless prior written permission is obtained from Blueprint Reality Inc.
***********************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

namespace BlueprintReality.MixCast
{
    #region Virtual Lighting
    public enum ExpLightType
    {
        None,
        Point,
        Directional,
        Spot
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ExpLight
    {
        public ExpLightType type;

        public Vector3 color;

        public Vector3 pos;
        public Vector3 forward;

        public float range;     //for Point and Spot
        public float angle;     //for Spot
    }

    //avoiding array to avoid memory allocation (trust me)!
    [StructLayout(LayoutKind.Sequential)]
    public struct ExpLights
    {
        public const int MaxLights = 16;

        public ExpLight light0;
        public ExpLight light1;
        public ExpLight light2;
        public ExpLight light3;
        public ExpLight light4;
        public ExpLight light5;
        public ExpLight light6;
        public ExpLight light7;
        public ExpLight light8;
        public ExpLight light9;
        public ExpLight light10;
        public ExpLight light11;
        public ExpLight light12;
        public ExpLight light13;
        public ExpLight light14;
        public ExpLight light15;

        public ExpLight this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0: return light0;
                    case 1: return light1;
                    case 2: return light2;
                    case 3: return light3;
                    case 4: return light4;
                    case 5: return light5;
                    case 6: return light6;
                    case 7: return light7;
                    case 8: return light8;
                    case 9: return light9;
                    case 10: return light10;
                    case 11: return light11;
                    case 12: return light12;
                    case 13: return light13;
                    case 14: return light14;
                    case 15: return light15;
                }

                throw new ArgumentOutOfRangeException();
            }
            set
            {
                switch (index)
                {
                    case 0: light0 = value; break;
                    case 1: light1 = value; break;
                    case 2: light2 = value; break;
                    case 3: light3 = value; break;
                    case 4: light4 = value; break;
                    case 5: light5 = value; break;
                    case 6: light6 = value; break;
                    case 7: light7 = value; break;
                    case 8: light8 = value; break;
                    case 9: light9 = value; break;
                    case 10: light10 = value; break;
                    case 11: light11 = value; break;
                    case 12: light12 = value; break;
                    case 13: light13 = value; break;
                    case 14: light14 = value; break;
                    case 15: light15 = value; break;

                    default: throw new ArgumentOutOfRangeException();
				}
            }
        }
    }
    #endregion

    #region Tracked People
    public enum TrackedPersonJoint
    {
        Pelvis = 0,
        NavalSpine = 1,
        ChestSpine = 2,
        Neck = 3,
        ClavicleLeft = 4,
        ShoulderLeft = 5,
        ElbowLeft = 6,
        WristLeft = 7,
        HandLeft = 8,
        HandTipLeft = 9,
        ThumbLeft = 10,
        ClavicleRight = 11,
        ShoulderRight = 12,
        ElbowRight = 13,
        WristRight = 14,
        HandRight = 15,
        HandTipRight = 16,
        ThumbRight = 17,
        HipLeft = 18,
        KneeLeft = 19,
        AnkleLeft = 20,
        FootLeft = 21,
        HipRight = 22,
        KneeRight = 23,
        AnkleRight = 24,
        FootRight = 25,
        Head = 26,
        Nose = 27,
        EyeLeft = 28,
        EarLeft = 29,
        EyeRight = 30,
        EarRight = 31,
        HeelLeft = 32,
        HeelRight = 33,

        Count = 34
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TrackedPersonJointPose
    {
        [MarshalAs(UnmanagedType.U1)]
        public bool tracked;

        public Vector3 position;
        public Quaternion localRotation;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct TrackedPersonPose
    {
        public TrackedPersonJointPose pelvis;
        public TrackedPersonJointPose navalSpine;
        public TrackedPersonJointPose chestSpine;
        public TrackedPersonJointPose neck;
        public TrackedPersonJointPose clavicleLeft;
        public TrackedPersonJointPose shoulderLeft;
        public TrackedPersonJointPose elbowLeft;
        public TrackedPersonJointPose wristLeft;
        public TrackedPersonJointPose handLeft;
        public TrackedPersonJointPose handTipLeft;
        public TrackedPersonJointPose thumbLeft;
        public TrackedPersonJointPose clavicleRight;
        public TrackedPersonJointPose shoulderRight;
        public TrackedPersonJointPose elbowRight;
        public TrackedPersonJointPose wristRight;
        public TrackedPersonJointPose handRight;
        public TrackedPersonJointPose handTipRight;
        public TrackedPersonJointPose thumbRight;
        public TrackedPersonJointPose hipLeft;
        public TrackedPersonJointPose kneeLeft;
        public TrackedPersonJointPose ankleLeft;
        public TrackedPersonJointPose footLeft;
        public TrackedPersonJointPose hipRight;
        public TrackedPersonJointPose kneeRight;
        public TrackedPersonJointPose ankleRight;
        public TrackedPersonJointPose footRight;
        public TrackedPersonJointPose head;
        public TrackedPersonJointPose nose;
        public TrackedPersonJointPose eyeLeft;
        public TrackedPersonJointPose earLeft;
        public TrackedPersonJointPose eyeRight;
        public TrackedPersonJointPose earRight;
        public TrackedPersonJointPose heelLeft;
        public TrackedPersonJointPose heelRight;

        public TrackedPersonJointPose this[int index]
        {
            get
            {
                switch (index)
                {
                    case (int)TrackedPersonJoint.Pelvis: return pelvis;
                    case (int)TrackedPersonJoint.NavalSpine: return navalSpine;
                    case (int)TrackedPersonJoint.ChestSpine: return chestSpine;
                    case (int)TrackedPersonJoint.Neck: return neck;
                    case (int)TrackedPersonJoint.ClavicleLeft: return clavicleLeft;
                    case (int)TrackedPersonJoint.ShoulderLeft: return shoulderLeft;
                    case (int)TrackedPersonJoint.ElbowLeft: return elbowLeft;
                    case (int)TrackedPersonJoint.WristLeft: return wristLeft;
                    case (int)TrackedPersonJoint.HandLeft: return handLeft;
                    case (int)TrackedPersonJoint.HandTipLeft: return handTipLeft;
                    case (int)TrackedPersonJoint.ThumbLeft: return thumbLeft;
                    case (int)TrackedPersonJoint.ClavicleRight: return clavicleRight;
                    case (int)TrackedPersonJoint.ShoulderRight: return shoulderRight;
                    case (int)TrackedPersonJoint.ElbowRight: return elbowRight;
                    case (int)TrackedPersonJoint.WristRight: return wristRight;
                    case (int)TrackedPersonJoint.HandRight: return handRight;
                    case (int)TrackedPersonJoint.HandTipRight: return handTipRight;
                    case (int)TrackedPersonJoint.ThumbRight: return thumbRight;
                    case (int)TrackedPersonJoint.HipLeft: return hipLeft;
                    case (int)TrackedPersonJoint.KneeLeft: return kneeLeft;
                    case (int)TrackedPersonJoint.AnkleLeft: return ankleLeft;
                    case (int)TrackedPersonJoint.FootLeft: return footLeft;
                    case (int)TrackedPersonJoint.HipRight: return hipRight;
                    case (int)TrackedPersonJoint.KneeRight: return kneeRight;
                    case (int)TrackedPersonJoint.AnkleRight: return ankleRight;
                    case (int)TrackedPersonJoint.FootRight: return footRight;
                    case (int)TrackedPersonJoint.Head: return head;
                    case (int)TrackedPersonJoint.Nose: return nose;
                    case (int)TrackedPersonJoint.EyeLeft: return eyeLeft;
                    case (int)TrackedPersonJoint.EarLeft: return earLeft;
                    case (int)TrackedPersonJoint.EyeRight: return eyeRight;
                    case (int)TrackedPersonJoint.EarRight: return earRight;
                    case (int)TrackedPersonJoint.HeelLeft: return heelLeft;
                    case (int)TrackedPersonJoint.HeelRight: return heelRight;
                }

                throw new ArgumentOutOfRangeException();
            }
            set
            {
                switch (index)
                {
                    case (int)TrackedPersonJoint.Pelvis: pelvis = value; break;
                    case (int)TrackedPersonJoint.NavalSpine: navalSpine = value; break;
                    case (int)TrackedPersonJoint.ChestSpine: chestSpine = value; break;
                    case (int)TrackedPersonJoint.Neck: neck = value; break;
                    case (int)TrackedPersonJoint.ClavicleLeft: clavicleLeft = value; break;
                    case (int)TrackedPersonJoint.ShoulderLeft: shoulderLeft = value; break;
                    case (int)TrackedPersonJoint.ElbowLeft: elbowLeft = value; break;
                    case (int)TrackedPersonJoint.WristLeft: wristLeft = value; break;
                    case (int)TrackedPersonJoint.HandLeft: handLeft = value; break;
                    case (int)TrackedPersonJoint.HandTipLeft: handTipLeft = value; break;
                    case (int)TrackedPersonJoint.ThumbLeft: thumbLeft = value; break;
                    case (int)TrackedPersonJoint.ClavicleRight: clavicleRight = value; break;
                    case (int)TrackedPersonJoint.ShoulderRight: shoulderRight = value; break;
                    case (int)TrackedPersonJoint.ElbowRight: elbowRight = value; break;
                    case (int)TrackedPersonJoint.WristRight: wristRight = value; break;
                    case (int)TrackedPersonJoint.HandRight: handRight = value; break;
                    case (int)TrackedPersonJoint.HandTipRight: handTipRight = value; break;
                    case (int)TrackedPersonJoint.ThumbRight: thumbRight = value; break;
                    case (int)TrackedPersonJoint.HipLeft: hipLeft = value; break;
                    case (int)TrackedPersonJoint.KneeLeft: kneeLeft = value; break;
                    case (int)TrackedPersonJoint.AnkleLeft: ankleLeft = value; break;
                    case (int)TrackedPersonJoint.FootLeft: footLeft = value; break;
                    case (int)TrackedPersonJoint.HipRight: hipRight = value; break;
                    case (int)TrackedPersonJoint.KneeRight: kneeRight = value; break;
                    case (int)TrackedPersonJoint.AnkleRight: ankleRight = value; break;
                    case (int)TrackedPersonJoint.FootRight: footRight = value; break;
                    case (int)TrackedPersonJoint.Head: head = value; break;
                    case (int)TrackedPersonJoint.Nose: nose = value; break;
                    case (int)TrackedPersonJoint.EyeLeft: eyeLeft = value; break;
                    case (int)TrackedPersonJoint.EarLeft: earLeft = value; break;
                    case (int)TrackedPersonJoint.EyeRight: eyeRight = value; break;
                    case (int)TrackedPersonJoint.EarRight: earRight = value; break;
                    case (int)TrackedPersonJoint.HeelLeft: heelLeft = value; break;
                    case (int)TrackedPersonJoint.HeelRight: heelRight = value; break;

                    default: throw new ArgumentOutOfRangeException();
				}
            }
        }
    
        public bool HasAnyTracking
        {
            get
            {
                for (int i = 0; i < (int)TrackedPersonJoint.Count; i++)
                    if (this[i].tracked)
                        return true;
                return false;
            }
        }
    }
	#endregion

	#region Projected Cards
	[StructLayout(LayoutKind.Sequential)]
	public struct ProjectedCard
    {
        public Vector3 originPos;
        public Quaternion originRot;
        public float projFoV;
        public Vector2 projOpticalCenter;

		public uint colorTexWidth;
		public uint colorTexHeight;
		public IntPtr colorTex;

		public uint depthTexWidth;
		public uint depthTexHeight;
		public IntPtr depthTex;
	}
	#endregion

	#region Frame
	public enum ExpCamFlagBit
    {
        FirstPerson = 1 << 0,
        Translucent = 1 << 1,
        SeparateOpaque = 1 << 2,
    }

    [StructLayout(LayoutKind.Sequential)]
    public class ExpFrame
    {
        public const int MaxPeople = 4;

        //Generated by MixCast Client
        public ulong frameIndex;
        public ulong syncTime;

        public float camFramerate;
        public uint camWidth;
        public uint camHeight;

        public Vector3 camPos;
        public Quaternion camRot;
        public float camFoV;

        [MarshalAs(UnmanagedType.U1)]
        public bool renderForeground;
        [MarshalAs(UnmanagedType.U1)]
        public bool renderFull;

        public uint camFlags;
        public bool HasCamFlag(ExpCamFlagBit flag)
        {
            return (camFlags & (uint)flag) > 0;
        }

        public float occlusionApproxDepth;
        public IntPtr occlusionTex;

        public IntPtr layersTex;    //Generated by SDK
        public ExpLights lights;    //Generated by SDK

        public IntPtr opaqueLayerTex;    //Generated by SDK

        //Added in 2.5.2
        public Vector2 camOpticalCenter;

        public TrackedPersonPose personPose0;
        public TrackedPersonPose personPose1;
        public TrackedPersonPose personPose2;
        public TrackedPersonPose personPose3;
        public TrackedPersonPose GetPersonPose(int index) 
        {
            switch (index)
            {
                case 0: return personPose0;
                case 1: return personPose1;
                case 2: return personPose2;
                case 3: return personPose3;
            }
            throw new ArgumentOutOfRangeException();
        }
        public void SetPersonPose(int index, TrackedPersonPose pose)
        {
            switch (index)
            {
                case 0: personPose0 = pose; break;
                case 1: personPose1 = pose; break;
                case 2: personPose2 = pose; break;
                case 3: personPose3 = pose; break;

                default: throw new ArgumentOutOfRangeException();
            }
        }


        public ProjectedCard projectedCard0;
        public ProjectedCard projectedCard1;
        public ProjectedCard projectedCard2;
        public ProjectedCard projectedCard3;

		public ProjectedCard GetProjectedCard(int index)
		{
			switch (index)
			{
				case 0: return projectedCard0;
				case 1: return projectedCard1;
				case 2: return projectedCard2;
				case 3: return projectedCard3;
			}
			throw new ArgumentOutOfRangeException();
		}
		public void SetProjectedCard(int index, ProjectedCard card)
		{
			switch (index)
			{
				case 0: projectedCard0 = card; break;
				case 1: projectedCard1 = card; break;
				case 2: projectedCard2 = card; break;
				case 3: projectedCard3 = card; break;

				default: throw new ArgumentOutOfRangeException();
			}
		}


		public void CopyFrom(ExpFrame other)
        {
            frameIndex = other.frameIndex;
            syncTime = other.syncTime;

            camFramerate = other.camFramerate;
            camWidth = other.camWidth;
            camHeight = other.camHeight;

            camPos = other.camPos;
            camRot = other.camRot;
            camFoV = other.camFoV;

            renderForeground = other.renderForeground;
            renderFull = other.renderFull;

            camFlags = other.camFlags;

            occlusionApproxDepth = other.occlusionApproxDepth;
            occlusionTex = other.occlusionTex;

            layersTex = other.layersTex;
            lights = other.lights;

            opaqueLayerTex = other.opaqueLayerTex;

            camOpticalCenter = other.camOpticalCenter;

            personPose0 = other.personPose0;
            personPose1 = other.personPose1;
            personPose2 = other.personPose2;
            personPose3 = other.personPose3;

            projectedCard0 = other.projectedCard0;
            projectedCard1 = other.projectedCard1;
            projectedCard2 = other.projectedCard2;
            projectedCard3 = other.projectedCard3;
        }
    }
	#endregion
}
