/*******************************************************************************
 *  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.services.displaysystemmanagement.dbhelper;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


import cdac.medinfo.sdk.dcm30_04.baselibs.DataSet;
import cdac.medinfo.sdk.dcm30_04.baselibs.DicomV3Factory;
import cdac.medinfo.sdk.dcm30_04.baselibs.ExplicitDE;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImplicitDE;
import cdac.medinfo.sdk.dcm30_04.baselibs.SQ;
import cdac.medinfo.sdk.dcm30_04.baselibs.Tag;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataElement;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataTypeSQ;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDicomValue;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDictionaryReader;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IItem;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITag;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITransferSyntax;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.DataTypeConvertor;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConstants;
import cdac.medinfo.sdk.dcm30_04.utils.ICollectionIterator;
import cdac.medinfo.sdk.dcm30_04.utils.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.StandardMethods;

/**
 * DisplaySystem_DBQuery interacts with database and performs queries fired.
 * It performs the queries fired for creation, updation and retrieval of Display System SOP Instances
 */
public class DisplaySystem_DBQuery 
{
	private Connection objcon = null;

	/**
	 * Sets connection
	 * @param objcon object of Connection
	 */
	public void setConnection(Connection objcon)
	{
		this.objcon = objcon;
	}

	/**
	 * Creates the SOPInstance.
	 * It inserts the information of all DataElements of provided Dataset  
	 * @param objIDataSet instance of Dataset
	 * @param strSOPInstanceUID value of SOPInstance UID
	 * @return boolean status as true or false
	 * @throws SQLException if SQL Operation fails
	 */
	boolean createDisplaySystemInstance(IDataSet objIDataSet,String strSOPInstanceUID)throws SQLException
	{
		String strTableName = "DISPLAY_SYSTEM_DEINFO";
		
		String strValue = null;
		for(IDataElement objDataElement : objIDataSet)
		{
			/*
			 * This If-Else block inserts DataElements in database.
			 * It inserts all first level data elements and for SQ element it inserts that SQ element and all item elements belonging to that SQ.  
			 */
			IDicomValue objIDicomValue = objDataElement.getDicomValue();
			if(!(objIDicomValue instanceof SQ))
			{
				if(objIDicomValue.getValue() != null)
					strValue = DataTypeConvertor.dicomDatatoString(objIDicomValue);
				insertInstanceDEInfo(strTableName,strSOPInstanceUID,objIDicomValue.getIVRType(), objDataElement.getTag().getGroupNumber(),objDataElement.getTag().getElementNumber(), false, null, strValue, null,null);
			}
			else
			{
				String strSQId = StandardMethods.generateUUID();
				insertInstanceDEInfo(strTableName,strSOPInstanceUID,objIDicomValue.getIVRType(), objDataElement.getTag().getGroupNumber(),objDataElement.getTag().getElementNumber(), true, null, null, strSQId,null);
				insertSQInfo(strTableName,objIDicomValue,strSOPInstanceUID,strSQId,null);
			}
		}
		return true;
	}

	
	/**
	 * Checks whether provided SOP Instance is created or not.
	 * @param strSOPInstance value of SOP Instance UID 
	 * @return boolean shows status as success or failure
	 * @throws SQLException if sql operation fails
	 */
	boolean isSOPInstanceExist(String strSOPInstance)throws SQLException
	{
		String query = "Select Value from DISPLAY_SYSTEM_DEINFO where SOPInstanceUID = '"+ strSOPInstance + "'";
		PreparedStatement ps = objcon.prepareStatement(query);
		ResultSet rs = ps.executeQuery();
		if(rs.next())
			return true;

		return false;
	}

	/**
	 * Retrieves the information of a given SOP Instance UID and populates the Dataset.
	 * @param objIDataSet instance of input Dataset
	 * @param strSOPInstanceUID value of SOPInstance UID
	 * @return objIDataSet instance of resultant Dataset
	 * @throws SQLException if SQL operation fails
	 */
	IDataSet retrieveDisplaySystemInstanceInfo(ISingleCollection<ITag> objITagCollection,String strSOPInstanceUID)throws SQLException
	{
		String strTablename = "DISPLAY_SYSTEM_DEINFO"; 
		IDataSet objResDataset = new DataSet();
		ISingleCollection<DisplaySystem_RowDescriptor> objRowCollection = populateRows(strTablename,strSOPInstanceUID);
		if(objRowCollection.size() == 0)
			return null;
		generateDataSet(objResDataset,objRowCollection,objITagCollection);
		return objResDataset;
	}
	


	/**
	 * Inserts DataElement of a sequence and all its Items.
	 */
	private boolean insertSQInfo(String strTableName,IDicomValue objIDicomValue,String strSOPInstanceUID,String strSQUUid,String strParentSQID)throws SQLException
	{
		SQ objSQDicomValue = (SQ)objIDicomValue;
		ICollectionIterator<IItem> objSQItemIterator = objSQDicomValue.iterator();
		String strValue = null;
		while(objSQItemIterator.hasNext())
		{
			/*
			 * Iterating over Items of provided sequence and inserting DataElements which are not SQ.
			 * for SQ it recursively calls itself with changed SQUUID and ParentSQID.
			 * A sequence and all 
			 */
			IItem objItem = objSQItemIterator.next();
			String strItemUUID = StandardMethods.generateUUID();
			ICollectionIterator<IDataElement> objDEIterator = objItem.iterator();
			while(objDEIterator.hasNext())
			{
				IDataElement objDataElement = objDEIterator.next();
				IDicomValue objTempDicomValue = objDataElement.getDicomValue();
				if(!(objTempDicomValue instanceof SQ))
				{
					if(objTempDicomValue.getValue() != null)
						strValue = DataTypeConvertor.dicomDatatoString(objTempDicomValue);
					insertInstanceDEInfo(strTableName,strSOPInstanceUID,objTempDicomValue.getIVRType(), objDataElement.getTag().getGroupNumber(),objDataElement.getTag().getElementNumber(), false, strParentSQID, strValue, strSQUUid,strItemUUID);
				}
				else
				{
					String strSQId = StandardMethods.generateUUID();
					insertInstanceDEInfo(strTableName,strSOPInstanceUID,objTempDicomValue.getIVRType(), objDataElement.getTag().getGroupNumber(),objDataElement.getTag().getElementNumber(), true, strSQUUid, null, strSQId,strItemUUID);
					insertSQInfo(strTableName,objTempDicomValue, strSOPInstanceUID,strSQId,strSQUUid);
				}
			}
		}
		return true;
	}
	/**
	 * Inserts the information of DataElement in Database
	 */
	private boolean insertInstanceDEInfo(String strTableName,String strSOPInstanceUID,int iVRType,int iGroupNumber,int iElementNumber,boolean isSQ,String strParentSQID,String strValue,String strSQID,String strItemID)throws SQLException
	{
		String strQuery = "insert into " +strTableName+" (DERowID,SOPInstanceUID,VRType,GroupNumber,ElementNumber,IsSQ,ParentSQUID,Value,SQUUID,ItemUUID) values (?,?,?,?,?,?,?,?,?,?)";

		PreparedStatement ps = objcon.prepareStatement(strQuery);
		ps.setString(1,StandardMethods.generateUUID());
		ps.setString(2,strSOPInstanceUID);
		ps.setInt(3,iVRType);
		ps.setInt(4,iGroupNumber);
		ps.setInt(5,iElementNumber);
		if(isSQ)
			ps.setString(6,"Y");
		else
			ps.setString(6,"N");
		ps.setString(7,strParentSQID);
		ps.setString(8,strValue);
		ps.setString(9,strSQID);
		ps.setString(10,strItemID);
		ps.executeUpdate();

		return true;
	}

	/**
	 * Populates the collection of RowDescriptor for a given SOP Instance UID
	 */
	private ISingleCollection<DisplaySystem_RowDescriptor> populateRows(String strTableName,String strSOPInstanceUID)throws SQLException
	{
		ISingleCollection<DisplaySystem_RowDescriptor> objRowCollection = new SingleCollection<DisplaySystem_RowDescriptor>();
		String query = "Select * from " + strTableName + " where SOPInstanceUID = '"+ strSOPInstanceUID + "'";
		PreparedStatement ps = objcon.prepareStatement(query);
		ResultSet rs = ps.executeQuery();
		DisplaySystem_RowDescriptor objRowDescriptor = null;
		while(rs.next())
		{
			objRowDescriptor = new DisplaySystem_RowDescriptor();
			objRowDescriptor.setDERowID(rs.getString("DERowID"));
			objRowDescriptor.setVRType(rs.getInt("VRType"));
			objRowDescriptor.setGroupNumber(rs.getInt("GroupNumber"));
			objRowDescriptor.setElementNumber(rs.getInt("ElementNumber"));
			String isSQ = rs.getString("IsSQ");
			if(isSQ.equalsIgnoreCase("Y"))
				objRowDescriptor.setSQ(true);
			else
				objRowDescriptor.setSQ(false);
			objRowDescriptor.setParentSQUID(rs.getString("ParentSQUID"));
			objRowDescriptor.setValue(rs.getString("Value"));
			objRowDescriptor.setSQUID(rs.getString("SQUUID"));
			objRowDescriptor.setItemUID(rs.getString("ItemUUID"));
			objRowCollection.add(objRowDescriptor);
		}
		return objRowCollection;
	}

	/**
	 * Populates the Dataset from given collection of Data elements Rows according to the queried Dataset.
	 */
	private void generateDataSet(IDataSet objIDataSet,ISingleCollection<DisplaySystem_RowDescriptor> objRowsCollection,ISingleCollection<ITag> objITagCollection)
	{
		IDictionaryReader objIDictionaryReader = objIDataSet.getDictionaryReader();
		ITransferSyntax objITransferSyntax = objIDataSet.getTransferSyntax();
		IDataElement objIDataElement = null;
		int iGroupNumber,iElementNumber;

		populateFirstLevelDE(objIDataSet, objRowsCollection,objITagCollection);

		ICollectionIterator<DisplaySystem_RowDescriptor> objRowsIterator = objRowsCollection.iterator();
		while(objRowsIterator.hasNext())
		{
			DisplaySystem_RowDescriptor objRow = objRowsIterator.next();
			iGroupNumber = objRow.getGroupNumber();
			iElementNumber = objRow.getElementNumber();
			if(objRow.isSQ() && objRow.getParentSQUID() == null)
			{
				IDataTypeSQ objSQ = DicomV3Factory.createInstance().createSequence(iGroupNumber, iElementNumber, objIDataSet.getTransferSyntax());

				objRowsCollection.remove(objRow);
				//Populates the all Items of this sequence 
				createSequence(objSQ,objRow.getSQUID(),objRowsCollection);
				//Adding the SQ Item in Dataset
				objRowsIterator = objRowsCollection.iterator();
				if(objITagCollection == null || objITagCollection.contains(new Tag(iGroupNumber, iElementNumber)))
				{
					//Adding the SQ Item in Dataset
					if(objITransferSyntax.getVRTransferSyntax()==DicomConstants.DCM_VR_IMPLICIT)
						objIDataElement = new ImplicitDE(iGroupNumber, iElementNumber,objSQ,objSQ.getTransferSyntax(),objIDictionaryReader);
					else
						objIDataElement = new ExplicitDE(iGroupNumber,iElementNumber,DicomConstants.DCM_DT_SQ,objSQ);
					objIDataSet.addDataElement(objIDataElement);
				}
			}
		}
	}

	/**
	 * Populates the first level Data elements in Dataset
	 */
	private void populateFirstLevelDE(IDataSet objRstDataSet,ISingleCollection<DisplaySystem_RowDescriptor> objDisplaySystem_RowsList,ISingleCollection<ITag> objITagCollection)
	{
		IDictionaryReader objIDictionaryReader = objRstDataSet.getDictionaryReader();
		ITransferSyntax objITransferSyntax = objRstDataSet.getTransferSyntax();
		IDataElement objIDataElement = null;
		int iVRType,iGroupNumber,iElementNumber;
		ICollectionIterator<DisplaySystem_RowDescriptor> objIterator = objDisplaySystem_RowsList.iterator();
		while(objIterator.hasNext())
		{
			DisplaySystem_RowDescriptor objRow = objIterator.next();
			iGroupNumber = objRow.getGroupNumber();
			iElementNumber = objRow.getElementNumber();
			iVRType = objRow.getVRType();
			if(!objRow.isSQ() && objRow.getItemUID() == null)
			{
				if(objITagCollection == null || objITagCollection.contains(new Tag(iGroupNumber, iElementNumber)))
				{
					objIDataElement = DataTypeConvertor.stringtoDataElement(objRow.getValue(),iGroupNumber,iElementNumber,iVRType,objITransferSyntax,objIDictionaryReader);

					objRstDataSet.addDataElement(objIDataElement);
				}
				objDisplaySystem_RowsList.remove(objRow);
				objIterator = objDisplaySystem_RowsList.iterator();
			}
		}
	}

	/**
	 * Populates the Dataset with a Sequence and all its Items information 
	 */
	private void createSequence(IDataTypeSQ objSQ,String strSQUID,ISingleCollection<DisplaySystem_RowDescriptor> objDisplaySystem_RowsList)
	{
		ICollectionIterator<DisplaySystem_RowDescriptor> objRowsIterator = objDisplaySystem_RowsList.iterator();
		while(objRowsIterator.hasNext())
		{
			DisplaySystem_RowDescriptor objRow = objRowsIterator.next();
			if(!objRow.isSQ() && objRow.getSQUID() != null && objRow.getSQUID().equals(strSQUID))
			{
				IItem objItem = objSQ.getNewItem();
				createSQItems(objItem, objRow.getItemUID(),objDisplaySystem_RowsList);
				objRowsIterator = objDisplaySystem_RowsList.iterator();
				objSQ.addItem(objItem);
			}
		}
	}

	/**
	 * Populates the Item with information of all its DataElements 
	 */
	private void createSQItems(IItem objItem , String strItemUid,ISingleCollection<DisplaySystem_RowDescriptor> objDisplaySystem_RowsList)
	{
		int iVRType,iGroupNumber,iElementNumber;
		IDictionaryReader objIDictionaryReader = objItem.getDictionaryReader();
		ITransferSyntax objITransferSyntax = objItem.getTransferSyntax();
		IDataElement objIDataElement = null;
		ICollectionIterator<DisplaySystem_RowDescriptor> objRowsIterator = objDisplaySystem_RowsList.iterator();
		while(objRowsIterator.hasNext())
		{
			DisplaySystem_RowDescriptor objRow = objRowsIterator.next();
			iGroupNumber = objRow.getGroupNumber();
			iElementNumber = objRow.getElementNumber();
			iVRType = objRow.getVRType();
			if(objRow.getItemUID() != null && objRow.getItemUID().equals(strItemUid))
			{
				if(!objRow.isSQ())
				{
					objIDataElement = DataTypeConvertor.stringtoDataElement(objRow.getValue(),iGroupNumber,iElementNumber,iVRType,objITransferSyntax,objIDictionaryReader);
					objItem.addDataElement(objIDataElement);

					objDisplaySystem_RowsList.remove(objRow);
					objRowsIterator = objDisplaySystem_RowsList.iterator();
				}
				else if(objRow.isSQ())
				{
					IDataTypeSQ objSQ = DicomV3Factory.createInstance().createSequence(iGroupNumber, iElementNumber, objItem.getTransferSyntax());
					objDisplaySystem_RowsList.remove(objRow);
					//Populates the all Items of this sequence 
					createSequence(objSQ,objRow.getSQUID(),objDisplaySystem_RowsList);
					objRowsIterator = objDisplaySystem_RowsList.iterator();
					//Adding the SQ DicomValue in Item
					if(objITransferSyntax.getVRTransferSyntax()==DicomConstants.DCM_VR_IMPLICIT)
						objIDataElement = new ImplicitDE(iGroupNumber, iElementNumber,objSQ,objSQ.getTransferSyntax(),objIDictionaryReader);
					else
						objIDataElement = new ExplicitDE(iGroupNumber,iElementNumber,DicomConstants.DCM_DT_SQ,objSQ);
					objItem.addDataElement(objIDataElement);
				}
			}
		}
	}
}
