/******************************************************************************
 * DiagnosticPanel.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.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.IO;


namespace GODS
{
	using MarkData;

	/// <summary>
	/// DiagnosticPanel is the panel holding diagnostic image and related marks
	/// </summary>
	public class DiagnosticPanel : System.Windows.Forms.PictureBox
	{
		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		private GODS.DiagnosticImage diagnosticImage;
		private GODS.AnnoForm myAnnoForm;
		private Mark currentMark;
		private ArrayList marks;
		private MarkSet usedMarkSet;
		
		/// <summary>
		/// contor
		/// </summary>
		public DiagnosticPanel(AnnoForm annoForm, DiagnosticImage dImage)
		{
			InitializeComponent();
			this.myAnnoForm = annoForm;
			this.diagnosticImage = dImage;
			this.Image = dImage.RealImage;
			this.AllowDrop = true;
			this.BackColor = System.Drawing.Color.White;
			this.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
			this.SizeMode = PictureBoxSizeMode.AutoSize;
			this.marks = new ArrayList();
		}

		/// <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()
		{
			System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(DiagnosticPanel));
			// 
			// DiagnosticPanel
			// 
			this.Image = ((System.Drawing.Image)(resources.GetObject("$this.Image")));
			this.Click += new System.EventHandler(this.DiagnosticPanel_Click);
			this.DragEnter += new System.Windows.Forms.DragEventHandler(this.DiagnosticPanel_DragEnter);
			this.DragDrop += new System.Windows.Forms.DragEventHandler(this.DiagnosticPanel_DragDrop);

		}
		#endregion

		public Mark CurrentMark
		{
			get 
			{
				return this.currentMark;
			}
			set
			{
				this.currentMark = value;
			}
		}

		public string LatestSummary
		{
			get 
			{
				return this.diagnosticImage.LatestSummary;	
			}
		}

		protected internal ArrayList Marks
		{
			get
			{
				return this.marks;
			}
		}

		protected internal void updateSummary()
		{
			this.myAnnoForm.BuildSummary(this.diagnosticImage.buildSummary(this.marks));
		}

		/// <summary>
		/// Event handler for drag a mark from template into the DiagnosticPanel
		/// It will just change the cursor effect
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void DiagnosticPanel_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
		{
			e.Effect = DragDropEffects.Move;
		}

		/// <summary>
		/// Event handler for dropping the mark on the diagnostic panel
		/// from the mark panel.
		/// It get the Mark object passed in the event parameter
		/// with Dataformat "Mark" (Refer to Mark class _MouseDown method)
		/// It will call AddCurrentMark method which check if the drop position
		/// is valid and calculate the clock and position updating the mark
		/// and add the mark to DiagnosticPanel.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void DiagnosticPanel_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
		{
			//get data from event parameter
			//Tricky for getting an object. use DataObject and data format
			Mark curMark = (Mark)e.Data.GetData("Mark");
			
			Point curPoint = this.PointToClient(new Point(e.X,e.Y));

			// checking whether the mark is the first mark on panel
			// if so, set the markset ID for this panel, 
			// else refuse to add the mark
			if (curMark != null)
			{
				this.setUsedMarkSetByExisting();

				if (this.marks.Count == 0)
				{
					this.usedMarkSet = curMark.Markset;
					this.myAnnoForm.MarksetID = curMark.Markset.MarkSetID;
				}
				else
				{
					if (this.usedMarkSet.MarkSetID != curMark.Markset.MarkSetID)
					{

						MessageBox.Show(this,
							"This image has been annotated with markset "
							+ this.usedMarkSet.MarksetName + " in category "
							+ this.usedMarkSet.Category.CategoryName
							+ "." + System.Environment.NewLine
							+ "You cannot annotate the same image with different mark set.", 
							"Error",
							MessageBoxButtons.OK, 
							MessageBoxIcon.Error);
						return;
					}
				}

				AddCurrentMark(curPoint,curMark);
			}
		}
		
		/// <summary>
		/// add a new mark
		/// </summary>
		/// <param name="curPoint"></param>
		/// <param name="curMark"></param>
		protected internal void AddCurrentMark(Point curPoint, Mark curMark)
		{
			if (this.myAnnoForm.checkIsFinal())
				return;
			//we want use the cursor position be the position of the mark center
			curMark.Location = new Point(curPoint.X - (int)((float)curMark.Width/2),curPoint.Y-(int)((float)curMark.Height/2));

			//Once in DiagnosticPanel mark should be able to drag drop and fully activated
			//thus the isOnImage is true
			curMark.IsOnImage = true;

			//Borderstyle indicates it is selected automatically
			changeCurrentMark(curMark);

			notifyAnnotFormChanged();

			//add mark to DiagnosticPanel
			this.Controls.Add(curMark);
			this.marks.Add(curMark);
			this.Refresh();
			
			//show the summary.
			this.updateSummary();
			this.myAnnoForm.highlightSummary(this.GetCurrentMarkIndex());
		}

		public void addMarkFromDB(Mark curMark)
		{
			curMark.IsOnImage = true;
			//add mark to DiagnosticPanel
			this.Controls.Add(curMark);
			this.marks.Add(curMark);
			this.updateSummary();
		}

		/// <summary>
		/// This method is used to resize all marks
		/// For some reason the resize can't be triggered directly
		/// by calling doResize();
		/// 
		/// There maybe other ways, but this is only way I come up can
		/// actually solve this problem after long time debugging.
		/// </summary>
		public void adjustAllSize()
		{
			foreach(Mark mark in this.Marks)
			{
				this.changeCurrentMark(mark);
			}
			if (this.currentMark != null)
				this.currentMark.BorderStyle = BorderStyle.None;
		}

		public void clearAllMarks()
		{
			this.Controls.Clear();
			this.marks.Clear();
			this.updateSummary();
		}
		/// <summary>
		/// This will add the mark w/o image to whatever active anno form
		/// </summary>
		/// <param name="curMark"></param>
		protected internal void AddNoImageMark(Mark curMark)
		{
			if (!this.myAnnoForm.checkIsFinal() && curMark != null) 
			{
				this.myAnnoForm.MarksetID = curMark.Markset.MarkSetID;
				Mark mark = this.existInPanel(curMark);
				if (mark == null) // not in the panel
				{
					//Although this mark is not visible on the image, still add it
					//to the mark list and set the isOnImage to true
					curMark.IsOnImage = true;
					notifyAnnotFormChanged();

					//add mark to DiagnosticPanel mark list, but not in the control
					this.marks.Add(curMark);
				}
				else
				{
					mark.CurrentValueIndex = curMark.CurrentValueIndex;
					curMark = mark;
				}
				changeCurrentMark(curMark);

				//show the summary
				this.updateSummary();
				this.myAnnoForm.highlightSummary(this.GetCurrentMarkIndex());
			}
		}

		public void notifyAnnotFormChanged()
		{
			if (this.myAnnoForm != GODS.GODSMain.ActiveAnnoForm)
				this.myAnnoForm.setAsActiveForm();
			this.myAnnoForm.IsModified = true;
		}

		private Mark existInPanel(Mark curMark)
		{
			foreach (Mark mark in this.marks)
			{
				if (mark.Equals(curMark))
					return mark;
			}
			return null;
		}

		public void deleteCurrentMark()
		{
			if (!this.myAnnoForm.checkIsFinal()) 
			{
				this.marks.Remove(this.currentMark);
				this.Controls.Remove(this.currentMark);
				this.currentMark = null;
				this.updateSummary();
				notifyAnnotFormChanged();
			}
		}

		// Only called when clicking on the summary item
		public void updateCurrentMark(int index)
		{
			Mark mark = (Mark) marks[index];
			this.changeCurrentMark(mark);
		}

		// Only called when clicking on marks
		public void updateCurrentMark(Mark mark)
		{
			if (this.marks.Contains(mark))
				this.changeCurrentMark(mark);
		}

		private void changeCurrentMark(Mark mark)
		{
			if (this.currentMark != null)
				this.currentMark.BorderStyle = BorderStyle.None;
            this.currentMark = mark;

			if (this.currentMark.HasImage) 
			{
				this.currentMark.BorderStyle = BorderStyle.FixedSingle;
				this.currentMark.Size = new Size(mark.Image.Width, mark.Image.Height);
				this.currentMark.SizeMode = PictureBoxSizeMode.StretchImage;
			}
			this.myAnnoForm.highlightSummary(this.GetCurrentMarkIndex());
		}

		public void saveAnnoImage(string path)
		{
			this.diagnosticImage.saveAnnoImage(path, this.marks);
		}

		/// <summary>
		/// get the selected mark idx on the controls collection
		/// return -1 if none is selected.
		/// It is used mostly to highlight the summary list in mainform
		/// </summary>
		/// <returns></returns>
		public int GetCurrentMarkIndex()
		{
			return this.GetMarkIndex(this.currentMark);
		}

		public int GetMarkIndex(Mark theMark)
		{
			int i = 0;
			foreach (Mark mark in this.marks)
			{
				if (mark == theMark)
				{
					return i;
				}
				i++;
			}
			return -1;
		}

		/// <summary>
		/// click on panel event handler, used to deselect all marks
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void DiagnosticPanel_Click(object sender, System.EventArgs e)
		{
			if (this.currentMark != null)
				this.currentMark.BorderStyle = BorderStyle.None;
		}

		public void changeMarkWidth(double width)
		{
			if (this.currentMark!= null && this.currentMark.HasImage)
			{
				this.currentMark.MarkWidth = width;
				this.updateSummary();
				this.myAnnoForm.highlightSummary(this.GetCurrentMarkIndex());
				this.notifyAnnotFormChanged();
			}
		}

		public void changeMarkHeight(double height)
		{
			if (this.currentMark!= null && this.currentMark.HasImage)
			{
				this.currentMark.MarkHeight = height;
				this.updateSummary();
				this.myAnnoForm.highlightSummary(this.GetCurrentMarkIndex());
				this.notifyAnnotFormChanged();
			}
		}
		public void setUsedMarkSetByExisting()
		{

			if (this.marks.Count !=0)
			{
				this.usedMarkSet = ((Mark)marks[0]).Markset;
				this.myAnnoForm.MarksetID = usedMarkSet.MarkSetID;
			}
		}
	}
}
