// vectorMap.cs  
// --------------------------------------------------------
// July 5,2004 
// By Xiaotao Shu.


using System;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Collections;
using System.Windows.Forms;

using TrackingController;

namespace tools
{
	/// <summary>
	/// A MapLineEntry is a single line of points(positions) from the vector map file( APRS's *.map);
	///   
	/// </summary>
	
	public class MapLineEntry
	{
		private Pen m_pen=null;		      //draw line
		private Brush m_brush=null;		  //fill the object
		private ArrayList m_points;       //points the line including 
		
		public MapLineEntry(byte aByte)
		{
			//00h: 1-picel line, 01h=2-pixel line, 80h filled object 1-pixel border, 81h filled 2-pixel border
			float fPenWidth= (float)(aByte&0x01)+1.0f;
			m_pen  = new Pen(Color.Black,fPenWidth); //for drawing the line
			m_brush= null;                           //for filling object. if is null, not fill object
			m_points=new ArrayList();
		} //end the constructor

		public Brush Brush
		{
			set	{ m_brush = value;		}
			get	{ return m_brush;	}
		}
		public Pen Pen
		{
			set	{ m_pen = value;	}
			get	{ return m_pen;	}
		}
		
		//get the m_points
		public ArrayList getPoints()
		{
			return m_points;
		}

		//add a point into arraylist m_points
		public void addPoint(MapPointEntry aPoint)
		{
			m_pen.Color = aPoint.Color;
			if (aPoint.IsNormalLine)
				m_brush = null;
			else
				m_brush = new SolidBrush(aPoint.BrushColor);
			m_points.Add(aPoint);
		}
	}

	/// <summary>
	/// A MapPointEntry is a single point on map.  Generally it includes text
	/// to describe what the point is representing.
	/// </summary>

	public class MapPointEntry
	{
		//private bool m_currentLoc;
		private bool m_isNormalLine;
		private Color m_brushColor;
		private Color m_color;
		private Position m_location;

		public MapPointEntry(byte[] aVector)
		{
		    m_isNormalLine=false;
			//the first byte of the vector represents the line color.
			//the second byte of vector represents the color of filled object:
			//00h: Nornal line, 80h-hotpink(red), 83h-yellow.....the defaut color is hotpink
			//by xiaotao
			//get the color of filled object
			int iBrushColor = (int)aVector[1];
			
			if (iBrushColor == 0) //normal line
				m_isNormalLine= true;
			else 
			{
				//set filled object color 
				if (iBrushColor > 0x8f ||iBrushColor < 0x80)
					iBrushColor = 0x80;
				m_brushColor = MyColor.color(iBrushColor);
			}
			
			double lat,lon;
			//in aprs map file,longitude and latitude are 4bytes long integer ("big Endian" format),
			//it is represented tenths of seconds of distance(00 00'00.1") from the (180W,90N) 
			//in our system: position latitude from 90 to -90 (90N to 90S), longitude form -180 to 180 (180W to 180E)
			//store the data by the "big endian"
			//by xiaotao
			lon = ((aVector[2]*0x1000000+aVector[3]*0x10000+aVector[4]*0x100+aVector[5]) -180.0*3600*10)/(3600*10); 
			lat = ( 90.0*3600*10 - (aVector[6]*0x1000000+aVector[7]*0x10000+aVector[8]*0x100+aVector[9]))/(3600*10);
			m_location = new Position(lat,lon);
			m_color = MyColor.color((int)aVector[0]);
		}

		public bool IsNormalLine 
		{
			set	{	m_isNormalLine = value;	}
			get {	return m_isNormalLine;	}
		}
		public Color BrushColor
		{
			set	{	m_brushColor = value;	}
			get {	return m_brushColor;	}
		}
		public Color Color
		{
			set	{	m_color = value;	}
			get {	return m_color;		}
		}
		public Position Position
		{
			set	{	m_location = value;	}
			get {	return m_location;	}
		}

	} //class MapPointEntry end


	/// <summary>
	/// A MapLabelEntry is a single label on map.  
	/// </summary>

	public class MapLabelEntry :System.Object
	{
		public Color m_color;
		public Position m_location;
		public int m_alig;
		public int m_mag;   //determine the amount of magnification needed before label become displayable 
		public string m_text;
		public bool isSymbol = false;

		public MapLabelEntry(byte[] aVector)
		{
			int colortype;
			if (aVector[12] == '$') //APRS symbol label
			{
				isSymbol = true;
				/*char temp = (char)aVector[14];
				if (temp<31 || temp >39)
					temp = (char)38;*/
				colortype = 9; //blue
				char[] aLabel = new char[29];
				for (int i=0; i<29; i++)
				{
					if (aVector[i+15]==0)
						aLabel[i] = ' ';
					else 
						aLabel[i] = Convert.ToChar(aVector[i+15]);
				}
				m_text = new String(aLabel);
				m_text = m_text.Trim();
			}
			else   //text label
			{
				colortype = aVector[0] & ~0x80;
				char[] aLabel = new char[32];
				for (int i=0; i< 32; i++)
				{
					if (aVector[i+12]==0)
						aLabel[i] = ' ';
					else 
						aLabel[i] = Convert.ToChar(aVector[i+12]);
				}
				m_text = new String(aLabel);
				m_text =m_text.Trim();
			}
			m_mag = aVector[10]*256+aVector[11];
			m_color = MyColor.color(colortype);
			m_alig = aVector[1];  
			
			//in aprs map file,longitude and latitude are 4bytes long integer ("big Endian" format),
			//it is represented tenths of seconds od distance(00 00'00.1") from the (180W,90N) 
			//in our system: position latitude from 90 to -90 (90N to 90S), longitude form -180 to 180 (180W to 180E)
			//by xiaotao
			double lat,lon;
			lon = ((aVector[2]*0x1000000+aVector[3]*0x10000+aVector[4]*0x100+aVector[5]) -180.0*3600*10)/(3600*10); 
			lat = ( 90.0*3600*10 - (aVector[6]*0x1000000+aVector[7]*0x10000+aVector[8]*0x100+aVector[9]))/(3600*10);
			
			m_location = new Position(lat,lon);
		}
	} //class MapLabelEntry end

	/// <summary>
	/// This class manages the data in a .MAP file.  It knows how to read one, 
	/// parsing each line in the file into data in the Points or Lines list.
	/// It doesn't currently support writing anything back out.
	/// </summary>

	public class VectorMap
	{
		//map attributes
		public string m_type;
		public string m_title;
		public double m_top;   
		public double m_bottom;   
		public double m_left;   
		public double m_right;   
		
		// The map file data
		public ArrayList m_lines;
		public ArrayList m_labels;

		//VectorMap constructor		
		public VectorMap(string Filename)
		{
			m_lines = new ArrayList();
			m_labels = new ArrayList();
			//get data from map file
			ProcessFile(Filename);
		}
		
		
		//parse map data from the mapfile:map header is 256bytes, each vector 10 bytes, each label 44bytes
		//by xiaotao
		void ProcessFile(string Filename)
		{
			// Open the file stream and read map data.
			using (FileStream fs = File.Open(Filename, FileMode.Open, FileAccess.Read, FileShare.Read)) 
			{
				try 
				{	
					//read the map file header
					byte[] buffer = new byte[256];
					int numReaded;
					
					//read header port of map file to buffer:256 bytes
					numReaded = fs.Read(buffer,0,256); 
					//if it isn't a map then throw a exception
					if (numReaded<256)  
					{
						MessageBox.Show(Filename+" is not APRS's Map file");
						throw(new Exception());
					}

					//parse the map type from the map header: 0-3
					char[] temp_type = new char[4];
					for (int i=0; i<4; i++)
					{
						temp_type[i] = Convert.ToChar(buffer[i]);
					}
					m_type=new String(temp_type);
					
					//parse the map title:40-71
					char[] temp_title = new char[32];
					for (int i=40; i<72; i++)
					{
						if (buffer[i] == 0) 
							temp_title[i-40] = ' ';
						else
							temp_title[i-40] = Convert.ToChar(buffer[i]);
					}
					m_title = new String(temp_title);
					m_title = m_title.Trim();

					//map corrdinate format: left:84-87,right88-91, top:92-95 and bottom:96-99;big endian
					//in aprs map file,longitude and latitude are 4bytes long integer ("big Endian" format),
					//it is represented tenths of seconds od distance(00 00'00.1") from the (180W,90N) 
					//our position Latitude from 90 to -90,  longitude from -180 to 180
					//tenth of second: 180.0*3600*10
					//by xiaotao
					this.m_left = ((buffer[84]*0x1000000+buffer[85]*0x10000+buffer[86]*0x100+buffer[87]) - 180.0*3600*10)/(3600*10);
					this.m_right = ((buffer[88]*16777216+buffer[89]*0x10000+buffer[90]*0x100+buffer[91]) - 180.0*3600*10)/(3600*10);
					this.m_top = (90.0*3600*10 - (buffer[92]*0x1000000+buffer[93]*0x10000+buffer[94]*0x100+buffer[95]))/(3600*10);
					this.m_bottom = (90.0*3600*10 - (buffer[96]*0x1000000+buffer[97]*0x10000+buffer[98]*0x100+buffer[99]))/(3600*10);
					
					//vectors number :108-111, label number 112-115
					long numVectors = buffer[108]*0x1000000+buffer[109]*0x10000+buffer[110]*0x100+buffer[111];
					long numLabels = buffer[112]*0x1000000+buffer[113]*0x10000+buffer[114]*0x100+buffer[115];

					//read vectors
					MapLineEntry newLine = null;
					for (int i=0; i<numVectors;i++)
					{
						byte[] aVector = new byte[10];
						//locate a vector from map file: each vector is 10bytes length
						//read a vector
						fs.Read(aVector,0,10);
						if (aVector[0] == 0xFF) //FFh is line start
						{
							//add the MaplineEntry into m_lines
							if (newLine != null)
								m_lines.Add(newLine);
							newLine = new MapLineEntry(aVector[1]);  //line's type
						}
						if (newLine != null)
						{
							newLine.addPoint(new MapPointEntry(aVector));
						}

						//store the last one line
						if ( i == numVectors-1 ) 
							if (newLine != null)
								m_lines.Add(newLine);
					}
					
					//read labels
					for (int i=0; i<numLabels;i++)
					{
						byte[] buffer_label = new byte[44];
						
						//locate a label from map file: 
						//read a label and add to labels
						numReaded=fs.Read(buffer_label,0,44);
						if (numReaded == 44)
						{
							m_labels.Add(new MapLabelEntry(buffer_label));
						}
					}
				} 
				catch (Exception e) 
				{
					MessageBox.Show(e.ToString());
				}
			}
		}

	} // VectorMap class end
}  //the namespace end
