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 gmRTL.Core;
using Microsoft.VisualBasic.CompilerServices;
using System.Linq;
using System.Collections.Generic;

namespace Waves
{
   public class ObjGrid3D
   {
      private float xmin = 0.0F;     //  Min X and Y values.
      private float Zmin = 0.0F;
      private float dx = 0.0F;     //  Spacing between rows of data.
      private float dz = 0.0F;
      private int NumX = 0;     //  Number of X and Y entries.
      private int NumZ = 0;
      private basWaves.Point3D[,] Points = new basWaves.Point3D[0,0];     //  Data values.
      //  Draw a line between the points. Set the max and min values for the line.
      public void DrawAndSetLine(System.Windows.Forms.PictureBox canvas,float x1,float y1,float x2,float y2,int[] hi,int[] lo)
      {
         float tmp = 0.0F;
         int ix = 0;
         int iy = 0;
         float y = 0.0F;
         float dy = 0.0F;

         //  Deal only with integers.
         x1 = x1.ToInt();
         y1 = y1.ToInt();
         x2 = x2.ToInt();
         y2 = y2.ToInt();

         //  Make x1 < x2.
         if (x2 < x1)
         {
            tmp = x1;
            x1 = x2;
            x2 = tmp;
            tmp = y1;
            y1 = y2;
            y2 = tmp;
         }

         //  Draw the line.
         gmRTL.GUI.ControlHelper.Line(canvas,x1,y1,x2,y2,System.Drawing.ColorTranslator.ToOle(canvas.ForeColor));

         //  Deal with vertical lines separately.
         if (x1 == x2)
         {
            if (y1 < y2)
            {
               lo[x1.ToInt()] = y1.ToInt();
               hi[x1.ToInt()] = y2.ToInt();
            }
            else
            {
               lo[x1.ToInt()] = y2.ToInt();
               hi[x1.ToInt()] = y1.ToInt();
            }
            return;
         }

         //  Deal with non-vertical lines.
         dy = (float)((y2 - y1) / (x2 - x1).ToInt());
         y = y1;
         for(ix = x1.ToInt(); ix <= x2.ToInt(); ix += 1)
         {
            iy = y.ToInt();

            lo[ix] = iy;
            hi[ix] = iy;

            y = y + dy;
         }
      }
      //  Draw a line between the points using and updating the max and min arrays.
      public void DrawLine(System.Windows.Forms.PictureBox canvas,float x1,float y1,float x2,float y2,int[] hi,int[] lo)
      {
         float tmp = 0.0F;
         int ix = 0;
         int iy = 0;
         float y = 0.0F;
         float dy = 0.0F;
         int firstx = 0;
         int firsty = 0;
         bool skipping = false;
         bool above = false;

         //  Deal only with integers.
         x1 = x1.ToInt();
         y1 = y1.ToInt();
         x2 = x2.ToInt();
         y2 = y2.ToInt();

         //  Make x1 < x2.
         if (x2 < x1)
         {
            tmp = x1;
            x1 = x2;
            x2 = tmp;
            tmp = y1;
            y1 = y2;
            y2 = tmp;
         }

         //  Deal with vertical lines separately.
         if (x1 == x2)
         {
            //  Make y1 < y2.
            if (y2 < y1)
            {
               tmp = y1;
               y1 = y2;
               y2 = tmp;
            }
            if (y1 <= lo[x1.ToInt()])
            {
               if (y2 <= lo[x1.ToInt()])
               {
                  gmRTL.GUI.ControlHelper.Line(canvas,x1,y1,x2,y2,System.Drawing.ColorTranslator.ToOle(canvas.ForeColor));
               }
               else
               {
                  gmRTL.GUI.ControlHelper.Line(canvas,x1,y1,x2,lo[x2.ToInt()],System.Drawing.ColorTranslator.ToOle(canvas.ForeColor));
               }
               lo[x1.ToInt()] = y1.ToInt();
            }
            if (y2 >= hi[x2.ToInt()])
            {
               if (y1 >= hi[x2.ToInt()])
               {
                  gmRTL.GUI.ControlHelper.Line(canvas,x1,y1,x2,y2,System.Drawing.ColorTranslator.ToOle(canvas.ForeColor));
               }
               else
               {
                  gmRTL.GUI.ControlHelper.Line(canvas,x1,hi[x1.ToInt()],x2,y2,System.Drawing.ColorTranslator.ToOle(canvas.ForeColor));
               }
               hi[x2.ToInt()] = y2.ToInt();
            }
            return;
         }

         //  Deal with non-vertical lines.
         dy = (float)((y2 - y1) / (x2 - x1).ToInt());
         y = y1;

         //  Find the first visible point.
         skipping = true;
         for(ix = x1.ToInt(); ix <= x2.ToInt(); ix += 1)
         {
            iy = y.ToInt();
            //  See if this point is visible.
            if (iy <= lo[ix])
            {
               if (skipping)
               {
                  //  Start a new line below.
                  skipping = false;
                  above = false;
                  firstx = ix;
                  firsty = lo[ix];
               }
            }
            else if (iy >= hi[ix])
            {
               if (skipping)
               {
                  //  Start a new line above.
                  skipping = false;
                  above = true;
                  firstx = ix;
                  firsty = hi[ix];
               }
            }
            else
            {
               //  This point is not visible.
               if (!skipping)
               {
                  //  Draw the previous visible line.
                  if (above)
                  {
                     //  The line is coming from
                     //  above. Connect it to hi(ix).
                     gmRTL.GUI.ControlHelper.Line(canvas,firstx,firsty,ix,hi[ix],System.Drawing.ColorTranslator.ToOle(canvas.ForeColor));
                  }
                  else
                  {
                     //  The line is coming from
                     //  below. Connect it to lo(ix).
                     gmRTL.GUI.ControlHelper.Line(canvas,firstx,firsty,ix,lo[ix],System.Drawing.ColorTranslator.ToOle(canvas.ForeColor));
                  }

                  skipping = true;
               }
            }

            if (iy < lo[ix])
            {
               lo[ix] = iy;
            }
            if (iy > hi[ix])
            {
               hi[ix] = iy;
            }

            y = y + dy;
         }

         //  Draw to the last point if necessary.
         if (!skipping)
         {
            gmRTL.GUI.ControlHelper.Line(canvas,firstx,firsty,x2,y2,System.Drawing.ColorTranslator.ToOle(canvas.ForeColor));
         }
      }
      //  Create the Points array.
      public void SetBounds(float x1,float deltax,int xnum,float z1,float deltaz,int znum)
      {
         int i = 0;
         int j = 0;
         float x = 0.0F;
         float z = 0.0F;

         xmin = x1;
         Zmin = z1;
         dx = deltax;
         dz = deltaz;
         NumX = xnum;
         NumZ = znum;
         Points = new basWaves.Point3D[NumX + 1,NumZ + 1];

         x = xmin;
         for(i = 1; i <= NumX; i += 1)
         {
            z = Zmin;
            for(j = 1; j <= NumZ; j += 1)
            {
               Points[i,j] = basWaves.init_Point3D();
               Points[i,j].coord[1] = x;
               Points[i,j].coord[2] = 0;
               Points[i,j].coord[3] = z;
               Points[i,j].coord[4] = 1;
               z = z + dz;
            }
            x = x + dx;
         }
      }
      //  Save the indicated data value.
      public void SetValue(float x,float y,float z)
      {
         int i = 0;
         int j = 0;

         i = ((x - xmin) / dx + 1).ToInt();
         j = ((z - Zmin) / dz + 1).ToInt();
         Points[i,j].coord[2] = y;
      }
      //  Return a string indicating the object type.
      public string ObjectType
      {
         get
         {
            return "GRID";
         }
      }
      //  Apply a transformation matrix which may not
      //  contain 0, 0, 0, 1 in the last column to the
      //  object.
      public void ApplyFull(float[,] M)
      {
         int i = 0;
         int j = 0;

         for(i = 1; i <= NumX; i += 1)
         {
            for(j = 1; j <= NumZ; j += 1)
            {
               basWaves.m3ApplyFull(Points[i,j].coord,M,Points[i,j].trans);
            }
         }
      }
      //  Apply a transformation matrix to the object.
      public void Apply(float[,] M)
      {
         int i = 0;
         int j = 0;

         for(i = 1; i <= NumX; i += 1)
         {
            for(j = 1; j <= NumZ; j += 1)
            {
               basWaves.m3Apply(Points[i,j].coord,M,Points[i,j].trans);
            }
         }
      }
      //  Draw the grid without hidden surfaces using the hi-lo algorithm
      public void DrawWithoutHidden(System.Windows.Forms.PictureBox canvas,object r)
      {
         int xmin = 0;
         int xmax = 0;
         int[] lo = Array.Empty<int>();
         int[] hi = Array.Empty<int>();
         int ix = 0;
         int i = 0;
         int j = 0;

         //  Bound the X values.
         xmin = Points[1,1].trans[1].ToInt();
         xmax = xmin;
         for(i = 1; i <= NumX; i += 1)
         {
            for(j = 1; j <= NumZ; j += 1)
            {
               ix = Points[i,j].trans[1].ToInt();
               if (xmin > ix)
               {
                  xmin = ix;
               }
               if (xmax < ix)
               {
                  xmax = ix;
               }
            }
         }

         //  Create the max and min arrays.
         lo = new int[xmax + 1];
         hi = new int[xmax + 1];

         //  Draw the X and Z front edges.
         for(i = 2; i <= NumX; i += 1)
         {
            //  Draw the edge between
            //  Points(i - 1, NumZ) and Points(i, NumZ)
            //  and set max and min for its values.
            DrawAndSetLine(canvas,Points[i - 1,NumZ].trans[1],Points[i - 1,NumZ].trans[2],Points[i,NumZ].trans[1],Points[i,NumZ].trans[2],hi,lo);
         }
         for(i = 2; i <= NumZ; i += 1)
         {
            //  Draw the edge between
            //  Points(NumX, i - 1) and Points(NumX, i)
            //  and set max and min for its values.
            DrawAndSetLine(canvas,Points[NumX,i - 1].trans[1],Points[NumX,i - 1].trans[2],Points[NumX,i].trans[1],Points[NumX,i].trans[2],hi,lo);
         }


         for(i = NumX - 1; i >= 1; i += -1)
         {
            for(j = NumZ - 1; j >= 1; j += -1)
            {

               //  This only happens with perspective projection.

               if (Points[i + 1,j].trans[1] >= Points[i,j].trans[1])
               {
                  DrawLine(canvas,Points[i,j].trans[1],Points[i,j].trans[2],Points[i,j + 1].trans[1],Points[i,j + 1].trans[2],hi,lo);
                  DrawLine(canvas,Points[i,j].trans[1],Points[i,j].trans[2],Points[i + 1,j].trans[1],Points[i + 1,j].trans[2],hi,lo);
               }
               else
               {
                  DrawLine(canvas,Points[i,j].trans[1],Points[i,j].trans[2],Points[i + 1,j].trans[1],Points[i + 1,j].trans[2],hi,lo);
                  DrawLine(canvas,Points[i,j].trans[1],Points[i,j].trans[2],Points[i,j + 1].trans[1],Points[i,j + 1].trans[2],hi,lo);
               }
            }
         }
      }
      //  Draw the transformed points on the PictureBox.
      public void Draw(System.Windows.Forms.PictureBox canvas,object r)
      {
         DrawWithoutHidden(canvas,r);
      }
   }
}
