/*******************************************************************************
 *  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.baselibs;

import java.io.IOException;

import cdac.medinfo.sdk.dcm30_04.commoninterface.IDicomStream;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITag;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidDataTypeException;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidValueException;
import cdac.medinfo.sdk.dcm30_04.exceptions.ValidationException;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConstants;
import cdac.medinfo.sdk.dcm30_04.utils.DicomLogger;

import cdac.medinfo.sdk.dcm30_04.utils.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.StandardMethods;

/**
 * Data Type : Attribute Tag
 * Ordered pair of 16-bit unsigned integers that is the value of a Data Element Tag.
 */
public class AT extends DicomValue  
{
	
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 1L;

	protected String strRegex;

	/**
	 * Creates an object of AT with default initialization.
	 */
	AT()
	{
		//Regular Expression to validate the Input
		strRegex = "";
		iVRType = DicomConstants.DCM_DT_AT;
	}	

	/**
	 * Retrieves the maximum length allowed for AT DataType by DICOM Standard.
	 * @return integer - maximum length allowed for AT DataType by DICOM Standard.
	 */
	public long getMaxLength()
	{
		return DicomConstants.DCM_AT_MAXLENGTH;
	}

	/**
	 * This method validates the AT DataType value.
	 * @param objToValidate object value of AT DataType
	 * @return boolean - status indicating success/failure of validation
	 */
	public boolean validate(Object objToValidate)
	{
		return true;
	}

	/**
	 * This method validates the AT DataType value.
	 * @return boolean - status indicating success/failure of validation
	 */
	public boolean validate()
	{
		return validate(value);
	}
	/*
	 * This method is modified to use Collection instead of vector.
	 */
	/**
	 * This method reads all groupNumbers followed by respective ElementNumbers from the Stream and stores it in collection.Also sets value to DicomValue.
	 * @param objIDicomStream object of stream from which the AT datatype is to be read
	 * @param iLength length of data to read
	 * @return boolean - status indicating success/failure of read function
	 * @throws InvalidDataTypeException If data type validation fails.
	 * @throws IOException If Input/Output operation fails.
	 */
	public boolean read(IDicomStream objIDicomStream,int iLength) throws InvalidDataTypeException, IOException
	{
		short shGroupNumber=0,shElementNumber=0;
		ITag objITag = null;
		ISingleCollection<ITag> objSingleCollection = new SingleCollection<ITag>();
		//AT datatype can contain a series of 4 bytes
		//Ex. Suppose the data in AT value is (0018,FFFF).
		//This will be read as 18H,00H,FFH,FFH and stored in
		//a collection.The total size now of collection will 4.

		/*
		 * The Groupnumber and Element Number will now be read into Tag Object
		 * and set to DicomValue 
		 */
		if(iLength == 4)
		{
			shGroupNumber = objIDicomStream.readShort();
			shElementNumber = objIDicomStream.readShort();
			objITag = new Tag(shGroupNumber,shElementNumber);
			objSingleCollection.add(objITag);
			setValue(objSingleCollection);
			return true;
		}
		//Check Length is a multiple of 4
		else if((iLength != 0) && (iLength > 4) && ((iLength % 4) == 0))
		{
			int index =0;
			
			/*
			 * When there is a series of GroupNumber and ElementNumber,The Groupnumber 
			 * and Element Number will now be read into Tag Object and the Tag object 
			 * is added in a collection.This vector is at last set to DicomValue. 
			 */
			while(index < (iLength / 4))
			{
				//Get the Group numbER
				shGroupNumber=objIDicomStream.readShort();
				//Get the Element number
				shElementNumber=objIDicomStream.readShort();
				objITag = new Tag(shGroupNumber,shElementNumber);
				objSingleCollection.add(objITag);
				index++;
			}
			setValue(objSingleCollection);
		}
		return true;
	}

	/*
	 * Previously the logic was written wrongly. Now this method retrieves Tags from the collections and writes them onto the stream
	 */
	/**
	 * This method retrieves Tag values from the value and writes them onto the stream
	 * @param objIDicomStream object of stream onto which the AT DataType is to be serialized
	 * @return integer - returns the status indicating success/failure of write method 
	 * @throws IOException If Input/Output operation fails.
	 */
	@SuppressWarnings("unchecked")
	public int write(IDicomStream objIDicomStream) throws IOException
	{
		ISingleCollection<ITag> objSingleCollection = null;
		ITag objITag = null;
		short sData = 0;

		//Get the Value
		Object objValue = getValue();	

		/* 
		 * If the value is a Single ITag object then get the GroupNumber
		 * and the ElementNumber from the ITag object and write to stream. 
		 */
		if(objValue instanceof ITag)
		{	
			objITag = (Tag)objValue;
			sData = (short)objITag.getGroupNumber();
			objIDicomStream.writeShort(sData);

			sData = (short)objITag.getElementNumber();
			objIDicomStream.writeShort(sData);

		}
		else if(objValue instanceof ISingleCollection)
		{
			/*
			 * Now the collection will contain Tag Elements. We get the GroupNumber
			 * and ElementNumber from the Tag and write it to stream.
			 */
			objSingleCollection = (ISingleCollection<ITag>)objValue;
			int index = 0;

			while(index < objSingleCollection.size())
			{
				//Get GroupNumber
				objITag = objSingleCollection.get(index);

				if(objITag != null)
				{
					//Write the Group number
					objIDicomStream.writeShort((short)objITag.getGroupNumber());
					objIDicomStream.writeShort((short)objITag.getElementNumber());
				}
				index++;
			}
		}
		return 1;		
	}

	/**
	 * Retrieves the actual value length of the AT DataType.
	 * @return integer - actual length if the value of AT DataType
	 */
	@SuppressWarnings("unchecked")
	public int getValueLength()
	{
		int iValueLength =0;
		if(value != null)
		{
			if(value instanceof ITag)
				iValueLength = 4;
			else if (value instanceof ISingleCollection)
			{
				ISingleCollection<ITag> objSingleCollection = (ISingleCollection)value;
				for(ITag objTag : objSingleCollection)
				{
					if(objTag != null)
						iValueLength = iValueLength + 4 ;
				}
			}
		}
		return iValueLength;
	}

	/**
	 * Sets the value of AT DataType
	 * @param objValue value to be set in the AT DataType 
	 */
	public void setValue(Object objValue)
	{
		try
		{
			validate(objValue);
			value=objValue;
			/*
			 * This If-Else block checking whether given value is valid for this Type or not and sets VM according to that
			 */
			if(value != null)
			{
				if(value instanceof ITag)
					iVM = 1;
				else if(value instanceof ISingleCollection)
					iVM = ((ISingleCollection)value).size();
				else
					throw new InvalidValueException(StandardMethods.getStrVRtype(iVRType));
			}
		}
		catch( InvalidValueException ive)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(ive.getMessage());
			}
			throw new InvalidDataTypeException(ive.getMessage(),ive);
		}
		catch( ValidationException ive)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(ive.getMessage());
			}
			throw new InvalidDataTypeException(ive.getMessage(),ive);
		}
	}

	/**
	 * This function returns the String value of AE Data Type.
	 */
	@SuppressWarnings("unchecked")
	public String toString()
	{
		if(value != null)
		{
			if (value instanceof ITag)
			{
				ITag objITag = (ITag)value;
				return objITag.toString();
			}
			else if (value instanceof ISingleCollection)
			{
				ISingleCollection<ITag> objSingleCollection = (ISingleCollection)value;
				String strValue = "";
				for(int i=0; i<objSingleCollection.size(); i++)
				{
					ITag objITag = objSingleCollection.get(i);
					strValue = strValue + objITag.toString()+ "\\";
				}
				return strValue;
			}
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	/**
	 *  Dumps the value of Attribute in readable form.
	 *  @param objIDicomStream : the Stream on which the Data is to be written.
	 *  @throws IOException - Exception is thrown indicating runtime failure of writing operation.
	 */
	public void dumpDICOMInfo(IDicomStream objIDicomStream) throws IOException
	{
		if(value instanceof ITag)
		{
			objIDicomStream.writeString("Value : " + (ITag)value + " ");
		}
		else if (value instanceof SingleCollection)
		{
			ISingleCollection<ITag> iTagValueArray = (ISingleCollection<ITag>)value;

			objIDicomStream.writeString("[");

			for(ITag objITag : iTagValueArray)
			{
				objIDicomStream.writeString(" <");
				objITag.dumpDICOMInfo(objIDicomStream);
				objIDicomStream.writeString("> ");
			}

			objIDicomStream.writeString("]");
		}
	}
}
