using System.Diagnostics;
using System.Drawing;
using Spark3D.Algebra3D;

namespace Spark3D.Objects3D
{
    public class Polygon3D
    {
        public Vector3D[] itsVerticies;
        public Vector3D itsNormal; //Unit vector.
        public Vector3D itsCenterOfMass;
        public Color itsFullColour; //Colour in full illumination;

        public double itsFrontIllumnationFactor;
        public double itsBackIllumnationFactor;
        public Color itsFrontColour;
        public Color itsBackColour;

        public Polygon3D(Vector3D[] vs, Color c)
        {
            itsVerticies = vs;
            itsFrontColour = Color.FromArgb(0, 0, 0); //Very dark!
            itsBackColour = Color.FromArgb(0, 0, 0); //Very dark!
            itsFullColour = c;
            Debug.Assert(NumVerticies >= 3);
            Vector3D v1 = itsVerticies[1] - itsVerticies[0];
            Vector3D v2 = itsVerticies[2] - itsVerticies[0];
            itsNormal = Vector3D.Cross(v1, v2);
            itsNormal.Normalize();
            itsCenterOfMass = new Vector3D(0.0, 0.0, 0.0);
            foreach (Vector3D v in itsVerticies)
            {
                itsCenterOfMass += v;
            }
            itsCenterOfMass = itsCenterOfMass / (double)NumVerticies;
            // Font and back are arbitrary.  Here front = the side the normal vector sticks out of.
            itsFrontIllumnationFactor = 0.0;
            itsBackIllumnationFactor = 0.0;
        }

        public int NumVerticies
        {
            get { return itsVerticies.Length; }
        }

        public void AddLight(Vector3D lightPosition)
        {
            double cos = Cosine(lightPosition);
            if (cos > 0.0)
                AddLight(cos, ref itsFrontIllumnationFactor, ref itsFrontColour);
            else if (cos < 0.0)
                AddLight(-cos, ref itsBackIllumnationFactor, ref itsBackColour);
        }

        private void AddLight(double intensity, ref double sideIllumnationFactor, ref Color sideColour)
        {
            Debug.Assert(intensity > 0.0);
            sideIllumnationFactor += intensity;
            if (sideIllumnationFactor > 1.0)
            {
                sideIllumnationFactor = 1.0;
            }
            sideColour = Color.FromArgb
                      (
                      (int)(itsFullColour.R * sideIllumnationFactor),
                      (int)(itsFullColour.G * sideIllumnationFactor),
                      (int)(itsFullColour.B * sideIllumnationFactor)
                  );

        }

        // Decide which side the viewer is on and give the colour for that side.
        public Color Colour(Vector3D camera, Matrix3D R)
        {
            Vector3D delta = R * itsCenterOfMass - camera;
            delta.Normalize();
            double cos = delta * (R * itsNormal);
            return (cos < 0.0) ? itsBackColour : itsFrontColour;
        }

        //Cosine of the angle between the polygin normal and light position vector.
        private double Cosine(Vector3D r)
        {
            Vector3D v = r - itsCenterOfMass;
            v.Normalize();
            double cos = v * itsNormal; //Cosine of the angle between the polygin normal and light position vector.
            return cos;
        }
    }
}
