using System;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Geom
{
	/// <summary>
	/// Summary description for Xform.
	/// </summary>
	/// <summary>
	/// Xform class carries a Graphics.Transform matrix and and info string
	/// Depends on the Coord class for survey specific transformations.
	/// </summary>
	public class Xform
	{
		public static string Revision
		{
			get 
			{
				return "$Revision: 1.1.1.1 $";
			}
		}
		public string info;				// information about the field for debugging
		public string dbgMsg;			// used for debugging 
		public Matrix m;
		private bool  debug = true;
		private Coord coord;

		public Xform()
		{}

		public Xform(string info_)
		{
			info = info_;
		}

		/// <summary>
		/// Added this to get affine transformation of SDSS
		/// </summary>
		/// <param name="coord">Coord class corresponding to each SDSS frame</param>
		/// <param name="p">Projection class will be SDSS type</param>
		public Xform (Coord coord, IProjection p)
		{
			this.coord = coord;
			m = getAffineTransform ((SDSSProjection) p, coord);
			
		}

		/// <summary>
		/// Method is used to get the affine transformation of WCS frame
		/// </summary>
		/// <param name="c">WCS frame</param>
		/// <param name="p">Projection class</param>
		public Xform(FrameWCS c, IProjection p)
		{
			info = c.info;
			m    = getAffineTransform(c,p);
		}

		/// <summary>
		/// return the coord. Required during drawing other options
		/// Was added when merging the generic imgcutout and SDSS imgcutou
		/// </summary>
		/// <returns>Coord class corresponding to each SDSS frame</returns>
		public Coord getCoord ()
		{
			return coord;
		}
		
		/// <summary>
		/// getAffineTransform. Compute the affine transformation for the
		/// graphic context that maps the pixel coordinates of a Frame to
		/// the world coordinate system of the projection of the canvas.
		/// </summary>
		/// <param name="c">The input astrometric transformation of the Frame</param>
		/// <returns>Matrix for the canvas graphic context</returns>
		public Matrix getAffineTransform(FrameWCS fc, IProjection pr)
		{
			// compute the corners in both coordinate systems
			PointF[] p	= FrameWCS.FieldGeometry(true);
			PointF[] g	= new PointF[p.Length];
            PointEq[] tmp = new PointEq[4];
			//FrameWCS fc	= new FrameWCS();
			//fc.Copy(c);
			fc.scale	= 1.0;

			//------------------------
			// loop through the array
			//------------------------
			for(int i=0;i<p.Length;i++)
			{
                g[i] = pr.EqToScreen(fc.FrameToEq(p[i]));
                if (debug && false)
				{
					dbgMsg	= fc.info.ToString()+":\n";
					dbgMsg += i.ToString("F0")+": ";
					dbgMsg += fc.ra.ToString("F5")+",";
					dbgMsg += fc.dec.ToString("F5")+"\n";
				}
			}

			//------------------------------------
			// build up the terms for solving the 
			// best fit affine transformation
			//------------------------------------
			float qu=0, qv=0, qux=0, quy=0, qvx=0, qvy=0, 
				  qx=0, qy=0, qxx=0, qyy=0, qxy=0;

			for(int i=0;i<4;i++) 
			{
				qu  += g[i].X/4;
				qv  += g[i].Y/4;

				qux += g[i].X * p[i].X/4;
				qvx += g[i].Y * p[i].X/4;
				quy += g[i].X * p[i].Y/4;
				qvy += g[i].Y * p[i].Y/4;

				qx  += p[i].X/4;
				qy  += p[i].Y/4;

				qxx += p[i].X * p[i].X/4;
				qxy += p[i].X * p[i].Y/4;
				qyy += p[i].Y * p[i].Y/4;
			}

			//--------------------------------------
			// we seek the affine transformation 
			// in the form (see Petzold book p. 292)
			//		x' = sx*x + rx * y + dx
			//		y' = ry*x + sy * y + dy;
			//--------------------------------------

			double d = (float) (det3x3(new double[] {1.0, qx, qy,  qx, qxx, qxy,  qy, qxy, qyy}));
			float dx = (float) (det3x3(new double[] { qu, qx, qy, qux, qxx, qxy, quy, qxy, qyy})/d);
			float sx = (float) (det3x3(new double[] {1.0, qu, qy,  qx, qux, qxy,  qy, quy, qyy})/d);
			float rx = (float) (det3x3(new double[] {1.0, qx, qu,  qx, qxx, qux,  qy, qxy, quy})/d);
			float dy = (float) (det3x3(new double[] { qv, qx, qy, qvx, qxx, qxy, qvy, qxy, qyy})/d);
			float ry = (float) (det3x3(new double[] {1.0, qv, qy,  qx, qvx, qxy,  qy, qvy, qyy})/d);
			float sy = (float) (det3x3(new double[] {1.0, qx, qv,  qx, qxx, qvx,  qy, qxy, qvy})/d);

			return new Matrix(sx,ry,rx,sy,dx,dy);
		}

		/// <summary>
		/// GetAffineTransform. Compute the affine transformation for the
		/// graphic context that maps the pixel coordinates of a Frame to
		/// the world coordinate system of the projection of the canvas.
		/// </summary>
		/// <param name="coord">The input astrometric transformation of the Frame</param>
		/// <returns>Matrix for the canvas graphic context</returns>
		public Matrix getAffineTransform(SDSSProjection proj, Coord frameCoord)
		{
			// compute the corners in both coordinate systems

			PointF[] p	= SdssConstants.FieldGeometry(true);
			int len		= p.Length;	
			PointF[] g	= new PointF[len];
			Coord fc	= new Coord();
			fc.copy(frameCoord);
			fc.scale	= 1.0;

			// loop through the array
			for(int i=0;i<len;i++)
			{
				fc.FrameToEq(p[i].X,p[i].Y);
				g[i] = proj.EqToScreen(fc.ra, fc.dec);
			}

			// build up the terms for solving the best fit affine transformation
			
			float qu=0, qv=0, qux=0, quy=0, qvx=0, qvy=0, 
				qx=0, qy=0, qxx=0, qyy=0, qxy=0;

			for(int i=0;i<4;i++) 
			{
				qu  += g[i].X/4;
				qv  += g[i].Y/4;

				qux += g[i].X * p[i].X/4;
				qvx += g[i].Y * p[i].X/4;
				quy += g[i].X * p[i].Y/4;
				qvy += g[i].Y * p[i].Y/4;

				qx  += p[i].X/4;
				qy  += p[i].Y/4;

				qxx += p[i].X * p[i].X/4;
				qxy += p[i].X * p[i].Y/4;
				qyy += p[i].Y * p[i].Y/4;
			}
			
			// we seek the affine transformation in the form (see Petzold book p. 292)
			//		x' = sx*x + rx * y + dx
			//		y' = ry*x + sy * y + dy;

			double d = (float) (det3x3(new double[] {1.0, qx, qy,  qx, qxx, qxy,  qy, qxy, qyy}));
			float dx = (float) (det3x3(new double[] { qu, qx, qy, qux, qxx, qxy, quy, qxy, qyy})/d);
			float sx = (float) (det3x3(new double[] {1.0, qu, qy,  qx, qux, qxy,  qy, quy, qyy})/d);
			float rx = (float) (det3x3(new double[] {1.0, qx, qu,  qx, qxx, qux,  qy, qxy, quy})/d);
			float dy = (float) (det3x3(new double[] { qv, qx, qy, qvx, qxx, qxy, qvy, qxy, qyy})/d);
			float ry = (float) (det3x3(new double[] {1.0, qv, qy,  qx, qvx, qxy,  qy, qvy, qyy})/d);
			float sy = (float) (det3x3(new double[] {1.0, qx, qv,  qx, qxx, qvx,  qy, qxy, qvy})/d);

			/*
			if (debug && false)
			{
				debugMessage.AppendFormat("Affine: [{0}][ {1}, {2},  {3}, {4}, {5}, {6}]\n",coord.info, sx,ry,rx,sy,dx,dy);        
				debugMessage.AppendFormat("             [ W {0}, H {1}]\n",p[2].X-p[0].X, p[2].Y-p[0].Y);        
				debugMessage.AppendFormat("             [ W {0}, H {1}]\n",g[2].X-g[0].X, g[2].Y-g[0].Y);        
			}
			*/
           
			return new Matrix(sx,ry,rx,sy,dx,dy);
		}

		/// <summary>
		/// det3x3. Computes the determinant of a 3x3 matrix.
		/// </summary>
		/// <param name="a">Vector of 9 doubles, the elements of the matrix</param>
		/// <returns></returns>
		private double det3x3(double[] a)
		{
			return (a[0]*a[4]*a[8] + a[1]*a[5]*a[6] + a[2]*a[3]*a[7] 
				- a[0]*a[7]*a[5] - a[3]*a[1]*a[8] - a[6]*a[4]*a[2]);
		}
	}	// End of Xform class

}
