/******************************************************************************
 * Mark.cs 
 * 
 * <Copyright and License> 
 * 
 * This file is part of GODS, a package for annotating medical images. 
 * 
 * Copyright (C) 2004 
 * Xuedong Chen 
 * Heri Mariwa 
 * Kimmy (Hsiang-ling) Lin 
 * Rui Yu 
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 
 * of the License, or (at your option) any later version. 
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
 *  
 * For further questions about this file, please send email to 
 * gods@cs.umb.edu or visit us at http://gods.sourceforge.net.  
 ******************************************************************************/

using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

namespace GODS.MarkData
{
	/// <summary>
	/// Summary description for Mark.
	/// </summary>
	public class Mark : System.Windows.Forms.PictureBox
	{
		public static int MARKWIDTH = 72;
		public static int MARKHEIGHT = 35;
		private int markID;
		private MarkSet markset;
		private MarkGroup markGroup;
		private string markName;
		private string imagePath;
		private string description;
		private string markComment;   // the description of the mark
		private int currentValueIndex = -1;
		private ArrayList presetValues;
		private bool removable = true;
		private bool hasImage = false;
		private bool isMouseDown = false;
		private bool isOnImage = false; //isOnImage=true:mark on image; isOnImage=false:mark on panel
		private double markWidth;   // the imaginary width of the mark.
		private double markHeight;  // the imaginary hdight of the mark.
		private float ratio = 0.25f;
		private int resize = 0;
		private Size origSize;
		private Point pointOnMark;       //coordinate on the mark used to get correct mark position

		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public Mark(int markID, MarkSet markset, MarkGroup markGroup, string markName, string path, string desc)
			: this(-1, -1, markID, markset, markGroup, markName, path, desc) {}

		public Mark(MarkSet markset, MarkGroup markGroup, string markName, string path, string desc) 
			: this(-1, -1, -1, markset, markGroup, markName, path, desc)
		{}

		public Mark(MarkSet markset, MarkGroup markGroup, string markName, string path, string desc, ArrayList values) 
			: this(-1, -1, -1, markset, markGroup, markName, path, desc)
		{
			this.presetValues = values;
		}

		public Mark(int markID,MarkSet markset, MarkGroup markGroup, string markName, string path, string desc, ArrayList values) 
			: this(-1, -1, markID, markset, markGroup, markName, path, desc)
		{
			if((values!=null)&&(values.Count>0))
				this.presetValues = values;
		}

		public Mark(int posX, int posY, int markID, MarkSet markset, MarkGroup markGroup,
			string markName, string path, string desc)
		{
			///
			/// Required for Windows.Forms Class Composition Designer support
			///
			InitializeComponent();
			this.markID = markID;
			this.markset = markset;
			this.markGroup = markGroup;
			this.markName = markName;
			this.description = desc;
			this.imagePath = path;
			this.SetMarkImage(imagePath);
			this.initMark(posX, posY);
		}

		public int MarkID
		{
			get 
			{
				return this.markID;
			}
		}

		public int CurrentValueIndex
		{
			get 
			{
				return this.currentValueIndex;
			}
			set
			{
				this.currentValueIndex = value;
			}
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			this.doResize();
			base.OnPaint(e);
		}
		public string CurrentValue
		{
			get
			{
				return (string) this.presetValues[this.currentValueIndex];
			}
		}



		public bool Removable
		{
			get 
			{
				return this.removable;
			}
		}

		public void SetMarkImage(string imagePath)
		{
			if (imagePath != null && !imagePath.Trim().Equals("")) 
			{
				this.hasImage = true;
				if(isRelativePath(imagePath))
				{
					imagePath = GODSMain.ExecDir + @"\" + imagePath;
					
				}
				this.initMarkImage(imagePath);
			}
		}

		public bool isRelativePath(string path)
		{
			if(path[0] == '.')
			{
				return true;
			}
			return false;
		}
		private void initMarkImage(string imagePath)
		{
			this.Image = Image.FromFile(imagePath);
			this.SizeMode = PictureBoxSizeMode.CenterImage;
			this.markWidth = 1.0;
			this.markHeight = 1.0;
			this.origSize = new Size(this.Image.Width, this.Image.Height);
		}

		private void initMark(int posX, int posY)
		{
			this.markComment = "";
			this.pointOnMark = new Point(posX,posY);
			this.Location = new Point(posX, posY);
		}

		/// <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 Component 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()
		{
			components = new System.ComponentModel.Container();
			// 
			// Mark
			// 
			this.BackColor = System.Drawing.Color.Transparent;
			this.Cursor = System.Windows.Forms.Cursors.Default;
			this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseUp);
			this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseMove);
			this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseDown);
		}
		#endregion

		public string Description
		{
			get
			{
				return this.description;
			}
			set
			{
				this.description = value;
			}
		}
		
		public MarkSet Markset 
		{
			get 
			{
				return this.markset;
			}
			set
			{
				this.markset = value;
			}
		}

		public MarkGroup Markgroup
		{
			get
			{
				return this.markGroup;
			}
			set
			{
				this.markGroup = value;
			}
		}

		public string MarkName
		{
			get 
			{
				return this.markName;
			}
			set
			{
				this.markName = value;
			}
		}

		public string ImagePath
		{
			get
			{
				return this.imagePath;
			}
			set
			{
				this.imagePath = value;
			}
		}

		public ArrayList PresetValues
		{
			get 
			{
				return this.presetValues;
			}
			set
			{
				this.presetValues = value;
			}
		}




		public bool HasImage
		{
			get 
			{
				return this.hasImage;
			}
		}

		public bool HasPresetValues
		{
			get 
			{
				return (this.presetValues != null);
			}
		}

		/// <summary>
		/// getter and setter for markWidth
		/// </summary>
		public double MarkWidth
		{
			get
			{
				return markWidth;	
			}
			set
			{
				markWidth = value;
			}
		}

		/// <summary>
		/// getter and setter for imaginary height
		/// </summary>
		public double MarkHeight
		{
			get
			{
				return markHeight;	
			}
			set
			{
				markHeight = value;
			}
		}

		/// <summary>
		/// getter and setter for markComment
		/// </summary>
		public string MarkComment
		{
			get
			{
				return markComment;	
			}
			set
			{
				markComment = value;
			}
		}

		public int Ratio
		{
			get
			{
				return resize;
			}
			set
			{
				resize = value;
			}
		}

		public bool IsOnImage
		{
			get 
			{
				return this.isOnImage;
			}
			set
			{
				this.isOnImage = value;
			}
		}

		public void addValue(string newVal)
		{
			if (this.presetValues == null) 
			{
				this.presetValues = new ArrayList();
				this.presetValues.Add(newVal);
			}
			else 
			{
				string val = this.getValue(newVal);
				if (val == null) 
				{
					this.presetValues.Add(newVal);
				}
				else
				{
					throw new System.Exception("Dupplicate value: annotation with the same value already exists.");
				}
			}
		}

		public void removeValue(string val)
		{
			this.presetValues.Remove(val);
		}

		private bool isCurrentMark()
		{
			return ((Parent.GetType() == Type.GetType("GODS.DiagnosticPanel")) && 
				(this == ((DiagnosticPanel)Parent).CurrentMark));
		}


		/// <summary>
		/// Mouse down event handler
		/// if mark is not on image, it is about to be dragged from template
		/// to the DiagnosticPanel. thus we need set it as dragdrop: dodragdrop
		/// with the DataObject "MarK" to be send
		/// 
		/// if mark is on image then, it is about to adjust the position on the 
		/// panel, the original position is recorded and the cursor is changed to
		/// hand shape.
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Mark_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			isMouseDown = true;
			if(isOnImage)
			{
				this.Cursor= Cursors.Hand;
				pointOnMark = this.PointToClient(Cursor.Position);
				
				//make it selected
				DiagnosticPanel diagPanel = (DiagnosticPanel) this.Parent;
				if (this != diagPanel.CurrentMark) 
				{
					diagPanel.updateCurrentMark(this);
				}
			}
			else
			{
				//the string format "Mark" must be specified
				//in order to have the other side get it efficiently
				Mark tmpMark = (Mark) this.Clone();
				DataObject dataobj = new DataObject("Mark",tmpMark);
				((Mark)sender).DoDragDrop(dataobj, DragDropEffects.Move);
			}
		}

		/// <summary>
		/// adjust mark position according to the DiagnosticPanel.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Mark_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if(isOnImage && isMouseDown)
			{
				if (this.Cursor.Equals(Cursors.Hand))
				{
					Point p = this.Parent.PointToClient(Cursor.Position);
					this.Location = new Point(p.X - pointOnMark.X, p.Y-pointOnMark.Y);
					DiagnosticPanel diagPanel = (DiagnosticPanel) this.Parent;
					diagPanel.notifyAnnotFormChanged();
				}
			}
		}

		
		/// <summary>
		/// mouse up event handler
		/// According to different mark status. on image or on template
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Mark_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			isMouseDown = false;
			if (isOnImage)
			{   
				DiagnosticPanel diagPanel = (DiagnosticPanel) this.Parent;
				Point center = new Point(this.Location.X+this.Width/2,this.Location.Y+this.Height/2);
				Point p = this.Location;
				this.Cursor = Cursors.Default;
				diagPanel.updateSummary();
				diagPanel.updateCurrentMark(this);
			}
		}

		/// <summary>
		/// clone mark so that with a marklist we can 
		/// generate all marks on the DiagnosticPanel
		/// </summary>
		/// <returns></returns>
		public object Clone()
		{
			return this.Clone(0);
		}

		/// <summary>
		/// clone mark so that with a marklist we can 
		/// generate all marks on the DiagnosticPanel
		/// </summary>
		/// <returns></returns>
		public object Clone(int ratio)
		{
			Mark clonedMark = new Mark(this.markID, this.markset, this.markGroup, this.markName, this.imagePath, this.description);
			clonedMark.presetValues = this.presetValues;
			clonedMark.currentValueIndex = this.currentValueIndex;
			clonedMark.origSize = new Size(origSize.Width, origSize.Height);
			clonedMark.resize = ratio;
			clonedMark.Size = new Size(this.Width, this.Height);
			return clonedMark;
		}
		
		//Method to zoom in the size of the mark
		public void ZoomIn()
		{   
			resize++;
			doResize();
		}

		//Method to zoom out the size of the mark
		public void ZoomOut()
		{   
			resize--;
			doResize();
		}

		public override string ToString()
		{
			return this.markName;
		}

		private string getValue(string val)
		{
			if (this.presetValues == null)
				return null;
			foreach (string str in this.presetValues)
			{
				if (val.Equals(str)) 
				{
					return str;
				}
			}
			return null;
		}

		public void doResize() 
		{
			this.Size = new Size((int)(this.origSize.Width*(1+resize*ratio)), 
				(int)(this.origSize.Height*(1+resize*ratio)));
		}

		public bool Equals(Mark mark)
		{
			return (this.markID == mark.markID);
		}

	}
}
