--[[
	This script identifies the image properties of Mammograpy (MG) images
	Specific properties extracted are:
	view projection, based on View Position or MG View coded sequence
	image description, based on series description tag,
	or laterality and image type
	Cristian Bonciu	July-September 2004
	-	Created and implemented the functionality from pseudo-code prototypes provided by Allan
	Allan Noordvyk      		27 OCT 2004
     -	Added new common-for-DP flag to Register call for MG Projection.
	Allan Noordvyk      		05 MAY 2005
     -	Added exception handling for GE units that provide 'DX-MAM" as series
          description rather than something useful.
     -	Added raw indication when FOR PROCESSING is the Intent
	Allan Noordvyk      		22 JUL 2005
     -	Added check of Burned-in-Annotation tag for use in detecting R2 montage images
	Cristian Bonciu     		30 NOV 2005
     -	Modified ExtractProjectionMG to be able to use both coded (SP3) and decoded (SP4)
          sequence attributes (CRDB00037844)
	Mahesh Nagarajan		26 JAN 2006
     -	Added new projection codes :-
          viewMap["R-1024A"] = "XCCL" -- cranio-caudal exaggerated laterally
          viewMap["R-1024B"] = "XCCM"-- cranio-caudal exaggerated medially	
	Mahesh Nagarajan		23 MAY 2006
     -	Added new function ExtractThumbnailGroup() to ensure that CAD images go to the end
		of the thumbnail bar
	Mahesh Nagarajan		14 JUN  2006
     -	Changed the description for MG sequences; it is more useful to have the laterality+projection
		than blindly returning the series description from the DICOM header
	Mahesh Nagarajan		04 OCT 2006
     -	Added new property to determine if image was implant displaced using DICOM tag
		(0054, 0222) NMI View Angulation Modifier Code Sequence
	Mahesh Nagarajan		02 NOV 2006
     -	Added new property "magnification factor" to determine if MG image is magnified or not using
		DICOM tag (0018, 1114) ACQ Estimated Radiographic Magnification Factor
	Mahesh Nagarajan		09 NOV 2006
	-	Implant Displaced property should be set to yes if the projection type is CCID or MLOID
	Cartik Sharma			14 FEB 2007
	-	Added new property to distinguish images based on spot compression using information from
		(0054,0222) NMI Angular Modifier Code Sequence
	Mahesh Nagarajan		25 APR 2007
	-	Implant displacement is also indicated with by projection types CCID and MLOID; this needs
		to be taken into account as well.
	Mahesh Nagarajan		14 MAY 2007
	-	Implant displayed images from the Fuji modality at Caritas do not properly embed projection
		information in (0054, 0220) NMI View Code Sequence; we need to extract infromation from the
		series description for these images.
	-	Spot Compressed images from FUJI are indicated by the word "Spot" in the series description, this
		must also be indicated inthe text generated for the thumbnails
	-	A slight change with our approach to implant displacement; CCID and MLOID are no longer
		recognised as valid projection types; this information is split into projection type as CC or MLO
		and MG implant displacement as "Yes"
	-	All MG properties are now considered for "starring" in display protocols
	-	Decoded projection value mapping fails when the values are in upper case in the DICOM header,
		this  is now fixed by converting the extracted value to lower case before matching.
	-	Changed the property variable name for Spot Compression from NMI_SPOT_FACTOR to
		MG_SPOT_COMP_PROP for more consistency in naming
	Mahesh Nagarajan		23 MAY 2007
	-	DICOM/IHE standards state that an image is considered magnified if its radiographic magnification
		factor is significantly greater than 1; this means that values of 1.02 cannot be considered as magnified.
		While these somewhat loose definitions are being clarified, the magnification threshold is increased to 1.15.
	Mahesh Nagarajan		05 JUL 2007
	-	Implant Displacement is also indicated by an "ID" in the series description; since this can be
		confused as some part of a bigger string, we only look for "<space>ID<space>". 	
	-	Added support for checking if Cleavage Views were being mis-identified as "CC". This is not really a mis-
		identification but radiologists want to be able to differentiate between regular CC images and cleavage views.
	Mahesh Nagarajan		14 AUG 2007	
	-	Added support for extended projection modifiers that are usually provided in the DICOM tag
		(0054, 0222) NMI View Angulation Modifier Code Sequence; note that since "cleavage" is one such
		view modifier, the previous check for cleavage view is replaced with this new function.
	-	Added support for new "magnified" indicator i.e. the string "magnification" in (0054, 0222) NMI View
		Angulation Modifier Code Sequence
	-	Updated the default list of MG projection types - added the new view modifier types while removing defunct
		implant displacement types.
	Mahesh Nagarajan		31 OCT 2007
	-	Added support for VIDAR MG digitizers at University of Wisconsin; both projection and laterality information
	     must be extracted directly from the series description
 	Mahesh Nagarajan		31 DEC 2007
	-	Increasing the thumbnail group number of CAD images to ensure that they fall behind derived images on the
		thumbnail bar.
	Mahesh Nagarajan		07 FEB 2008
	-	Adding back site-specific code snippets; after a long discussion with Cristian B., it is understood that this code has to stay
		in the code until proper site files have been installed. Such code is added back but within "dollar sign" demarcations to
		ensure easy removal at a later date.
	Mahesh Nagarajan		07 MAR 2008
	-	A comparison of the new and older version of the ExtractMG scipts indicates differences that cannot be
		accounted for; these changes are added back into the newer branch to prevent any loss of functionality
	Shudong Liu			07 JUL 2009
	- 	Implemented MG Projection View Overlay Support feature. (CRDB74451, 76611)
	Shudong Liu			11 SEP 2009
	- 	Implemented MG "NOT FOR DIAGNOSIS" warning message  (CRDB39341)
--]]

-- ////////////////////////////////////////////////////////////////////////////
-- definitions of the DICOM elements IDs used in this file
-- ////////////////////////////////////////////////////////////////////////////
function DeclareTags()
	CODE_VALUE_TAG = maketag("0008","0100")
    ACQ_MAG_FACTOR = maketag ("0018", "1114")
	VIEW_POSITION_TAG = maketag("0018","5101")
	MG_CODED_VIEW_TAG = maketag("0054","0220")
    NMI_VIEW_ANGL_MODFR_TAG = maketag("0054","0222")
end
	
-- ////////////////////////////////////////////////////////////////////////////
-- property IDs (3711,1501)- (3711,15FF) range
-- ////////////////////////////////////////////////////////////////////////////
function RegisterProperties()
   -- property IDs for MG should be in the range (3711,1501)- (3711,15FF) range
   Register("MG_PROJECTION_PROP","1501","string", "AT,ML,MLO,MLORI,MLORS,LM,LMO,CC,CCRL,CCRM,CV,FB,TAN,SIO,XCC,XCCL,XCCM","MG Projection",{1})

   -- This property determines if the anatomical region in question (I wonder what
   -- it could be in an MG script...Gee) is implant displaced or not
   -- Relevant tag: NMI View Angulation Modifier Code Sequence
   Register("MG_IMP_DISP_PROP","1502","string", "Yes,No","MG Implant Displacement",{1})

   -- This property determines if the MG image is magnified or not
   -- Relevant tag: ACQ Estimated Radiographic Magnification Factor
   Register("MG_MAG_FACTOR_PROP","1503","string", "Yes,No","MG Magnification Factor",{1})

    -- This property determines if the MG image is spot compressed or not
	-- Relevant tag: Spot Compression Tag
	Register("MG_SPOT_COMP_PROP", "1504", "string", "Yes,No", "MG Spot Compression Factor",{1})

	--These following 3  LABEL PROP are used for display ONLY and should not be added into DP editor. They will be configured in TextToOverlay file.
	Register("MG_PROJ_MODI_LABEL_PROP", "1505", "string", "MCCIDRL, SLMORLID", "MG Projection View Label",{0})
	Register("MG_LATERAL_LABEL_PROP", "1506", "string", "L, R, B", "MG Laterality Label",{0})
	Register("MG_LAT_PROJ_MODI_LABEL_PROP", "1507", "string", "RMCCIDRL, LSLMORLID", "MG Laterality Projection View Label",{0})

end

-- ////////////////////////////////////////////////////////////////////////////
-- string definitions
-- ////////////////////////////////////////////////////////////////////////////

-- ////////////////////////////////////////////////////////////////////////////
-- MG property extraction functions
-- ////////////////////////////////////////////////////////////////////////////

-- ////////////////////////////////////////////////////////////////////////////
function ExtractProjectionMG()
-- ////////////////////////////////////////////////////////////////////////////
-- Get the view projection information for the image by looking in the
-- View Position attribute (this is known to work for Lorad digital mammography units)
-- Other vendors should store this information in the MG View coded sequence
-- which is mandatory
-- ////////////////////////////////////////////////////////////////////////////
-- come back, see if we need to check first the MG View instead of View position
-- ////////////////////////////////////////////////////////////////////////////

	-- First let's try using the ACQ View Position attribute that GE and some other use
	local anAttr = NewPropFromTag(MG_PROJECTION_PROP, VIEW_POSITION_TAG)

	-- If the View Position had a useful value that allowed the property to be created...
	if (anAttr) then
		-- Remap non-DICOM conformant implant displaced values like CCID to proper
		-- DICOM recognised projections like CC
		if (ValueForTag(VIEW_POSITION_TAG) == "CCID") then
				
			return NewPropWithValue(MG_PROJECTION_PROP, "CC")
		elseif (ValueForTag(VIEW_POSITION_TAG) == "MLOID") then
		
			return NewPropWithValue(MG_PROJECTION_PROP, "MLO")
		end
		
		return anAttr   -- All done
		  --==============================
	end

	-- If we are here then we need to look at coded view lookup (like Fischer uses).
	local bIsViewCoded = true
	anAttr = GetAttr(CODE_VALUE_TAG)

	-- If this image doesn't have that either...
	if (not anAttr) then
		anAttr = GetAttr(MG_CODED_VIEW_TAG) -- CB: see if the decoded version is there
		if( not anAttr ) then
			TraceC( "MG Image has neither View Position or Coded Values - Look into Series Description.\n")
			local seriesDescTag = GetAttr(SERIES_DESC_TAG)
			if ( seriesDescTag ) then
				local matchStr = nil
				matchStr = BestWordMatchInAttr(seriesDescTag, "CC", "MLO", "ML", "LM", "CV", "FB", "SIO",
																	 "XCC", "XCCL", "XCCM", "TAN", "MLORI", "MLORS", "CCRL",
																	 "CCRL", "AT")
				if (matchStr) then
					return NewPropWithValue(MG_PROJECTION_PROP, matchStr)
				end
			end
			
			TraceC( "Cannot find sufficient information to determine projection of the image.\n")
			return nil -- none available
		end
		bIsViewCoded = false -- we found the decoded value
		--==============================
	end

	--TraceC("found " .. nValues .. " code value values \n")
	if( bIsViewCoded == true ) then -- CB: old way
		-- ---------------- begin initialize the MG View codes sequence map
		local viewMap = {}				-- create as a table
		viewMap["R-10224"]	=	"ML"	-- medio-lateral
		viewMap["R-10226"] 	=  	"MLO"	-- medio-lateral oblique
		viewMap["R-10228"] 	=  	"LM"	-- latero-medial
		viewMap["R-10230"] 	=  	"LMO"	-- latero-medial oblique
		viewMap["R-10242"] 	= 	"CC"	-- cranio-caudal
		viewMap["R-10244"] 	=  	"FB"	-- caudo-cranial (from below)
		viewMap["R-102D0"] 	=  	"SIO"	-- superolateral to inferomedial oblique
		viewMap["R-102CF"] 	=	"XCC"	-- exaggerated cranio-caudal
		viewMap["Y-X1770"]	=	"XCCL"	-- cranio-caudal exaggerated laterally
		viewMap["Y-X1771"]	=	"XCCM"	-- cranio-caudal exaggerated medially
		viewMap["R-1024A"]	=	"XCCL"       -- cranio-caudal exaggerated laterally
		viewMap["R-1024B"]	=	"XCCM"      -- cranio-caudal exaggerated medially
		-- ---------------- end initialize the MG View codes sequence map
		-- for each of the coded values...
		local nValues = anAttr:GetSize()
		for i=1,nValues,1 do
		
			local sCodeValue = anAttr:Get(i)
			--TraceC("code value " .. sCodeValue .. "\n")
		
			-- Look up code in standard mammography view table
			local sCodeACR = viewMap[sCodeValue]
		
			-- If we found a match
			if ( sCodeACR ) then
				-- We can use that as our projection.
				TraceC( "MG Image has View Position as code value - MG Projection will be assigned.\n")
				return NewPropWithValue( MG_PROJECTION_PROP, sCodeACR );
			end
		end
	else
		local sCodeACR = GetProjectionByViewCode()
		-- If we found a match
		if ( sCodeACR ) then
			-- We can use that as our projection.
			TraceC( "MG Image has View Position as decoded code value - MG Projection will be assigned.\n")
			return NewPropWithValue( MG_PROJECTION_PROP, sCodeACR );
		end
	end
	
	-- Caritas has an old FUJI system that indicates implant displacement in the (0054, 0220) View Code Sequence; this means that
	-- we have no clue what the actual projection is for such implant displaced images. For this we rely on the series description.
	if ( ValueForTag(DEVICE_MANUFACTURER_TAG) == "FUJI PHOTO FILM Co., ltd." ) then
		
		local seriesDescTag = GetAttr(SERIES_DESC_TAG)
		if (seriesDescTag~=nil and BestWordMatchInAttr(seriesDescTag, "RCC", "LCC", "CC")) then
			-- Indicative of a CC View
			return NewPropWithValue( MG_PROJECTION_PROP, "CC" );
		elseif (seriesDescTag~=nil and BestWordMatchInAttr(seriesDescTag, "RMLO", "LMLO", "MLO")) then
			-- Indicative of a MLO View
			return NewPropWithValue( MG_PROJECTION_PROP, "MLO" );
		end
	end

	-- If we got here there were no matching codes.
	TraceC( "None of the codes in the MG image match a standard view - no MG Pojection assigned.\n")
	return nil
end

function ExtractProjectionModifierMG()
-- Often, more information about the projection type is indicated in the DICOM
-- tag (0054, 0222) NMI View Angulation Modifier Code Sequence; this information
-- is extracted here
-- ////////////////////////////////////////////////////////////////////////////
	
	local mgProjTypeAttr = GetAttr(MG_PROJECTION_PROP)
	local nmiViewModifierAttr = GetAttr(NMI_VIEW_ANGL_MODFR_TAG)
	if ( mgProjTypeAttr == nil or nmiViewModifierAttr== nil ) then
		-- nothing more to do
		return
	end

	local mgProjType = ValueForTag(MG_PROJECTION_PROP)
	
	--[[  sliu May 14, 2009: commented out the old code for updating projection view value according projection modifiers
	                                        The same rules are used in the updated code to handle multiple modifiers.
	local viewProjModifiers = {}				-- create as a table
	viewProjModifiers["cleavage"]	= 			"CV"
	viewProjModifiers["axillary tail"] = 		"AT"
	viewProjModifiers["rolled lateral"] = 		mgProjType.."RL"
	viewProjModifiers["rolled medial"] = 		mgProjType.."RM"
	viewProjModifiers["rolled inferior"] = 	mgProjType.."RI" 	
	viewProjModifiers["rolled superior"] = 	mgProjType.."RS" 	                 	
	viewProjModifiers["tangential"] = 		"TAN"
	
	local sModifierValue = ValueForTag(NMI_VIEW_ANGL_MODFR_TAG)
	-- Convert code value to lower case to eliminate case-related false mis-matches
	sModifierValue = string.lower(sModifierValue)
	-- Look up code in view modifier table
	local sModifiedProjType = viewProjModifiers[sModifierValue]
	
	--ErrorC( "Found Projection string: " .. sModifiedProjType .. "\n" )
	
	--]]
	local bHasRI = false
	local bHasRL = false
	local bHasRM = false
	local bHasRS = false	
	
	local sModifiedProjType = mgProjType
	local nValues = nmiViewModifierAttr:GetSize()
	for i=1,nValues,1 do
		local sModifierValue = nmiViewModifierAttr:Get(i)
		
		if (string.lower(sModifierValue) == "rolled inferior") then
			bHasRI = true
		elseif (string.lower(sModifierValue) == "rolled lateral") then
			bHasRL = true
		elseif (string.lower(sModifierValue) == "rolled medial") then
			bHasRM = true
		elseif (string.lower(sModifierValue) == "rolled superior") then
			bHasRS = true
		elseif (string.lower(sModifierValue) == "cleavage") then
			sModifiedProjType = "CV"
		elseif (string.lower(sModifierValue) == "axillary tail") then					
			sModifiedProjType = "AT"				
		elseif (string.lower(sModifierValue) == "tangential") then
			sModifiedProjType = "TAN"
		elseif (string.lower(sModifierValue) == "magnification") then
			NewPropWithValue(MG_MAG_FACTOR_PROP, "Yes")
		elseif ( string.lower(sModifierValue) == "spot compression") then
			NewPropWithValue(MG_SPOT_COMP_PROP, "Yes")
		elseif ( string.lower(sModifierValue) == "implant displaced") then
			NewPropWithValue(MG_IMP_DISP_PROP, "Yes")
		end
	end
	
	if (bHasRI) then
		sModifiedProjType = sModifiedProjType .. "RI"
	end
	if (bHasRL) then
		sModifiedProjType = sModifiedProjType .. "RL"
	end
	if (bHasRM) then
		sModifiedProjType = sModifiedProjType .. "RM"
	end
	if (bHasRS) then
		sModifiedProjType = sModifiedProjType .. "RS"
	end
		
	if ( sModifiedProjType ~= mgProjType ) then
			TraceC( "MG Projection has been modified - New Projection will be assigned.\n")
			return NewPropWithValue( MG_PROJECTION_PROP, sModifiedProjType );
	end
end

function ExtractThumbnailGroupMG()
-- ////////////////////////////////////////////////////////////////////////////
-- This function assigns a thumbnail group value to al MG images. All images are assigned
-- "01" while CAD images are assigned "06"
-- Slight modification to the above rule - premium view images from GE workstations are
-- assigned "00" so that they get precedence
-- ///////////////////////////////////////////////////////////////////////////
    local grpValue = nil
    local isCAD = nil
	
    -- Currently, the only indicator of CAD images is that the manufacturer tag
    -- would have the value "R2 Technology".
    RTWO_TECH_VAL = "R2 Technology"
    local manufacturerTag = GetAttr(DEVICE_MANUFACTURER_TAG)
    if (manufacturerTag) then
	    isCAD = BestMatchInAttr(manufacturerTag, RTWO_TECH_VAL)
	    if (isCAD) then
		    grpValue = "30"
	    end
    end
	
	-- Check for premium view images from GE devices
	if (ValueForTag(DEVICE_MANUFACTURER_TAG) == "GE MEDICAL SYSTEMS") then
		local acqDeviceProcTag = GetAttr(ACQ_DEV_PROCESS_TAG)
		if (acqDeviceProcTag~=nil and ValueForTag(ACQ_DEV_PROCESS_TAG) == "PREMIUM_VIEW") then
			grpValue = "00"
		end
	end
	
    return NewPropWithValue(THUMBNAIL_GRP_PROP, grpValue)
end

function ExtractImplantDisplacedMG()
-- ////////////////////////////////////////////////////////////////////////////
	-- This function determines if the image is question is implant
        -- displaced or not
-- ///////////////////////////////////////////////////////////////////////////
	local isImplantDisp = "No"
	IMP_DISP_VAL = "Implant Displaced"

	--local viewAngularModfr = GetAttr(NMI_VIEW_ANGL_MODFR_TAG)
	local viewCodeSeq = GetAttr(MG_CODED_VIEW_TAG)
	
	-- 1. The primary source of this information is the (0054, 0222) NMI View Angulation Modifier Code Sequence
	--if (viewAngularModfr ~= nil and BestWordMatchInAttr(viewAngularModfr, IMP_DISP_VAL)) then
	--	isImplantDisp = "Yes"
	--end
	local sIDValue = ValueForTag( MG_IMP_DISP_PROP )
	if ( sIDValue ~= nil and sIDValue == "Yes" ) then
		-- The property already get set when NMI_VIEW_ANGL_MODFR_TAG was handled in ExtractProjectionModifierMG
		return
	end
	
	-- 2. Older Fuji devices indicate this info in (0054, 0220) NMI View Code Sequence; not ideal as this is where we
	-- expect projection information...but we can still handle it.
	if (viewCodeSeq ~= nil and BestWordMatchInAttr(viewCodeSeq, IMP_DISP_VAL)) then
		isImplantDisp = "Yes"
	end
	
	-- 3. Some sites use proper implant displaced projection types in (0018, 5101)ACQ View Position
	if (ValueForTag(VIEW_POSITION_TAG) == "CCID" or
			ValueForTag(VIEW_POSITION_TAG) == "MLOID") then
		isImplantDisp = "Yes"
	end
	
	-- 4. Implant displacement is also indicated by "ID" in the series description; to prevent false
	-- matches, we search for "ID" quite specifically
	local seriesDescTag = ValueForTag( SERIES_DESC_TAG )
    if (seriesDescTag ~= nil and
                 (string.find(seriesDescTag,"%sID$",1) or
				  string.find(seriesDescTag,"%sID%s",1) or
				  string.find(seriesDescTag,"^ID%s",1))) then
		isImplantDisp = "Yes"
	end

	return NewPropWithValue(MG_IMP_DISP_PROP, isImplantDisp)
end

function ExtractMagnificationFactorMG()
-- ////////////////////////////////////////////////////////////////////////////
	-- This function determines if the image is question is implant
        -- displaced or not
-- ///////////////////////////////////////////////////////////////////////////
	local isMagnified = "No"

	-- ideal way of identifying magnified views; magnification is indicated as a
	-- view modifier in (0054, 0222)
	--local viewAngModifer = ValueForTag(NMI_VIEW_ANGL_MODFR_TAG)
	--if (viewAngModifer~= nil and string.lower(viewAngModifer) == "magnification") then
	--	isMagnified = "Yes"
	--end
	local sMAGValue = ValueForTag( MG_MAG_FACTOR_PROP )
	if (sMAGValue ~= nil and sMAGValue == "Yes") then
		-- The property already get set when NMI_VIEW_ANGL_MODFR_TAG was handled in ExtractProjectionModifierMG
		return
	end	
	
	-- noticed at Caritas, magnified views are indicated by an "M" in the series
	-- description; note -> must be careful about using single letter strings
    local acqDescTag = ValueForTag( ACQ_DEV_PROCESS_TAG )
    if (acqDescTag ~= nil and
                 (string.find(acqDescTag,"%sM$",1) or
				  string.find(acqDescTag,"%sM%s",1) or
				  string.find(acqDescTag,"^M%s",1))) then
		isMagnified = "Yes"
	end
	
	-- least ideal way of distinguishing a mag view is to examine the magnification
	-- factor; greater than 1.15 indicates a magnified view
	-- Note: This is incorrect use of this tag; the information here isnt actually
	-- indicating image magnification but how to factor the pixel sizing
	local magFactor = ValueForTag(ACQ_MAG_FACTOR)
	
	if (magFactor ~= nil and (magFactor > 1.15)) then
		isMagnified = "Yes"
	end
	
	return NewPropWithValue(MG_MAG_FACTOR_PROP, isMagnified)
end

function ExtractDescriptionMG()
-- ////////////////////////////////////////////////////////////////////////////
	-- This function sets the image description property of the image based
	-- on an examination of the header values and previously derived image
	-- properties.  The set property is returned.
	--
	-- This method will always return some kind of property.  If it can't
	-- come up with anything else, it will use a question mark for the
	-- image description just so that we have something to show in the UI.
	--
	-- IMPORTANT: As this function makes use of derived properties, it should
	-- be called LAST, after all other extraction functions have been run.
-- ///////////////////////////////////////////////////////////////////////////

    -- If the common function found a description value...
    local seriesDescProp = GetAttr( SERIES_DESC_PROP )
	
	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
	-- Site Specific Code for Hackettstown Regional Hospital
	local manuStr = ValueForTag(DEVICE_MANUFACTURER_TAG)
	local siteName = GetAttr(INSTITUTION_NAME_TAG)
	
	if ( siteName~=nil and BestMatchInAttr(siteName, "Hackettstown") ) then
		if ( manuStr == "FUJI PHOTO FILM Co., ltd." ) then
			-- Custom change to provide 3711, 1103 based on series description property.
			return seriesDescProp;
		end
	end
	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
	
--[[    if (seriesDescProp) then

        local seriesStr = ValueForTag(SERIES_DESC_PROP)

        -- If it isn't just the special no-info placeholder and isn't a GE
        -- GE Senograph's repetitive echo of the study description...
        if (seriesStr ~= "?" and seriesStr ~= "DX-MAM" and
            not(seriesStr == ValueForTag(STUDY_DESC_TAG))) then
            TraceC( "Common script found useful description... skipping special MG logic.\n" )
            return seriesDescProp;
            --==============================================
        end
    end

    TraceC( "No good description found by common script... using special MG logic.\n" )]]--
    local wordAttr
	-- Words from derived attributes

    local sDescription = ""
	-- The description string we are building
	
    -- Try to build a descriptive sentence by using derived properties
    wordAttr = GetAttr(LATERALITY_PROP)
    if ( wordAttr ) then
          TraceC( "Adding laterality to series description.\n" )
          sDescription = MergeStrings(" ",sDescription,wordAttr:Get(1))
    end

    wordAttr = GetAttr(MG_MAG_FACTOR_PROP) -- magnification information
    if ( wordAttr and (ValueForTag(MG_MAG_FACTOR_PROP) == "Yes")) then
          TraceC( "Adding magnification to series description.\n" )
          sDescription = MergeStrings(" ",sDescription,"M")
    end

    wordAttr = GetAttr(MG_PROJECTION_PROP) -- projection information
    if ( wordAttr ) then
          TraceC( "Adding MG projection to series description.\n" )
          sDescription = MergeStrings(" ",sDescription, wordAttr:Get(1))
    end
	
	wordAttr = GetAttr(MG_IMP_DISP_PROP) -- projection information
	if ( wordAttr and (ValueForTag(MG_IMP_DISP_PROP) == "Yes")) then
          TraceC( "Adding implant displacement to series description.\n" )
          sDescription = MergeStrings(" ",sDescription,"ID")
	end
	
	wordAttr = GetAttr(MG_SPOT_COMP_PROP) -- spot compression information
	if ( wordAttr and (ValueForTag(MG_SPOT_COMP_PROP) == "Yes")) then
          TraceC( "Adding spot compression to series description.\n" )
          sDescription = MergeStrings(" ",sDescription,"SPOT")
	end

    -- If this is raw data for processing...
    if ( ValueForTag( INTENT_PROP ) == "FOR PROCESSING" ) then
          TraceC( "Adding indication of raw data to series description.\n" )
          sDescription = MergeStrings( " - ", sDescription, "raw" )
    end

    -- If we were not able to build a description from the attributes above..
    if ( not sDescription or string.len(sDescription) == 0) then

           TraceC( "Neither laterality nor MG projection available for building series description.\n" )

           if ( ValueForTag( BURNED_ANNOTATION_TAG ) == "YES" ) then

               TraceC( "Assuming an R2 CAD montage image.\n" )
               sDescription = MergeStrings( " ", sDescription, "CAD" )

           else

              -- Leave earlier property unchanged.
              return seriesDescProp;
              -- ======================================
           end
    end

    -- We will be creating a new property.
    seriesDescProp = AttrValue:new()

    -- Use the constructed string as the series description property
    seriesDescProp:AppendVal( sDescription )
    SetAttr(SERIES_DESC_PROP, seriesDescProp) -- add it as a property
    return seriesDescProp
end

function ExtractSpotFactorMG()
-- ////////////////////////////////////////////////////////////////////////////
	-- This function determines if the image is spot compressed or not
-- ///////////////////////////////////////////////////////////////////////////	
	local isSpotCompressed = "No"
	SPOT_VAL = "Spot Compression"
	
	--local spotCompFr = GetAttr(NMI_VIEW_ANGL_MODFR_TAG)
	--if (spotCompFr ~= nil and BestWordMatchInAttr(spotCompFr, SPOT_VAL)) then
	--	isSpotCompressed = "Yes"
	--end
	local sSpotValue = ValueForTag( MG_SPOT_COMP_PROP )
	if (sSpotValue ~= nil and sSpotValue == "Yes") then
		-- The property already get set when NMI_VIEW_ANGL_MODFR_TAG was handled in ExtractProjectionModifierMG
		return
	end		
	
	-- Spot Compression is also indicated in series description
	local seriesDescTag = GetAttr(SERIES_DESC_TAG)
	if (seriesDescTag ~= nil and BestWordMatchInAttr(seriesDescTag, "Spot")) then
		isSpotCompressed = "Yes"
	end
	
	return NewPropWithValue(MG_SPOT_COMP_PROP, isSpotCompressed)
end

function ExtractLateralityMG()
-- ////////////////////////////////////////////////////////////////////////////
	-- This function looks for laterality information in the series description only
	-- if this information has not already been extracted.
-- ////////////////////////////////////////////////////////////////////////////

	local latValue = GetAttr(LATERALITY_PROP)
	if (latValue ~=nil) then
		-- Nothing more to do
		return
	end
	
	local seriesDescTag = ValueForTag( SERIES_DESC_TAG )
    if (seriesDescTag ~= nil and
                 (string.find(seriesDescTag,"%sL$",1) or
				  string.find(seriesDescTag,"%sL%s",1) or
				  string.find(seriesDescTag,"^L%s",1))) then
		return NewPropWithValue(LATERALITY_PROP, "L")
	end
	
	if (seriesDescTag ~= nil and
                 (string.find(seriesDescTag,"%sR$",1) or
				  string.find(seriesDescTag,"%sR%s",1) or
				  string.find(seriesDescTag,"^R%s",1))) then
		return NewPropWithValue(LATERALITY_PROP, "R")
	end
		
	-- We fail yet again...
	return nil
end


function ExtractProjectionViewLabelMG(sSeparator)

	local sProjection = GetProjectionByViewCode()
	if (sProjection == nil) then
		return
	end
	
	if(sSeparator == nil) then
		sSeparator = ""
	end
	
	local bMagnification = false
	local bSpotCompression = false
	local sLaterality = ValueForTag(IMG_LATERALITY_TAG)

	local sSuffixModifier = ""
	local nmiViewModifierAttr = GetAttr(NMI_VIEW_ANGL_MODFR_TAG)
	if ( nmiViewModifierAttr ~= nil ) then	
		local bImplantDisplaced = false
		local bHasRI = false
		local bHasRL = false
		local bHasRM = false
		local bHasRS = false
		
		local nValues = nmiViewModifierAttr:GetSize()
		for i=1,nValues,1 do
			local sModifierValue = nmiViewModifierAttr:Get(i)
			
			if (string.lower(sModifierValue) == "rolled inferior") then
				bHasRI = true
			elseif (string.lower(sModifierValue) == "rolled lateral") then
				bHasRL = true
			elseif (string.lower(sModifierValue) == "rolled medial") then
				bHasRM = true
			elseif (string.lower(sModifierValue) == "rolled superior") then
				bHasRS = true
			elseif (string.lower(sModifierValue) == "cleavage" and
					sProjection == "CC") then
					
				sProjection = "CV"
				if (sLaterality ~= nil and sLaterality =="B") then
					sLaterality = ""
				end
				
			elseif (string.lower(sModifierValue) == "axillary tail" and
			        sProjection == "MLO") then					
				sProjection = "AT"				
			elseif (string.lower(sModifierValue) == "tangential") then
				sProjection = "TAN"
			elseif (string.lower(sModifierValue) == "magnification") then
				bMagnification = true
			elseif ( string.lower(sModifierValue) == "spot compression") then
				bSpotCompression = true	
			elseif ( string.lower(sModifierValue) == "implant displaced") then
				bImplantDisplaced = true		
			end
		end
		
		if(bImplantDisplaced) then
			sSuffixModifier = "ID"
		end
		if (bHasRI) then
			sSuffixModifier = MergeStrings(sSeparator, sSuffixModifier, "RI")
		end
		if (bHasRL) then
			sSuffixModifier = MergeStrings(sSeparator, sSuffixModifier, "RL")
		end
		if (bHasRM) then
			sSuffixModifier = MergeStrings(sSeparator, sSuffixModifier, "RM")	
		end
		if (bHasRS) then
			sSuffixModifier = MergeStrings(sSeparator, sSuffixModifier, "RS")	
		end
	end
	
	local sProjModiLabel = MergeStrings(sSeparator, sProjection, sSuffixModifier)
	
	if (bSpotCompression) then
		sProjModiLabel = MergeStrings(sSeparator, "S", sProjModiLabel)
	end
	
	if (bMagnification) then
		sProjModiLabel = MergeStrings(sSeparator, "M", sProjModiLabel)
	end
		
	local sLatProjModi = sProjModiLabel
	if (sLaterality ~= nil and sLaterality ~="U" and sLaterality ~= "") then
		NewPropWithValue(MG_LATERAL_LABEL_PROP, sLaterality)
		sLatProjModi = MergeStrings(sSeparator, sLaterality ,sProjModiLabel)
	end
	
	NewPropWithValue(MG_PROJ_MODI_LABEL_PROP, sProjModiLabel)
	return NewPropWithValue(MG_LAT_PROJ_MODI_LABEL_PROP, sLatProjModi)

end

function GetProjectionByViewCode()

    local strResult
	
	local anAttr = GetAttr(MG_CODED_VIEW_TAG)
	if( not anAttr ) then
		TraceC( "There is no MG view code sequence.\n")
		return nil
	end
	
	------------------- begin initialize the MG View code sequence map (SP4 decoded)
	local viewMapDecoded = {}				-- create as a table
	viewMapDecoded["medio-lateral"]				 =	            "ML"	-- medio-lateral
	viewMapDecoded["medio-lateral oblique"] 	 =  	        "MLO"	-- medio-lateral oblique
	viewMapDecoded["latero-medial"] 			 =  	        "LM"	-- latero-medial
	viewMapDecoded["latero-medial oblique"] 	 =  	        "LMO"	-- latero-medial oblique
	viewMapDecoded["cranio-caudal"] 			 =   			"CC"	-- cranio-caudal
	viewMapDecoded["caudo-cranial (from below)"] =  	        "FB"	-- caudo-cranial (from below)
	viewMapDecoded["superolateral to inferomedial oblique"] =  	"SIO"	-- superolateral to inferomedial oblique
	viewMapDecoded["exaggerated cranio-caudal"] 			=	"XCC"	-- exaggerated cranio-caudal
	viewMapDecoded["cranio-caudal exaggerated laterally"] 	=	"XCCL"	-- cranio-caudal exaggerated laterally
	viewMapDecoded["cranio-caudal exaggerated medially"]	=	"XCCM"	-- cranio-caudal exaggerated medially
	-- ---------------- end initialize the MG View code sequence map (SP4 decoded)
	
	local sCodeValue = anAttr:Get(1) -- one value expected here
	-- Convert code value to lower case
	sCodeValue = string.lower(sCodeValue)	
	-- Look up code in standard mammography view table
	local strResult = viewMapDecoded[sCodeValue]

    if( string.len(strResult) == 0 ) then
        return nil
    else
        return strResult
    end		
end

function ExtractTextOverlayWarningMG()
-- ////////////////////////////////////////////////////////////////////////////
-- This function checks a MG image to find out whether a "NOT FOR DIAGNOSIS"
-- warning message should be created.
--  For MG images the rules for creating TEXT_OVERLAY_WARNING_PROP sequence property
--  are as follows :
--
--    If
--        Presentation Intent Type (0008,0068) <> "FOR PRESENTATION" or
--        SOP Class UID (0008,0016) <> "1.2.840.10008.5.1.4.1.1.1.2" or
--        Lossy Image Processing (0028,2110) <> "00" or
--        Detector Type (0018,7004) == "FILM"
--   Then
--        Create TEXT_OVERLAY_WARNING_PROP sequence property with value "NOT FOR DIAGNOSIS"
--
-- ///////////////////////////////////////////////////////////////////////////

------------------------------------------------------------------------------
--
-- WARNING : In current implementations (11.6, 11.7, 11.8) changing the
-- value of the "NOT FOR DIAGNOSIS" warning message will not result in the
-- properly localized warning message being displayed on non-English systems.
-- The only localized warning message that may be displayed on non-English
-- systems is the appropriate translation of the phrase "NOT FOR DIAGNOSIS".
-- The warning message that is displayed on English systems may be
-- changed by providing an alternative implementation of this function in
-- a site-specific script.
--
------------------------------------------------------------------------------
	local sOverlayVal = "NOT FOR DIAGNOSIS"

	if ((ValueForTag(INTENT_PROP) ~= "FOR PRESENTATION")) then
		return NewPropWithValue(TEXT_OVERLAY_WARNING_PROP, sOverlayVal)
	end

    local MG_FOR_PRESENTATION_CLASS_UID = "1.2.840.10008.5.1.4.1.1.1.2"
	local sopClassAttr = GetAttr(SOP_CLASS_UID_TAG)
	if ( sopClassAttr~=nil and not BestMatchInAttr(sopClassAttr, MG_FOR_PRESENTATION_CLASS_UID) ) then
		return NewPropWithValue(TEXT_OVERLAY_WARNING_PROP, sOverlayVal)
	end

	if ((ValueForTag(LOSSY_IMAGE_COMPRESSION_TAG) ~= "00")) then
		return NewPropWithValue(TEXT_OVERLAY_WARNING_PROP, sOverlayVal)
	end

	local sDetectorType = ValueForTag(DETECTOR_TYPE_TAG)
	if ( sDetectorType~=nil) then
		local sDetectorTypeUpcase = string.upper(sDetectorType)
		if( sDetectorTypeUpcase == "FILM" ) then
			return NewPropWithValue(TEXT_OVERLAY_WARNING_PROP, sOverlayVal)
		end
	end

end

-- ////////////////////////////////////////////////////////////////////////////
-- extract MG method
-- ////////////////////////////////////////////////////////////////////////////

function Execute()

    -- If this is the wrong script for the supplied image...
    if (not SanityOK("MG")) then
      return nil
    end

    DeclareTags()

    -- Tag our output with the script version
    NewPropWithValue( MODALITY_SCRIPT_VER_PROP, "$Revision: 6 $ - $Modtime: 7/08/10 3:54p $ - MG" )

    ExtractProjectionMG()
	ExtractProjectionModifierMG()
    ExtractLateralityMG()
    ExtractThumbnailGroupMG()
    ExtractImplantDisplacedMG()
    ExtractMagnificationFactorMG()
    ExtractSpotFactorMG()	
    ExtractDescriptionMG()
	ExtractProjectionViewLabelMG()
	ExtractTextOverlayWarningMG()	
end

