context: #region License
  
  // Copyright (c) 2013, ClearCanvas Inc.
  // All rights reserved.
  // http://www.ClearCanvas.ca
  //
  // This file is part of the ClearCanvas RIS/PACS open source project.
  //
  // The ClearCanvas RIS/PACS open source project 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 3 of the
  // License, or (at your option) any later version.
  //
  // The ClearCanvas RIS/PACS open source project 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
  // the ClearCanvas RIS/PACS open source project.  If not, see
  // <http://www.gnu.org/licenses/>.
  
  #endregion
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Drawing;
  using Macro.Common;
  using Macro.Dicom.IO;
  
  namespace Macro.Dicom.Iod.Modules
  {
  	/// <summary>
  	/// OverlayPlane Module and MultiFrameOverlay Module
  	/// </summary>
  	/// <seealso cref="OverlayPlaneModuleIod.this"/>
  	/// <remarks>As defined in the DICOM Standard 2008, Part 3, Sections C.9.2 (Table C.9-2) and C.9.3 (Table C.9-3)</remarks>
  	public class OverlayPlaneModuleIod : IodBase, IEnumerable<OverlayPlane>
  	{
  		/// <summary>
  		/// Initializes a new instance of the <see cref="OverlayPlaneModuleIod"/> class.
  		/// </summary>	
  		public OverlayPlaneModuleIod() : base() {}
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="OverlayPlaneModuleIod"/> class.
  		/// </summary>
  		public OverlayPlaneModuleIod(IDicomAttributeProvider dicomAttributeProvider) : base(dicomAttributeProvider) {}
  
  		/// <summary>
  		/// Gets the Overlays in the underlying collection. The index must be between 0 and 15 inclusive.
  		/// </summary>
  		/// <remarks>
  		/// The implementation of the Overlay Plane module involving repeating groups is a holdover
  		/// from previous versions of the DICOM Standard. For each of the 16 allowed overlays, there
  		/// exists a separate set of tags bearing the same element numbers but with a group number
  		/// of the form 60xx, where xx is an even number from 00 to 1E inclusive. In order to make
  		/// these IOD classes easier to use, each of these 16 sets of tags are represented as
  		/// separate items of a collection, and may be addressed by an index between 0 and 15
  		/// inclusive (mapping to the even groups from 6000 to 601E).
  		/// </remarks>
  		public OverlayPlane this[int index]
  		{
  			get
  			{
  				Platform.CheckArgumentRange(index, 0, 15, "index");
  				return new OverlayPlane(index, this.DicomAttributeProvider);
  			}
  		}
  
  		/// <summary>
  		/// Removes all the tags associated with a particular group from the underlying data source.
  		/// </summary>
  		/// <param name="index">The index of the group to remove.</param>
  		public void Delete(int index)
  		{
  			Platform.CheckArgumentRange(index, 0, 15, "index");
  			foreach (uint tag in this[index].DefinedTags)
  				base.DicomAttributeProvider[tag] = null;
  		}
  
  		public bool HasOverlayPlane(int index)
  		{
  			if (index < 0 || index >= 16)
  				return false;
  			DicomAttribute attrib;
  			if (!base.DicomAttributeProvider.TryGetAttribute(ComputeTagOffset(index) + DicomTags.OverlayBitPosition, out attrib))
  				return false;
  			else if (attrib != null)
  				return !attrib.IsEmpty;
  			else
  				return false;
  		}
  
  		// TODO (CR May 2011): Doesn't feel 100% like this method belongs here,
  		// but it doesn't quite fit into DicomPixelData either, which is probably where it should go.
  
  		/// <summary>
  		/// Extracts all overlays embedded in pixel data and populates the <see cref="DicomTags.OverlayData"/> attribute.
  		/// </summary>
  		/// <returns>True if any overlays were actually extracted, otherwise false.</returns>
  		/// <remarks>Certain restrictions apply to the use of this method:
  		/// <list type="bullet">
  		/// <item><see cref="IodBase.DicomAttributeProvider"/> must be a <see cref="DicomAttributeCollection"/>.</item>
  		/// <item>The <see cref="DicomTags.PixelData"/> attribute must not contain encapsulated pixel data.
  		/// Check the <see cref="TransferSyntax"/> of the source message before calling this method.</item>
  		/// </list>
  		/// </remarks>
  		public bool ExtractEmbeddedOverlays()
  		{
  			var collection = base.DicomAttributeProvider as DicomAttributeCollection;
  			if (collection == null)
  				throw new ArgumentException("Module must wrap a DicomAttributeCollection in order to extract overlays.");
  
  			DicomAttribute attribute;
  			if (!DicomAttributeProvider.TryGetAttribute(DicomTags.PixelData, out attribute))
  			{
  				Platform.Log(LogLevel.Debug, "Sop does not appear to have any pixel data from which to extract embedded overlays.");
  				return false;
  			}
  
  			if (attribute is DicomFragmentSequence)
  			{
  				Platform.Log(LogLevel.Debug, "Sop pixel data must be uncompressed to extract overlays.");
  				return false;
  			}
  
  			if (attribute.IsNull)
  			{
  				Platform.Log(LogLevel.Debug, "Sop pixel data has no valid value and cannot have embedded overlays extracted.");
  				return false;
  			}
  
  			var pixelData = new DicomUncompressedPixelData(collection);
  			bool anyExtracted = false;
  			// Check if Overlay is embedded in pixels
  			foreach (var overlay in this)
  			{
  				if (overlay.HasOverlayData)
  					continue;
  
  				Platform.Log(LogLevel.Debug, "SOP Instance has embedded overlay in pixel data, extracting");
  				overlay.ExtractEmbeddedOverlay(pixelData);
  				anyExtracted = true;
  			}
  
  			return anyExtracted;
  		}
  
  		internal static uint ComputeTagOffset(int index)
  		{
  			return (uint) index*2*0x10000;
  		}
  
  		/// <summary>
  		/// Gets an enumeration of <see cref="DicomTag"/>s used by this module.
  		/// </summary>
  		public static IEnumerable<uint> DefinedTags
  		{
  			get
  			{
  				for (int n = 0; n < 16; n++)
  				{
  					uint tagOffset = ComputeTagOffset(n);
  					yield return tagOffset + DicomTags.OverlayBitPosition;
  					yield return tagOffset + DicomTags.OverlayBitsAllocated;
  					yield return tagOffset + DicomTags.OverlayColumns;
  					yield return tagOffset + DicomTags.OverlayData;
  					yield return tagOffset + DicomTags.OverlayDescription;
  					yield return tagOffset + DicomTags.OverlayLabel;
  					yield return tagOffset + DicomTags.OverlayOrigin;
  					yield return tagOffset + DicomTags.OverlayRows;
  					yield return tagOffset + DicomTags.OverlaySubtype;
  					yield return tagOffset + DicomTags.OverlayType;
  					yield return tagOffset + DicomTags.RoiArea;
  					yield return tagOffset + DicomTags.RoiMean;
  					yield return tagOffset + DicomTags.RoiStandardDeviation;
  					yield return tagOffset + DicomTags.NumberOfFramesInOverlay;
  					yield return tagOffset + DicomTags.ImageFrameOrigin;
  				}
  			}
  		}
  
  		#region IEnumerable<OverlayPlane> Members
  
  		public IEnumerator<OverlayPlane> GetEnumerator()
  		{
  			for (int n = 0; n < 16; n++)
  			{
  				if (this.HasOverlayPlane(n))
  					yield return this[n];
  			}
  		}
  
  		IEnumerator IEnumerable.GetEnumerator()
  		{
  			return this.GetEnumerator();
  		}
  
  		#endregion
  	}
  
  	/// <summary>
  	/// Enumerated values for the <see cref="DicomTags.OverlayType"/> attribute indicating whether this overlay represents a region of interest or other graphics.
  	/// </summary>
  	/// <remarks>As defined in the DICOM Standard 2008, Part 3, Section C.9.2 (Table C.9-2)</remarks>
  	public enum OverlayType
  	{
  		/// <summary>
  		/// Graphics
  		/// </summary>
  		G,
  
  		/// <summary>
  		/// ROI
  		/// </summary>
  		R,
  
  		/// <summary>
  		/// Represents the null value, which is equivalent to the unknown status.
  		/// </summary>
  		None
  	}
  
  	/// <summary>
  	/// Defined terms for the <see cref="DicomTags.OverlaySubtype"/> attribute identifying the intended purpose of the <see cref="OverlayType"/>.
  	/// </summary>
  	/// <remarks>
  	/// <para>As defined in the DICOM Standard 2008, Part 3, Section C.9.2.1.3</para>
  	/// <para>
  	/// Additional or alternative Defined Terms may be specified in modality specific Modules,
  	/// such as in the Ultrasound Image Module, C.8.5.6.1.11.
  	/// </para>
  	/// </remarks>
  	public class OverlaySubtype
  	{
  		/// <summary>
  		/// User created graphic annotation (e.g. operator).
  		/// </summary>
  		public static readonly OverlaySubtype User = new OverlaySubtype("USER");
  
  		/// <summary>
  		/// Machine or algorithm generated graphic annotation, such as output of a Computer Assisted Diagnosis algorithm.
  		/// </summary>
  		public static readonly OverlaySubtype Automated = new OverlaySubtype("AUTOMATED");
  
  		/// <summary>
  		/// Gets the <see cref="OverlaySubtype"/> matching the given defined term.
  		/// </summary>
  		/// <param name="definedTerm">The defined term.</param>
  		/// <returns>The defined term.</returns>
  		public static OverlaySubtype FromDefinedTerm(string definedTerm)
  		{
  			foreach (OverlaySubtype term in DefinedTerms)
  			{
  				if (term.DefinedTerm == definedTerm)
  					return term;
  			}
  			return null;
  		}
  
  		/// <summary>
  		/// Enumerates the defined terms.
  		/// </summary>
  		public static IEnumerable<OverlaySubtype> DefinedTerms
  		{
  			get
  			{
  				yield return User;
  				yield return Automated;
  			}
  		}
  
  		/// <summary>
  		/// Gets the defined term this object represents.
  		/// </summary>
  		public readonly string DefinedTerm;
  
  		/// <summary>
  		/// Constructs a new object with the given defined term.
  		/// </summary>
  		/// <param name="definedTerm">The defined term.</param>
  		protected OverlaySubtype(string definedTerm)
  		{
  			if (string.IsNullOrEmpty(definedTerm))
  				throw new ArgumentNullException("definedTerm");
  			this.DefinedTerm = definedTerm;
  		}
  
  		public override sealed int GetHashCode()
  		{
  			return this.DefinedTerm.GetHashCode() ^ -0x2D417CC3;
  		}
  
  		public override sealed bool Equals(object obj)
  		{
  			return (obj is OverlaySubtype && ((OverlaySubtype) obj).DefinedTerm.Equals(this.DefinedTerm));
  		}
  
  		public override sealed string ToString()
  		{
  			return this.DefinedTerm;
  		}
  	}
  
  	/// <summary>
  	/// Overlay Plane Group
  	/// </summary>
  	/// <seealso cref="OverlayPlaneModuleIod.this"/>
  	/// <remarks>As defined in the DICOM Standard 2008, Part 3, Sections C.9.2 (Table C.9-2) and C.9.3 (Table C.9-3)</remarks>
  	public class OverlayPlane : IodBase
  	{
  		private readonly int _index;
  		private readonly uint _tagOffset;
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="OverlayPlane"/> class.
  		/// </summary>
  		/// <param name="index">The zero-based index of this overlay.</param>
  		/// <param name="dicomAttributeProvider">The underlying collection.</param>
  		internal OverlayPlane(int index, IDicomAttributeProvider dicomAttributeProvider) : base(dicomAttributeProvider)
  		{
  			_index = index;
  			_tagOffset = OverlayPlaneModuleIod.ComputeTagOffset(_index);
  		}
  
  		/// <summary>
  		/// Gets the zero-based index of the overlay to which this group refers (0-15).
  		/// </summary>
  		public int Index
  		{
  			get { return _index; }
  		}
  
  		/// <summary>
  		/// Gets the DICOM tag group number.
  		/// </summary>
  		public ushort Group
  		{
  			get { return (ushort) (_index*2 + 0x6000); }
  		}
  
  		/// <summary>
  		/// Gets the DICOM tag value offset from the defined base tags (such as <see cref="DicomTags.OverlayActivationLayer"/>).
  		/// </summary>
  		public uint TagOffset
  		{
  			get { return _tagOffset; }
  		}
  
  		/// <summary>
  		/// Gets a value indicating whether or not the <see cref="OverlayData"/> is stored in 16-bit big-endian words.
  		/// </summary>
  		public bool IsBigEndianOW
  		{
  			get { return ByteBuffer.LocalMachineEndian == Endian.Big && base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayData] is DicomAttributeOW; }
  		}
  
  		/// <summary>
  		/// Gets a value indicating if the overlay data for this plane is embedded in the unused bits of the <see cref="DicomTags.PixelData"/>.
  		/// </summary>
  		/// <remarks>
  		/// A previous implementation of this property checked for consistency of the attributes in the Overlay Plane Module
  		/// with the attributes in the Image Pixel Module. This has since been deprecated as there are legitimate
  		/// use cases where the Image Pixel Module is not necessarily in the same dataset as the Overlay Plane Module.
  		/// This property now simply returns the inverse of <see cref="HasOverlayData"/>.
  		/// </remarks>
  		[Obsolete("This property has been deprecated in favour of checking the inverse of HasOverlayData.")]
  		public bool IsEmbedded
  		{
  			get { return !HasOverlayData; }
  		}
  
  		/// <summary>
  		/// Gets a value indicating if the <see cref="DicomTags.OverlayData"/> attribute exists for this plane in the underlying collection.
  		/// </summary>
  		/// <remarks>
  		/// In previous versions of the DICOM Standard, overlay data bits could be stored in unused bits of an image SOP's
  		/// <see cref="DicomTags.PixelData"/>, provided the image had 1 sample per pixel and the overlay pixels had a
  		/// 1-to-1 relationship with the image pixels. If the overlay plane was stored in such a manner, the <see cref="DicomTags.OverlayData"/>
  		/// attribute would not be included at all. This property may be used to determine whether or not the <see cref="DicomTags.OverlayData"/>
  		/// exists in the collection in order to support legacy SOP instances.
  		/// </remarks>
  		public bool HasOverlayData
  		{
  			get
  			{
  				DicomAttribute attribute = base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayData];
  				return !attribute.IsEmpty && !attribute.IsNull;
  			}
  		}
  
  		/// <summary>
  		/// Gets or sets the value of OverlayRows in the underlying collection. Type 1.
  		/// </summary>
  		public int OverlayRows
  		{
  			get { return base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayRows].GetInt32(0, 0); }
  			set { base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayRows].SetInt32(0, value); }
  		}
  
  		/// <summary>
  		/// Gets or sets the value of OverlayColumns in the underlying collection. Type 1.
  		/// </summary>
  		public int OverlayColumns
  		{
  			get { return base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayColumns].GetInt32(0, 0); }
  			set { base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayColumns].SetInt32(0, value); }
  		}
  
  		/// <summary>
  		/// Gets or sets the value of OverlayType in the underlying collection. Type 1.
  		/// </summary>
  		public OverlayType OverlayType
  		{
  			get { return ParseEnum(base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayType].GetString(0, string.Empty), OverlayType.None); }
  			set
  			{
  				if (value == OverlayType.None)
  					throw new ArgumentOutOfRangeException("value", "OverlayType is Type 1 Required.");
  				SetAttributeFromEnum(base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayType], value, true);
  			}
  		}
  
  		/// <summary>
  		/// Gets or sets the value of OverlayOrigin in the underlying collection. Type 1.
  		/// </summary>
  		public Point? OverlayOrigin
  		{
  			get
  			{
  				DicomAttribute attribute = base.DicomAttributeProvider[_tagOffset + DicomTags.OverlayOrigin];
  				int[] result = new int[2];
  				if (attribute.TryGetInt32(0, out result[0]))
  					if (attribute.TryGetInt32(1, out result[1]))
  
gt: 						return new Point(result[0], result[1]);