using System;
using System.Configuration;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Data.OleDb;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.IO;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Net;
using Geom;

namespace GenericImgCutout
{
    /// <summary>
    /// NonSDSSSurvey implementation details. Derived from Survey
    /// </summary>
    public class NonSDSSSurvey : Survey
    {
        private StringBuilder query;			// It can be a SQL SELECT query or 
        // a string like SR(12,20) 
        // which will mark Stars with magnitudes between 12 and 20 in the R band												
        // The code follows the pattern:
        // objType: S | G | P  --	S for Stars
        //							G for Galaxies
        //							P for Both Stars and Galaxies																							
        // bad: u | g | r | i | z | a --  will select objects with
        //		
        //         band  BETWEEN low_mag AND high_mag
        // 
        // if band is 'a' then it will look for all the objects 
        // with values betwen low_mag and high_mag for any band	(compositions of OR)
        // if band and magnitude ranges are not specified then will mark only Stars, 
        // Galaxies, or PhotoPrimary objects 
        //-------------------------------------
        // Derived parameters
        //-------------------------------------
        // zoom: the image pyramid level [0..max_zoom]
        // imageScale: the additional scaling of 
        // the image beyond walking the pyramid

        public NonSDSSSurvey(string surveyName, string sConnect)
        {
            //
            // TODO: Add constructor logic here
            //
            this.surveyName = surveyName;
            this.sConnect = String.Copy(sConnect);

        }

        public override byte[] GetJpeg(
            double ra_,						// right ascension in J2000 degrees
            double dec_, 						// declination in J2000 degrees
            double scale_,						// arcsec/pixel (0.3961267 is native 1:1 for SDSS)
            Int32 width_,						// image width  (in pixels)
            Int32 height_,					// image height (in pixels)
            string opt_,						// drawing options
            string rid_,						// list of regionIDs
            string query_
            )
        {

            validateInput(ra_, dec_, scale_, height_, width_, opt_, rid_, query_);
            string qmsg = "ra=" + ra_.ToString("F5")
                + ", dec=" + dec_.ToString("F5") + "\n"
                + "scale = " + scale.ToString("F5") + "\n";


            /* Non SDSS Specific */
            zoom = 0;
            ppd = 3600.0 / scale;
            imageScale = ppd / FrameWCS.PixelsPerDegree;

            while (zoom < max_zoom & imageScale <= .5)
            {
                zoom++;											// go higher in the pyramid
                imageScale *= 2;								// change the scaling accordingly
            }
            zoomScale = (float)Math.Pow(2, zoom);				// set the scale according to the real zoom
            size = (float)((zoom >= 2) ? 12/zoom : 9.5 * imageScale);	// set symbol size based on zoom level


            qmsg += "imageScale = " + imageScale.ToString("F5") + "\n"
                + "zoomScale  = " + zoomScale.ToString("F5") + "\n";
            //--------------------------------------------------------------------------
            // set SQL search radii for fields and objects given in terms of ArcMinutes
            //--------------------------------------------------------------------------
            radius = 60.0 * 0.5 * Math.Sqrt(width * width + height * height) / ppd;
            fradius = FrameWCS.GetFrameHalfDiag () + radius;

            if (drawQuery) validateQuery(query_);

            try
            {
                // Create a canvas object that holds information to map the WCS images to Screen
                canvas = new Canvas(width, height, imageScale, ppd, debug);
                connectToDataBase();

                // Non SDSS Specific Projection 
                canvas.InitializeProjection(ra, dec, ppd, "TAN");
                canvas.addDebugMessage(qmsg);

                ///<comment>
                /// This is not being used at all 
                ///</comment>
                // Get the viewPort 
                // viewPort = canvas.GetViewPort();

                //----------------------------------------
                if (drawFrames | drawField)
                    getFrames();

                if (drawField)
                    getFields();

                if (drawPhotoObjs | drawSpecObjs | drawTargetObjs)
                    getObjects();

                if (drawQuery) getQueryObjects(query_);
                if (drawList) getListObjects();

                if (drawLabel) getLabel();
                if (drawGrid) canvas.drawCrosshair();
                
                //-----------------------------------------------
                // draw the debug messages before the inversion
                //-----------------------------------------------
                if (debug)
                {
                    canvas.drawAxis(ra, dec);
                    canvas.addDebugMessage(dbgMsg.ToString());
                    canvas.drawDebugMessage();
                }
                // Invert image if invert flag is present
                if (invertImage) canvas.Invert();
            }
            catch (Exception e)
            {
                canvas.displayMessage(e.Message);
            }
            finally { disconnectFromDataBase(); }

            // return the image to the calling routine (called from getJpeg ().
            return (canvas.getBuffer());

        }

        /// <summary>
        /// getFrames. Fetch the images and paint them onto the canvas.
        /// </summary>
        public override void getFrames()
        {
            FrameWCS currentFrame = null;		// coord of current frame
            Xform xform = null;					// the transform of current frame

            // Get the SQL to retrieve all the frames that are near to the
            // ref point using the fGetNearbyFrameEq function. This will also
            // return information about the frame i.e. (crval1, crval2, crpix etc)
            String frameSql = FrameWCS.GetFrameSQL(ra, dec, fradius, zoom, true);
            SqlCommand frameCommand = new SqlCommand(frameSql, SqlConn);

            if (debug && false) canvas.addDebugMessage("Frame Query: \n" + frameSql);
            try
            {
                xTable = new Hashtable();
                reader = frameCommand.ExecuteReader();

                while (reader.Read())
                {
                    //---------------------------------------------					
                    // get the astrometry parameters of this frame
                    //---------------------------------------------
                    currentFrame = new FrameWCS(reader, zoomScale, drawFrames);
                    string fieldId = currentFrame.fieldid;

                    //---------------------------------------------
                    // compute and save the canvas transformation
                    //---------------------------------------------
                    xform = new Xform(currentFrame, canvas.proj);
                    //xTable.Add(fieldId, xform);

                    if (debug) canvas.addDebugMessage(xform.dbgMsg);

                    if (drawFrames)
                    {
                        //---------------------------------
                        // draw the tile and discard bytes
                        //---------------------------------
                        canvas.drawFrame(xform, currentFrame.tile);
                        currentFrame.tile.Dispose();

                        if (currentFrame.fieldid.LastIndexOf ("2473076666268647424_0_20") >= 0)
                        {
                            PointEq ctl = currentFrame.FrameToEq(new PointF(0, 0));
                            PointEq cbr = currentFrame.FrameToEq(new PointF(512, 512));
                            PointEq ctr = currentFrame.FrameToEq(new PointF(512, 0));
                            PointEq cbl = currentFrame.FrameToEq(new PointF(0, 512));
                            PointEq c_0_1024 = currentFrame.FrameToEq(new PointF(0, 1024));
                            //break;
                        }
                    }
                    //break;
                }
                if (reader != null) reader.Close();
            }
            catch
            {
                throw new Exception("Error in drawing Frames");
            }
        }

        /// <summary>
        /// getFields. Display the outlines of the Fields on the canvas.
        /// </summary>
        public override void getFields()
        {
            try
            {
                foreach (DictionaryEntry d in xTable)
                {
                    canvas.drawField((Xform)xTable[d.Key]);
                }
            }
            catch
            {
                throw new Exception("Error in drawing Field outlines");
            }
        }

        /// <summary>
        /// getObjects. Display a mark at the location of objects.
        /// </summary>
        public override void getObjects()
        {
            byte flag = 0;
            double baseMag = 22;
            baseMag -= (zoom) * 0.5;
            string magLimit = baseMag.ToString();

            StringBuilder sQ = new StringBuilder(" select ");
            sQ.Append("p.ra, p.dec, cast(2 as int) as flag ");
            sQ.AppendFormat("from dbo.fGetNearbyObjEq({0},{1},{2}) n, ", ra, dec, radius);
            sQ.Append("PhotoPrimary p where n.objid=p.objid and (p.nuv_mag between 10 and " + magLimit + ")");

            try
            {
                SqlConnection photoConn = new SqlConnection("Initial Catalog=Galex_GR2;Data Source=dlmsdb001; User ID=test;Password=riuzg54; timeout=120;");
                photoConn.Open();

                SqlCommand cmd = new SqlCommand(sQ.ToString(), photoConn);
                cmd.CommandTimeout = 0;

                double oRa, oDec;
                byte oFlag;

                reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    oRa = Convert.ToDouble(reader[0]);		// get ra
                    oDec = Convert.ToDouble(reader[1]);		// get dec
                    oFlag = Convert.ToByte(reader[2]);		// get flag												

                    if (drawSpecObjs && (oFlag & FrameWCS.sflag) > 0)
                        canvas.drawSpecObj(oRa, oDec, size);
                    if (drawPhotoObjs && (oFlag & FrameWCS.pflag) > 0)
                        canvas.drawPhotoObj(oRa, oDec, size);
                    if (drawTargetObjs && (oFlag & FrameWCS.tflag) > 0)
                        canvas.drawTargetObj(oRa, oDec, size);
                }
                if (reader != null) reader.Close();
            }
            catch (Exception e)
            {
                showException("getObjects() [Photo|Spec|Target]", sQ.ToString(), e);
            }
        }

        #region Try Marias Mark Query by Object

        /// <summary>
        /// getListObjects(). Mark objects from a list.
        /// The function processes each line creating a DataTable of objects.
        /// The fisrt line in the list must have the name of the columns below.
        /// The list may include as many columns as desired as far as 
        /// RA and DEC are included.
        /// </summary>
        private void getListObjects()
        {
            // create the table that will hold query results
            DataTable dt = new DataTable("Coordinates");
            try
            {
                string[] querylines = query.ToString().Split(new char[] { '\n' });
                string line = cleanLine(querylines[0]);
                string[] columns = line.Split(new char[] { ' ', '\t', ',', ';' });

                for (int j = 0; j < columns.Length; j++)
                    dt.Columns.Add(columns[j]);

                for (int i = 1; i < querylines.Length; i++)
                {
                    line = cleanLine(querylines[i]);
                    string[] fields = line.Split(new char[] { ' ', '\t' });
                    if (fields.Length != columns.Length)
                    {
                        if (line.CompareTo("") == 0) continue;
                        else
                            throw new Exception("Wrong number of columns in row " + i.ToString());
                    }

                    DataRow newRow;
                    newRow = dt.NewRow();
                    for (int j = 0; j < fields.Length; j++)
                    {
                        newRow[columns[j]] = fields[j];
                    }

                    string sra = Convert.ToString(newRow["ra"]);
                    if (sra.IndexOf(":") != -1)
                        newRow["ra"] = Survey.hms2deg(sra).ToString();

                    string sdec = Convert.ToString(newRow["dec"]);
                    if (sdec.IndexOf(":") != -1)
                        newRow["dec"] = Survey.dms2deg(sdec).ToString();

                    dt.Rows.Add(newRow);
                }

                drawTable(dt);
            }
            catch (Exception e)
            {
                errMsg.AppendFormat("Wrong mark query. {0} \n\nYour input was:\n{1}", e.Message, query.ToString());
            }
        }

        /// <summary>
        /// getQueryObjects. Mark objects selected by a SQL query.
        /// </summary>
        private void getQueryObjects(string originalQ)
        {
            try
            {
                SqlConnection photoConn = new SqlConnection("Initial Catalog=Galex_GR2;Data Source=dlmsdb001; User ID=test;Password=riuzg54;");
                photoConn.Open();

                //set up the data adapter to get our data...
                DataSet ds = new DataSet();

                SqlDataAdapter da = new SqlDataAdapter(query.ToString(), photoConn);
                da.Fill(ds, "MarkedObjects");
                if (ds.Tables["MarkedObjects"].Columns["error_message"] != null)
                {
                    throw new Exception("");
                }
                drawTable(ds.Tables["MarkedObjects"]);
                photoConn.Close();

            }
            catch (Exception e)
            {
                errMsg.AppendFormat("Wrong mark query. {0} \n\nYour input was:\n{1}", e.Message, originalQ);
            }
        }


        /// <summary>
        /// cleanLine.() 
        /// Replaces separation caracters ';' and ',' for spaces and then
        /// double spaces for only one space to avoid unnecessary columns
        /// when building the attributes table.
        /// </summary>
        private string cleanLine(string ln)
        {
            string line = ln;
            line = line.Replace(";", " ");
            line = line.Replace(",", " ");
            while (line.IndexOf("  ") != -1)
            {
                line = line.Replace("  ", " ");
            }
            line = line.Trim();
            return line;
        }


        /// <summary>
        /// drawTable(). Mark with a triangle objects included in a table .
        /// </summary>
        private void drawTable(DataTable dt)
        {
            double oRa = 0.0, oDec = 0.0;


            foreach (DataRow dataRow in dt.Rows)
            {
                oRa = Convert.ToDouble(dataRow["ra"]);
                oDec = Convert.ToDouble(dataRow["dec"]);

                canvas.drawQueryObj(oRa, oDec, size);
            }
        }


        private enum QUERYTYPE
        {
            SQL = 0,	// SQL
            FLT = 1,	// Filter
            LST = 2,	// List of objects
            UKN = 3
        }

        ///<summary>
        /// getQueryType(). Determines the type of marking query. SQL, List of objects or Filter
        ///</summary>				
        private QUERYTYPE getQueryType(string qry)
        {

            if (qry.IndexOf("SELECT") != -1) return QUERYTYPE.SQL; //SQL
            if ((qry.IndexOf("RA") != -1) && (qry.IndexOf("DEC") != -1)) return QUERYTYPE.LST; //List of objects
            if (qry.IndexOfAny(new char[] { 'S', 'G', 'P' }) != -1) return QUERYTYPE.FLT;

            return QUERYTYPE.UKN;
        }


        ///<summary>
        /// check_and_set_ObjType(). Determines what type of objects are being filtering and builds the proper SQL query.
        /// Returns the Table alias to be used by the check_and_set_Band() function.
        ///</summary>	
        private string check_and_set_ObjType(char objType)
        {
            string table = "", t = "";
            switch (objType)
            {
                case 'P':
                    table = "PhotoPrimary";
                    t = "p";
                    break;
                case 'G':
                    table = "Galaxy";
                    t = "g";
                    break;
                case 'S':
                    table = "Star";
                    t = "s";
                    break;
                default:
                    throw new Exception("Valid object types: P | S | G \n");
            }
            query = new StringBuilder("SELECT ");
            query.AppendFormat("{0}.ra as ra, {0}.dec as dec \n", t);
            query.AppendFormat("FROM dbo.fGetNearbyObjEq({0},{1},{2}) n JOIN {3} {4} \n",
                ra, dec, radius, table, t);
            query.AppendFormat("ON n.objId = {0}.objId \n", t);
            return t;
        }

        ///<summary>
        /// validateQuery(). Filtering queries of the form SR(10,20) are decomposed 
        /// and the query string is composed.
        ///</summary>		
        private bool validateQuery(string myq)
        {
            bool correctQuery = true;
            char objType;
            char band;
            double low_mag, high_mag;

            # region Done to incorporate line breaks in text box
            string tmpquery = myq;
            string[] qryArray = tmpquery.Split('_');
            string query_ = qryArray[0];
            for (int i = 1; i < qryArray.Length; i++)
            {
                query_ += "\n" + qryArray[i];
            }

            query_ = query_.ToUpper();
            myq = query_;
            #endregion


            QUERYTYPE queryType = getQueryType(query_);

            switch (queryType)
            {
                case QUERYTYPE.SQL:
                    string server_name = "skyservice";
                    string rows = "500000";
                    string access = "ImgCutout";

                    /// If the query contains SELECT, the query is considered correct and the validation 
                    /// is postponed to be run by spExecute store procedure.
                    query = new StringBuilder();
                    //query.AppendFormat("EXEC spExecuteSQL '{0}', '{1}' ", myq, rows);
                    query.AppendFormat(myq);
                    //This needs to be clarify
                    // EXEC spExecuteSQL '" + c +"  ', 100000,'" + server_name + "','" + windows_name + "','" + remote_addr + "','" + access + "'";
                    break;

                case QUERYTYPE.LST:
                    query = new StringBuilder(myq);
                    drawQuery = false;
                    drawList = true;
                    break;

                case QUERYTYPE.FLT:
                    try
                    {
                        query_ = query_.Replace(" ", ""); //Get rid of the spaces		
                        objType = query_[0];

                        string t = check_and_set_ObjType(objType);

                        if (query_.Length > 1)
                        {

                            band = query_[1];
                            int openB = query_.IndexOf('(');
                            int closeB = query_.IndexOf(')');

                            //it should leave only the numbers
                            StringBuilder magnitudes = new StringBuilder(
                                query_.Substring(openB + 1, closeB - openB - 1));

                            string m = magnitudes.ToString();
                            string[] mag = m.Split(new char[] { ',' });

                            low_mag = Convert.ToDouble(mag[0]);
                            high_mag = Convert.ToDouble(mag[1]);
                            check_and_set_Band(band, t, low_mag, high_mag);
                        }
                    }
                    catch (Exception e)
                    {
                        errMsg.AppendFormat("Wrong mark query. {0}\n\nYour input was:\n{1}", e.Message, myq);
                        correctQuery = false;
                    }
                    break;

                default:
                    errMsg.AppendFormat("Wrong mark query. String in wrong format \n\nYour input was:\n{0}", myq);
                    correctQuery = false;
                    drawQuery = false;
                    break;
            }
            return correctQuery;
        }

        ///<summary>
        /// check_and_set_Band(). Determines what Band is being filtering and builds the proper SQL query.
        ///</summary>
        private void check_and_set_Band(char band, string t, double low_mag, double high_mag)
        {
            switch (band)
            {
                case 'U':
                case 'G':
                case 'R':
                case 'I':
                case 'Z':
                    query.AppendFormat("WHERE {0}.{1} BETWEEN {2} AND {3}",
                        t, band.ToString(), low_mag, high_mag);
                    break;
                case 'A':
                    query.AppendFormat("WHERE {0}.u BETWEEN {1} AND {2} \n",
                        t, low_mag, high_mag);
                    query.AppendFormat("OR {0}.g BETWEEN {1} AND {2} \n",
                        t, low_mag, high_mag);
                    query.AppendFormat("OR {0}.r BETWEEN {1} AND {2} \n",
                        t, low_mag, high_mag);
                    query.AppendFormat("OR {0}.i BETWEEN {1} AND {2} \n",
                        t, low_mag, high_mag);
                    query.AppendFormat("OR {0}.z BETWEEN {1} AND {2} \n",
                        t, low_mag, high_mag);
                    break;
                default:
                    throw new Exception("Valid band values : A | U | G | R | I | Z \n");
            }
        }

        #endregion

    }
}
