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

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;


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.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.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;

/**
 * This class interacts with database and performs queries fired.
 */
class RelevantPatientInfoQry_DBQuery 
{
	
	private Connection objcon = null;

	//This set used in recursive method for retrieving matching RelevantPatientInfoQryDATASETUUIDs in Sequence.
	private Set<String> setIntersectedDatasetUIDList;// = new TreeSet<String>();

	/**
	 * Sets Connection Object for used for querying Database.
	 * @param objcon object of Connection
	 */
	void setConnection(Connection objcon)
	{
		this.objcon = objcon;
	}

	boolean storeInstanceInfo(IDataSet objIDataSet)throws SQLException
	{
		objcon.setAutoCommit(false);
		String strDatasetUUID = StandardMethods.generateUUID(); 

		String strValue = null;
		int iGroupNo,iElementNo,iVRType;
		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();
			iGroupNo = objDataElement.getTag().getGroupNumber();
			iElementNo = objDataElement.getTag().getElementNumber();
			iVRType = objIDicomValue.getIVRType();
			if(!(objIDicomValue instanceof SQ))
			{
				if(objIDicomValue.getValue() != null)
					strValue = DataTypeConvertor.dicomDatatoString(objIDicomValue);
				insertRelevantPatientInfoQryInstanceInfo(strDatasetUUID,iGroupNo ,iElementNo , false, null, strValue, null, null,0, iVRType);
			}
			else
			{
				String strSQId = StandardMethods.generateUUID();
				insertRelevantPatientInfoQryInstanceInfo(strDatasetUUID,iGroupNo ,iElementNo , true, null, null, strSQId, null, 0, iVRType);
				insertSQInfo(objIDicomValue,strDatasetUUID,strSQId,null,1);
			}
		}
		objcon.commit();
		objcon.setAutoCommit(true);

		return true;

	}


	/**
	 * Inserts DataElement of a sequence and all its Items.
	 */
	private boolean insertSQInfo(IDicomValue objIDicomValue,String strDatasetUID,String strSQUUid,String strParentSQID,int iDELevel)throws SQLException
	{
		SQ objSQDicomData = (SQ)objIDicomValue;
		ICollectionIterator<IItem> objSQItemIterator = objSQDicomData.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();
			int iGroupNo,iElementNo,iVRType;
			ICollectionIterator<IDataElement> objDEIterator = objItem.iterator();
			while(objDEIterator.hasNext())
			{
				IDataElement objDataElement = objDEIterator.next();
				IDicomValue objTempDicomData = objDataElement.getDicomValue();
				iGroupNo = objDataElement.getTag().getGroupNumber();
				iElementNo = objDataElement.getTag().getElementNumber();
				iVRType = objTempDicomData.getIVRType();
				if(!(objTempDicomData instanceof SQ))
				{
					if(objTempDicomData.getValue() != null)
						strValue = DataTypeConvertor.dicomDatatoString(objTempDicomData);
					insertRelevantPatientInfoQryInstanceInfo(strDatasetUID,iGroupNo,iElementNo, false, strParentSQID, strValue, strSQUUid, strItemUUID, iDELevel, iVRType);
				}
				else
				{
					String strSQId = StandardMethods.generateUUID();
					insertRelevantPatientInfoQryInstanceInfo(strDatasetUID,iGroupNo,iElementNo, true, strSQUUid, null, strSQId,strItemUUID, iDELevel, iVRType);
					insertSQInfo(objTempDicomData,strDatasetUID,strSQId,strSQUUid,++iDELevel);
				}
			}
		}
		return true;
	}

	/**
	 * Inserts the information of DataElement in Database
	 */
	private boolean insertRelevantPatientInfoQryInstanceInfo(String strDatasetUUID,int iGroupNumber,int iElementNumber,boolean isSQ,String strParentSQID,String strValue,
			String strSQID,String strItemID,int iDELevel,int iDEVR)throws SQLException
			{
		String strQuery = "insert into RelevantPatientInfoQry_DEINFO(DEROWID,DATASETUUID,GROUPNUMBER,ELEMENTNUMBER,ISSQ,PARENTSQUID,VALUE,SQUUID,ITEMUUID,DELEVEL,DEVR) " +
		"values(?,?,?,?,?,?,?,?,?,?,?)";

		PreparedStatement ps = objcon.prepareStatement(strQuery);
		ps.setString(1,StandardMethods.generateUUID());
		ps.setString(2, strDatasetUUID);
		ps.setInt(3,iGroupNumber);
		ps.setInt(4,iElementNumber);
		if(isSQ)
			ps.setInt(5, 1);
		else
			ps.setInt(5, 0);
		ps.setString(6,strParentSQID);
		ps.setString(7, strValue);
		ps.setString(8,strSQID);
		ps.setString(9,strItemID);
		ps.setInt(10,iDELevel);
		ps.setInt(11,iDEVR);
		ps.executeUpdate();
		return true;
			}


	/**
	 * Depending on the keys in the IDataSet it finds the matching DataSets and returns status.
	 * According to the matched DataSets it populates the Dataset collection taken as out parameter.
	 * 
	 * @param objIDataSet The data set containing identifiers.
	 * @param objRstDataCollection the matched data set collection
	 * 
	 * @return	<tt>true</tt> if one or more matches are found, otherwise
	 * 			<tt>false</tt>
	 */
	boolean find(IDataSet objIDataSet, ISingleCollection<IDataSet> objRstDataCollection) throws SQLException 
	{
		ICollectionIterator<IDataElement> objIDataSetIterator = objIDataSet.iterator();

		while(objIDataSetIterator.hasNext())
		{
			IDataElement objDataElement = objIDataSetIterator.next();				
			int iGroupNumber = objDataElement.getTag().getGroupNumber();
			int iElementNumber = objDataElement.getTag().getElementNumber();

			if(!(objDataElement.getDicomValue() instanceof SQ))
			{
				if(null != objDataElement.getDicomValue().toString())
				{
					Statement objStmt = objcon.createStatement();
					Set<String> setCurrentResultDatasetUIDList = new HashSet<String>();
					String strQuery = 						  
						"Select DISTINCT DATASETUUID from RelevantPatientInfoQry_DEINFO where GROUPNUMBER = "+iGroupNumber +""
						+ "and ELEMENTNUMBER = "+iElementNumber+""
						+ "and VALUE ='"+DataTypeConvertor.dicomDatatoString(objDataElement.getDicomValue())+"'" 
						+ "and ITEMUUID is NULL and SQUUID is NULL and ISSQ = 0 and PARENTSQUID is NULL and DELEVEL=0";
					// Find all RelevantPatientInfoQryDATASETUUIDs Which has this DataElement and Value
					ResultSet itemsRecordSet = objStmt.executeQuery(strQuery);
					while(itemsRecordSet.next())
					{
						setCurrentResultDatasetUIDList.add(itemsRecordSet.getString("DATASETUUID"));
					}

					itemsRecordSet.close();
					objStmt.close();

					if(null == setIntersectedDatasetUIDList && setCurrentResultDatasetUIDList.size()>0)
						setIntersectedDatasetUIDList = new TreeSet<String>(setCurrentResultDatasetUIDList);
					else if(null != setIntersectedDatasetUIDList && setIntersectedDatasetUIDList.size()>0&& setCurrentResultDatasetUIDList.size()>0)
						setIntersectedDatasetUIDList.retainAll(setCurrentResultDatasetUIDList);

				}
			}
			else  
			{	
				//Add RelevantPatientInfoQryDATASETUUIDs for Sequences
				//get RelevantPatientInfoQryDATASETUUIDs  having matching records in Sequence Attributes.	
				//Set<String> setRelevantPatientInfoQryDatasetuuidsForSequence = 
				getRelevantPatientInfoQryDatasetUIDBySequence((SQ)objDataElement.getDicomValue(),1); //SQ,DELEVEL
			}			
		}

		// For RelevantPatientInfoQryDATASETUUID retrieve DataSet
		if(null != setIntersectedDatasetUIDList)
		{
			Iterator<String> objIntersectedDatasetUIDIterator = setIntersectedDatasetUIDList.iterator();
			while (objIntersectedDatasetUIDIterator.hasNext())
			{			
				objRstDataCollection.add(retrieveRelevantPatientInfoQryInstanceInfo(objIDataSet,objIntersectedDatasetUIDIterator.next().toString()));
			}
		}
		return true;
	}

	private void getRelevantPatientInfoQryDatasetUIDBySequence(SQ objSQDataElement,int iDELevel) throws SQLException
	{
		ICollectionIterator<IItem> objSQItemIterator = objSQDataElement.iterator();

		while(objSQItemIterator.hasNext())
		{
			IItem objItem = objSQItemIterator.next();
			ICollectionIterator<IDataElement> objDataElementIterator = objItem.iterator();
			while(objDataElementIterator.hasNext())
			{
				IDataElement objDataElement = objDataElementIterator.next();							
				int iGroupNumber = objDataElement.getTag().getGroupNumber();
				int iElementNumber = objDataElement.getTag().getElementNumber();

				if(!(objDataElement.getDicomValue() instanceof SQ))
				{
					if(null != objDataElement.getDicomValue().toString())
					{
						Statement objStmt = objcon.createStatement();
						Set<String> settmpCurrentResultDatasetUIDList = new HashSet<String>();
						String strQuery = 						  
							"Select DISTINCT DATASETUUID from RelevantPatientInfoQry_DEINFO where GROUPNUMBER = "+iGroupNumber +""
							+ "and ELEMENTNUMBER = "+iElementNumber+""
							+ "and VALUE ='"+ DataTypeConvertor.dicomDatatoString(objDataElement.getDicomValue())+"'" 
							+ "and DELEVEL = "+ iDELevel;
						// Find all RelevantPatientInfoQryDATASETUUIDs Which has this DataElement and Value
						ResultSet itemsRecordSet = objStmt.executeQuery(strQuery);					

						while(itemsRecordSet.next())
						{
							settmpCurrentResultDatasetUIDList.add(itemsRecordSet.getString("DATASETUUID"));
						}

						itemsRecordSet.close();
						objStmt.close();

						if(null == setIntersectedDatasetUIDList && settmpCurrentResultDatasetUIDList.size()>0)
							setIntersectedDatasetUIDList = new TreeSet<String>(settmpCurrentResultDatasetUIDList);
						else if(null != setIntersectedDatasetUIDList && setIntersectedDatasetUIDList.size()>0 && settmpCurrentResultDatasetUIDList.size()>0)
							setIntersectedDatasetUIDList.retainAll(settmpCurrentResultDatasetUIDList);
					}
				}
				else
				{
					getRelevantPatientInfoQryDatasetUIDBySequence((SQ)objDataElement.getDicomValue(),++iDELevel);
				}				
			}
		}
	}


	private IDataSet retrieveRelevantPatientInfoQryInstanceInfo(IDataSet objQryDataSet,String strDatasetUID)throws SQLException
	{
		IDataSet objResDataset = new DataSet(objQryDataSet.getTransferSyntax(),objQryDataSet.getDictionaryReader());
		ISingleCollection<RelevantPatientInfoQry_RowDescriptor> objRowCollection = populateRows(strDatasetUID);
		generateDataSet(objResDataset,objRowCollection,objQryDataSet);
		return objResDataset;
	}

	/**
	 * Populates the collection of RowDescriptor for a given Data set UID
	 * @return ISingleCollection<RelevantPatientInfoQry_RowDescriptor>
	 */
	private ISingleCollection<RelevantPatientInfoQry_RowDescriptor> populateRows(String strDatasetUID)throws SQLException
	{
		ISingleCollection<RelevantPatientInfoQry_RowDescriptor> objRowCollection = new SingleCollection<RelevantPatientInfoQry_RowDescriptor>();
		String query = "Select * from RelevantPatientInfoQry_DEINFO where DATASETUUID = '"+ strDatasetUID + "'";
		PreparedStatement ps = objcon.prepareStatement(query);
		ResultSet rs = ps.executeQuery();
		RelevantPatientInfoQry_RowDescriptor objRowDescriptor = null;

		while(rs.next())
		{
			objRowDescriptor = new RelevantPatientInfoQry_RowDescriptor();
			objRowDescriptor.setDEROWID(rs.getString("DEROWID"));
			objRowDescriptor.setDATASETUUID(rs.getString("DATASETUUID"));	
			objRowDescriptor.setGROUPNUMBER(rs.getInt("GROUPNUMBER"));
			objRowDescriptor.setELEMENTNUMBER(rs.getInt("ELEMENTNUMBER"));

			int isSQ = rs.getInt("ISSQ");
			if(1 == isSQ)				
				objRowDescriptor.setSQ(true);
			else
				objRowDescriptor.setSQ(false);			

			objRowDescriptor.setPARENTSQUID(rs.getString("PARENTSQUID"));
			objRowDescriptor.setVALUE(rs.getString("VALUE"));
			objRowDescriptor.setSQUUID(rs.getString("SQUUID"));
			objRowDescriptor.setITEMUUID(rs.getString("ITEMUUID"));
			objRowDescriptor.setDELEVEL(rs.getInt("DELEVEL"));
			objRowDescriptor.setDEVR(rs.getInt("DEVR"));

			objRowCollection.add(objRowDescriptor);
		}

		return objRowCollection;
	}

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

		populateFirstLevelDE(objIDataSet, objRowsCollection,objQryDataSet);

		ICollectionIterator<RelevantPatientInfoQry_RowDescriptor> objRowsIterator = objRowsCollection.iterator();
		while(objRowsIterator.hasNext())
		{
			RelevantPatientInfoQry_RowDescriptor objRow = objRowsIterator.next();
			iGroupNumber = objRow.getGROUPNUMBER();
			iElementNumber = objRow.getELEMENTNUMBER();
			//	iVRType = objRow.getDEVR();
			if(objRow.isSQ() && objRow.getPARENTSQUID() == null)
			{
				IDataTypeSQ objSQ = DicomV3Factory.createInstance().createSequence(iGroupNumber, iElementNumber, objIDataSet.getTransferSyntax());
				objRowsCollection.remove(objRow);
				createSequence(objSQ,objRow.getSQUUID(),objRowsCollection);
				objRowsIterator = objRowsCollection.iterator();
				if(objQryDataSet == null || objQryDataSet.isPresent(iGroupNumber, iElementNumber))
				{
					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);
					//	objIDataSet.addDataElement(StandardMethods.stringtoIDataElement(objRow.getVALUE(), iGroupNumber, iElementNumber, iVRType, objITransferSyntax, objIDictionaryReader));
				}
			}
		}
	}

	/**
	 * Populates the first level Data elements in Data set
	 */
	private void populateFirstLevelDE(IDataSet objRstDataSet,ISingleCollection<RelevantPatientInfoQry_RowDescriptor> objobjRelevantPatientInfoQry_RowsList,IDataSet objQryDataSet)
	{
		IDictionaryReader objIDictionaryReader = objRstDataSet.getDictionaryReader();
		ITransferSyntax objITransferSyntax = objRstDataSet.getTransferSyntax();
		//	IDataElement objIDataElement = null;
		int iVRType,iGroupNumber,iElementNumber;
		ICollectionIterator<RelevantPatientInfoQry_RowDescriptor> objIterator = objobjRelevantPatientInfoQry_RowsList.iterator();
		while(objIterator.hasNext())
		{
			RelevantPatientInfoQry_RowDescriptor objRow = objIterator.next();
			iGroupNumber = objRow.getGROUPNUMBER();
			iElementNumber = objRow.getELEMENTNUMBER();
			iVRType = objRow.getDEVR(); //StandardMethods.getiVRtype(objIDictionaryReader.getVR(iGroupNumber,iElementNumber));
			if(!objRow.isSQ() && objRow.getITEMUUID() == null)
			{
				if(objQryDataSet == null || objQryDataSet.isPresent(iGroupNumber, iElementNumber))
				{
					objRstDataSet.addDataElement(DataTypeConvertor.stringtoDataElement(objRow.getVALUE(), iGroupNumber, iElementNumber, iVRType, objITransferSyntax, objIDictionaryReader));	
				}
				objobjRelevantPatientInfoQry_RowsList.remove(objRow);
				objIterator = objobjRelevantPatientInfoQry_RowsList.iterator();
			}
		}
	}

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

	/** 
	 * Populates the Item with information of all its DataElements 
	 */
	private void createSQItems(IItem objItem , String strItemUid,ISingleCollection<RelevantPatientInfoQry_RowDescriptor> objRelevantPatientInfoQry_RowsList)
	{
		int iVRType,iGroupNumber,iElementNumber;
		IDictionaryReader objIDictionaryReader = objItem.getDictionaryReader();
		ITransferSyntax objITransferSyntax = objItem.getTransferSyntax();
		IDataElement objIDataElement = null;
		ICollectionIterator<RelevantPatientInfoQry_RowDescriptor> objRowsIterator = objRelevantPatientInfoQry_RowsList.iterator();
		while(objRowsIterator.hasNext())
		{
			RelevantPatientInfoQry_RowDescriptor objRow = objRowsIterator.next();
			iGroupNumber = objRow.getGROUPNUMBER();
			iElementNumber = objRow.getELEMENTNUMBER();
			iVRType = 	objRow.getDEVR();		//StandardMethods.getiVRtype(objIDictionaryReader.getVR(iGroupNumber,iElementNumber));
			if(objRow.getITEMUUID()!= null && objRow.getITEMUUID().equals(strItemUid))
			{
				if(!objRow.isSQ())
				{
					objItem.addDataElement(DataTypeConvertor.stringtoDataElement(objRow.getVALUE(), iGroupNumber, iElementNumber, iVRType, objITransferSyntax, objIDictionaryReader));

					objRelevantPatientInfoQry_RowsList.remove(objRow);
					objRowsIterator = objRelevantPatientInfoQry_RowsList.iterator();
				}
				else if(objRow.isSQ())
				{
					IDataTypeSQ objSQ = DicomV3Factory.createInstance().createSequence(iGroupNumber, iElementNumber, objItem.getTransferSyntax());
					objRelevantPatientInfoQry_RowsList.remove(objRow);
					createSequence(objSQ,objRow.getSQUUID(),objRelevantPatientInfoQry_RowsList);
					objRowsIterator = objRelevantPatientInfoQry_RowsList.iterator();
					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);
				}
			}
		}
	}
}