using JigLibX.Collision;
using JigLibX.Geometry;
using JigLibX.Physics;
using Microsoft.Xna.Framework;

namespace Innovation
{
    // Provides a base object type for physics simulation
    public abstract class PhysicsObject : Component, I3DComponent
    {
        // Local copy of the mass of the object
        float mass = 1;

        // The Body managed by the PhysicsObject
        public Body Body;

        // The CollisionSkin managed by the PhysicsObject
        public CollisionSkin CollisionSkin;

        public MaterialProperties PhysicsMaterial = new MaterialProperties(0.2f, 0.7f, 0.6f);

        // The mass of the PhysicsObject
        public float Mass
        {
            get { return mass; }
            set
            {
                // Set the new value
                mass = value;

                // Fix transforms
                Vector3 com = SetMass(value);
                if (CollisionSkin != null)
                    CollisionSkin.ApplyLocalTransform(
                        new JigLibX.Math.Transform(-com, Matrix.Identity));
            }
        }

        // The PhysicsObject's position
        public Vector3 Position
        {
            get { return Body.Position; }
            set { Body.MoveTo(value, Body.Orientation); }
        }

        // The PhysicsObject's orientation
        public Matrix Rotation
        {
            get { return Body.Orientation; }
            set { Body.MoveTo(Body.Position, value); }
        }

        // The PhysicsObject's rotation as a Euler Vector
        public Vector3 EulerRotation
        {
            get { return MathUtil.MatrixToVector3(Rotation); }
            set { Rotation = MathUtil.Vector3ToMatrix(value); }
        }

        // Whether or not the physics object is locked in place
        public bool Immovable
        {
            get { return Body.Immovable; }
            set { Body.Immovable = value; }
        }

        public Vector3 Scale
        {
            get { return Vector3.One; }
            set { }
        }

        // Returns the PhysicsObject's BoundingBox
        public BoundingBox BoundingBox
        {
            get
            {
                if (Body.CollisionSkin != null)
                    return Body.CollisionSkin.WorldBoundingBox;
                else
                    return new BoundingBox(Position - Vector3.One,
                        Position + Vector3.One);
            }
        }

        // The body's velocity
        public Vector3 Velocity
        {
            get { return Body.Velocity; }
            set { Body.Velocity = value; }
        }

        // Constructors
        public PhysicsObject() : base() { }

        // Sets up the body and collision skin
        protected void InitializeBody()
        {
            Body = new Body();
            CollisionSkin = new CollisionSkin(Body);
            Body.CollisionSkin = this.CollisionSkin;
            Body.EnableBody();
        }

        // Sets the mass of the PhysicsObject
        public Vector3 SetMass(float mass)
        {
            PrimitiveProperties primitiveProperties =
                new PrimitiveProperties(
                    PrimitiveProperties.MassDistributionEnum.Solid,
                    PrimitiveProperties.MassTypeEnum.Density, mass);

            float junk; Vector3 com; Matrix it, itCoM;

            CollisionSkin.GetMassProperties(primitiveProperties,
                out junk, out com, out it, out itCoM);
            Body.BodyInertia = itCoM;
            Body.Mass = junk;

            return com;
        }

        // Disables physics body and component
        public override void DisableComponent()
        {
            Body.DisableBody();
            base.DisableComponent();
        }

        public override void SaveSerializationData(SerializationData Data)
        {
            base.SaveSerializationData(Data);
            
            Data.AddData("PhysicsObject.Immovable", this.Immovable);
            Data.AddData("PhysicsObject.Velocity", this.Velocity);
            Data.AddData("PhysicsObject.Mass", this.Mass);

            Data.AddData("PhysicsObject.PhysicsMaterial.DynamicRoughness", PhysicsMaterial.DynamicRoughness);
            Data.AddData("PhysicsObject.PhysicsMaterial.Elasticity", PhysicsMaterial.Elasticity);
            Data.AddData("PhysicsObject.PhysicsMaterial.StaticRoughness", PhysicsMaterial.StaticRoughness);
        }

        public override void LoadFromSerializationData(SerializationData Data)
        {
            base.LoadFromSerializationData(Data);
            
            this.Immovable = Data.GetData<bool>("PhysicsObject.Immovable");
            this.Mass = Data.GetData<float>("PhysicsObject.Mass");
            this.Velocity = Data.GetData<Vector3>("PhysicsObject.Velocity");

            this.PhysicsMaterial.DynamicRoughness = Data.GetData<float>("PhysicsObject.PhysicsMaterial.DynamicRoughness");
            this.PhysicsMaterial.Elasticity = Data.GetData<float>("PhysicsObject.PhysicsMaterial.Elasticity");
            this.PhysicsMaterial.StaticRoughness = Data.GetData<float>("PhysicsObject.PhysicsMaterial.StaticRoughness");
        }
    }
}