/*******************************************************************************
 * 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 static org.junit.Assert.*;
import in.cdac.medinfo.csnotk.csnolib.agents.SNOMEDAgent;
import in.cdac.medinfo.csnotk.csnolib.api.ISNOMEDAgent;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumAcceptability;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumDirection;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumLangRefset;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumRelationshipType;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumSemanticTag;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumState;
import in.cdac.medinfo.csnotk.csnolib.model.CompositeDescription;
import in.cdac.medinfo.csnotk.csnolib.model.Concept;
import in.cdac.medinfo.csnotk.csnolib.model.RelationshipTC;
import in.cdac.medinfo.csnotk.csnolib.model.Refset;

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

import org.junit.Test;
//Logs
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import in.cdac.medinfo.csnotk.csnolib.util.LoggerUtility;

/**
 * TestSNOMEDAgentJUnit class contains the test cases related to search and suggest of SNOMED CT.
 *
 */
public class TestSNOMEDAgentJUnit {

	private static ISNOMEDAgent agent=null;
	private static final String FEVER = "fever";
	private static final String MYOCARDIAL_INFARCTION = "22298006";

	static{
		agent = new SNOMEDAgent();
		agent.init();
        Logger logger = LogManager.getLogger(TestSNOMEDAgentJUnit.class);
		logger.info(LoggerUtility.CONSOLE, "Do you want to create SNOMED CT database (y/n)");
		BufferedReader serviceInput = new BufferedReader(
				new InputStreamReader(System.in));
		try {
			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();
				agent.createSNOMEDdb(snomedDirPath, true);
			}
		} catch (IOException e1) {
			//Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e1));
		}
	}


	@Test
	public void testSearchAcceptabilityForNullAndExtremreturnLimit() {
		String matchTerm = FEVER;
		EnumState enumState = EnumState.ACTIVE;
		List<String> semanticTagList = new ArrayList<>();
		semanticTagList.add(EnumSemanticTag.ALL.getValue());
		int returnLimit = 100;
		EnumAcceptability enumAcceptability = EnumAcceptability.PREFERRED;

		Set<CompositeDescription> descriptions = agent.search(matchTerm, enumState,
				semanticTagList, enumAcceptability, returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		// Check for null and Count in case extreme bounds.
		Set<CompositeDescription> descriptionsAllCase = agent.search(matchTerm,
				enumState, semanticTagList, enumAcceptability, -1, false,null,null,false);
		assertNotNull(descriptionsAllCase);

		Set<CompositeDescription> descriptionsUpperBound = agent.search(matchTerm,
				enumState, semanticTagList, enumAcceptability, 100000, false,null,null,false);
		assertNotNull(descriptionsUpperBound);
		assertEquals(descriptionsUpperBound.size(), descriptionsAllCase.size());

		Set<CompositeDescription> descriptionsLowerBound = agent.search(matchTerm,
				enumState, semanticTagList, enumAcceptability, -100000, false,null,null,false);
		assertNotNull(descriptionsLowerBound);
		assertEquals(descriptionsLowerBound.size(), descriptionsAllCase.size());

		descriptions = agent.search(matchTerm, enumState, semanticTagList, null, -1, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, enumState, null, null, -1, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, null, null, null, -1, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, null, semanticTagList,
				enumAcceptability, returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, null, null, enumAcceptability,
				returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, null, semanticTagList, null, returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, enumState, null,
				enumAcceptability, returnLimit, false,null,null,false);
		assertNotNull(descriptions);

	}

	@Test
	public void testSearchSemanticTagForNullAndExtremreturnLimit() {
		String matchTerm = FEVER;
		EnumState enumState = EnumState.ACTIVE;
		List<String> semanticTagList = Arrays.asList(EnumSemanticTag.ALL.getValue());
		int returnLimit = 100;
		Set<CompositeDescription> descriptions = agent.search(matchTerm, enumState,semanticTagList,  returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		// Check for null and Count in case extreme bounds.
		Set<CompositeDescription> descriptionsAllCase = agent.search(matchTerm,enumState, semanticTagList,  -1, false,null,null,false);
		assertNotNull(descriptionsAllCase);

		Set<CompositeDescription> descriptionsUpperBound = agent.search(matchTerm,enumState, semanticTagList,  100000, false,null,null,false);
		assertNotNull(descriptionsUpperBound);
		assertEquals(descriptionsUpperBound.size(), descriptionsAllCase.size());

		Set<CompositeDescription> descriptionsLowerBound = agent.search(matchTerm,enumState, semanticTagList,  -2, false,null,null,false);
		assertNotNull(descriptionsLowerBound);
		assertEquals(descriptionsLowerBound.size(), descriptionsAllCase.size());

		descriptions = agent.search(matchTerm, null, semanticTagList,  -1, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, enumState, null,  -1, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, null, null,  -1, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, null, null, returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, null, semanticTagList,  returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, enumState, null,returnLimit, false,null,null,false);
		assertNotNull(descriptions);
	}

	@Test
	public void testSearchStateForNullAndExtremreturnLimit() {
		String matchTerm = FEVER;
		EnumState enumState = EnumState.ACTIVE;
		int returnLimit = 100;
		Set<CompositeDescription> descriptions = agent.search(matchTerm, enumState,  returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		// Check for null and Count in case extreme bounds.
		Set<CompositeDescription> descriptionsAllCase = agent.search(matchTerm,enumState,   -1, false,null,null,false);
		assertNotNull(descriptionsAllCase);

		Set<CompositeDescription> descriptionsUpperBound = agent.search(matchTerm,enumState,   100001, false,null,null,false);
		assertNotNull(descriptionsUpperBound);
		assertEquals(descriptionsUpperBound.size(), descriptionsAllCase.size());

		Set<CompositeDescription> descriptionsLowerBound = agent.search(matchTerm,enumState,   -2, false,null,null,false);
		assertNotNull(descriptionsLowerBound);
		assertEquals(descriptionsLowerBound.size(), descriptionsAllCase.size());

		descriptions = agent.search(matchTerm, null,   -1, false,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.search(matchTerm, null,  returnLimit, false,null,null,false);
		assertNotNull(descriptions);
	}

	@Test
	public void testSearchByLimitForNullAndExtremreturnLimit() {
		String matchTerm = FEVER;
		int returnLimit = 100;
		Set<CompositeDescription> descriptions = agent.search(matchTerm,   returnLimit, false,null,null,false);
		assertNotNull(descriptions);

		// Check for null and Count in case extreme bounds.
		Set<CompositeDescription> descriptionsAllCase = agent.search(matchTerm, -1, false,null,null,false);
		assertNotNull(descriptionsAllCase);

		Set<CompositeDescription> descriptionsUpperBound = agent.search(matchTerm,   100001, false,null,null,false);
		assertNotNull(descriptionsUpperBound);
		assertEquals(descriptionsUpperBound.size(), descriptionsAllCase.size());

		Set<CompositeDescription> descriptionsLowerBound = agent.search(matchTerm, -2, false,null,null,false);
		assertNotNull(descriptionsLowerBound);
		assertEquals(descriptionsLowerBound.size(), descriptionsAllCase.size());
	}

	@Test
	public void testSuggestAcceptabilityForNullAndExtremreturnLimit() {
		String matchTerm = FEVER;
		EnumState enumState = EnumState.ACTIVE;
		List<String> semanticTagList = Arrays.asList(EnumSemanticTag.ALL.getValue());
		int returnLimit = 100;
		EnumAcceptability enumAcceptability = EnumAcceptability.PREFERRED;

		Set<String> descriptions = agent.getSuggestions(matchTerm, enumState,semanticTagList, enumAcceptability, returnLimit,null,null,false);
		assertNotNull(descriptions);

		// Check for null and Count in case extreme bounds.
		Set<String> descriptionsAllCase = agent.getSuggestions(matchTerm,enumState, semanticTagList, enumAcceptability, -1,null,null,false);
		assertNotNull(descriptionsAllCase);

		Set<String> descriptionsUpperBound = agent.getSuggestions(matchTerm,enumState, semanticTagList, enumAcceptability, 100001,null,null,false);
		assertNotNull(descriptionsUpperBound);
		assertEquals(descriptionsUpperBound.size(), descriptionsAllCase.size());

		Set<String> descriptionsLowerBound = agent.getSuggestions(matchTerm,enumState, semanticTagList, enumAcceptability, -2,null,null,false);
		assertNotNull(descriptionsLowerBound);
		assertEquals(descriptionsLowerBound.size(), descriptionsAllCase.size());

		descriptions = agent.getSuggestions(matchTerm, enumState, semanticTagList, null, -1,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, enumState, null, null, -1,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, null, null, null, -1,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, null, semanticTagList,
				enumAcceptability, returnLimit,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, null, null, enumAcceptability,
				returnLimit,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, null, semanticTagList, null, returnLimit,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, enumState, null,
				enumAcceptability, returnLimit,null,null,false);
		assertNotNull(descriptions);
	}

	@Test
	public void testSuggestSemanticTagForNullAndExtremreturnLimit() {
		String matchTerm = FEVER;
		EnumState enumState = EnumState.ACTIVE;
		List<String> semanticTagList = Arrays.asList(EnumSemanticTag.ALL.getValue());
		int returnLimit = 100;

		Set<String> descriptions = agent.getSuggestions(matchTerm, enumState,semanticTagList,  returnLimit,null,null,false);
		assertNotNull(descriptions);

		// Check for null and Count in case extreme bounds.
		Set<String> descriptionsAllCase = agent.getSuggestions(matchTerm,enumState, semanticTagList,-1,null,null,false);
		assertNotNull(descriptionsAllCase);

		Set<String> descriptionsUpperBound = agent.getSuggestions(matchTerm,enumState, semanticTagList,  100001,null,null,false);
		assertNotNull(descriptionsUpperBound);
		assertEquals(descriptionsUpperBound.size(), descriptionsAllCase.size());

		Set<String> descriptionsLowerBound = agent.getSuggestions(matchTerm,enumState, semanticTagList,  -2,null,null,false);
		assertNotNull(descriptionsLowerBound);
		assertEquals(descriptionsLowerBound.size(), descriptionsAllCase.size());

		descriptions = agent.getSuggestions(matchTerm, null, semanticTagList,  -1,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, enumState, null,  -1,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, null, null,  -1,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, null, null, returnLimit,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, null, semanticTagList,  returnLimit,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, enumState, null,returnLimit,null,null,false);
		assertNotNull(descriptions);
	}

	@Test
	public void testSuggestStateForNullAndExtremreturnLimit() {
		String matchTerm = FEVER;
		EnumState enumState = EnumState.ACTIVE;
		int returnLimit = 100;
		Set<String> descriptions = agent.getSuggestions(matchTerm, enumState,  returnLimit,null,null,false);
		assertNotNull(descriptions);
		// Check for null and Count in case extreme bounds.
		Set<String> descriptionsAllCase = agent.getSuggestions(matchTerm,enumState,   -1,null,null,false);
		assertNotNull(descriptionsAllCase);

		Set<String> descriptionsUpperBound = agent.getSuggestions(matchTerm,enumState,   100001,null,null,false);
		assertNotNull(descriptionsUpperBound);
		assertEquals(descriptionsUpperBound.size(), descriptionsAllCase.size());

		Set<String> descriptionsLowerBound = agent.getSuggestions(matchTerm,enumState,   -2,null,null,false);
		assertNotNull(descriptionsLowerBound);
		assertEquals(descriptionsLowerBound.size(), descriptionsAllCase.size());

		descriptions = agent.getSuggestions(matchTerm, null,   -1,null,null,false);
		assertNotNull(descriptions);

		descriptions = agent.getSuggestions(matchTerm, null,  returnLimit,null,null,false);
		assertNotNull(descriptions);
	}

	@Test
	public void testSuggestByLimitForNullAndExtremreturnLimit() {
		String matchTerm = FEVER;
		int returnLimit = 100;
		Set<String> descriptions = agent.getSuggestions(matchTerm,   returnLimit,null,null,false);
		assertNotNull(descriptions);
		// Check for null and Count in case extreme bounds.
		Set<String> descriptionsAllCase = agent.getSuggestions(matchTerm, -1,null,null,false);
		assertNotNull(descriptionsAllCase);

		Set<String> descriptionsUpperBound = agent.getSuggestions(matchTerm,   100000,null,null,false);
		assertNotNull(descriptionsUpperBound);
		assertEquals(descriptionsUpperBound.size(), descriptionsAllCase.size());

		Set<String> descriptionsLowerBound = agent.getSuggestions(matchTerm, -100000,null,null,false);
		assertNotNull(descriptionsLowerBound);
		assertEquals(descriptionsLowerBound.size(), descriptionsAllCase.size());
	}

	@Test
	public void testExploreGetChildren() {
		String conceptId = MYOCARDIAL_INFARCTION;
		Set<Concept> descriptions = null;
		descriptions = agent.getRelations(conceptId, EnumRelationshipType.IS_A.getValue(), EnumDirection.DESTINATION);
		//Check for test Explore by parent concept for multiple Children.
		assertTrue(descriptions.size()!=0);
	}

	@Test
	public void testExploreGetChildrenForNull() {

		String conceptId = "408687004";
		Set <Concept> descriptions = null;
		descriptions = agent.getRelations(conceptId, EnumRelationshipType.IS_A.getValue(), EnumDirection.DESTINATION);
		//Check for test Explore by parent concept for no Children.
		assertEquals(descriptions.size(),0);
	}

	@Test
	public void testExploreGetParentsForNull() {

		String conceptId = "138875005";
		Set <Concept> descriptions = null;
		descriptions = agent.getRelations(conceptId, EnumRelationshipType.IS_A.getValue(), EnumDirection.SOURCE);
		//Check for test Explore by child concept for no parent.
		assertEquals(descriptions.size(), 0);
	}

	@Test
	public void testExploreGetParents() {

		String conceptId = MYOCARDIAL_INFARCTION;
		Set <Concept> descriptions = null;
		// Check for Explore by child concept for parent(s).
		descriptions = agent.getRelations(conceptId, EnumRelationshipType.IS_A.getValue(), EnumDirection.SOURCE);
		assertTrue(descriptions.size()!=0);
	}

	@Test
	public void testExploreGetAlldescendantsForNull()  {
		String conceptId = "408687004";
		Set <Concept> descriptions = agent.getAllDescendantConcepts(conceptId);
		//Check for Explore by parent concept for no descendants.
		assertEquals(descriptions.size(), 0);
	}

	@Test
	public void testExploreGetAlldescendants()  {
		String conceptId = MYOCARDIAL_INFARCTION;
		Set <Concept> descriptions = agent.getAllDescendantConcepts(conceptId);
		//Check for Explore by parent concept for all descendants.
		assertTrue(descriptions.size()!=0);
	}

	@Test
	public void testImmediateDescendantsWithCountForNull()  {
		String conceptId = "408687004";
		Set<RelationshipTC> relationshipTCs = agent.getIsARelationshipsWithCount(conceptId);
		//Check for Explore by parent concept for no descendants.
		assertEquals(relationshipTCs.size(), 0);
	}

	@Test
	public void testImmediateDescendantsWithCount()  {
		String conceptId = MYOCARDIAL_INFARCTION;
		Set<RelationshipTC> relationshipTCs = agent.getIsARelationshipsWithCount(conceptId);
		//Check for Explore by parent concept for immediate descendants.
		assertTrue(relationshipTCs.size()!=0);
	}

	@Test
	public void testExploreGetAllConceptByAttribute(){

		assertTrue(agent.getConcepts("Pathological process","441862004").size()>0);

		assertTrue(agent.getConcepts("Pathological process",Arrays.asList("disorder"),"441862004").size()>0);

		//destinationId is must

		assertNull(agent.getConcepts("procedure site - direct"));

		assertNull(agent.getConcepts("Pathological process",Arrays.asList("disorder")));

		ArrayList<String> semanticList=null;
		assertNull(agent.getConcepts("Pathological process",semanticList));

		String destinationId=null;
		assertNull(agent.getConcepts("Pathological process",destinationId));
	}




	@Test
	public void testLookUpConcept()  {
		EnumLangRefset enumLangRefset = EnumLangRefset.UK;
		String conceptId = "38362002";
		List<CompositeDescription> descriptions = agent.getAllDescriptions(conceptId, enumLangRefset);
		//Check for LookUp by concept for all its descriptions and relationships.
		assertNotNull(descriptions);
		assertTrue(descriptions.size()!=0);
	}

	@Test
	public void testLookUpRelationship() {

		String conceptId = MYOCARDIAL_INFARCTION;
		Set<Concept> descriptions = agent.getRelations(conceptId,"all",EnumDirection.getEnum("source"));
		//Check for LookUp Relationships by concept as source for all relationships.
		assertNotNull(descriptions);
		assertTrue(descriptions.size()!=0);
	}

	@Test
	public void testLookUpRelationsDirection()  {
		EnumDirection direction = EnumDirection.SOURCE ;
		String relation = EnumRelationshipType.IS_A.getId() ;
		String conceptId = MYOCARDIAL_INFARCTION;
		Concept descriptions = agent.getConcept(conceptId);
		Set <Concept> relationship = descriptions.getRelations(relation, direction);
		//Check for LookUp Relationships by concept as source for is a relationship.
		assertTrue(relationship.size()!=0);
	}

	@Test
	public void testLookUpRelationsDirectionForNull()  {
		EnumDirection direction = EnumDirection.DESTINATION ;
		String relation = "363698007";//EnumRelationshipType.FINDING_SITE ;
		String conceptId = MYOCARDIAL_INFARCTION;
		Concept descriptions = agent.getConcept(conceptId);
		Set <Concept> relationship = descriptions.getRelations(relation, direction);
		assertEquals(relationship.size(), 0);
	}

	@Test
	public void testExploreGetAllancestors()  {
		String conceptId = MYOCARDIAL_INFARCTION;
		Set <Concept> descriptions = agent.getAllAncestorConcepts(conceptId);
		//Check for Explore by parent concept for all descendants.
		assertTrue(descriptions.size()!=0);
	}

    /**
     * test case to get all description id, term and FSN for a concept Id
     */
    @Test
    public void testGetAllDescriptions()
    {
        Logger logger = LogManager.getLogger(TestQueryManager.class);
        String conceptId = MYOCARDIAL_INFARCTION;
        EnumLangRefset langrefset=EnumLangRefset.US;
        List<CompositeDescription> compositeDescriptions = agent.getAllDescriptions(conceptId,langrefset);
        assertTrue(compositeDescriptions.size()>0);
        for(CompositeDescription compositeDesc:compositeDescriptions)
            logger.info(LoggerUtility.CONSOLE, "{},{},{}",compositeDesc.getId(),compositeDesc.getTerm(),compositeDesc.getConceptFsn());
    }

    /**
     * test case to get add descendants for a concept Id
     */
    @Test
    public void testGetAllDescendants()
    {
        Logger logger = LogManager.getLogger(TestQueryManager.class);
        String conceptId=MYOCARDIAL_INFARCTION;
        Set<Concept> concepts= agent.getAllDescendantConcepts(conceptId);
        assertTrue(concepts.size()>0);
    }

    /**
     * test case to get all refset members for a refset Id
     */
    @Test
    public void testGetAllRefsetMembers()
    {
        //General dentistry reference set must be imported
        Logger logger = LogManager.getLogger(TestQueryManager.class);
        String refsetId="721144007";
        Set<Concept> concepts = agent.getRefsetMember(refsetId);
        logger.info(LoggerUtility.CONSOLE, "Total number of Refset Members : {}",concepts.size());
        assertTrue(concepts.size()>0);
    }

    /**
     * test case to get all refset identifier.
     */
    @Test
    public void testGetAllRefsets()
    {
        //Atleast one refset must be imported
        Logger logger = LogManager.getLogger(TestQueryManager.class);
        Set<Refset> refsetList = agent.getAllRefset();
        logger.info(LoggerUtility.CONSOLE, "Total number of Refsets  : {}",refsetList.size());
        for (Refset r : refsetList)
            logger.info(LoggerUtility.CONSOLE, r.getRefsetConcept().getId());
        assertTrue(refsetList.size()>0);
    }

}
