--[[
     This script identifies the image properties of Computed Tomography (CT) 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        
	-	prototype used for development testing
	Cristian Bonciu, July-September 2004        
	-	implemented the functionality from pseudo-code prototypes provided by Allan 
	Allan Noordvyk     		 14 SEP 2005            
	-	Rewrote ExtractPhaseCT to be conservative about using REL Acquisition number to set Phase.
		Now it should only do it for specific devices (and modes) where this is known to be useful, 
		rather than assuming it will work for all but a few problem devices.
	Allan Noordvyk      		01 OCT 2005
	-	Added MODALITY_SCRIPT_VER_PROP setting to better detect when old scripts are being run.
	Allan Noordvyk      		13 NOV 2005
	-	Commented out phase setting for Toshiba Aquilion as we are seeing oversplits at San Angelo 
		Medical Center when we are not discerning in when to use REL Acquisition Number for phase.
	Allan Noordvyk			14 NOV 2005
	-	Added "Delayed" to start of built series descriptions to better identified delayed series.
	Mahesh Nagarajan    		05 DEC 2005
	-	Added phase splitting for Siemens Sensation 16 based on studies seen at High Point, NC. Phase 
		property is based on the REL Acquisition Number
	-	Commented the implementation above until further discussion with Allan and testing it with other 
		Siemens Sensation 16 Studies
	-	Added function "ExtractThumbnailGroupCT" to allow user option of moving CT Scouts to the front
	Mahesh Nagarajan    		17 JAN 2006
	-	Extended phase extraction for GE models; REL Acquisition Number is accepted for phase for the
		following 		models; (1) LightSpeed QX/i (2) HiSpeed CT/i. This restriction was required as the
		Ultra and Plus models of the GE's LightSpeed series devices did not handle Acquisition Number properly.
	Mahesh Nagarajan   		25 JAN 2006
	-	On request by Fletcher-Allan, added new property "Filter Type"; this uses the DICOM tag (0018, 1160)
		ACQ Filter Type to determine what filter was applied on the images of the sequence
	Mahesh Nagarajan   		02 MAR 2006
	-	On request by PACS Admin of St. Lukes Hospital, removed extraction of phase  for all CT devices there.
	Mahesh Nagarajan		03 MAR 2006
	-	CRDB00038038: Another form of phase identification is added through a new property called "Phase
		Name". The  property currently takes four values - (1) Before (2) Arterial (3) Capillary (4) Venous; 
		though as other string valued properties this will be subject to a lot of changes and syntax variations.
	Mahesh Nagarajan		28 MAR 2006
	-	On request by Winchester, CT scouts are set up-front on the thumbnail bar for studies performed there
	Mahesh Nagarajan		03 APR 2006
	-	On request by Provena St. Josephs, turned off phase splitting for all CT devices
	Mahesh Nagarajan		08 MAY 2006
	-	Disabled phase-extraction routines for Roswell Park & Montgomery Memorial Hospital
	Mahesh Nagarajan		12 MAY 2006
	-	Disabling Phase-Splittingby default; this will be introduced for just those sites that require it
	Mahesh Nagarajan		30 MAY 2006
	-	Enhanced the scope of CT property "Filter Type"; applies to all sites now, and not just FAHC
	Mahesh Nagarajan		09 JUN 2006
	-	Changed thumbnail ordering so that Timing Boluses and Smart Prep series appear before the scouts 
		& screen saves at Spectrum
	-	Interchanged the priority of screen saves and scouts for Spectrum
	Mahesh Nagarajan		26 JUN 2006
	-	Now that phase-splitting is disabled for all sites, the scope of device-specific logic for phase info 
		extraction is extended to all GE LightSpeed devices
	Mahesh Nagarajan		18 SEP 2006
	-	Added more aliases for Univ of Wisc. to ensure that phase-splitting is activated for all their CT devices
	-	High Point now wants CT scouts at the back 
	-	Only those studies at U of W with the string "Phasic" in the series description will be split by phase
	Michael Lai				09 NOV 2006
	-	Remove Interior Health from isPhaseSplittingSite
	Mahesh Nagarajan		14 NOV 2006
	-	Changing Filter Type property's flag to ensure that it does not participate in the process of starring for
		HRS smart drag and drop DP features
	Mahesh Nagarajan   		01 FEB 2007
	-	Site customized code will be removed and re-located to ExtractCT.site files -> starting with 11.5, unless extraction
		routines are fundamentally flawed, all change requests will be incorporated into a site file.
	Mahesh Nagarajan		22 MAY 2007
	-	Modified the process of return phase property values; using the function "NewPropWithValue" automatically converts
		the returned values to string types; this causes problems while sorting on phase property values. The "NewPropWithValue" 
		function call is replaced by a more stringent process of setting this property with the value in the proper value representation
	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		08 FEB 2008
	-	Adding Ridgecrest Regional to list of sites that want phase splitting; and crossing fingers that this doesnt blow up in my face!	
	Mahesh Nagarajan		11 APR 2008		
	-	Changing the property type of PHASE_PROP from int to string; this is specifically done for Dr. Mendel of Caritas to help him 
		use this property in his hanging protocols
	Mahesh Nagarajan		26 MAY 2008
	-	Changed the thumbnail group assignment for CT images at CHU Tours Trousseau on request from the site; they want to see
		RESUME/RESUME1/RESUME2	sequences first, followed by RES sequences and then scouts.		
	Mahesh Nagarajan		30 JUL 2008
	-	Modified the changes for CHU Tours to ensure that all CT modalities fall into the scope of the thumbnail order modifications
	Shudong Liu  January 7, 2009
	 - Added GHC as an alias of University of Wisconsin to ensure that phase-splitting is activated for all their CT devices (REI11190)
    James Zhang Feburary 19, 2010
	-  Adding GE Discovery CT750 HD to list of models that support phase splitting (CRDB00084169/REI 17787)
--]]

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

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

function RegisterProperties()
-- ////////////////////////////////////////////////////////////////////////////
-- This function is called automatically at load of script to register any
-- modality specific properties.  Prior to IP Library build 39 it must be 
-- present even if empty.
-- Modality specific property IDs (3711,1201)- (3711,12FF) range
--     If modality specific properties need to be defined, then replace X in the above 
--     numeric ranges with a value that is distinct from those used by other modalities.
-- ////////////////////////////////////////////////////////////////////////////

    -- FILTER_TYPE_PROP indicates the filter applied while acquiring the CT images.
    --
    -- Usage in DP:	        To be evaluated; requested by Fletcher-Allan
    -- Related DICOM Tags:	ACQ Filter Type <0018,1160> 

    Register("FILTER_TYPE_PROP", "1201", "string", "", "Filter Type",{0})
end

-- ////////////////////////////////////////////////////////////////////////////
-- string definitions 
-- ////////////////////////////////////////////////////////////////////////////
    CORONAL_VAL = "Coronal"
    SAGITTAL_VAL = "Sagittal"
    AXIAL_VAL = "Axial"
    OBLIQUE_VAL = "Oblique"
    
    HELICAL_MODE_VAL = "HELICAL MODE"  -- Specific string used by GE scanners      
    
-- ////////////////////////////////////////////////////////////////////////////
-- CT property extraction functions 
-- ////////////////////////////////////////////////////////////////////////////

function ExtractFilterTypeCT()
-- This function blindly returns the value found in (0018, 1160) ACQ Fliter Type
    
    local acqFilterTypeTag = ValueForTag(ACQ_FILTER_TYPE_TAG)

    TraceC("Extracting filter type...\n")
    return NewPropWithValue( FILTER_TYPE_PROP, acqFilterTypeTag )
end

--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
function ExtractThumbnailGroupCT()
-- This function allows a low-level form of site customization for thumbnail ordering;--
-- ////////////////////////////////////////////////////////////////////////////
    local newGrpValue = nil
    
    -- Sites that require this customization go here
    WINCHESTER_VAL = "Winchester"
    SPECTRUM_VAL = "Spectrum"
	BUTTERWORTH_VAL = "Butterworth"  -- Alternate for Spectrum
	WEST_MICH_VAL = "West Mich"  -- Alternate for Spectrum
	BLODGETT_VAL = "Blodgett" -- Alternate for Spectrum
    COLUMBUS_VAL = "Columbus" 
	RESURRECT_VAL = "Resurrection"
	CH_TROUSSEAU_VAL = "TROUSSEAU"
	
    local SiteName = GetAttr( INSTITUTION_NAME_TAG )
	
	-- Generic Site Customization: All sites that require scouts up front on the thumbnail bar
    if ( SiteName ~= nil and BestWordMatchInAttr(SiteName, WINCHESTER_VAL, COLUMBUS_VAL ) ) then	 
		local imageType = ValueForTag(IMAGE_TYPE_PROP)
		if  (imageType == "Scout") then
			newGrpValue = "00"
		end
    end
	
	-- Site customization for ressurection to push scouts to the very end
	if ( SiteName ~= nil and BestWordMatchInAttr(SiteName, RESURRECT_VAL) ) then
		local imageType = ValueForTag(IMAGE_TYPE_PROP)
		if  (imageType == "Scout") then
			newGrpValue = "40"
		end
	end

	-- Site customization for C.H. Trousseau to implement their won thumbnail ordering
    if ( ValueForTag(INSTITUTION_ADDR_TAG) == "TOURS" ) then	 
		
		local seriesDescAttr = GetAttr(SERIES_DESC_TAG)
		if  ( seriesDescAttr~=nil and BestWordMatchInAttr(seriesDescAttr, "RESUME", "RESUME1", "RESUME2") ) then
			newGrpValue = "00"
		end
		
		if  ( seriesDescAttr~=nil and BestWordMatchInAttr(seriesDescAttr, "RES") ) then
			newGrpValue = "00a"
		end
    end
	 
	-- Site customization for Spectrum to ensure that SmartPrep & Timing Bolus come before scouts
    if ( SiteName ~= nil and BestWordMatchInAttr(SiteName, SPECTRUM_VAL, BUTTERWORTH_VAL, BLODGETT_VAL, WEST_MICH_VAL) ) then
		
		local seriesDescAttr = GetAttr(SERIES_DESC_TAG)
		SMART_PREP_VAL = "Smart Prep"
		TIMING_BOLUS_VAL = "Timing Bolus"
		DOSE_REPORT_VAL = "Dose Report"
		if (seriesDescAttr ~= nil and BestWordMatchInAttr(seriesDescAttr, SMART_PREP_VAL, TIMING_BOLUS_VAL) ) then
			newGrpValue = "16"
		end
		
		local imageTypeValue = ValueForTag(IMAGE_TYPE_PROP)
		if  (imageTypeValue == "Scout") then
			newGrpValue = "26"		
		elseif (imageTypeValue == "Screen Save") then 
			-- We must still send "Dose Report" screen saves to the very back
			if (seriesDescAttr ~= nil and BestWordMatchInAttr(seriesDescAttr, DOSE_REPORT_VAL)) then
				newGrpValue = "31"
			else
				newGrpValue = "21"
			end
		end
		
    end	

    return NewPropWithValue( THUMBNAIL_GRP_PROP, newGrpValue )
end
--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
function IsPhaseSplittingSite()
-- ////////////////////////////////////////////////////////////////////////////
-- The purpose of this function is to keep track of sites that require phase splitting.

    local SiteName = GetAttr( INSTITUTION_NAME_TAG )
	
    WEST_CLINIC_VAL = "West Clinic"
	FROEDTERT_VAL = "Froedtert"
	RIDGECREST_VAL = "Ridgecrest Regional"
    
    if ( SiteName ~= nil and BestMatchInAttr(SiteName, WEST_CLINIC_VAL, FROEDTERT_VAL,
															RIDGECREST_VAL) ) then
		return true
    end

	-- Special Logic for U of W sites until they can sort their CT mess; it is not enough that the CT exam comes from U of W, but the series 
	-- description must have the string "phasic' in it
    UNIV_WISC_VAL = "University of Wisconsin"
    ALT_WISC_VAL1 = "UW Hosp"
	ALT_WISC_VAL2 = "U.W. Hosp."
	ALT_WISC_VAL3 = "University of WI"
	ALT_WISC_VAL4 = "UW East"
	ALT_WISC_VAL5 = "UW Research"
	ALT_WISC_VAL7 = "V.A.M.C Madison"
	ALT_WISC_VAL8 = "UW Madison"
	ALT_WISC_VAL9 = "UWHOSPTL"
	ALT_WISC_VAL10 = "UWHC"
	ALT_WISC_VAL11 = "GHC Hatchery Hill"
	
	if ( SiteName ~= nil and BestMatchInAttr(SiteName,
            UNIV_WISC_VAL, ALT_WISC_VAL1, ALT_WISC_VAL2,
            ALT_WISC_VAL3, ALT_WISC_VAL4, ALT_WISC_VAL5, 
            ALT_WISC_VAL7, ALT_WISC_VAL8, ALT_WISC_VAL9,
            ALT_WISC_VAL10, ALT_WISC_VAL11)) then
		
		local SeriesDescAttr = GetAttr(SERIES_DESC_TAG)
		PHASIC_VAL = "Phasic"
		
		if (SeriesDescAttr ~= nil and BestMatchInAttr(SeriesDescAttr, PHASIC_VAL)) then
			return true
		end
    end
		
	-- If we are here, no phase-splitting so just return false
	return false
end
--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

function ExtractPhaseCT()
-- This function identifies the acquisition phase and sets the PHASE_PROP appropriately.  This
-- is disabled by default; this can be "turned on" for just those sites that require it. Also, this
-- currently this just means copying the value in the REL Acquisition Number property <0020,0012> 
-- for a small selection devices that are known to use this value for indicating phase, in the selected
-- modes of operation.
-- ////////////////////////////////////////////////////////////////////////////

	local ActivateSplittingByPhase = false
	
    --$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
	-- Check if the images were acquired at a site that requires phase-splitting
	ActivateSplittingByPhase = IsPhaseSplittingSite()
	
    if (not(ActivateSplittingByPhase)) then
		TraceC( "Site does not require phase splitting -- Goodbye!.\n" )
		return nil
	end
	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

	-- If we are here, we are dealing with images from a site that requires phase-splitting
    local relAcqNumber = ValueForTag( ACQ_NUMBER_TAG )
    
     -- If the image conatains no relative acquisition number...
    if (relAcqNumber == nil) then
        -- No phase possibility need be investigated
        TraceC( "No relative acquisition number -- so no device-specific phase logic to do.\n" )
        return nil
        -- ==============================================
    end
        
    -- If no device manufacturer information...
    local manuStr = ValueForTag(DEVICE_MANUFACTURER_TAG)
    if (not manuStr) then
        -- We won't be able to do special logic
        TraceC( "Unknown manufacturer's device -- so no device-specific phase logic to do.\n" )
        return nil
        -- =======================================
    end
    
    -- If no model information...
    local modelStr = ValueForTag( DEVICE_MODEL_TAG )
    if (not modelStr) then
        -- We won't be able to do special logic
        TraceC( "Unknown device model -- so no device-specific phase logic to do.\n" )
        return nil
        -- =======================================
    end
--[[--------------- Disabled until we can get more information from Toshiba/Siemens
	----------------engineers -- can result in minor and major over-splits otherwise.
     -- If the device vendor is Toshiba and is an Aquilion...
     if (manuStr =="TOSHIBA" and modelStr == "Aquilion") then
         -- Safe to use relative acquisition number.
         TraceC( "Recognized device (" .. manuStr .. " - " .. modelStr .. 
                 ") - phase set by relative acquistion number.\n" )
         return NewPropWithValue( PHASE_PROP, relAcqNumber )
         -- =======================================
     end

	--If the device vendor is Siemens and is a Sensation 16...
     if (manuStr =="SIEMENS" and modelStr == "Sensation 16") then
         -- Safe to use relative acquisition number.
         TraceC( "Recognized device (" .. manuStr .. " - " .. modelStr .. 
                 ") - phase set by relative acquistion number.\n" )
         return NewPropWithValue( PHASE_PROP, relAcqNumber )
         -- =======================================
     end
--]]
	
    -- If the device vendor is GE and is a LightSpeed QX/i or HiSpeed CT/i...
    if ( manuStr == "GE MEDICAL SYSTEMS" and 
         ( string.find(modelStr,"LightSpeed",           1, true) or
           string.find(modelStr,"HiSpeed CT/i",         1, true) or
		   string.find(modelStr,"BrightSpeed",          1, true) or
           string.find(modelStr, "Discovery CT750 HD",  1, true))
       ) then
      
        -- Get the scan options value
        local scanModeStr = ValueForTag( SCAN_OPTIONS_TAG )
    
        -- If there wasn't one...
        if (not scanModeStr) then
           TraceC( "Scan mode not specified for GE device... skipping special GE scan mode logic.\n" )
           return nil
           --==============================================
        end
    
        -- If the GE device is not in helical scan mode...
        if (scanModeStr ~= HELICAL_MODE_VAL) then
           -- Relative acquiswtion number has nothing to do with phase
           TraceC( "GE device not in Helical scan mode...  ignore relative acquisiton number.\n" )
           return nil
           -- =======================================
        end   

       -- Safe to use relative acquisition number.
        TraceC( "Recognized GE device (" .. modelStr .. 
                ") in correct mode - phase set by relative acquistion number.\n" )
		return NewPropWithValue( PHASE_PROP, relAcqNumber )
        -- =======================================
        
    end
    
    -- If we are here then we don't have special logic for this device
    TraceC( "No special phase detection logic for device: " .. manuStr .. " - " .. modelStr )
    return nil
    -- =======================================    
end

-- ////////////////////////////////////////////////////////////////////////////
function ExtractPhaseNameCT()
-- ////////////////////////////////////////////////////////////////////////////

	local matchStr = nil
	local seriesDescTag = GetAttr(SERIES_DESC_TAG)
	
	if (seriesDescTag) then
	
		matchStr = BestMatchInAttr( seriesDescTag, BEFORE_VAL, ARTERIAL_VAL,
												CAPILLARY_VAL, VENOUS_VAL )
	end
	
	return NewPropWithValue( PHASE_NAME_PROP, matchStr)	
end

-- ////////////////////////////////////////////////////////////////////////////
function ExtractImageDescriptionCT() -- call it always after extractEcho()
-- ////////////////////////////////////////////////////////////////////////////

    -- 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 skipping no-info placeholder...
        if (seriesDescProp:Get(1) ~= "?") then
            TraceC( "Common script found description... skipping special CT logic.\n" )
            return seriesDescProp;
            --==============================================
        end
    end
    
    TraceC( "No good description found by common script... using special CT logic.\n" ) 

    -- Try to build a description based on key components.
    local seriesDescStr
    
        -- If we are a delayed series...
    if (ValueForTag(DELAYED_PROP) == "Yes") then 
        -- Start with indication that series is delayed is present to custom series description
        seriesDescStr = "Delayed"
    end
    
    seriesDescStr = SmartAppendWords( seriesDescStr, " ", ValueForTag(VIEW_PROP) )
    seriesDescStr = SmartAppendWords( seriesDescStr, " ", ValueForTag(LATERALITY_PROP) )
    seriesDescStr = SmartAppendWords( seriesDescStr, " ", ValueForTag(BODY_REGION_PROP) )
      
    -- If we have an image type other than boring old "Original"...
    local imageTypeStr = ValueForTag(IMAGE_TYPE_PROP) 
    if (imageTypeStr and imageTypeStr ~= "Original" and imageTypeStr ~= "ORIGINAL") then 
        -- Append non-boring image type to custom series description
        seriesDescStr = SmartAppendWords( seriesDescStr, " ", imageTypeStr )
    end
    
    -- If we have contrast...
    if (ValueForTag(CONTRAST_PROP) == "Yes") then 
        -- Append indication that contrast is present to custom series description
        seriesDescStr = SmartAppendWords( seriesDescStr, " ", "+C")
    end
    
    -- If we had no success above...
    if (not seriesDescStr or string.len(seriesDescStr) == 0) then
        seriesDescStr = "?"
    end
    
    return NewPropWithValue( SERIES_DESC_PROP, seriesDescStr )
    
end	

-- ////////////////////////////////////////////////////////////////////////////
-- extract CT method
-- ////////////////////////////////////////////////////////////////////////////

function Execute()

    TraceC("Extract CT executing.\n")
   
   -- If this is the wrong script for the supplied image...
    if (not SanityOK("CT")) then
      return nil
    end
    
    -- Tag our output with the script version
    NewPropWithValue( MODALITY_SCRIPT_VER_PROP, "$Revision: 2 $ - $Modtime: 19/02/10 10:43a $ - CT" )

    ExtractThumbnailGroupCT()
	ExtractFilterTypeCT()
    ExtractPhaseCT()
    ExtractPhaseNameCT()
    ExtractImageDescriptionCT()
    
    TraceC("Extract CT execution complete.\n")
    --==============================================
end


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