using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;


namespace GlobeMaker
{
   /// <summary>
   /// Summary description for Form1.
   /// </summary>
   public class Form1 : System.Windows.Forms.Form
   {
      private AxPAJANTLib.AxPajantImageCtrl axPajantImageCtrl1;
      private System.Windows.Forms.Button OutputFileButton;
      private System.Windows.Forms.NumericUpDown OutputImageWidthCtrl;
      private System.Windows.Forms.Button InputFileButton;
      private System.Windows.Forms.NumericUpDown OutputImageHeightCtrl;
      private System.Windows.Forms.Label label1;
      private System.Windows.Forms.Label label2;
      private System.Windows.Forms.Label label3;
      private System.Windows.Forms.Label label4;
      private System.Windows.Forms.Label label5;
      private System.Windows.Forms.Label label6;
      private System.Windows.Forms.Button DismissButton;
      private System.Windows.Forms.Button RunButton;
      private System.Windows.Forms.Label label7;
      private System.Windows.Forms.NumericUpDown NumberOfGoresCtrl;
      private System.Windows.Forms.Label label8;
      private System.Windows.Forms.NumericUpDown OverlapGoresByCtrl;
      /// <summary>
      /// Required designer variable.
      /// </summary>
      private System.ComponentModel.Container components = null;

      public Form1()
      {
         //
         // Required for Windows Form Designer support
         //
         InitializeComponent();

         //
         // TODO: Add any constructor code after InitializeComponent call
         //
      }

      /// <summary>
      /// Clean up any resources being used.
      /// </summary>
      protected override void Dispose( bool disposing )
      {
         if( disposing )
         {
            if (components != null)
            {
               components.Dispose();
            }
         }
         base.Dispose( disposing );
      }

      #region Windows Form Designer generated code
      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
         System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
         this.axPajantImageCtrl1 = new AxPAJANTLib.AxPajantImageCtrl();
         this.OutputFileButton = new System.Windows.Forms.Button();
         this.OutputImageWidthCtrl = new System.Windows.Forms.NumericUpDown();
         this.InputFileButton = new System.Windows.Forms.Button();
         this.OutputImageHeightCtrl = new System.Windows.Forms.NumericUpDown();
         this.label1 = new System.Windows.Forms.Label();
         this.label2 = new System.Windows.Forms.Label();
         this.label3 = new System.Windows.Forms.Label();
         this.label4 = new System.Windows.Forms.Label();
         this.label5 = new System.Windows.Forms.Label();
         this.label6 = new System.Windows.Forms.Label();
         this.DismissButton = new System.Windows.Forms.Button();
         this.RunButton = new System.Windows.Forms.Button();
         this.label7 = new System.Windows.Forms.Label();
         this.NumberOfGoresCtrl = new System.Windows.Forms.NumericUpDown();
         this.OverlapGoresByCtrl = new System.Windows.Forms.NumericUpDown();
         this.label8 = new System.Windows.Forms.Label();
         ((System.ComponentModel.ISupportInitialize)(this.axPajantImageCtrl1)).BeginInit();
         ((System.ComponentModel.ISupportInitialize)(this.OutputImageWidthCtrl)).BeginInit();
         ((System.ComponentModel.ISupportInitialize)(this.OutputImageHeightCtrl)).BeginInit();
         ((System.ComponentModel.ISupportInitialize)(this.NumberOfGoresCtrl)).BeginInit();
         ((System.ComponentModel.ISupportInitialize)(this.OverlapGoresByCtrl)).BeginInit();
         this.SuspendLayout();
         // 
         // axPajantImageCtrl1
         // 
         this.axPajantImageCtrl1.Enabled = true;
         this.axPajantImageCtrl1.Location = new System.Drawing.Point(440, 8);
         this.axPajantImageCtrl1.Name = "axPajantImageCtrl1";
         this.axPajantImageCtrl1.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("axPajantImageCtrl1.OcxState")));
         this.axPajantImageCtrl1.Size = new System.Drawing.Size(288, 192);
         this.axPajantImageCtrl1.TabIndex = 0;
         // 
         // OutputFileButton
         // 
         this.OutputFileButton.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
         this.OutputFileButton.Location = new System.Drawing.Point(88, 144);
         this.OutputFileButton.Name = "OutputFileButton";
         this.OutputFileButton.Size = new System.Drawing.Size(344, 48);
         this.OutputFileButton.TabIndex = 1;
         this.OutputFileButton.Text = "out.jpg";
         this.OutputFileButton.Click += new System.EventHandler(this.OutputFileButton_Click);
         // 
         // OutputImageWidthCtrl
         // 
         this.OutputImageWidthCtrl.Location = new System.Drawing.Point(88, 200);
         this.OutputImageWidthCtrl.Maximum = new System.Decimal(new int[] {
                                                             8192,
                                                             0,
                                                             0,
                                                             0});
         this.OutputImageWidthCtrl.Minimum = new System.Decimal(new int[] {
                                                             512,
                                                             0,
                                                             0,
                                                             0});
         this.OutputImageWidthCtrl.Name = "OutputImageWidthCtrl";
         this.OutputImageWidthCtrl.TabIndex = 2;
         this.OutputImageWidthCtrl.Value = new System.Decimal(new int[] {
                                                            512,
                                                            0,
                                                            0,
                                                            0});
         // 
         // InputFileButton
         // 
         this.InputFileButton.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
         this.InputFileButton.Location = new System.Drawing.Point(88, 48);
         this.InputFileButton.Name = "InputFileButton";
         this.InputFileButton.Size = new System.Drawing.Size(344, 48);
         this.InputFileButton.TabIndex = 3;
         this.InputFileButton.Text = "in.jpg";
         this.InputFileButton.Click += new System.EventHandler(this.InputFileButton_Click);
         // 
         // OutputImageHeightCtrl
         // 
         this.OutputImageHeightCtrl.Location = new System.Drawing.Point(88, 240);
         this.OutputImageHeightCtrl.Maximum = new System.Decimal(new int[] {
                                                              4096,
                                                              0,
                                                              0,
                                                              0});
         this.OutputImageHeightCtrl.Minimum = new System.Decimal(new int[] {
                                                              256,
                                                              0,
                                                              0,
                                                              0});
         this.OutputImageHeightCtrl.Name = "OutputImageHeightCtrl";
         this.OutputImageHeightCtrl.TabIndex = 4;
         this.OutputImageHeightCtrl.Value = new System.Decimal(new int[] {
                                                            256,
                                                            0,
                                                            0,
                                                            0});
         // 
         // label1
         // 
         this.label1.Location = new System.Drawing.Point(8, 104);
         this.label1.Name = "label1";
         this.label1.Size = new System.Drawing.Size(160, 32);
         this.label1.TabIndex = 5;
         this.label1.Text = "Output Image";
         // 
         // label2
         // 
         this.label2.Location = new System.Drawing.Point(24, 144);
         this.label2.Name = "label2";
         this.label2.Size = new System.Drawing.Size(56, 24);
         this.label2.TabIndex = 6;
         this.label2.Text = "Name:";
         // 
         // label3
         // 
         this.label3.Location = new System.Drawing.Point(24, 200);
         this.label3.Name = "label3";
         this.label3.Size = new System.Drawing.Size(56, 32);
         this.label3.TabIndex = 7;
         this.label3.Text = "Width";
         // 
         // label4
         // 
         this.label4.Location = new System.Drawing.Point(24, 240);
         this.label4.Name = "label4";
         this.label4.Size = new System.Drawing.Size(56, 32);
         this.label4.TabIndex = 8;
         this.label4.Text = "Height";
         // 
         // label5
         // 
         this.label5.Location = new System.Drawing.Point(8, 8);
         this.label5.Name = "label5";
         this.label5.Size = new System.Drawing.Size(160, 32);
         this.label5.TabIndex = 9;
         this.label5.Text = "Input Image";
         // 
         // label6
         // 
         this.label6.Location = new System.Drawing.Point(24, 48);
         this.label6.Name = "label6";
         this.label6.Size = new System.Drawing.Size(56, 24);
         this.label6.TabIndex = 10;
         this.label6.Text = "Name:";
         // 
         // DismissButton
         // 
         this.DismissButton.Location = new System.Drawing.Point(544, 320);
         this.DismissButton.Name = "DismissButton";
         this.DismissButton.Size = new System.Drawing.Size(184, 32);
         this.DismissButton.TabIndex = 11;
         this.DismissButton.Text = "Dismiss";
         this.DismissButton.Click += new System.EventHandler(this.DismissButton_Click);
         // 
         // RunButton
         // 
         this.RunButton.Location = new System.Drawing.Point(8, 320);
         this.RunButton.Name = "RunButton";
         this.RunButton.Size = new System.Drawing.Size(184, 32);
         this.RunButton.TabIndex = 12;
         this.RunButton.Text = "Run";
         this.RunButton.Click += new System.EventHandler(this.RunButton_Click);
         // 
         // label7
         // 
         this.label7.Location = new System.Drawing.Point(24, 280);
         this.label7.Name = "label7";
         this.label7.Size = new System.Drawing.Size(56, 32);
         this.label7.TabIndex = 15;
         this.label7.Text = "Gores";
         // 
         // NumberOfGoresCtrl
         // 
         this.NumberOfGoresCtrl.Location = new System.Drawing.Point(88, 280);
         this.NumberOfGoresCtrl.Maximum = new System.Decimal(new int[] {
                                                           60,
                                                           0,
                                                           0,
                                                           0});
         this.NumberOfGoresCtrl.Minimum = new System.Decimal(new int[] {
                                                           1,
                                                           0,
                                                           0,
                                                           0});
         this.NumberOfGoresCtrl.Name = "NumberOfGoresCtrl";
         this.NumberOfGoresCtrl.TabIndex = 13;
         this.NumberOfGoresCtrl.Value = new System.Decimal(new int[] {
                                                         1,
                                                         0,
                                                         0,
                                                         0});
         // 
         // OverlapGoresByCtrl
         // 
         this.OverlapGoresByCtrl.Location = new System.Drawing.Point(368, 280);
         this.OverlapGoresByCtrl.Maximum = new System.Decimal(new int[] {
                                                            60,
                                                            0,
                                                            0,
                                                            0});
         this.OverlapGoresByCtrl.Name = "OverlapGoresByCtrl";
         this.OverlapGoresByCtrl.Size = new System.Drawing.Size(64, 20);
         this.OverlapGoresByCtrl.TabIndex = 16;
         this.OverlapGoresByCtrl.Value = new System.Decimal(new int[] {
                                                          1,
                                                          0,
                                                          0,
                                                          0});
         // 
         // label8
         // 
         this.label8.Location = new System.Drawing.Point(224, 280);
         this.label8.Name = "label8";
         this.label8.Size = new System.Drawing.Size(144, 32);
         this.label8.TabIndex = 17;
         this.label8.Text = "Overap Gores by (degrees)";
         // 
         // Form1
         // 
         this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
         this.ClientSize = new System.Drawing.Size(736, 358);
         this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                        this.label8,
                                                        this.OverlapGoresByCtrl,
                                                        this.label7,
                                                        this.NumberOfGoresCtrl,
                                                        this.RunButton,
                                                        this.DismissButton,
                                                        this.label6,
                                                        this.label5,
                                                        this.label4,
                                                        this.label3,
                                                        this.label2,
                                                        this.label1,
                                                        this.OutputImageHeightCtrl,
                                                        this.InputFileButton,
                                                        this.OutputImageWidthCtrl,
                                                        this.OutputFileButton,
                                                        this.axPajantImageCtrl1});
         this.Name = "Form1";
         this.Text = "Form1";
         ((System.ComponentModel.ISupportInitialize)(this.axPajantImageCtrl1)).EndInit();
         ((System.ComponentModel.ISupportInitialize)(this.OutputImageWidthCtrl)).EndInit();
         ((System.ComponentModel.ISupportInitialize)(this.OutputImageHeightCtrl)).EndInit();
         ((System.ComponentModel.ISupportInitialize)(this.NumberOfGoresCtrl)).EndInit();
         ((System.ComponentModel.ISupportInitialize)(this.OverlapGoresByCtrl)).EndInit();
         this.ResumeLayout(false);

      }
      #endregion

      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main()
      {
         Application.Run(new Form1());
      }

      private void RunButton_Click(object sender, System.EventArgs e)
      {
         // In here is the object that does all the work.
         PAJANTLib.PajantImage pi=axPajantImageCtrl1.pi;
         // Tell the control that I am a registered user.
         ///string userName="You supply this to Pajant";
         ///string password="Pajant will provide this";
         ///pi.Register(userName, password);

         string inputFileName= InputFileButton.Text;// string from text edit control
         string outputFileName= OutputFileButton.Text; // string from text edit control
         int N = (int) NumberOfGoresCtrl.Value; // number of gores from numeric spin edit control
         int Wo= (int) OutputImageWidthCtrl.Value; // output image width  from numeric spin edit control
         int Ho= (int)OutputImageHeightCtrl.Value; //  output image height  from numeric spin edit control
         double slack = (double) OverlapGoresByCtrl.Value; // amount of slack in the map-to-gore projection calculation

         // Load the JPG file into the control.
         pi.Load(inputFileName, 0);

         int Wi=pi.Width; // width of input image
         int Hi=pi.Height; // height of input image

         byte [,] r=new byte[Wo,Ho]; // red values of each position in the output image
         byte [,] g=new byte[Wo,Ho]; // green values of each position in the output image
         byte [,] b=new byte[Wo,Ho]; // blue values of each position in the output image

         double W = 360.0/N; // the width of a gore.

         for(int yo=0; yo<Ho; yo++)
         {
            double yg=180.0*yo/Ho-90.0; // the latitude on the globe, which   corresponds to the y-coordinate yo on the output image.
            double cosyg=Math.Cos(yg*Math.PI/180.0);

            for(int xo=0; xo<Wo;xo++)
            {
               double xgs=360.0*xo/Wo-180.0; // the x coordinate (in degrees)of the location on the sinusoidally projected map, which corresponds to
                                      // the x-coordinate xo on the output image.
               int i= (int) Math.Round(Math.Floor(1.0+(xgs+180.0)/W)); // the gore number in which xgs and xgr lie (leftmost gore = number 1; rightmost = number N)
                double c=(i-0.5)*W-180.0; // longitude of the centre of gore #i
               double xgr=(xgs-c)/cosyg+c; // the x coordinate (in degrees) (i.e. the longitude) of the location on the globe which corresponds to
                                    // the x coordinate xo on the output image.

               if(xgr-slack/2>i*W-180.0 || xgr+slack/2<(i-1.0)*W-180.0) // (xo,yo) is a point   that lies between the gores on the projection onto the output image, so colour it grey
                                                               // (note allow a bit of slack!)
               {
                  r[xo,yo]=127;
                  g[xo,yo]=127;
                  b[xo,yo]=127;
               }
               else
               {
                  // find the point (xi,yi) on the input image that corresponds to (xgs,yg) on the sinusoidally projected map.
                  double xi=Wi*(xgr+180)/360.0;
                  double yi=Hi*(yg+90.0)/180.0;

                  if(xi>=Wi || yi>=Hi || xi<0 || yi <0 ) // (xo, yo) corresponds to a point entirely outside the input image,
                                                        // so colour it grey
                  {
                      r[xo,yo]=127;
                     g[xo,yo]=127;
                     b[xo,yo]=127;
                  }
                  else
                  {
                     // interpolate a value of r g and b for (xo,yo) in the output image based upon the values at the grid-points surrounding
                     // (xi,yi) in the input image
                     int xb= (int) Math.Round(Math.Floor(xi)); // the integer x coordinate before xi
                     int xa=xb+1;
                     int yb= (int) Math.Round(Math.Floor(yi)); // the integer y coordinate before yi
                     int ya=yb+1;

/*
byte Rbb=0, Gbb=0, Bbb=0;
GetPixel(xb,yb, ref Rbb, ref Gbb, ref Bbb);
r[xo,yo]=Rbb;
g[xo,yo]=Gbb;
b[xo,yo]=Bbb;
*/
                     if(ya==Hi && xa==Wi) // (xb,yb) is the top right right point of the image - don't interpolate at all
                     {
                        byte Rbb=0, Gbb=0, Bbb=0;
                        GetPixel(xb,yb, ref Rbb, ref Gbb, ref Bbb);
                        r[xo,yo]=Rbb;
                        g[xo,yo]=Gbb;
                        b[xo,yo]=Bbb;
                     }
                     else if(ya==Hi) // and xa!=Wi: (xb,yb) lies in the top line of the image - linear interpolate horizontally
                     {
                        byte Rbb=0, Gbb=0, Bbb=0, Rab=0, Gab=0, Bab=0;
                        GetPixel(xb,yb, ref Rbb, ref Gbb, ref Bbb);
                        GetPixel(xa,yb, ref Rab, ref Gab, ref Bab);
                        r[xo,yo]= (byte) Math.Round(Lerp1(Rbb,Rab,xi-xb));
                        g[xo,yo]= (byte) Math.Round(Lerp1(Gbb,Gab,xi-xb));
                        b[xo,yo]= (byte) Math.Round(Lerp1(Bbb,Bab,xi-xb));
                     }
                     else if(xa==Wi) // and ya!=Hi: (xb,yb) lies in the rightmost line of the image - linear interpolate vertically
                     {
                        byte Rbb=0, Gbb=0, Bbb=0, Rba=0, Gba=0, Bba=0;
                        GetPixel(xb,yb, ref Rbb, ref Gbb, ref Bbb);
                        GetPixel(xb,ya, ref Rba, ref Gba, ref Bba);
                        r[xo,yo]= (byte) Math.Round(Lerp1(Rbb,Rba,yi-yb));
                        g[xo,yo]= (byte) Math.Round(Lerp1(Gbb,Gba,yi-yb));
                        b[xo,yo]= (byte) Math.Round(Lerp1(Bbb,Bba,yi-yb));
                     }
                     else // do a bilinear interpolation
                     {
                        byte Rbb=0, Gbb=0, Bbb=0, Rab=0, Gab=0, Bab=0, Rba=0, Gba=0, Bba=0, Raa=0, Gaa=0, Baa=0;
                        GetPixel(xb,yb, ref Rbb, ref Gbb, ref Bbb);
                        GetPixel(xa,yb, ref Rab, ref Gab, ref Bab);
                        GetPixel(xb,ya, ref Rba, ref Gba, ref Bba);
                        GetPixel(xa,ya, ref Raa, ref Gaa, ref Baa);

                        r[xo,yo]=  (byte) Math.Round(BLerp1(Rbb,Rab,Rba,Raa,
                           xi-xb, yi-yb));
                        g[xo,yo]= (byte) Math.Round(BLerp1(Gbb,Gab,Gba,Gaa,
                           xi-xb, yi-yb));
                        b[xo,yo]= (byte) Math.Round(BLerp1(Bbb,Bab,Bba,Baa,
                           xi-xb, yi-yb));
                     }

                  }
               }
            }
         }
         // The arrays r[,], g[,] and b[,] now contain the RGB values that we want to write in the output image.

         // prepare the control to hold our output image.
         pi.ResizeImage(Wo,Ho,PJT_NEWIMAGE);

         // Write the RGB values into the control
         for(int yo=0; yo<Ho; yo++)
         {
            for(int xo=0; xo<Wo;xo++)
            {
               SetPixel(xo,yo,r[xo,yo],g[xo,yo],b[xo,yo]);
            }
         }
         // save the output image into the file
         pi.Save(outputFileName, 0);


      } // end of method RunButton_Click


      private void GetPixel(int x, int y, ref byte r, ref byte g, ref byte b)
      {
         PAJANTLib.PajantImage pi=axPajantImageCtrl1.pi;

         int rgb=0, dummy=0;
         pi.GetPixel(x,y, ref rgb, ref dummy, ref dummy, ref dummy, PJT_COORDINATES | PJT_COLORREF);
         int ri=(rgb & 0x00FF0000) >> 16;
         int gi=(rgb & 0x0000FF00) >> 8;
         int bi=rgb & 0x000000FF;

         r= (byte) ri;
         g= (byte) gi;
         b= (byte) bi;
      }

      private void SetPixel(int x, int y, byte r, byte g, byte b)
      {
         PAJANTLib.PajantImage pi=axPajantImageCtrl1.pi;

         int dummy=0;

         int ri=r; ri<<=16;
         int gi=g; gi<<=8;
         int bi=b;
         pi.SetPixel(x,y, ri|gi|bi, dummy, dummy, dummy, PJT_COORDINATES | PJT_COLORREF);
      }

      private double Lerp1(double vb, double va, double x) // linear interpolation between vb and va, assuming that the grid points are 1 unit apart and x is  between 0 and 1.
      {
         double v=vb+x*(va-vb);
         return v;
      }

      private double BLerp1(double vbb, double vab, double vba, double vaa, double x, double y) // bilinear interpolation between vbb, vba, vab and vaa, assuming that the grid points are 1 unit apart and x and y are between 0 and 1.
      {
         double v=(1-x)*(1-y)*vbb + x*(1-y)*vab + x*y*vaa + (1-x)*y*vba;
         return v;
      }

      private int PJT_COLORREF=0x100;
      private int PJT_COORDINATES=0x1;
      private int PJT_NEWIMAGE=0x3;

      private void InputFileButton_Click(object sender, System.EventArgs e)
      {
         Stream myStream;
         OpenFileDialog openFileDialog1 = new OpenFileDialog();

         openFileDialog1.InitialDirectory = "c:\\" ;
         openFileDialog1.Filter = "jpg files (*.jpg)|*.jpg|All files (*.*)|*.*" ;
         openFileDialog1.FilterIndex = 2 ;
         openFileDialog1.RestoreDirectory = true ;

         if(openFileDialog1.ShowDialog() == DialogResult.OK)
         {
            if((myStream = openFileDialog1.OpenFile())!= null)
            {
               myStream.Close();
               InputFileButton.Text=openFileDialog1.FileName;
            }
            else
            {
               MessageBox.Show("Cannot open file: "+openFileDialog1.FileName);
            }
         }

      }

      private void OutputFileButton_Click(object sender, System.EventArgs e)
      {
         Stream myStream ;
         SaveFileDialog saveFileDialog1 = new SaveFileDialog();

         saveFileDialog1.Filter = "jpg files (*.jpg)|*.jpg|All files (*.*)|*.*"  ;
         saveFileDialog1.FilterIndex = 2 ;
         saveFileDialog1.RestoreDirectory = true ;

         if(saveFileDialog1.ShowDialog() == DialogResult.OK)
         {
            if((myStream = saveFileDialog1.OpenFile()) != null)
            {
               myStream.Close();
               OutputFileButton.Text=saveFileDialog1.FileName;
            }
            else
            {
               MessageBox.Show("Cannot write file: "+saveFileDialog1.FileName);
            }
         }

      }

      private void DismissButton_Click(object sender, System.EventArgs e)
      {
         Application.Exit();
      }

   };
}