/*******************************************************************************
 * Copyright 2014 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 in.cdac.medinfo.csnotk.csnolib.test;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import in.cdac.medinfo.csnotk.csnolib.agents.SNOMEDAgent;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumDirection;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumRelationshipType;
import in.cdac.medinfo.csnotk.csnolib.model.Concept;
import in.cdac.medinfo.csnotk.csnolib.model.Relationship;
//Logs
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import in.cdac.medinfo.csnotk.csnolib.util.LoggerUtility;


/**
 * SampleforExploreAPI class contains the sample code for implementing the
 * explore API. Explore API includes- retrieving parent Concepts, child Concepts
 * and descendants.
 */
public class SNOMEDAgentExploreAPI {
	/**
	 * Main method
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			SNOMEDAgent objAgent = new SNOMEDAgent();
			objAgent.init();
            Logger logger = LogManager.getLogger(SNOMEDAgentExploreAPI.class);
			logger.info(LoggerUtility.CONSOLE, "Do you want to create SNOMED CT database (y/n)");
			BufferedReader serviceInput = new BufferedReader(
					new InputStreamReader(System.in));

				String dbOverride = serviceInput.readLine();
				if(dbOverride.equalsIgnoreCase("y"))
				{
					logger.info(LoggerUtility.CONSOLE, "Please Enter SNOMED CT Release file path till Snapshot");
					String snomedDirPath=serviceInput.readLine();
					boolean check=objAgent.createSNOMEDdb(snomedDirPath, true);
					if(check){
						logger.info(LoggerUtility.CONSOLE, "Do you want to create SNOMED CT Refset database (y/n)");
						String refsetDbOverride = serviceInput.readLine();
						if(refsetDbOverride.equalsIgnoreCase("y"))
						{
							objAgent.createSNOMEDRefsetdb(snomedDirPath, true);
						}
					}

				}
				logger.info(LoggerUtility.CONSOLE, "Do you want to create SNOMED CT Extension database (y/n)");


					String extDbOverride = serviceInput.readLine();
					if(extDbOverride.equalsIgnoreCase("y"))
					{
						logger.info(LoggerUtility.CONSOLE, "Please Enter SNOMED CT Extension Release directory path till Snapshot");
						String snomedExtDirPath=serviceInput.readLine();
						objAgent.createExtensiondb(snomedExtDirPath, true);
					}


			logger.info(LoggerUtility.CONSOLE, "Sample for Explore API. Please select the service you want to use\n"
					+"1.Retrieve all descendants\n"
					+"2.Retrieve all ancestors \n"
					+"3.Retrieve all parents\n"
					+"4.Retrieve all children\n"
					+"5.Retrieve all concepts by attribute type\n"
					+"6.Retrieve all concepts by attribute type and semantic tag\n"
					+"7.Retrieve all concepts by attribute type and destination id\n"
					+"8.Retrieve all concepts by attribute type, destination id and semantic tag\n"
					+ "Please enter any of the above options:");
			/**
			 * serviceInput takes input from the user. It specifies the service
			 * the user wants to use.
			 */

			//converts the user input to integer value
			String sInput = serviceInput.readLine();

			if(sInput.isEmpty())
				throw new IllegalArgumentException("Error:Invalid option");
			else if(!sInput.matches("\\d"))
				throw new IllegalArgumentException("Error:Invalid option, only numbers are permitted");
			/**
			 * Instance of SNOMEDAgent class
			 */


			//switch case that processes based on the service
			//selected by the user.
			switch (Integer.parseInt(sInput)) {
			case 1: {
				logger.info(LoggerUtility.CONSOLE, "Please enter Concept Id:");
				/**
				 * conceptIdinput takes value of Concept Id from the user
				 */
				BufferedReader conceptIdinput = new BufferedReader(
						new InputStreamReader(System.in));
				/**
				 * conceptId refers to Id of a particular SNOMED CT Concept
				 */
				String conceptId = conceptIdinput.readLine().trim();// "243120004";


				/**
				 * getAllDescendentConcepts returns the list of SNOMED CT
				 * Concepts that are descendants [immediate children and
				 * grand-children] of the specified SNOMED CT Concept Id
				 */
				Set<Concept> lstDescendants = objAgent
						.getAllDescendantConcepts(conceptId);

				if(null==lstDescendants){
					logger.info(LoggerUtility.CONSOLE, "Invalid Concept ID");
					break;
				}else if(lstDescendants!=null && lstDescendants.isEmpty()){
					logger.info(LoggerUtility.CONSOLE, "Descendant not present for given concept");
					break;
				}



				// Prints the list of descendant Concepts
				logger.info(LoggerUtility.CONSOLE, "Descendants:");
				for (Concept concept : lstDescendants) {
					logger.info(LoggerUtility.CONSOLE, "ID: {} FSN: {}" , concept.getId()
							, concept.getFullySpecifiedName());
				}
				logger.info(LoggerUtility.CONSOLE, "No. of Descendants for {} are : {}\n", conceptId
						, lstDescendants.size());
				break;
			}
			case 2: {
				logger.info(LoggerUtility.CONSOLE, "Please enter Concept Id:");
				/**
				 * conceptIdinput takes value of Concept Id from the user
				 */
				BufferedReader conceptIdinput = new BufferedReader(
						new InputStreamReader(System.in));
				/**
				 * conceptId refers to Id of a particular SNOMED CT Concept
				 */
				String conceptId = conceptIdinput.readLine().trim();// "243120004";


				/**
				 * getAllAncestorConcepts returns the list of SNOMED CT
				 * Concepts that are ancestors [immediate parent and
				 * grand-parent] of the specified SNOMED CT Concept Id
				 */
				Set<Concept> lstAncestors = objAgent
						.getAllAncestorConcepts(conceptId);

				if(null==lstAncestors){
					logger.info(LoggerUtility.CONSOLE, "Invalid Concept ID");
					break;
				}else if(lstAncestors!=null && lstAncestors.isEmpty()){
					logger.info(LoggerUtility.CONSOLE, "Descendant not present for given concept");
					break;
				}



				// Prints the list of descendant Concepts
				logger.info(LoggerUtility.CONSOLE, "Descendants:");
				for (Concept concept : lstAncestors) {
					logger.info(LoggerUtility.CONSOLE, "ID: {} FSN: {}" , concept.getId()
							, concept.getFullySpecifiedName());
				}
				logger.info(LoggerUtility.CONSOLE, "No. of Descendants for {} are : {}\n" , conceptId
						, lstAncestors.size());
				break;
			}
			case 3: {
				logger.info(LoggerUtility.CONSOLE, "Please enter Concept Id:");
				/**
				 * conceptIdinput takes value of Concept Id from the user
				 */
				BufferedReader conceptIdinput = new BufferedReader(
						new InputStreamReader(System.in));
				/**
				 * conceptId refers to Id of a particular SNOMED CT Concept
				 */
				String conceptId = conceptIdinput.readLine().trim();// "243120004";


				/**
				 * getParents returns the list of parent Concepts of the
				 * specified SNOMED CT Concept Id
				 */
				//Set<Concept> lstParents = objAgent.getParents(conceptId);
				Set<Concept> lstParents=objAgent.getRelations(conceptId,EnumRelationshipType.IS_A.getValue(), EnumDirection.SOURCE);

				if(null==lstParents || lstParents.size()==0){
					logger.info(LoggerUtility.CONSOLE, "Invalid Concept ID");
					break;
				}

				// Prints the list of parent Concepts
				logger.info(LoggerUtility.CONSOLE, "Parents:");
				for (Concept concept : lstParents) {
					logger.info(LoggerUtility.CONSOLE, "ID: {} FSN: {}" + concept.getId()
							, concept.getFullySpecifiedName());
				}
				logger.info(LoggerUtility.CONSOLE, "No. of Parents for {} are : {}\n" , conceptId
						, lstParents.size());
				break;
			}case 4: {
				logger.info(LoggerUtility.CONSOLE, "Please enter Concept Id:");
				/**
				 * conceptIdinput takes value of Concept Id from the user
				 */
				BufferedReader conceptIdinput = new BufferedReader(
						new InputStreamReader(System.in));
				/**
				 * conceptId refers to Id of a particular SNOMED CT Concept
				 */
				String conceptId = conceptIdinput.readLine().trim();// "243120004";


				/**
				 * getChildren returns the list of immediate child Concepts of
				 * the specified SNOMED CT Concept Id
				 */
				Set<Concept> lstChildren = objAgent.getRelations(conceptId,EnumRelationshipType.IS_A.getValue(), EnumDirection.DESTINATION);

				if(null==lstChildren || lstChildren.size()==0){
					logger.info(LoggerUtility.CONSOLE, "Invalid Concept ID");
					break;
				}

				// Prints the list of child Concepts
				logger.info(LoggerUtility.CONSOLE, "Children:");
				for (Concept concept : lstChildren) {
					logger.info(LoggerUtility.CONSOLE, "ID: {} FSN: {}" , concept.getId()
							, concept.getFullySpecifiedName());
				}
				logger.info(LoggerUtility.CONSOLE, "No. of Children for {} are : {}\n" , conceptId
						, lstChildren.size());
				break;
			}case 5:{
                logger.info(LoggerUtility.CONSOLE, "This api is used to retrieve concepts which match given attribute type\n"
                		+ "Parameter: attribute type\n"
                		+ "It refers to the attribute type in which results are to be searched for."
                		+ "\nPossible values are as follows-\n"
                		+ "causative agent, finding site, associated morphology,procedure site,"
                		+ "has intent,has active ingredient, due to,has active ingredient,etc\n"
                		+ "Please enter attribute type: ");
                String attributeName = serviceInput.readLine();


                if(attributeName!=null){
                	if(attributeName.isEmpty())
                		throw new IllegalArgumentException("ERROR:Attribute type can't be empty");
                	else if(!attributeName.matches("^[A-Za-z\\s\\-]*$"))
                		throw new IllegalArgumentException("ERROR:Invalid attribute type");
                }else
                	throw new IllegalArgumentException("ERROR:Attribute type can't be null");

                logger.info(LoggerUtility.CONSOLE, "Retrieving concepts, please wait...");
                List<Relationship> lstAttribute = objAgent.getConcepts(attributeName);

                if(lstAttribute!=null){
                //print list element
                for(Relationship relation: lstAttribute){
                	logger.info(LoggerUtility.CONSOLE, "Source Concept Id: {}--{};",relation.getSourceConcept().getId()
                ,relation.getSourceConcept().getFullySpecifiedName());
                }
                if(lstAttribute.isEmpty())
                	logger.info(LoggerUtility.CONSOLE, "No concepts found");
                else
                	logger.info(LoggerUtility.CONSOLE, "Total concepts found : {}",lstAttribute.size());
				}else
					logger.info(LoggerUtility.CONSOLE, "No concepts found");
                break;
			}case 6:{
                logger.info(LoggerUtility.CONSOLE, "This api is used to retrieve concepts which match given attribute type,"
                		+ " destination id and semantic in its relationship tag\n"
                		+ "Parameter: attribute type\n"
                		+ "It refers to the attribute type in which results are to be searched for."
                		+ "\nPossible values are as follows-\n"
                		+ "causative agent, finding site, associated morphology,procedure site,"
                		+ "has intent,has active ingredient, due to,has active ingredient,etc\n"
                		+ "Please enter attribute type: ");
                String attributeName = serviceInput.readLine();


                if(attributeName!=null){
                	if(attributeName.isEmpty())
                		throw new IllegalArgumentException("ERROR:Attribute type can't be empty");
                	else if(!attributeName.matches("^[A-Za-z\\s\\-]*$"))
                		throw new IllegalArgumentException("ERROR:Invalid attribute type");
                }else
                	throw new IllegalArgumentException("ERROR:Attribute type can't be null");


                logger.info(LoggerUtility.CONSOLE, "Parameter: semantictag\n"
						+ "It refers to the semantic tag in which the results are"
						+ "to be searched for. Pass multiple semantic by using ++ between two semantic tag."
						+ " Possible values are as follows-\n"
						+ "all - It will search for SNOMED CT terms in all semantic tags\n"
						+ "Other possible values are- procedure, disorder, finding,"
						+ " observable entity, body structure, organism, substance, "
						+ "specimen, special concept, linkage concept, physical force,"
						+ " event, environment, geographic location, social concept, "
						+ "situation, staging scale, physical object, qualifier value,"
						+ " record artifact, person, link assertion, namespace concept,"
						+ " attribute, assessment scale, racial group, tumor staging, "
						+ "occupation, morphologic abnormality, cell, cell structure, "
						+ "ethnic group, product, inactive concept, navigational concept,"
						+ " life style, regime/therapy, religion/philosophy."
						+ " \nPlease enter semantic tag:");

                List<String> semanticTagList = new ArrayList<String>();
                semanticTagList.addAll(Arrays.asList(serviceInput.readLine().split("[++]")));

                if(semanticTagList.isEmpty())
                	throw new IllegalArgumentException("Semantic tag can't be null");

                logger.info(LoggerUtility.CONSOLE, "Retrieving concepts, please wait...");
                List<Relationship> lstAttribute = objAgent.getConcepts(attributeName, semanticTagList);
                if(lstAttribute!=null){
                    //print list element
                    for(Relationship relation: lstAttribute){
                    	logger.info(LoggerUtility.CONSOLE, "Source Concept Id: {}--{};",relation.getSourceConcept().getId()
                    ,relation.getSourceConcept().getFullySpecifiedName());
                    }
                    if(lstAttribute.isEmpty())
                    	logger.info(LoggerUtility.CONSOLE, "No concepts found");
                    else
                    	logger.info(LoggerUtility.CONSOLE, "Total concepts found : {}",lstAttribute.size());
    				}else
    					logger.info(LoggerUtility.CONSOLE, "No concepts found");
                break;
			}case 7:{
                logger.info(LoggerUtility.CONSOLE, "This api is used to retrieve concepts which match given attribute type,"
                		+ " destination id and semantic in its relationship tag\n"
                		+ "Parameter: attribute type\n"
                		+ "It refers to the attribute type in which results are to be searched for."
                		+ "\nPossible values are as follows-\n"
                		+ "causative agent, finding site, associated morphology,procedure site,"
                		+ "has intent,has active ingredient, due to,has active ingredient,etc\n"
                		+ "Please enter attribute type: ");
                String attributeName = serviceInput.readLine();


                if(attributeName!=null){
                	if(attributeName.isEmpty())
                		throw new IllegalArgumentException("ERROR:Attribute type can't be empty");
                	else if(!attributeName.matches("^[A-Za-z\\s\\-]*$"))
                		throw new IllegalArgumentException("ERROR:Invalid attribute type");
                }else
                	throw new IllegalArgumentException("ERROR:Attribute type can't be null");


                logger.info(LoggerUtility.CONSOLE, "Enter Destination Id:");
                String destinationId=serviceInput.readLine();
                if(destinationId!=null){
                	if(destinationId.isEmpty())
                		throw new IllegalArgumentException("ERROR:Destination id can't be empty");
                	else if(!destinationId.matches("^[\\d]*$") || destinationId.length()>16 )
                		throw new IllegalArgumentException("ERROR:Invalid Destination id");
                	else if(destinationId.charAt(destinationId.length()-2)!='0')
                		throw new IllegalArgumentException("ERROR:Invalid Destination id");
                }else
                	throw new IllegalArgumentException("ERROR:Destination id can't be null");


                logger.info(LoggerUtility.CONSOLE, "Retrieving concepts, please wait...");
                List<Relationship> lstAttribute = objAgent.getConcepts(attributeName,destinationId);
                if(lstAttribute!=null){
                    //print list element
                    for(Relationship relation: lstAttribute){
                    	logger.info(LoggerUtility.CONSOLE, "Source Concept Id: {}--{};",relation.getSourceConcept().getId()
                    ,relation.getSourceConcept().getFullySpecifiedName());
                    }
                    if(lstAttribute.isEmpty())
                    	logger.info(LoggerUtility.CONSOLE, "No concepts found");
                    else
                    	logger.info(LoggerUtility.CONSOLE, "Total concepts found : {}",lstAttribute.size());
    				}else
    					logger.info(LoggerUtility.CONSOLE, "No concepts found");
                break;
			}case 8:{
                logger.info(LoggerUtility.CONSOLE, "This api is used to retrieve concepts which match given attribute type,"
                		+ " destination id and semantic in its relationship tag\n"
                		+ "Parameter: attribute type\n"
                		+ "It refers to the attribute type in which results are to be searched for."
                		+ "\nPossible values are as follows-\n"
                		+ "causative agent, finding site, associated morphology,procedure site,"
                		+ "has intent,has active ingredient, due to,has active ingredient,etc\n"
                		+ "Please enter attribute type: ");
                String attributeName = serviceInput.readLine();


                if(attributeName!=null){
                	if(attributeName.isEmpty())
                		throw new IllegalArgumentException("ERROR:Attribute type can't be empty");
                	else if(!attributeName.matches("^[A-Za-z\\s\\-]*$"))
                		throw new IllegalArgumentException("ERROR:Invalid attribute type");
                }else
                	throw new IllegalArgumentException("ERROR:Attribute type can't be null");


                logger.info(LoggerUtility.CONSOLE, "Enter Destination Id:");
                String destinationId=serviceInput.readLine();
                if(destinationId!=null){
                	if(destinationId.isEmpty())
                		throw new IllegalArgumentException("ERROR:Destination id can't be empty");
                	else if(!destinationId.matches("^[\\d]*$") || destinationId.length()>16 )
                		throw new IllegalArgumentException("ERROR:Invalid Destination id");
                	else if(destinationId.charAt(destinationId.length()-2)!='0')
                		throw new IllegalArgumentException("ERROR:Invalid Destination id");
                }else
                	throw new IllegalArgumentException("ERROR:Destination id can't be null");



                logger.info(LoggerUtility.CONSOLE, "Parameter: semantictag\n"
						+ "It refers to the semantic tag in which the results are"
						+ "to be searched for. Pass multiple semantic by using ++ between two semantic tag."
						+ " Possible values are as follows-\n"
						+ "all - It will search for SNOMED CT terms in all semantic tags\n"
						+ "Other possible values are- procedure, disorder, finding,"
						+ " observable entity, body structure, organism, substance, "
						+ "specimen, special concept, linkage concept, physical force,"
						+ " event, environment, geographic location, social concept, "
						+ "situation, staging scale, physical object, qualifier value,"
						+ " record artifact, person, link assertion, namespace concept,"
						+ " attribute, assessment scale, racial group, tumor staging, "
						+ "occupation, morphologic abnormality, cell, cell structure, "
						+ "ethnic group, product, inactive concept, navigational concept,"
						+ " life style, regime/therapy, religion/philosophy."
						+ " \nPlease enter semantic tag:");

                List<String> semanticTagList = new ArrayList<String>();
                semanticTagList.addAll(Arrays.asList(serviceInput.readLine().split("[++]")));

                if(semanticTagList.isEmpty())
                	throw new IllegalArgumentException("Semantic tag can't be null");

                logger.info(LoggerUtility.CONSOLE, "Retrieving concepts, please wait...");
                List<Relationship> lstAttribute = objAgent.getConcepts(attributeName, semanticTagList,destinationId,false,null,false);

                if(lstAttribute!=null){
                    //print list element
                    for(Relationship relation: lstAttribute){
                    	logger.info(LoggerUtility.CONSOLE, "Source Concept Id: {}--{};",relation.getSourceConcept().getId()
                    ,relation.getSourceConcept().getFullySpecifiedName());
                    }
                    if(lstAttribute.isEmpty())
                    	logger.info(LoggerUtility.CONSOLE, "No concepts found");
                    else
                    	logger.info(LoggerUtility.CONSOLE, "Total concepts found : {}",lstAttribute.size());
    				}else
    					logger.info(LoggerUtility.CONSOLE, "No concepts found");
                break;
			}
			default: {
				logger.info(LoggerUtility.CONSOLE, "Invalid option");
				break;
			}
			}
		} catch(NumberFormatException nfe){
			Logger logger = LogManager.getLogger(SNOMEDAgentExploreAPI.class);
			logger.info(LoggerUtility.CONSOLE, nfe.getMessage());
		} catch(IllegalArgumentException e){
			Logger logger = LogManager.getLogger(SNOMEDAgentExploreAPI.class);
			logger.info(LoggerUtility.CONSOLE, e.getMessage());
		}catch (Exception e) {
			Logger logger = LogManager.getLogger(SNOMEDAgentExploreAPI.class);
			logger.error(LoggerUtility.CONSOLE, LoggerUtility.createErrorMessage(e));
		}
	}

}
