/*******************************************************************************
 *  Copyright 2017-2018 Centre for Development of Advanced Computing(C-DAC), Pune
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *   http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package cdac.medinfo.sdk.dcm30_04.dictionaryreaderwriters;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Locale;

import cdac.medinfo.sdk.dcm30_04.commoninterface.IDictionaryDataProvider;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDictionaryReader;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITag;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidDictionaryException;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.DicomLogger;
import cdac.medinfo.sdk.dcm30_04.utils.ResourceBundleWrapper;
import cdac.medinfo.sdk.dcm30_04.utils.StandardMethods;

/**
 * This is  a Thread safe Singleton class which provides facility to read data elements from DataDictionary. 
 * It uses DataDictionaryProvider to access DataDictionary. 
 * Dictionary Reader provides the flexibility for the user to use the default dictionary provided
 * with the library or use his/her own dictionary. Dictionary Reader is the implementation provided 
 * by the library which uses default dictionary written in xml file. User can extend from it or implement
 * IDictionaryReader interface to create his/her own dictionary reader and provide dictionary from 
 * where he/she stored it e.g. in a file or database. In this case user has to override populate() method
 * of the dictionary reader.
 * In the populate method, all that user has to do is basically retrieve the data from wherever he has kept it and 
 * call makeEntry method of DictionaryDataProvider to fill the dictionary at runtime.
 * 
 *   To create dictionary reader reading from xml file of data dictionary, steps required are:
 *   
 *   	IDictionaryReader objIDictionaryReader = DictionaryReader.createInstance(xmlfile, xsdfile);
 *      objIDictionaryReader.populate();
 *      
 *   To create dictionary reader reading from a place, steps required are:
 *   
 *   	IDictionaryReader objIDictionaryReader = DictionaryReader.createInstance();
 *      //override populate method to populate the dictionary from which dictionary reader can read. 
 *      objIDictionaryReader.populate();
 *      
 *   Using dictionary reader, user can retrieve the information based on the tag value. User can retreive
 *   name, value representation, minimum value multiplicity, maximum value multiplicity, retired status.
 *   
 *   Note: Some data elements are declared as retired from DICOM PS3 2004 standard. The retired status can
 *   also be retrieved through the dictionary reader. 
 *      
 */
public class DictionaryReader implements IDictionaryReader  
{	
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 1L;
	private final String strDataDictionaryPath = "DataDictionary.xml";
	private static DictionaryReader objDictionaryReader = null;
	IDictionaryDataProvider objIDictionaryDataProvider = null;
	private static Object mutex = new Object();

	private static XmlHandler objXmlHandler = null;

	/**
	 * This constructor is used if user wants to inherit DictionaryReader and override populate method 
	 */
	private DictionaryReader() throws InvalidDictionaryException
	{
		try
		{
			URL tempURL = DictionaryReader.class.getResource(strDataDictionaryPath);
			URLConnection objJarURLConnection = tempURL.openConnection();

			objXmlHandler = new XmlHandler(objJarURLConnection.getInputStream());
			objIDictionaryDataProvider = new DictionaryDataProvider();
			populate();
		}
		catch(IOException ioe)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(ioe.getMessage());
			}
			throw new InvalidDictionaryException(ioe.getMessage(),ioe);
		}
	}

	/**
	 * Accepts XML File Name and XSD FileName and populates the dictionary from the XML file
	 * @param strXMLFile name of the XML File representing Dicom Data Dictionary
	 * @param strXSDFile name of the XSD File representing XML Schema Definition
	 */
	private DictionaryReader(String strXmlFile, String strXsdFile) 
	{
		objIDictionaryDataProvider = new DictionaryDataProvider();

		objXmlHandler = new XmlHandler(strXmlFile, strXsdFile);
		objIDictionaryDataProvider = new DictionaryDataProvider();
		objXmlHandler.populate(objIDictionaryDataProvider);
	}

	/**
	 * Returns instance of Dictionary Reader if not created.
	 * @return - instance of DictionaryReader.
	 */

	public static DictionaryReader createInstance()throws InvalidDictionaryException
	{
		synchronized (mutex) 
		{
			if(objDictionaryReader == null)
			{
				objDictionaryReader = new DictionaryReader();	
			}
		}
		return objDictionaryReader;
	}

	/**
	 * Populates the Dictionary Data Provider with all the attributes from the Data Dictionary.
	 */
	public synchronized void populate() throws InvalidDictionaryException
	{
		if(!isDictionaryPopulated())
			objXmlHandler.populate(objIDictionaryDataProvider);
	}
	/**
	 * Constructs a RegistryTransactionAgent object and ensures that it is singleton.
	 * This method reads XML and XSD file path from application.
	 * @param strXMLFile String containing the XML File path.
	 * @param strXSDFile String containing the XSD File path.
	 * @return objRegistryTransactionAgent - object of RegistryTransactionAgent
	 */
	public static DictionaryReader createInstance(String strXMLFile, String strXSDFile)
	{

//		//If strXMLFilePath or strXSDFilePath is empty, RTA can't be created.
		if((strXMLFile.trim().equalsIgnoreCase("")) || (strXSDFile.trim().equalsIgnoreCase("")))  
			return null;
		synchronized (mutex) 
		{
			if(objDictionaryReader == null)
			{
				objDictionaryReader = new DictionaryReader(strXMLFile, strXSDFile);
			}
		}
		return objDictionaryReader;		
	}

	/**
	 * Returns the status of Dictionary whether it is populated or not.
	 * @return boolean - true, when data dictionary is populated.
	 */
	public boolean isDictionaryPopulated()
	{
		return objIDictionaryDataProvider.isPopulated();
	}

	/**
	 * Returns the name specified by Dicom Standard for the specified Tag
	 * @param iGroupNumber Group Number of Tag.
	 * @param iElementNumber Element Number of Tag.
	 * @return String - returns the name specified by Dicom Standard for the specified Tag
	 */
	public String getName(int iGroupNumber, int iElementNumber)
	{
		return objIDictionaryDataProvider.getName(iGroupNumber,iElementNumber);
	}

	/**
	 * Returns the VR Type specified by Dicom Standard for the specified Tag
	 * VR Type represents the Value Representation for the specified Tag
	 * @param iGroupNumber Group Number of Tag.
	 * @param iElementNumber Element Number of Tag. 
	 * @return String - returns the VR Type specified by Dicom Standard for the specified Tag
	 */
	public String getVR(int iGroupNumber, int iElementNumber)
	{
		return objIDictionaryDataProvider.getVR(iGroupNumber,iElementNumber);
	}

	/**
	 * Returns the minimum Value Multiplicity specified by Dicom Standard for the specified Tag
	 * VR Type represents the minimum Value Multiplicity for the specified Tag
	 * @param iGroupNumber Group Number of Tag.
	 * @param iElementNumber Element Number of Tag. 
	 * @return integer  - returns the VM specified by Dicom Standard for the specified Tag
	 */
	public int getMinVM(int iGroupNumber, int iElementNumber)
	{
		return objIDictionaryDataProvider.getMinVM(iGroupNumber,iElementNumber);
	}

	/**
	 * Returns the maximum Value Multiplicity specified by Dicom Standard for the specified Tag
	 * VR Type represents the maximum Value Multiplicity for the specified Tag. 
	 * @param iGroupNumber Group Number of Tag.
	 * @param iElementNumber Element Number of Tag.
	 * @return integer  - returns the VM specified by Dicom Standard for the specified Tag.
	 */
	public int getMaxVM(int iGroupNumber, int iElementNumber)
	{
		return objIDictionaryDataProvider.getMaxVM(iGroupNumber,iElementNumber);
	}

	/**
	 * Returns the status indicating whether the specified tag is retired from Dicom V3-2004 Standard Specification.
	 * @param iGroupNumber Group Number of Tag.
	 * @param iElementNumber Element Number of Tag.
	 * @return boolean  - the status indicating whether the specified tag is retired from Dicom V3-2004 Standard Specification
	 */
	public boolean isRetired(int iGroupNumber, int iElementNumber)
	{
		return objIDictionaryDataProvider.isRetired(iGroupNumber,iElementNumber);
	}

	/**
	 * Validates if the Value Representation(VR) and the Tag match according to the Data Dictionary.
	 * @param objITag Tag need to be verified.
	 * @param iVRType Value Representation to be matched.
	 * @return - success, if the Tag & VR match with Data Dictionary. false, if match is failed.
	 */
	public boolean isVRValid(ITag objITag, int iVRType)
	{
		int iVRFromDictionary = 0;
		//Get VR for the given Tag from Dictionary
		String strVRFromDictionary = getVR(objITag.getGroupNumber(), objITag.getElementNumber());
		iVRFromDictionary = StandardMethods.getiVRtype(strVRFromDictionary);

		//Compare the VR from Dictionary and given by user
		if(iVRFromDictionary == iVRType)
			return true;

		return false;
	}
	
	/**
	 * Retrieves the  Group Number and Element Number of Attribute Name provided.
	 * This function return a short array in which element at index 0 is Group Number
	 * and at index 1 is Element Number. 
	 * @param strDataElementName String value of Data Element name.
	 * @return short[] - return short array. The element at index 0 represents Group Number and 
	 * at index 1 represents Element Number. 
	 */
	public short[] getTag(String strDataElementName)
	{
		if(strDataElementName == null || strDataElementName.equals(""))
		{
			DicomConfig objDicomConfig = DicomConfig.createInstance();
			DicomLogger objDicomLogger = DicomLogger.getLogger();
			if(objDicomConfig.isLoggingOn())
			{
				objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","DataElement.DataElementNameNull",new Locale(DicomConfig.createInstance().getLocale())));
			}
			return null;
		}
		return objIDictionaryDataProvider.getTag(strDataElementName);
	}
}







