using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Data;
using VBNET = Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
using System.Linq;
using System.Collections.Generic;

namespace Waves
{
   public static class basWaves
   {
      [DllImport("kernel32")]
      public static extern int GetTickCount();
      public const double PI = 3.14159265358979;
      public const int INFINITY = 2147483647;
      public const int m3Parallel = 0;
      public const int m3Perspective = 1;
      public static Point3D init_Point3D()
      {
         // UPGRADE_INFO: Added struct array initialization helper
         var new_Point3D = new Point3D();
         new_Point3D.coord = new float[5];
         new_Point3D.trans = new float[5];
         return new_Point3D;
      }
      public struct Point3D
      {
         [System.Runtime.InteropServices.MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public float[] coord;
         [System.Runtime.InteropServices.MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public float[] trans;
      }
      public struct Segment3D
      {
         public int pt1;
         public int pt2;
      }
      //  Convert the spherical coordinates into Cartesian coordinates.
      public static void m3SphericalToCartesian(float r,float theta,float phi,ref float x,ref float y,ref float z)
      {
         float R2 = 0.0F;

         //  Create a line to the center of projection.
         y = (float)(r * Math.Sin(phi));
         R2 = (float)(r * Math.Cos(phi));
         x = (float)(R2 * Math.Cos(theta));
         z = (float)(R2 * Math.Sin(theta));
      }
      //  Create a transformation matrix for an oblique
      //  projection onto the X-Y plane.
      public static void m3ObliqueXY(float[,] M,float S,float theta)
      {
         m3Identity(M);
         M[3,1] = (float)(-S * Math.Cos(theta));
         M[3,2] = (float)(-S * Math.Sin(theta));
         M[3,3] = 0;
      }
      //  Create a transformation matrix for orthographic
      //  projection along the X axis.
      public static void m3OrthoSide(float[,] M)
      {
         m3Identity(M);
         M[1,1] = 0;
         M[3,1] = -1;
         M[3,3] = 0;
      }
      //  Create a transformation matrix for orthographic
      //  projection along the Y axis.
      public static void m3OrthoTop(float[,] M)
      {
         m3Identity(M);
         M[2,2] = 0;
         M[3,2] = -1;
         M[3,3] = 0;
      }
      //  Create a transformation matrix for orthographic
      //  projection along the Z axis.
      public static void m3OrthoFront(float[,] M)
      {
         m3Identity(M);
         M[3,3] = 0;
      }
      //  Create an identity matrix.
      public static void m3Identity(float[,] M)
      {
         int i = 0;
         int j = 0;

         for(i = 1; i <= 4; i += 1)
         {
            for(j = 1; j <= 4; j += 1)
            {
               if (i == j)
               {
                  M[i,j] = 1;
               }
               else
               {
                  M[i,j] = 0;
               }
            }
         }
      }
      //  Normalize a 3-D point vector.
      public static void m3NormalizeCoords(ref float x,ref float y,ref float z,ref float S)
      {
         x = (float)(x / S);
         y = (float)(y / S);
         z = (float)(z / S);
         S = 1;
      }
      //  Normalize a 3-D point vector.
      public static void m3NormalizePoint(float[] P)
      {
         int i = 0;
         float value = 0.0F;

         value = P[4];
         for(i = 1; i <= 3; i += 1)
         {
            P[i] = (float)(P[i] / value);
         }
         P[4] = 1;
      }
      //  Normalize a 3-D transformation matrix.
      public static void m3NormalizeMatrix(float[,] M)
      {
         int i = 0;
         int j = 0;
         float value = 0.0F;

         value = M[4,4];
         for(i = 1; i <= 4; i += 1)
         {
            for(j = 1; j <= 4; j += 1)
            {
               M[i,j] = (float)(M[i,j] / value);
            }
         }
      }
      //  Create a 3-D transformation matrix for a
      //  perspective projection along the Z axis onto
      //  the X-Y plane with focus at the origin and the
      //  center of projection at distance (0, 0, D).
      public static void m3PerspectiveXZ(float[,] M,float D)
      {
         m3Identity(M);
         if (D != 0)
         {
            M[3,4] = (float)(-1 / D);
         }
      }
      //  Create a 3-D transformation matrix
      private static float[,] m3Project_M1 = new float[5,5];
      private static float[,] m3Project_M2 = new float[5,5];
      private static float[,] m3Project_M3 = new float[5,5];
      private static float[,] m3Project_M4 = new float[5,5];
      private static float[,] m3Project_M5 = new float[5,5];
      private static float[,] m3Project_M12 = new float[5,5];
      private static float[,] m3Project_M34 = new float[5,5];
      private static float[,] m3Project_M1234 = new float[5,5];
      public static void m3Project(float[,] M,int ptype,float Cx,float Cy,float Cz,float Fx,float Fy,float Fz,float ux,float uy,float uz)
      {
         float sin1 = 0.0F;
         float cos1 = 0.0F;
         float sin2 = 0.0F;
         float cos2 = 0.0F;
         float sin3 = 0.0F;
         float cos3 = 0.0F;
         float A = 0.0F;
         float b = 0.0F;
         float C = 0.0F;
         float d1 = 0.0F;
         float d2 = 0.0F;
         float d3 = 0.0F;
         float[] up1 = new float[5];
         float[] up2 = new float[5];

         //  Translate the focus to the origin.
         m3Translate(m3Project_M1,-Fx,-Fy,-Fz);

         A = Cx - Fx;
         b = Cy - Fy;
         C = Cz - Fz;
         d1 = (float)(Math.Sqrt(A * A + C * C));
         if (d1 != 0)
         {
            sin1 = (float)(-A / d1);
            cos1 = (float)(C / d1);
         }
         d2 = (float)(Math.Sqrt(A * A + b * b + C * C));
         if (d2 != 0)
         {
            sin2 = (float)(b / d2);
            cos2 = (float)(d1 / d2);
         }

         //  Rotate around the Y axis to place the
         //  center of projection in the Y-Z plane.
         m3Identity(m3Project_M2);

         //  If d1 = 0 then the center of projection
         //  already lies in the Y axis and thus the Y-Z plane.
         if (d1 != 0)
         {
            m3Project_M2[1,1] = cos1;
            m3Project_M2[1,3] = -sin1;
            m3Project_M2[3,1] = sin1;
            m3Project_M2[3,3] = cos1;
         }

         //  Rotate around the X axis to place the
         //  center of projection in the Z axis.
         m3Identity(m3Project_M3);

         //  If d2 = 0 then the center of projection
         //  lies at the origin. This makes projection
         //  impossible.
         if (d2 != 0)
         {
            m3Project_M3[2,2] = cos2;
            m3Project_M3[2,3] = sin2;
            m3Project_M3[3,2] = -sin2;
            m3Project_M3[3,3] = cos2;
         }

         //  Apply the rotations to the UP vector.
         up1[1] = ux;
         up1[2] = uy;
         up1[3] = uz;
         up1[4] = 1;
         m3Apply(up1,m3Project_M2,up2);
         m3Apply(up2,m3Project_M3,up1);

         //  Rotate around the Z axis to put the UP
         //  vector in the Y-Z plane.
         d3 = (float)(Math.Sqrt(up1[1] * up1[1] + up1[2] * up1[2]));
         m3Identity(m3Project_M4);

         //  If d3 = 0 then the UP vector is a zero
         //  vector so do nothing.
         if (d3 != 0)
         {
            sin3 = (float)(up1[1] / d3);
            cos3 = (float)(up1[2] / d3);
            m3Project_M4[1,1] = cos3;
            m3Project_M4[1,2] = sin3;
            m3Project_M4[2,1] = -sin3;
            m3Project_M4[2,2] = cos3;
         }

         //  Project.
         if (ptype == basWaves.m3Perspective && d2 != 0)
         {
            m3PerspectiveXZ(m3Project_M5,d2);
         }
         else
         {
            m3Identity(m3Project_M5);
         }

         //  Combine the transformations.
         m3MatMultiply(m3Project_M12,m3Project_M1,m3Project_M2);
         m3MatMultiply(m3Project_M34,m3Project_M3,m3Project_M4);
         m3MatMultiply(m3Project_M1234,m3Project_M12,m3Project_M34);
         if (ptype == basWaves.m3Perspective)
         {
            m3MatMultiplyFull(M,m3Project_M1234,m3Project_M5);
         }
         else
         {
            m3MatMultiply(M,m3Project_M1234,m3Project_M5);
         }
      }
      //  Create a 3-D transformation matrix for a
      //  perspective projection with:
      //        center of projection    (r, phi, theta)
      //        focus                   (fx, fy, fx)
      //        up vector               <ux, uy, uz>
      //  ptype should be m3Perspective or m3Parallel.
      public static void m3PProject(float[,] M,int ptype,float r,float phi,float theta,float Fx,float Fy,float Fz,float ux,float uy,float uz)
      {
         float Cx = 0.0F;
         float Cy = 0.0F;
         float Cz = 0.0F;
         float R2 = 0.0F;

         //  Convert to Cartesian coordinates.
         Cy = (float)(r * Math.Sin(phi));
         R2 = (float)(r * Math.Cos(phi));
         Cx = (float)(R2 * Math.Cos(theta));
         Cz = (float)(R2 * Math.Sin(theta));
         m3Project(M,ptype,Cx,Cy,Cz,Fx,Fy,Fz,ux,uy,uz);
      }
      //  Create a transformation matrix for reflecting
      //  across the plane passing through (p1, p2, p3)
      //  with normal vector <n1, n2, n3>.
      public static void m3Reflect(float[,] M,float p1,float p2,float p3,float n1,float n2,float n3)
      {
         float[,] t = new float[5,5];     //  Translate.
         float[,] R1 = new float[5,5];     //  Rotate 1.
         float[,] R2 = new float[5,5];     //  Rotate 2.
         float[,] S = new float[5,5];     //  Reflect.
         float[,] R2i = new float[5,5];     //  Unrotate 2.
         float[,] R1i = new float[5,5];     //  Unrotate 1.
         float[,] Ti = new float[5,5];     //  Untranslate.
         float D = 0.0F;
         float L = 0.0F;
         float[,] M12 = new float[5,5];
         float[,] M34 = new float[5,5];
         float[,] M1234 = new float[5,5];
         float[,] M56 = new float[5,5];
         float[,] M567 = new float[5,5];

         //  Translate the plane to the origin.
         m3Translate(t,-p1,-p2,-p3);
         m3Translate(Ti,p1,p2,p3);

         //  Rotate around Z-axis until the normal is in
         //  the Y-Z plane.
         m3Identity(R1);
         D = (float)(Math.Sqrt(n1 * n1 + n2 * n2));
         R1[1,1] = (float)(n2 / D);
         R1[1,2] = (float)(n1 / D);
         R1[2,1] = -R1[1,2];
         R1[2,2] = R1[1,1];

         m3Identity(R1i);
         R1i[1,1] = R1[1,1];
         R1i[1,2] = -R1[1,2];
         R1i[2,1] = -R1[2,1];
         R1i[2,2] = R1[2,2];

         //  Rotate around the X-axis until the normal
         //  lies along the Y axis.
         m3Identity(R2);
         L = (float)(Math.Sqrt(n1 * n1 + n2 * n2 + n3 * n3));
         R2[2,2] = (float)(D / L);
         R2[2,3] = (float)(-n3 / L);
         R2[3,2] = -R2[2,3];
         R2[3,3] = R2[2,2];

         m3Identity(R2i);
         R2i[2,2] = R2[2,2];
         R2i[2,3] = -R2[2,3];
         R2i[3,2] = -R2[3,2];
         R2i[3,3] = R2[3,3];

         //  Reflect across the X-Z plane.
         m3Identity(S);
         S[2,2] = -1;

         //  Combine the matrices.
         m3MatMultiply(M12,t,R1);
         m3MatMultiply(M34,R2,S);
         m3MatMultiply(M1234,M12,M34);
         m3MatMultiply(M56,R2i,R1i);
         m3MatMultiply(M567,M56,Ti);
         m3MatMultiply(M,M1234,M567);
      }
      //  Create a transformation matrix for rotating
      //  through angle theta around a line passing
      //  through (p1, p2, p3) in direction <d1, d2, d3>.
      //  Theta is measured counterclockwise as you look
      //  down the line opposite the line's direction.
      public static void m3LineRotate(float[,] M,float p1,float p2,float p3,float d1,float d2,float d3,float theta)
      {
         float[,] t = new float[5,5];     //  Translate.
         float[,] R1 = new float[5,5];     //  Rotate 1.
         float[,] R2 = new float[5,5];     //  Rotate 2.
         float[,] Rot3 = new float[5,5];     //  Rotate.
         float[,] R2i = new float[5,5];     //  Unrotate 2.
         float[,] R1i = new float[5,5];     //  Unrotate 1.
         float[,] Ti = new float[5,5];     //  Untranslate.
         float D = 0.0F;
         float L = 0.0F;
         float[,] M12 = new float[5,5];
         float[,] M34 = new float[5,5];
         float[,] M1234 = new float[5,5];
         float[,] M56 = new float[5,5];
         float[,] M567 = new float[5,5];

         //  Translate the plane to the origin.
         m3Translate(t,-p1,-p2,-p3);
         m3Translate(Ti,p1,p2,p3);

         //  Rotate around Z-axis until the line is in
         //  the Y-Z plane.
         m3Identity(R1);
         D = (float)(Math.Sqrt(d1 * d1 + d2 * d2));
         R1[1,1] = (float)(d2 / D);
         R1[1,2] = (float)(d1 / D);
         R1[2,1] = -R1[1,2];
         R1[2,2] = R1[1,1];

         m3Identity(R1i);
         R1i[1,1] = R1[1,1];
         R1i[1,2] = -R1[1,2];
         R1i[2,1] = -R1[2,1];
         R1i[2,2] = R1[2,2];

         //  Rotate around the X-axis until the line
         //  lies along the Y axis.
         m3Identity(R2);
         L = (float)(Math.Sqrt(d1 * d1 + d2 * d2 + d3 * d3));
         R2[2,2] = (float)(D / L);
         R2[2,3] = (float)(-d3 / L);
         R2[3,2] = -R2[2,3];
         R2[3,3] = R2[2,2];

         m3Identity(R2i);
         R2i[2,2] = R2[2,2];
         R2i[2,3] = -R2[2,3];
         R2i[3,2] = -R2[3,2];
         R2i[3,3] = R2[3,3];

         //  Rotate around the line (Y axis).
         m3YRotate(Rot3,theta);

         //  Combine the matrices.
         m3MatMultiply(M12,t,R1);
         m3MatMultiply(M34,R2,Rot3);
         m3MatMultiply(M1234,M12,M34);
         m3MatMultiply(M56,R2i,R1i);
         m3MatMultiply(M567,M56,Ti);
         m3MatMultiply(M,M1234,M567);
      }
      //  Create a 3-D transformation matrix for scaling
      //  by scale factors Sx, Sy, and Sz.
      public static void m3Scale(float[,] M,float Sx,float Sy,float Sz)
      {
         m3Identity(M);
         M[1,1] = Sx;
         M[2,2] = Sy;
         M[3,3] = Sz;
      }
      //  Create a 3-D transformation matrix for
      //  translation by Tx, Ty, and Tz.
      public static void m3Translate(float[,] M,float Tx,float Ty,float Tz)
      {
         m3Identity(M);
         M[4,1] = Tx;
         M[4,2] = Ty;
         M[4,3] = Tz;
      }
      //  Create a 3-D transformation matrix for rotation
      //  around the X axis (angle measured in radians).
      public static void m3XRotate(float[,] M,float theta)
      {
         m3Identity(M);
         M[2,2] = (float)(Math.Cos(theta));
         M[3,3] = M[2,2];
         M[2,3] = (float)(Math.Sin(theta));
         M[3,2] = -M[2,3];
      }
      //  Create a 3-D transformation matrix for rotation
      //  around the Y axis (angle measured in radians).
      public static void m3YRotate(float[,] M,float theta)
      {
         m3Identity(M);
         M[1,1] = (float)(Math.Cos(theta));
         M[3,3] = M[1,1];
         M[3,1] = (float)(Math.Sin(theta));
         M[1,3] = -M[3,1];
      }
      //  Create a 3-D transformation matrix for rotation
      //  around the Z axis (angle measured in radians).
      public static void m3ZRotate(float[,] M,float theta)
      {
         m3Identity(M);
         M[1,1] = (float)(Math.Cos(theta));
         M[2,2] = M[1,1];
         M[1,2] = (float)(Math.Sin(theta));
         M[2,1] = -M[1,2];
      }
      //  Create a matrix that rotates around the Y axis
      //  so the point (x, y, z) lies in the X-Z plane.
      public static void m3YRotateIntoXZ(float[,] Result,float x,float y,float z)
      {
         float D = 0.0F;

         m3Identity(Result);
         D = (float)(Math.Sqrt(x * x + y * y));
         Result[1,1] = (float)(x / D);
         Result[1,2] = (float)(-y / D);
         Result[2,1] = -Result[1,2];
         Result[2,2] = Result[1,1];
      }
      //  Set copy = orig.
      public static void m3MatCopy(float[,] copy,float[,] orig)
      {
         int i = 0;
         int j = 0;

         for(i = 1; i <= 4; i += 1)
         {
            for(j = 1; j <= 4; j += 1)
            {
               copy[i,j] = orig[i,j];
            }
         }
      }
      //  Apply a transformation matrix to a point where
      //  the transformation may not have 0, 0, 0, 1 in
      //  its final column. Normalize only the X and Y
      //  components of the result to preserve the Z
      //  information.
      public static void m3ApplyFull(float[] V,float[,] M,float[] Result)
      {
         int i = 0;
         int j = 0;
         float value = 0.0F;

         for(i = 1; i <= 4; i += 1)
         {
            value = 0;
            for(j = 1; j <= 4; j += 1)
            {
               value = value + V[j] * M[j,i];
            }
            Result[i] = value;
         }

         //  Renormalize the point.
         //  Note that value still holds Result(4).
         if (value != 0)
         {
            Result[1] = (float)(Result[1] / value);
            Result[2] = (float)(Result[2] / value);
            //  Do not transform the Z component.
         }
         else
         {
            //  Make the Z value greater than that of
            //  the center of projection so the point
            //  will be clipped.
            Result[3] = basWaves.INFINITY;
         }
         Result[4] = 1;
      }
      //  Apply a transformation matrix to a point.
      public static void m3Apply(float[] V,float[,] M,float[] Result)
      {
         Result[1] = V[1] * M[1,1] + V[2] * M[2,1] + V[3] * M[3,1] + M[4,1];
         Result[2] = V[1] * M[1,2] + V[2] * M[2,2] + V[3] * M[3,2] + M[4,2];
         Result[3] = V[1] * M[1,3] + V[2] * M[2,3] + V[3] * M[3,3] + M[4,3];
         Result[4] = 1;
      }
      //  Multiply two matrices together. The matrices
      //  may not contain 0, 0, 0, 1 in their last
      //  columns.
      public static void m3MatMultiplyFull(float[,] Result,float[,] A,float[,] b)
      {
         int i = 0;
         int j = 0;
         int k = 0;
         float value = 0.0F;

         for(i = 1; i <= 4; i += 1)
         {
            for(j = 1; j <= 4; j += 1)
            {
               value = 0;
               for(k = 1; k <= 4; k += 1)
               {
                  value = value + A[i,k] * b[k,j];
               }
               Result[i,j] = value;
            }
         }
      }
      //  Multiply two matrices together.
      public static void m3MatMultiply(float[,] Result,float[,] A,float[,] b)
      {
         Result[1,1] = A[1,1] * b[1,1] + A[1,2] * b[2,1] + A[1,3] * b[3,1];
         Result[1,2] = A[1,1] * b[1,2] + A[1,2] * b[2,2] + A[1,3] * b[3,2];
         Result[1,3] = A[1,1] * b[1,3] + A[1,2] * b[2,3] + A[1,3] * b[3,3];
         Result[1,4] = 0;
         Result[2,1] = A[2,1] * b[1,1] + A[2,2] * b[2,1] + A[2,3] * b[3,1];
         Result[2,2] = A[2,1] * b[1,2] + A[2,2] * b[2,2] + A[2,3] * b[3,2];
         Result[2,3] = A[2,1] * b[1,3] + A[2,2] * b[2,3] + A[2,3] * b[3,3];
         Result[2,4] = 0;
         Result[3,1] = A[3,1] * b[1,1] + A[3,2] * b[2,1] + A[3,3] * b[3,1];
         Result[3,2] = A[3,1] * b[1,2] + A[3,2] * b[2,2] + A[3,3] * b[3,2];
         Result[3,3] = A[3,1] * b[1,3] + A[3,2] * b[2,3] + A[3,3] * b[3,3];
         Result[3,4] = 0;
         Result[4,1] = A[4,1] * b[1,1] + A[4,2] * b[2,1] + A[4,3] * b[3,1] + b[4,1];
         Result[4,2] = A[4,1] * b[1,2] + A[4,2] * b[2,2] + A[4,3] * b[3,2] + b[4,2];
         Result[4,3] = A[4,1] * b[1,3] + A[4,2] * b[2,3] + A[4,3] * b[3,3] + b[4,3];
         Result[4,4] = 1;
      }
      //  Compute the cross product of two vectors.
      // Set <x, y, z> = <x1, y1, z1> X <x2, y2, z2>.
      public static void m3Cross(ref float x,ref float y,ref float z,float x1,float y1,float z1,float x2,float y2,float z2)
      {
         x = y1 * z2 - z1 * y2;
         y = z1 * x2 - x1 * z2;
         z = x1 * y2 - y1 * x2;
      }
      //  Give the vector the indicated length.
      public static void m3SizeVector(float L,ref float x,ref float y,ref float z)
      {
         L = (float)(L / Math.Sqrt(x * x + y * y + z * z));
         x = x * L;
         y = y * L;
         z = z * L;
      }
      public static void WaitTill(int next_time)
      {
         do
         {
            System.Windows.Forms.Application.DoEvents();
         } while(GetTickCount() < next_time);
      }
   }
}
