--[[
	This script identifies the image properties of Nuclear Medicine (NM) images
	Specific properties extracted are:
		image description, based on series description tag, 
		or scout, view, laterality and contrast when no series description is given
	Ovidiu Scripa,    July 2004
	 -  Created prototype used for development testing
	Cristian Bonciu, July-September 2004
     -	implemented the functionality from pseudo-code prototypes provided by Allan
	Allan Noordyvk   Nov 13, 2005
     -	Added script version property setting.
	Mahesh Nagarajan	Sep 28, 2007
     -	Added new property "NM Image ID" on request by benedictine Hospital (REI3372)
    James Zhang, Jan. 12, 2010
     -  Added ExtractThumbnailGroupNM for Spectrum Health to solve REI 17615.
--]]

-- Enable tracing in this module for debugging (comment out for production version)
-- AliDSTraceLevel = 2 

-- ////////////////////////////////////////////////////////////////////////////    
-- definitions of the DICOM elements IDs used in this file 
-- ////////////////////////////////////////////////////////////////////////////    
function DeclareTags()
	CODE_VALUE_TAG = maketag("0008","0100") 
    NMI_IMAGE_ID_TAG = maketag("0054","0400")
end

-- ////////////////////////////////////////////////////////////////////////////    
-- definitions of the DICOM elements IDs used in this file 
-- ////////////////////////////////////////////////////////////////////////////    

function RegisterProperties()
    -- property IDs for NM images should be in the range (3711,1401)- (3711,14FF)range
    
    local val_list = "CSA MPR\\CSAMANIPULATED,GATED\\EMISSION,RECON TOMO,STATIC\\EMISSION,WHOLE BODY\\EMISSION"    
    Register( "NM_IMAGE_TYPE_PROP", "1401", "string", val_list, "NM Image Type")
	
	Register ("NM_IMAGE_ID_PROP","1402","string","","NM Image ID", {1})
end
-- ////////////////////////////////////////////////////////////////////////////
-- string definitions 
-- ////////////////////////////////////////////////////////////////////////////

-- ////////////////////////////////////////////////////////////////////////////
-- NM property extraction functions 
-- ////////////////////////////////////////////////////////////////////////////

function ExtractBodyRegionNM()
-- ////////////////////////////////////////////////////////////////////////////
-- This function extends the standard body region checking to include reference
-- to the codes defined in the SNOMED-DICOM Microglossary related to NM.
-- ////////////////////////////////////////////////////////////////////////////

  -- If we already have a body region property defined in the same way as for other modalities...
  local anAttr = GetAttr( BODY_REGION_PROP )
  if (anAttr) then
      TraceC( "Common script found body region... skipping special NM logic.\n" )
      return anAttr
      --==============================================
  end
  
  -- If we are here then we need to look at coded body region lookup.
  anAttr = GetAttr(CODE_VALUE_TAG)

  -- If this image doesn't have that either...
  if (not anAttr) then 
      TraceC( "NM Image has neither Body Part Examined nor Coded Values - no Body Region will be assigned.\n")
      return nil -- none available
      --==============================
  end

  -- ---------------- begin initialize the body region related codes in  sequence map
  local viewMap = {}				-- create as a table
  viewMap["T-11000"] 	=  	"Skeletal"
  viewMap["T-1"] 	=  	"Bone"
  viewMap["T-A2"] 	=  	"Brain"
  viewMap["T-A2000"] 	=  	"Brain"
  viewMap["T-32000"]	=	"Heart"	
  viewMap["T-3"] 	=  	"Heart"
  viewMap["T-5000"] 	=  	"GI"
  viewMap["T-5"] 	=  	"GI"
  viewMap["T-62000"] 	=  	"Liver"
  viewMap["T-62"] 	=  	"Liver"
  viewMap["T-28000"] 	=  	"Lung"
  viewMap["T-28"] 	=  	"Lung"
  viewMap["T-71000"] 	=  	"Renal"
  viewMap["T-71"] 	=  	"Renal"
  viewMap["T-B6000"] 	=  	"Thyroid"
  viewMap["T-B6"] 	=  	"Thyroid"

  -- ---------------- end initialize the body region related codes in sequence map

  local nValues = anAttr:GetSize() 
  --TraceC("found " .. nValues .. " code value values \n")
  
  -- for each of the coded values...
  for i=1,nValues,1 do 
  
      local sCodeValue = anAttr:Get(i)
      --TraceC("code value " .. sCodeValue .. "\n") 
      
      -- Look up code in standard SNOMED body region code table
      local sValueSNOMED = viewMap[sCodeValue]
      
      -- If we found a match
      if ( sValueSNOMED ) then
          -- We can use that as our projection.
          return NewPropWithValue( BODY_REGION_PROP, sValueSNOMED ); 
      end
  end
  
  -- If we got here there were no matching codes.
  TraceC( "None of the codes in the NM image match standard anatomy - no body region assigned.\n")
  return nil

end

function ExtractNMImageType()
-- ////////////////////////////////////////////////////////////////////////////
-- This function determines the value of the modality specific "NM Image Type"
-- property by accessing the last element of the Image Type list (instead of the
-- analyzing the first element as common Image Type information does).
-- ////////////////////////////////////////////////////////////////////////////

  -- Get DICOM image type attribute
  anAttr = GetAttr(IMAGE_TYPE_TAG)    
  if (anAttr) then
  
      -- Get the number of values in image type.
      local nValues = anAttr:GetSize()
            
      -- if last value of image type is non-nil
      local rawVal = anAttr:Get(nValues)
      if (rawVal) then
          -- Use it directly
          return NewPropWithValue( NM_IMAGE_TYPE_PROP, rawVal );
          -- =============================================== 
      end
    
  end 

  -- If we are here we then we were unable to get a useful value, so fail.
  return nil
  
end

function ExtractImageIDNM()
-- ////////////////////////////////////////////////////////////////////////////
-- This function extracts the property NM Image ID; a property requested by
-- Benedictine Hospital to assist with their hanging protocol building. For now, this
-- function simply returns the contents of DICOM tag (0054,0400) NMI Image ID
-- ////////////////////////////////////////////////////////////////////////////

	return NewPropFromTag(NM_IMAGE_ID_PROP, NMI_IMAGE_ID_TAG)
end

function ExtractDescriptionNM() 
-- ////////////////////////////////////////////////////////////////////////////
	-- 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 was already successful finding a good description value...
    local seriesDescProp = GetAttr( SERIES_DESC_PROP )
    if (seriesDescProp) then
        -- If it isn't just the special no-info placeholder...
        if (seriesDescProp:Get(1) ~= "?") then
            TraceC( "Common script found description... skipping special NM logic.\n" )
            return seriesDescProp;
            --==============================================
        end
    end
    
    TraceC( "No good description found by common script... using special NM logic.\n" )      
  
    local sDescription = ""
	-- The description string we are building
	
    -- Try to build a descriptive sentence by using derived properties
    sDescription = MergeStrings(" ", sDescription, ValueForTag(NM_IMAGE_TYPE_PROP))
    sDescription = MergeStrings(" ", sDescription, ValueForTag(LATERALITY_PROP))
    sDescription = MergeStrings(" ", sDescription, ValueForTag(BODY_REGION_PROP))

    -- If we were not able to build a description from the attributes above..
    if ( not sDescription or string.len(sDescription) == 0) then
           TraceC( "No components available for building NM series description.\n" )
           -- Leave earlier property unchanged.
           return seriesDescProp;
    end

    -- Return new property.
    return NewPropWithValue( SERIES_DESC_PROP, sDescription )

end

function ExtractThumbnailGroupNM()
    -- Sites that require this customization go here
    SPECTRUM_EAST_VAL = "Spectrum Health East"
    local SiteName = GetAttr(INSTITUTION_NAME_TAG)

	-- Site customization to place secondary capture in the very beginning
	if (SiteName ~= nil and BestWordMatchInAttr(SiteName, SPECTRUM_EAST_VAL)) then
		if ("SECONDARY" == ValueForTag(NM_IMAGE_TYPE_PROP)) then
            NewPropWithValue(THUMBNAIL_GRP_PROP, "00")
		end
	end
end


-- ////////////////////////////////////////////////////////////////////////////
-- extract NM method
-- ////////////////////////////////////////////////////////////////////////////

function Execute()

    TraceC("Extract NM executing.\n")

    DeclareTags()
    
    -- If this is the wrong script for the supplied image...
    if (not SanityOK("NM")) then
      return nil
      -- =====================================================
    end
    
    -- Tag our output with the script version
    NewPropWithValue( MODALITY_SCRIPT_VER_PROP, "$Revision: 3 $ - $Modtime: 12/01/10 6:00p $ - NM" )
    
    ExtractNMImageType()
	ExtractImageIDNM()    
    ExtractBodyRegionNM()
    ExtractDescriptionNM()
    ExtractThumbnailGroupNM()
    
    TraceC("Extract NM execution complete.\n")

    --==============================================
end


if( AliDSDebugEdit == 1 ) then
    Execute() -- debug edit only
end
