/**
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
 *
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
 * graphic logo is a trademark of OpenMRS Inc.
 */
package org.openmrs.api;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.openmrs.Concept;
import org.openmrs.ConceptAnswer;
import org.openmrs.ConceptAttribute;
import org.openmrs.ConceptAttributeType;
import org.openmrs.ConceptClass;
import org.openmrs.ConceptComplex;
import org.openmrs.ConceptDatatype;
import org.openmrs.ConceptDescription;
import org.openmrs.ConceptMap;
import org.openmrs.ConceptMapType;
import org.openmrs.ConceptName;
import org.openmrs.ConceptNameTag;
import org.openmrs.ConceptNumeric;
import org.openmrs.ConceptProposal;
import org.openmrs.ConceptReferenceRange;
import org.openmrs.ConceptReferenceTerm;
import org.openmrs.ConceptReferenceTermMap;
import org.openmrs.ConceptSearchResult;
import org.openmrs.ConceptSet;
import org.openmrs.ConceptSource;
import org.openmrs.ConceptStopWord;
import org.openmrs.Drug;
import org.openmrs.DrugIngredient;
import org.openmrs.Person;
import org.openmrs.annotation.Authorized;
import org.openmrs.api.db.ConceptDAO;
import org.openmrs.util.PrivilegeConstants;

/**
 * Contains methods pertaining to creating/updating/deleting/retiring Concepts, Drugs, Concept
 * Proposals, and all other things 'Concept'.
 * <p>
 * To get a list of concepts: <pre>
 * 
 *
 *
 *
 * List&lt;Concept&gt; concepts = Context.getConceptService().getAllConcepts();
 * </pre> To get a single concept: <pre>
 * 
 *
 * 
 * 
 * // if there is a concept row in the database with concept_id = 3845
 * Concept concept = Context.getConceptService().getConcept(3845);
 * 
 * String name = concept.getPreferredName(Context.getLocale()).getName();
 * </pre> To save a concept to the database <pre>
 *   Concept concept = new Concept();
 *   concept.setConceptClass(Context.getConceptService().getConceptClass(3));
 *   concept.setDatatype(Context.getConceptService().getConceptDatatype(17));
 *   concept.setName...
 *   ... // and other required values on the concept
 *   Context.getConceptService().saveConcept(concept);
 * </pre>
 * 
 * @see org.openmrs.api.context.Context
 */
public interface ConceptService extends OpenmrsService {
	
	/**
	 * Sets the data access object for Concepts. The dao is used for saving and getting concepts
	 * to/from the database
	 * 
	 * @param dao The data access object to use
	 */
	public void setConceptDAO(ConceptDAO dao);
	
	/**
	 * Get Concept by its UUID
	 * 
	 * @param uuid
	 * @return concept or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getConceptByUuid(String uuid);
	
	/**
	 * Save or update the given <code>Concept</code> or <code>ConceptNumeric</code> in the database
	 * <p>
	 * If this is a new concept, the returned concept will have a new {@link Concept#getConceptId()}
	 * inserted into it that was generated by the database
	 * 
	 * @param concept The <code>Concept</code> or <code>ConceptNumeric</code> to save or update
	 * @return the <code>Concept</code> or <code>ConceptNumeric</code> that was saved or updated
	 * @throws APIException
	 * @throws ConceptsLockedException
	 * @throws ConceptInUseException
	 * <strong>Should</strong> put generated concept id onto returned concept
	 * <strong>Should</strong> create new concept in database
	 * <strong>Should</strong> update concept already existing in database
	 * <strong>Should</strong> generate id for new concept if none is specified
	 * <strong>Should</strong> keep id for new concept if one is specified
	 * <strong>Should</strong> save non ConceptNumeric object as conceptNumeric
	 * <strong>Should</strong> save non ConceptComplex object as conceptComplex
	 * <strong>Should</strong> save changes between concept numeric and complex
	 * <strong>Should</strong> save a ConceptNumeric as a concept
	 * <strong>Should</strong> save a new ConceptNumeric
	 * <strong>Should</strong> void the conceptName if the text of the name has changed
	 * <strong>Should</strong> create a new conceptName when the old name is changed
	 * <strong>Should</strong> set a preferred name for each locale if none is marked
	 * <strong>Should</strong> not fail when a duplicate name is edited to a unique value
	 * <strong>Should</strong> create a reference term for a concept mapping on the fly when editing a concept
	 * <strong>Should</strong> create a reference term for a concept mapping on the fly when creating a concept
	 * <strong>Should</strong> add new concept name
	 * <strong>Should</strong> not set audit info if the concept is not edited
	 * <strong>Should</strong> set audit info if an item is removed from any of its child collections
	 * <strong>Should</strong> set audit info if any item in the child collections is edited
	 * <strong>Should</strong> set audit info if an item is added to any of its child collections
	 * <strong>Should</strong> pass when saving a concept after removing a name
	 * <strong>Should</strong> save a conceptNumeric with allowDecimal value
	 */
	@Authorized({ PrivilegeConstants.MANAGE_CONCEPTS })
	public Concept saveConcept(Concept concept) throws APIException;
	
	/**
	 * Save or update the given <code>Drug</code> in the database. If this is a new drug, the
	 * returned drug object will have a new {@link Drug#getDrugId()} inserted into it that was
	 * generated by the database
	 * 
	 * @param drug The Drug to save or update
	 * @return the Drug that was saved or updated
	 * @throws APIException
	 * <strong>Should</strong> put generated drug id onto returned drug
	 * <strong>Should</strong> create new drug in database
	 * <strong>Should</strong> update drug already existing in database
	 */
	@Authorized({ PrivilegeConstants.MANAGE_CONCEPTS })
	public Drug saveDrug(Drug drug) throws APIException;
	
	/**
	 * Completely purge a <code>Concept</code> or <code>ConceptNumeric</code> from the database.
	 * This should not typically be used unless desperately needed. Most should just be retired. See
	 * {@link #retireConcept(Concept, String)}
	 * 
	 * @param conceptOrConceptNumeric The <code>Concept</code> or <code>ConceptNumeric</code> to
	 *            remove from the system
	 * @throws APIException
	 * <strong>Should</strong> purge the concept if not being used by an obs
	 * <strong>Should</strong> fail if any of the conceptNames of the concept is being used by an obs
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPTS)
	public void purgeConcept(Concept conceptOrConceptNumeric) throws APIException;
	
	/**
	 * Retiring a concept essentially removes it from circulation
	 * 
	 * @param conceptOrConceptNumeric The <code>Concept</code> or <code>ConceptNumeric</code> to
	 *            retire
	 * @param reason The retire reason
	 * @return the retired <code>Concept</code> or <code>ConceptNumeric</code>
	 * @throws APIException
	 * <strong>Should</strong> fail if no reason is given
	 * <strong>Should</strong> retire the given concept
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPTS)
	public Concept retireConcept(Concept conceptOrConceptNumeric, String reason) throws APIException;
	
	/**
	 * Retiring a Drug essentially removes it from circulation
	 * 
	 * @param drug The Drug to retire
	 * @param reason The retire reason
	 * @throws APIException
	 * @return the retired Drug
	 * <strong>Should</strong> retire the given Drug
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPTS)
	public Drug retireDrug(Drug drug, String reason) throws APIException;
	
	/**
	 * Marks a drug that is currently retired as not retired.
	 * 
	 * @param drug that is current set as retired
	 * @return the given drug, marked as not retired now, and saved to the db
	 * @throws APIException
	 * <strong>Should</strong> mark drug as not retired
	 * <strong>Should</strong> not change attributes of drug that is already not retired
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPTS)
	public Drug unretireDrug(Drug drug) throws APIException;
	
	/**
	 * Completely purge a Drug from the database. This should not typically be used unless
	 * desperately needed. Most Drugs should just be retired.
	 * 
	 * @param drug The Drug to remove from the system
	 * @throws APIException
	 * <strong>Should</strong> purge the given drug
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPTS)
	public void purgeDrug(Drug drug) throws APIException;
	
	/**
	 * Gets the concept with the given id
	 * 
	 * @param conceptId
	 * @return the matching Concept object
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getConcept(Integer conceptId) throws APIException;
	
	/**
	 * Gets the concept-name with the given id
	 * 
	 * @param conceptNameId
	 * @return the matching Concept object
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptName getConceptName(Integer conceptNameId) throws APIException;
	
	/**
	 * Gets the ConceptAnswer with the given id
	 * 
	 * @param conceptAnswerId
	 * @return the matching ConceptAnswer object
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptAnswer getConceptAnswer(Integer conceptAnswerId) throws APIException;
	
	/**
	 * Get the Drug with the given id
	 * 
	 * @param drugId
	 * @return the matching Drug object
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Drug getDrug(Integer drugId) throws APIException;
	
	/**
	 * Get the ConceptNumeric with the given id
	 * 
	 * @param conceptId The ConceptNumeric id
	 * @return the matching ConceptNumeric object
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptNumeric getConceptNumeric(Integer conceptId) throws APIException;
	
	/**
	 * Return a Concept class matching the given identifier
	 * 
	 * @throws APIException
	 * @param conceptClassId the concept class identifier
	 * @return the matching ConceptClass
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptClass getConceptClass(Integer conceptClassId) throws APIException;
	
	/**
	 * Return a list of unretired concepts sorted by concept id ascending and
	 * 
	 * @return a List&lt;Concept&gt; object containing all of the sorted concepts
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getAllConcepts() throws APIException;
	
	/**
	 * Return a list of concepts sorted on sortBy in dir direction (asc/desc)
	 * 
	 * @param sortBy The property name to sort by; if null or invalid, concept_id is used.
	 * @param asc true = sort ascending; false = sort descending
	 * @param includeRetired If <code>true</code>, retired concepts will also be returned
	 * @return a List&lt;Concept&gt; object containing all of the sorted concepts
	 * @throws APIException
	 * <strong>Should</strong> order by concept id and include retired when given no parameters
	 * <strong>Should</strong> order by concept id descending when set asc parameter to false
	 * <strong>Should</strong> exclude retired concepts when set includeRetired to false
	 * <strong>Should</strong> order by a concept name field
	 * <strong>Should</strong> order by a concept field
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getAllConcepts(String sortBy, boolean asc, boolean includeRetired) throws APIException;
	
	/**
	 * Returns a list of concepts matching any part of a concept name, this method is case
	 * insensitive to the concept name string
	 * 
	 * @param name The search string
	 * @throws APIException
	 * @return a List&lt;Concept&gt; object containing all of the matching concepts
	 * <strong>Should</strong> pass irrespective of the case of the passed parameter
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getConceptsByName(String name) throws APIException;
	
	/**
	 * Return a Concept that matches the name exactly
	 * 
	 * @param name The search string
	 * @throws APIException
	 * @return the found Concept
	 * <strong>Should</strong> get concept by name
	 * <strong>Should</strong> return null given null parameter
	 * <strong>Should</strong> find concepts with names in more specific locales
	 * <strong>Should</strong> find concepts with names in more generic locales
	 * <strong>Should</strong> find concepts with names in same specific locale
	 * <strong>Should</strong> return null given blank string
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getConceptByName(String name) throws APIException;
	
	/**
	 * Get Concept by id or name convenience method
	 * 
	 * @param conceptIdOrName
	 * @return the found Concept
	 * @throws APIException
	 * <strong>Should</strong> return null given null parameter
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getConcept(String conceptIdOrName) throws APIException;

	/**
	 * Get the concept by conceptRef where the conceptRef can either be: 1) an integer id like 5090
	 * 2) a mapping type id like "XYZ:HT" 3) a uuid like "a3e12268-74bf-11df-9768-17cfc9833272" 4) a
	 * name like "PLATELETS" 5) the fully qualified name of a Java constant which contains one of the above
	 * @param conceptRef the concept string identifier
	 * @since 2.6.0
	 * @return the concept if it exists otherwise null
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getConceptByReference(String conceptRef);
	
	/**
	 * Get Drug by its UUID
	 * 
	 * @param uuid
	 * @return drug of null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Drug getDrugByUuid(String uuid);
	
	/**
	 * Get Drug Ingredient by its UUID
	 * 
	 * @param uuid the uuid for the drug ingredient to get
	 * @return the drug ingredient if found, else null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public DrugIngredient getDrugIngredientByUuid(String uuid);
	
	/**
	 * Return the drug object corresponding to the given name or drugId
	 * 
	 * @param drugNameOrId String name or drugId to match exactly on
	 * @return matching Drug object
	 * @throws APIException
	 * <strong>Should</strong> return the matching drug object
	 * <strong>Should</strong> return null if no matching drug is found
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Drug getDrug(String drugNameOrId) throws APIException;
	
	/**
	 * Return a list of drugs currently in the database that are not retired
	 * 
	 * @throws APIException
	 * @return a List&lt;Drug&gt; object containing all drugs
	 * <strong>Should</strong> return a list of all drugs
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Drug> getAllDrugs() throws APIException;
	
	/**
	 * Return a list of drugs associated with the given concept
	 * 
	 * @throws APIException
	 * @param concept
	 * @return a List&lt;Drug&gt; object containing all matching drugs
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Drug> getDrugsByConcept(Concept concept) throws APIException;
	
	/**
	 * Get drugs by concept. This method is the utility method that should be used to generically
	 * retrieve all Drugs in the system.
	 * 
	 * @param includeRetired If <code>true</code> then the search will include voided Drugs
	 * @return A List&lt;Drug&gt; object containing all matching Drugs
	 * <strong>Should</strong> return all drugs including retired ones if given true
	 * <strong>Should</strong> return all drugs excluding retired ones if given false
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Drug> getAllDrugs(boolean includeRetired);
	
	/**
	 * Find drugs in the system. The string search can match either drug.name or drug.concept.name,
	 * drug.drugId or drug.concept.conceptId
	 * 
	 * @param phrase Search phrase
	 * @throws APIException
	 * @return A List&lt;Drug&gt; object containing all Drug matches
	 * <strong>Should</strong> not return drugs that are retired
	 * <strong>Should</strong> return drugs by drug id
	 * <strong>Should</strong> return drugs by drug concept id
	 * <strong>Should</strong> not fail if there is no drug by given id
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Drug> getDrugs(String phrase) throws APIException;
	
	/**
	 * @param cc ConceptClass
	 * @return Returns all concepts in a given class
	 * @throws APIException
	 * <strong>Should</strong> not fail due to no name in search
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getConceptsByClass(ConceptClass cc) throws APIException;
	
	/**
	 * Return a Concept class matching the given name
	 * 
	 * @param name
	 * @return ConceptClass matching the given name
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_CLASSES)
	public ConceptClass getConceptClassByName(String name) throws APIException;
	
	/**
	 * Return a list of concept classes currently in the database
	 * 
	 * @throws APIException
	 * @return List&lt;ConceptClass&gt; object with all ConceptClass objects
	 * <strong>Should</strong> return a list of all concept classes
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_CLASSES)
	public List<ConceptClass> getAllConceptClasses() throws APIException;
	
	/**
	 * Return a list of concept classes currently in the database
	 * 
	 * @param includeRetired include retired concept classes in the search results?
	 * @throws APIException
	 * @return List&lt;ConceptClass&gt; object with all ConceptClass objects
	 * <strong>Should</strong> return all concept classes including retired ones when given true
	 * <strong>Should</strong> return all concept classes excluding retired ones when given false
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_CLASSES)
	public List<ConceptClass> getAllConceptClasses(boolean includeRetired) throws APIException;
	
	/**
	 * Get ConceptClass by its UUID
	 * 
	 * @param uuid
	 * @return concept class or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_CLASSES)
	public ConceptClass getConceptClassByUuid(String uuid);
	
	/**
	 * Get ConceptAnswer by its UUID
	 * 
	 * @param uuid
	 * @return concept answer or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptAnswer getConceptAnswerByUuid(String uuid);
	
	/**
	 * Get ConceptName by its UUID
	 * 
	 * @param uuid
	 * @return concept name or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptName getConceptNameByUuid(String uuid);
	
	/**
	 * Get ConceptSet by its UUID
	 * 
	 * @param uuid
	 * @return concept set or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptSet getConceptSetByUuid(String uuid);
	
	/**
	 * Get ConceptSource by its UUID
	 * 
	 * @param uuid
	 * @return concept source or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_SOURCES)
	public ConceptSource getConceptSourceByUuid(String uuid);
	
	/**
	 * Creates or updates a concept class
	 * 
	 * @param cc ConceptClass to create or update
	 * @throws APIException
	 * <strong>Should</strong> save the the given ConceptClass
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_CLASSES)
	public ConceptClass saveConceptClass(ConceptClass cc) throws APIException;
	
	/**
	 * Purge a ConceptClass
	 * 
	 * @param cc ConceptClass to delete
	 * @throws APIException
	 * <strong>Should</strong> delete the given ConceptClass
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPT_CLASSES)
	public void purgeConceptClass(ConceptClass cc) throws APIException;
	
	/**
	 * Purge a ConceptNameTag
	 * 
	 * @param cnt ConceptNameTag to delete
	 * @throws APIException
	 * @since 1.10
	 * <strong>Should</strong> delete the specified conceptNameTag from the database
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_NAME_TAGS)
	public void purgeConceptNameTag(ConceptNameTag cnt) throws APIException;
	
	/**
	 * Return a list of all concept datatypes currently in the database
	 * 
	 * @throws APIException
	 * @return List of ConceptDatatypes
	 * <strong>Should</strong> give a list of all concept datatypes
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_DATATYPES)
	public List<ConceptDatatype> getAllConceptDatatypes() throws APIException;
	
	/**
	 * Return a list of concept datatypes currently in the database
	 * 
	 * @param includeRetired boolean - include the retired datatypes?
	 * @throws APIException
	 * @return List of ConceptDatatypes
	 * <strong>Should</strong> return all concept datatypes including retired ones when given true
	 * <strong>Should</strong> return all concept datatypes excluding retired ones when given false
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_DATATYPES)
	public List<ConceptDatatype> getAllConceptDatatypes(boolean includeRetired) throws APIException;
	
	/**
	 * Return a ConceptDatatype matching the given identifier
	 * 
	 * @param i Integer for the requested ConceptDatatype
	 * @return ConceptDatatype matching the given identifier
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_DATATYPES)
	public ConceptDatatype getConceptDatatype(Integer i) throws APIException;
	
	/**
	 * Get ConceptDatatype by its UUID
	 * 
	 * @param uuid
	 * @return concept data type or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_DATATYPES)
	public ConceptDatatype getConceptDatatypeByUuid(String uuid);
	
	/**
	 * Return a Concept datatype matching the given name
	 * 
	 * @param name
	 * @return ConceptDatatype matching the given name
	 * @throws APIException
	 * <strong>Should</strong> return an exact match on name
	 * <strong>Should</strong> not return a fuzzy match on name
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_DATATYPES)
	public ConceptDatatype getConceptDatatypeByName(String name) throws APIException;
	
	/**
	 * Return a list of the concept sets with concept_set matching concept
	 * <p>
	 * For example to find all concepts for ARVs, you would do
	 * getConceptSets(getConcept("ANTIRETROVIRAL MEDICATIONS")) and then take the conceptIds from
	 * the resulting list.
	 * 
	 * @param concept The concept representing the concept set
	 * @return A List&lt;ConceptSet&gt; object containing all matching ConceptSets
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptSet> getConceptSetsByConcept(Concept concept) throws APIException;
	
	/**
	 * Return a List of all concepts within a concept set
	 * 
	 * @param concept The concept representing the concept set
	 * @return A List&lt;Concept&gt; object containing all objects within the ConceptSet
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getConceptsByConceptSet(Concept concept) throws APIException;
	
	/**
	 * Find all sets that the given concept is a member of
	 * 
	 * @param concept
	 * @throws APIException
	 * @return A List&lt;ConceptSet&gt; object with all parent concept sets
	 * <strong>Should</strong> give a list of ConceptSet containing the given Concept
	 * <strong>Should</strong> give an empty list if no matching ConceptSet is found
	 * <strong>Should</strong> give an empty list if concept id is null
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptSet> getSetsContainingConcept(Concept concept) throws APIException;
	
	/**
	 * Get a List of all concept proposals
	 * 
	 * @param includeCompleted boolean - include completed proposals as well?
	 * @return a List&lt;ConceptProposal&gt; object of all found ConceptProposals
	 * @throws APIException
	 * <strong>Should</strong> return all concept proposals including retired ones when given true
	 * <strong>Should</strong> return all concept proposals excluding retired ones when given false
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_PROPOSALS)
	public List<ConceptProposal> getAllConceptProposals(boolean includeCompleted) throws APIException;
	
	/**
	 * Get ConceptNumeric by its UUID
	 * 
	 * @param uuid
	 * @return concept numeric or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptNumeric getConceptNumericByUuid(String uuid);
	
	/**
	 * Get a ConceptProposal by conceptProposalId
	 * 
	 * @param conceptProposalId the Integer concept proposal Id
	 * @return the found ConceptProposal
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_PROPOSALS)
	public ConceptProposal getConceptProposal(Integer conceptProposalId) throws APIException;
	
	/**
	 * Find matching concept proposals
	 * 
	 * @param text
	 * @return a List&lt;ConceptProposal&gt; object containing matching concept proposals
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_PROPOSALS)
	public List<ConceptProposal> getConceptProposals(String text) throws APIException;
	
	/**
	 * Find matching proposed concepts
	 * 
	 * @param text
	 * @return a List&lt;Concept&gt; object containing matching proposed concepts
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_PROPOSALS)
	public List<Concept> getProposedConcepts(String text) throws APIException;
	
	/**
	 * Saves/updates/proposes a concept proposal
	 * 
	 * @param conceptProposal The ConceptProposal to save
	 * @throws APIException
	 * @return the saved/updated ConceptProposal object
	 */
	@Authorized({ PrivilegeConstants.ADD_CONCEPT_PROPOSALS, PrivilegeConstants.EDIT_CONCEPT_PROPOSALS })
	public ConceptProposal saveConceptProposal(ConceptProposal conceptProposal) throws APIException;
	
	/**
	 * Removes a concept proposal from the database entirely.
	 * 
	 * @param cp
	 * @throws APIException
	 * <strong>Should</strong> purge the given concept proposal
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPT_PROPOSALS)
	public void purgeConceptProposal(ConceptProposal cp) throws APIException;
	
	/**
	 * Maps a concept proposal to a concept
	 * 
	 * @param cp
	 * @param mappedConcept
	 * @return the mappedConcept
	 * @throws APIException
	 * <strong>Should</strong> not require mapped concept on reject action
	 * <strong>Should</strong> allow rejecting proposals
	 * <strong>Should</strong> throw APIException when mapping to null concept
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPTS)
	public Concept mapConceptProposalToConcept(ConceptProposal cp, Concept mappedConcept) throws APIException;
	
	/**
	 * Maps a concept proposal to a concept
	 * 
	 * @param cp
	 * @param mappedConcept
	 * @param locale of concept proposal
	 * @return the mappedConcept
	 * @throws APIException
	 * <strong>Should</strong> not require mapped concept on reject action
	 * <strong>Should</strong> allow rejecting proposals
	 * <strong>Should</strong> throw APIException when mapping to null concept
	 * <strong>Should</strong> set value coded name when add synonym is selected
	 * <strong>Should</strong> not set value coded name when add concept is selected
	 * <strong>Should</strong> fail when adding a duplicate synonym
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPTS)
	public Concept mapConceptProposalToConcept(ConceptProposal cp, Concept mappedConcept, Locale locale) throws APIException;
	
	/**
	 * Returns all possible Concepts to which this concept is a value-coded answer. To navigate in
	 * the other direction, i.e., from Concept to its answers use Concept.getAnswers()
	 * 
	 * @param concept
	 * @return A List&lt;Concept&gt; containing all possible questions to which this concept is a
	 *         valued_Coded answer
	 * @throws APIException
	 * <strong>Should</strong> return an empty list if concept id is null
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getConceptsByAnswer(Concept concept) throws APIException;
	
	/**
	 * Finds the previous concept in the dictionary that has the next lowest concept id
	 * 
	 * @param concept the offset Concept
	 * @return the foundConcept
	 * @throws APIException
	 * <strong>Should</strong> return the concept previous to the given concept
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getPrevConcept(Concept concept) throws APIException;
	
	/**
	 * Finds the next concept in the dictionary that has the next largest concept id
	 * 
	 * @param concept the offset Concept
	 * @return the foundConcept
	 * @throws APIException
	 * <strong>Should</strong> return the concept next to the given concept
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getNextConcept(Concept concept) throws APIException;
	
	/**
	 * Check if the concepts are locked and if so, throw exception during manipulation of concept
	 * 
	 * @throws ConceptsLockedException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public void checkIfLocked() throws ConceptsLockedException;
	
	/**
	 * Get ConceptProposal by its UUID
	 * 
	 * @param uuid
	 * @return concept proposal or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_PROPOSALS)
	public ConceptProposal getConceptProposalByUuid(String uuid);
	
	/**
	 * Convenience method for finding concepts associated with drugs in formulary.
	 * 
	 * @return A List&lt;Concept&gt; object of all concepts that occur as a Drug.concept.
	 * @throws APIException
	 * <strong>Should</strong> give a list of all matching concepts
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getConceptsWithDrugsInFormulary() throws APIException;
	
	/**
	 * Get ConceptNameTag by its UUID
	 * 
	 * @param uuid
	 * @return the conceptNameTag with a matching uuid
	 * @see Concept#setPreferredName(ConceptName)
	 * @see Concept#setFullySpecifiedName(ConceptName)
	 * @see Concept#setShortName(ConceptName)
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptNameTag getConceptNameTagByUuid(String uuid);
	
	/**
	 * Get a ComplexConcept with the given conceptId
	 * 
	 * @param conceptId of the ComplexConcept
	 * @return a ConceptComplex object
	 * @since 1.5
	 * <strong>Should</strong> return a concept complex object
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptComplex getConceptComplex(Integer conceptId);
	
	/**
	 * Search for a ConceptNameTag by name
	 * 
	 * @param tag String name of ConceptNameTag
	 * @return ConceptNameTag matching the given String tag
	 * @see Concept#getPreferredName(Locale)
	 * @see Concept#getFullySpecifiedName(Locale)
	 * @see Concept#getShortNameInLocale(Locale)
	 * @see Concept#getShortestName(Locale, Boolean)
	 */
	@Authorized({ PrivilegeConstants.GET_CONCEPTS })
	public ConceptNameTag getConceptNameTagByName(String tag);
	
	/**
	 * Gets the set of unique Locales used by existing concept names.
	 * 
	 * @return set of used Locales
	 * <strong>Should</strong> return a list of matching locales
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Set<Locale> getLocalesOfConceptNames();
	
	/**
	 * Return a list of concept sources currently in the database Whether or not to return retired
	 * concept sources is decided by the boolean includeRetired param
	 * 
	 * @param includeRetired whether or not to include retired sources
	 * @return List of Concept source objects
	 * <strong>Should</strong> return all concept sources including retired ones when given true
	 * <strong>Should</strong> return all concept sources excluding retired ones when given false
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_SOURCES)
	public List<ConceptSource> getAllConceptSources(boolean includeRetired) throws APIException;
	
	/**
	 * Return a Concept source matching the given concept source id
	 * 
	 * @param i Integer conceptSourceId
	 * @return ConceptSource
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_SOURCES)
	public ConceptSource getConceptSource(Integer i) throws APIException;
	
	/**
	 * Create a new ConceptSource
	 * 
	 * @param conceptSource ConceptSource to create
	 * @throws APIException
	 * <strong>Should</strong> not set creator if one is supplied already
	 * <strong>Should</strong> not set date created if one is supplied already
	 * <strong>Should</strong> save a ConceptSource with a null hl7Code
	 * <strong>Should</strong> not save a ConceptSource if voided is null
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_SOURCES)
	public ConceptSource saveConceptSource(ConceptSource conceptSource) throws APIException;
	
	/**
	 * Delete ConceptSource
	 * 
	 * @param cs ConceptSource object delete
	 * @throws APIException
	 * <strong>Should</strong> purge the given concept source
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPT_SOURCES)
	public ConceptSource purgeConceptSource(ConceptSource cs) throws APIException;
	
	/**
	 * This effectively removes a concept source from the database. The source can still be
	 * referenced by old data, but no new data should use this source.
	 * 
	 * @param cs the concept source to retire
	 * @param reason why the concept source is to be retired, must not be empty of null
	 * @return the retired concept source
	 * @throws APIException
	 * <strong>Should</strong> retire concept source
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPT_SOURCES)
	public ConceptSource retireConceptSource(ConceptSource cs, String reason) throws APIException;
	
	/**
	 * Creates a new Concept name tag if none exists. If a tag exists with the same name then that
	 * existing tag is returned.
	 * 
	 * @param nameTag the concept name tag to be saved
	 * @return the newly created or existing concept name tag
	 * <strong>Should</strong> save a concept name tag if tag does not exist
	 * <strong>Should</strong> not save a concept name tag if tag exists
	 * <strong>Should</strong> not save a concept name tag if tag is null, empty or whitespace
	 * <strong>Should</strong> save a concept name tag if tag is supplied
	 * <strong>Should</strong> save an edited concept name tag
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_NAME_TAGS)
	public ConceptNameTag saveConceptNameTag(ConceptNameTag nameTag);
	
	/**
	 * Gets the highest concept-id used by a concept.
	 * 
	 * @return highest concept-id
	 * <strong>Should</strong> give the maximum concept-id
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Integer getMaxConceptId();
	
	/**
	 * Returns an iterator for all concepts, including retired and expired.
	 * 
	 * @return the Iterator
	 * <strong>Should</strong> start with the smallest concept id
	 * <strong>Should</strong> iterate over all concepts
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Iterator<Concept> conceptIterator();
	
	/**
	 * Looks up a concept via {@link ConceptMap} This will return the {@link Concept} which contains
	 * a {@link ConceptMap} entry whose <code>sourceCode</code> is equal to the passed
	 * <code>conceptCode</code> and whose {@link ConceptSource} has either a <code>name</code> or
	 * <code>hl7Code</code> that is equal to the passed <code>mappingCode</code>. Delegates to
	 * getConceptByMapping(code,sourceName,includeRetired) with includeRetired=true
	 * 
	 * @param code the code associated with a concept within a given {@link ConceptSource}
	 * @param sourceName the name or hl7Code of the {@link ConceptSource} to check
	 * @return the {@link Concept} that has the given mapping, or null if no {@link Concept} found
	 * @throws APIException
	 * <strong>Should</strong> get concept with given code and and source hl7 code
	 * <strong>Should</strong> get concept with given code and source name
	 * <strong>Should</strong> return null if source code does not exist
	 * <strong>Should</strong> return null if mapping does not exist
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getConceptByMapping(String code, String sourceName) throws APIException;
	
	/**
	 * Looks up a concept via {@link ConceptMap} This will return the {@link Concept} which contains
	 * a {@link ConceptMap} entry whose <code>sourceCode</code> is equal to the passed
	 * <code>conceptCode</code> and whose {@link ConceptSource} has either a <code>name</code> or
	 * <code>hl7Code</code> that is equal to the passed <code>mappingCode</code> . Operates under
	 * the assumption that each mappingCode in a {@link ConceptSource} references one and only one
	 * non-retired {@link Concept}: if the underlying dao method returns more than one non-retired
	 * concept, this method will throw an exception; if the underlying dao method returns more than
	 * one concept, but only one non-retired concept, this method will return the non-retired
	 * concept; if the dao only returns retired concepts, this method will simply return the first
	 * concept in the list returns by the dao method; retired concepts can be excluded by setting
	 * the includeRetired parameter to false, but the above logic still applies
	 *
	 * @param code the code associated with a concept within a given {@link ConceptSource}
	 * @param sourceName the name or hl7Code of the {@link ConceptSource} to check
	 * @param includeRetired whether or not to include retired concepts
	 * @return the {@link Concept} that has the given mapping, or null if no {@link Concept} found
	 * @throws APIException
	 * <strong>Should</strong> get concept with given code and and source hl7 code
	 * <strong>Should</strong> get concept with given code and source name
	 * <strong>Should</strong> return null if source code does not exist
	 * <strong>Should</strong> return null if mapping does not exist
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getConceptByMapping(String code, String sourceName, Boolean includeRetired) throws APIException;
	
	/**
	 * Looks up a concept via {@link ConceptMap} This will return the list of concepts
	 * {@link Concept}s which contain a {@link ConceptMap} entry whose <code>sourceCode</code> is
	 * equal to the passed <code>conceptCode</code> and whose {@link ConceptSource} has either a
	 * <code>name</code> or <code>hl7Code</code> that is equal to the passed
	 * <code>mappingCode</code>
	 * 
	 * @param code the code associated with a concept within a given {@link ConceptSource}
	 * @param sourceName the name or hl7Code of the {@link ConceptSource} to check
	 * @return the list of non-voided {@link Concept}s that has the given mapping, or null if no
	 *         {@link Concept} found
	 * @throws APIException if the specified source+code maps to more than one concept
	 * <strong>Should</strong> get concepts with given code and and source hl7 code
	 * <strong>Should</strong> get concepts with given code and source name
	 * <strong>Should</strong> return empty list if source code does not exist
	 * <strong>Should</strong> return empty list if mapping does not exist
	 * <strong>Should</strong> include retired concepts
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getConceptsByMapping(String code, String sourceName) throws APIException;
	
	/**
	 * Looks up a concept via {@link ConceptMap} This will return the list of {@link Concept}s which
	 * contain a {@link ConceptMap} entry whose <code>sourceCode</code> is equal to the passed
	 * <code>conceptCode</code> and whose {@link ConceptSource} has either a <code>name</code> or
	 * <code>hl7Code</code> that is equal to the passed <code>mappingCode</code>. Delegates to
	 * getConceptsByMapping(code,sourceName,includeRetired) with includeRetired=true
	 * 
	 * @param code the code associated with a concept within a given {@link ConceptSource}
	 * @param sourceName the name or hl7Code of the {@link ConceptSource} to check
	 * @param includeRetired whether or not to include retired concepts
	 * @return the list of non-voided {@link Concept}s that has the given mapping, or null if no
	 *         {@link Concept} found
	 * @throws APIException if the specified source+code maps to more than one concept
	 * <strong>Should</strong> get concepts with given code and and source hl7 code
	 * <strong>Should</strong> get concepts with given code and source name
	 * <strong>Should</strong> return empty list if source code does not exist
	 * <strong>Should</strong> return empty list if mapping does not exist
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getConceptsByMapping(String code, String sourceName, boolean includeRetired) throws APIException;

	/**
	 * Looks up concepts via {@link ConceptMap} This will return the list of ids for all
	 * {@link Concept}s which contain a {@link ConceptMap} entry whose <code>sourceCode</code> is
	 * equal to the passed <code>conceptCode</code> and whose {@link ConceptSource} has either a
	 * <code>name</code> or <code>hl7Code</code> that is equal to the passed
	 * <code>mappingCode</code>
	 *
	 * @param code the code associated with a concept within a given {@link ConceptSource}
	 * @param sourceName the name or hl7Code of the {@link ConceptSource} to check
	 * @param includeRetired whether or not to include retired concepts
	 * @return the list ids for all non-voided {@link Concept}s that have the given mapping, or an empty List if none found
	 * @throws APIException if the specified source+code maps to more than one concept
	 * @should get concepts with given code and and source hl7 code
	 * @should get concepts with given code and source name
	 * @should return empty list if source code does not exist
	 * @should return empty list if mapping does not exist
	 * @should include retired concepts
	 * @since 2.3
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Integer> getConceptIdsByMapping(String code, String sourceName, boolean includeRetired) throws APIException;
	
	/**
	 * Get all the concept name tags defined in the database, included voided ones
	 * 
	 * @since 1.5
	 * @return a list of the concept name tags stored in the dataset
	 * <strong>Should</strong> return a list of all concept name tags
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptNameTag> getAllConceptNameTags();
	
	/**
	 * Gets the {@link ConceptNameTag} with the given database primary key
	 * 
	 * @param id the concept name tag id to find
	 * @return the matching {@link ConceptNameTag} or null if none found
	 * @since 1.5
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public ConceptNameTag getConceptNameTag(Integer id);
	
	/**
	 * Get ConceptDescription by its UUID
	 * 
	 * @param uuid
	 * @return concept description or null
	 * <strong>Should</strong> find object given valid uuid
	 * <strong>Should</strong> return null if no object found with given uuid
	 */
	@Authorized({ PrivilegeConstants.GET_CONCEPTS })
	public ConceptDescription getConceptDescriptionByUuid(String uuid);
	
	/**
	 * Lookup a ConceptSource by its name property
	 * 
	 * @param conceptSourceName
	 * @return ConceptSource
	 * @throws APIException
	 * <strong>Should</strong> get ConceptSource with the given name
	 * <strong>Should</strong> return null if no ConceptSource with that name is found
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_SOURCES)
	public ConceptSource getConceptSourceByName(String conceptSourceName) throws APIException;
	
	/**
	 * Get a ConceptSource by its unique id.
	 *
	 * @param uniqueId the unique id
	 * @return the concept source matching given unique id
	 * @throws APIException
	 * <strong>Should</strong> get concept source with the given unique id
	 * <strong>Should</strong> return null if no concept source with given unique id is found
	 * <strong>Should</strong> return null if given an empty string
	 * <strong>Should</strong> fail if given null
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_SOURCES)
	public ConceptSource getConceptSourceByUniqueId(String uniqueId) throws APIException;
	
	/**
	 * Get a ConceptSource by its hl7Code.
	 *
	 * @param hl7Code the hl7Code
	 * @return the concept source matching given hl7Code
	 * @throws APIException
	 * <strong>Should</strong> get concept source with the given hl7Code
	 * <strong>Should</strong> return null if no concept source with given hl7Code is found
	 * <strong>Should</strong> return null if given an empty string
	 * <strong>Should</strong> fail if given null
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_SOURCES)
	public ConceptSource getConceptSourceByHL7Code(String hl7Code) throws APIException;
	
	/**
	 * Checks if there are any observations (including voided observations) for a concept.
	 * 
	 * @param concept which used or not used by an observation
	 * @return boolean true if the concept is used by an observation
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public boolean hasAnyObservation(Concept concept);
	
	/**
	 * Returns the TRUE concept
	 * 
	 * @return true concept
	 * <strong>Should</strong> return the true concept
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getTrueConcept();
	
	/**
	 * Returns the FALSE concept
	 * 
	 * @return false concept
	 * <strong>Should</strong> return the false concept
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getFalseConcept();
	
	/**
	 * Returns the UNKNOWN concept
	 *
	 * @return unknown concept
	 * <strong>Should</strong> return the unknown concept
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Concept getUnknownConcept();
	
	/**
	 * Changes the datatype of a concept from boolean to coded when it has observations it is
	 * associated to.
	 * 
	 * @param conceptToChange the concept which to change
	 * @throws APIException
	 * <strong>Should</strong> convert the datatype of a boolean concept to coded
	 * <strong>Should</strong> fail if the datatype of the concept is not boolean
	 * <strong>Should</strong> explicitly add true concept as a value_Coded answer
	 * <strong>Should</strong> explicitly add false concept as a value_Coded answer
	 */
	@Authorized({ PrivilegeConstants.MANAGE_CONCEPTS })
	public void convertBooleanConceptToCoded(Concept conceptToChange) throws APIException;
	
	/**
	 * Checks if there are any observations (including voided observations) using a conceptName.
	 * 
	 * @param conceptName which is used or not used by an observation
	 * @return boolean true if the conceptName is used by an observation otherwise false
	 * @throws APIException
	 * @since Version 1.7
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public boolean hasAnyObservation(ConceptName conceptName) throws APIException;
	
	/**
	 * Searches for concepts by the given parameters.
	 * 
	 * @param phrase matched to the start of any word in any of the names of a concept (if
	 *            blank/null, matches all concepts)
	 * @param locales List&lt;Locale&gt; to restrict to
	 * @param includeRetired boolean if false, will exclude retired concepts
	 * @param requireClasses List&lt;ConceptClass&gt; to restrict to
	 * @param excludeClasses List&lt;ConceptClass&gt; to leave out of results
	 * @param requireDatatypes List&lt;ConceptDatatype&gt; to restrict to
	 * @param excludeDatatypes List&lt;ConceptDatatype&gt; to leave out of results
	 * @param answersToConcept all results will be a possible answer to this concept
	 * @param start all results less than this number will be removed
	 * @param size if non zero, all results after <code>start</code> + <code>size</code> will be
	 *            removed
	 * @return a list of conceptSearchResults
	 * @throws APIException
	 * <strong>Should</strong> return concept search results that match unique concepts
	 * <strong>Should</strong> return a search result whose concept name contains all word tokens as first
	 * <strong>Should</strong> return a search result for phrase with stop words
	 * <strong>Should</strong> not return concepts with matching names that are voided
	 * <strong>Should</strong> return preferred names higher
	 * <strong>Should</strong> find concept by full code
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptSearchResult> getConcepts(String phrase, List<Locale> locales, boolean includeRetired,
	        List<ConceptClass> requireClasses, List<ConceptClass> excludeClasses, List<ConceptDatatype> requireDatatypes,
	        List<ConceptDatatype> excludeDatatypes, Concept answersToConcept, Integer start, Integer size)
	                throws APIException;
					
	/**
	 * Finds concepts that are possible value coded answers to concept parameter
	 * 
	 * @param phrase
	 * @param locale
	 * @param concept the answers to match on
	 * @return a list of conceptSearchResults
	 * @throws APIException
	 * <strong>Should</strong> return a list of all matching concept search results
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptSearchResult> findConceptAnswers(String phrase, Locale locale, Concept concept) throws APIException;
	
	/**
	 * Iterates over the words in names and synonyms (for each locale) and updates the concept
	 * index, note that this only updates the index of the specified concept. Use
	 * {@link ConceptService#updateConceptIndexes()} if you wish to update the entire concept index.
	 * 
	 * @param concept the concept whose index is to be updated
	 * @throws APIException
	 * @since 1.8
	 */
	@Authorized({ PrivilegeConstants.MANAGE_CONCEPTS })
	public void updateConceptIndex(Concept concept) throws APIException;
	
	/**
	 * Iterates over all concepts and calls updateConceptIndexes(Concept concept)
	 * 
	 * @throws APIException
	 * @since 1.8
	 */
	@Authorized({ PrivilegeConstants.MANAGE_CONCEPTS })
	public void updateConceptIndexes() throws APIException;
	
	/**
	 * Searches for concepts with the given parameters
	 * 
	 * @param phrase the string to search against (if blank/null, matches all concepts)
	 * @param locale the locale in which to search for the concepts
	 * @param includeRetired Specifies whether to include retired concepts
	 * @return a list ConceptSearchResults
	 * @throws APIException
	 * <strong>Should</strong> give a list of ConceptSearchResult for the matching Concepts
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptSearchResult> getConcepts(String phrase, Locale locale, boolean includeRetired) throws APIException;
	
	/**
	 * Return the number of concepts matching a search phrase and the specified arguments
	 * 
	 * @param phrase matched to the start of any word in any of the names of a concept
	 * @param locales List&lt;Locale&gt; to restrict to
	 * @param includeRetired Specifies whether to include retired concepts
	 * @param requireClasses List&lt;ConceptClass&gt; to restrict to
	 * @param excludeClasses List&lt;ConceptClass&gt; to leave out of results
	 * @param requireDatatypes List&lt;ConceptDatatype&gt; to restrict to
	 * @param excludeDatatypes List&lt;ConceptDatatype&gt; to leave out of results
	 * @param answersToConcept all results will be a possible answer to this concept
	 * @return the number of concepts matching the given search phrase
	 * @throws APIException
	 * @since 1.8
	 * <strong>Should</strong> return a count of unique concepts
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Integer getCountOfConcepts(String phrase, List<Locale> locales, boolean includeRetired,
	        List<ConceptClass> requireClasses, List<ConceptClass> excludeClasses, List<ConceptDatatype> requireDatatypes,
	        List<ConceptDatatype> excludeDatatypes, Concept answersToConcept);
			
	/**
	 * Return the number of drugs with matching names or concept drug names
	 * 
	 * @param drugName the name of the drug
	 * @param concept the drug concept
	 * @param searchOnPhrase Specifies if the search should match names starting with or contain the
	 *            text
	 * @param searchDrugConceptNames Specifies whether a search on concept names for the drug's
	 *            concept should be done or not
	 * @param includeRetired specifies whether to include retired drugs
	 * @return the number of matching drugs
	 * @throws APIException
	 * <strong>Should</strong> return the total number of matching drugs
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Integer getCountOfDrugs(String drugName, Concept concept, boolean searchOnPhrase, boolean searchDrugConceptNames,
	        boolean includeRetired) throws APIException;
			
	/**
	 * Returns a list of drugs with matching names or concept drug names and returns a specific
	 * number of them from the specified starting position. If start and length are not specified,
	 * then all matches are returned
	 * 
	 * @param drugName the name of the drug
	 * @param concept the drug concept
	 * @param searchKeywords (since 1.11) Specifies whether the search should match keywords or just
	 *            phrase
	 * @param searchDrugConceptNames Specifies whether a search on concept names for the drug's
	 *            concept should be done or not
	 * @param includeRetired specifies whether to include retired drugs
	 * @param start beginning index for the batch
	 * @param length number of drugs to return in the batch
	 * @return a list of matching drugs
	 * @throws APIException
	 * @since 1.8
	 * <strong>Should</strong> return a list of matching drugs
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Drug> getDrugs(String drugName, Concept concept, boolean searchKeywords, boolean searchDrugConceptNames,
	        boolean includeRetired, Integer start, Integer length) throws APIException;
			
	/**
	 * Gets the list of <code>ConceptStopWord</code> for given locale
	 * 
	 * @param locale The locale in which to search for the <code>ConceptStopWord</code>
	 * @return list of concept stop words for given locale
	 * <strong>Should</strong> return list of concept stop words for given locale
	 * <strong>Should</strong> return empty list if no stop words are found for the given locale
	 * <strong>Should</strong> return default Locale <code>ConceptStopWord</code> if Locale is null
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<String> getConceptStopWords(Locale locale);
	
	/**
	 * Save the given <code>ConceptStopWord</code> in the database
	 * <p>
	 * If this is a new concept stop word, the returned concept stop word will have a new
	 * {@link org.openmrs.ConceptStopWord#getConceptStopWordId()} inserted into it that was
	 * generated by the database
	 * </p>
	 * 
	 * @param conceptStopWord The <code>ConceptStopWord</code> to save or update
	 * @return the <code>ConceptStopWord</code> that was saved or updated
	 * @throws APIException
	 * <strong>Should</strong> generated concept stop word id onto returned concept stop word
	 * <strong>Should</strong> save concept stop word into database
	 * <strong>Should</strong> assign default Locale
	 * <strong>Should</strong> save concept stop word in uppercase
	 * <strong>Should</strong> fail if a duplicate conceptStopWord in a locale is added
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_STOP_WORDS)
	public ConceptStopWord saveConceptStopWord(ConceptStopWord conceptStopWord) throws APIException;
	
	/**
	 * Delete the given <code>ConceptStopWord</code> in the database
	 * 
	 * @param conceptStopWordId The <code>ConceptStopWord</code> to delete
	 * @throws APIException
	 * <strong>Should</strong> delete the given concept stop word from the database
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_STOP_WORDS)
	public void deleteConceptStopWord(Integer conceptStopWordId) throws APIException;
	
	/**
	 * Get all the concept stop words
	 * 
	 * @return List of <code>ConceptStopWord</code>
	 * <strong>Should</strong> return all the concept stop words
	 * <strong>Should</strong> return empty list if nothing found
	 * @since 1.8
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptStopWord> getAllConceptStopWords();
	
	/**
	 * Gets drugs by the given ingredient, which can be either the drug itself or any ingredient.
	 * 
	 * @return the list of drugs
	 * <strong>Should</strong> return drugs matched by intermediate concept
	 * <strong>Should</strong> return drugs matched by drug concept
	 * <strong>Should</strong> return empty list if nothing found
	 * <strong>Should</strong> raise exception if no concept is given
	 * @since 1.10
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Drug> getDrugsByIngredient(Concept ingredient);
	
	/**
	 * Returns a list of concept map types currently in the database excluding hidden ones
	 * 
	 * @return List of concept map type objects
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return all the concept map types excluding hidden ones
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_MAP_TYPES)
	public List<ConceptMapType> getActiveConceptMapTypes() throws APIException;
	
	/**
	 * Returns a list of concept map types currently in the database including or excluding retired
	 * and hidden ones as specified by the includeRetired and includeHidden arguments
	 * 
	 * @param includeRetired specifies if retired concept map types should be included
	 * @return List of concept map type objects
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return all the concept map types if includeRetired and hidden are set to true
	 * <strong>Should</strong> return only un retired concept map types if includeRetired is set to false
	 * <strong>Should</strong> not include hidden concept map types if includeHidden is set to false
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_MAP_TYPES)
	public List<ConceptMapType> getConceptMapTypes(boolean includeRetired, boolean includeHidden) throws APIException;
	
	/**
	 * Return a concept map type matching the given concept map type id
	 * 
	 * @param conceptMapTypeId Integer concept map type id
	 * @return ConceptMapType
	 * @since 1.9
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_MAP_TYPES)
	public ConceptMapType getConceptMapType(Integer conceptMapTypeId) throws APIException;
	
	/**
	 * Return a concept map type matching the given uuid
	 * 
	 * @param uuid the uuid to search against
	 * @return ConceptMapType
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return a conceptMapType matching the specified uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_MAP_TYPES)
	public ConceptMapType getConceptMapTypeByUuid(String uuid) throws APIException;
	
	/**
	 * Return a concept map type matching the given name
	 * 
	 * @param name the name to search against
	 * @return ConceptMapType
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return a conceptMapType matching the specified name
	 * <strong>Should</strong> be case insensitive
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_MAP_TYPES)
	public ConceptMapType getConceptMapTypeByName(String name) throws APIException;
	
	/**
	 * Saves or updates the specified concept map type in the database
	 * 
	 * @param conceptMapType the concept map type to save
	 * @return the saved conceptMapType
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> add the specified concept map type to the database and assign to it an id
	 * <strong>Should</strong> update an existing concept map type
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_MAP_TYPES)
	public ConceptMapType saveConceptMapType(ConceptMapType conceptMapType) throws APIException;
	
	/**
	 * Retiring a concept map type essentially removes it from circulation
	 * 
	 * @param conceptMapType the concept map type to retire
	 * @param retireReason the reason why the concept map type is being retired
	 * @return the retired concept map type
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> retire the specified conceptMapType with the given retire reason
	 * <strong>Should</strong> should set the default retire reason if none is given
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_MAP_TYPES)
	public ConceptMapType retireConceptMapType(ConceptMapType conceptMapType, String retireReason) throws APIException;
	
	/**
	 * Marks a concept map type that is currently retired as not retired.
	 * 
	 * @param conceptMapType the concept map type to unretire
	 * @return the unretired concept map type
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> unretire the specified concept map type and drop all retire related fields
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_MAP_TYPES)
	public ConceptMapType unretireConceptMapType(ConceptMapType conceptMapType) throws APIException;
	
	/**
	 * Completely purges a concept map type from the database
	 * 
	 * @param conceptMapType the concept map type to purge from the database
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> delete the specified conceptMapType from the database
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPT_MAP_TYPES)
	public void purgeConceptMapType(ConceptMapType conceptMapType) throws APIException;
	
	/**
	 * Returns a list of mappings from concepts to terms in the given reference terminology
	 * 
	 * @param conceptSource
	 * @return a List&lt;ConceptMap&gt; object
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return a List of ConceptMaps from the given source
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptMap> getConceptMappingsToSource(ConceptSource conceptSource) throws APIException;
	
	/**
	 * Gets a list of all concept reference terms saved in the database
	 * 
	 * @return a list of concept reference terms
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return all concept reference terms in the database
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public List<ConceptReferenceTerm> getAllConceptReferenceTerms() throws APIException;
	
	/**
	 * Gets a list of concept reference terms saved in the database
	 * 
	 * @param includeRetired specifies if retired concept reference terms should be included
	 * @return a list of concept reference terms
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return all the concept reference terms if includeRetired is set to true
	 * <strong>Should</strong> return only un retired concept reference terms if includeRetired is set to false
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public List<ConceptReferenceTerm> getConceptReferenceTerms(boolean includeRetired) throws APIException;
	
	/**
	 * Gets the concept reference term with the specified concept reference term id
	 * 
	 * @param conceptReferenceTermId the concept reference term id to search against
	 * @return the concept reference term object with the given concept reference term id
	 * @since 1.9
	 * @throws APIException
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public ConceptReferenceTerm getConceptReferenceTerm(Integer conceptReferenceTermId) throws APIException;
	
	/**
	 * Gets the concept reference term with the specified uuid
	 * 
	 * @param uuid the uuid to search against
	 * @return the concept reference term object with the given uuid
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return the concept reference term that matches the given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public ConceptReferenceTerm getConceptReferenceTermByUuid(String uuid) throws APIException;
	
	/**
	 * Gets a concept reference term with the specified name from the specified concept source
	 * ignoring all retired ones
	 * 
	 * @param name the name to match against
	 * @param conceptSource the concept source to match against
	 * @return concept reference term object
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return a concept reference term that matches the given name from the given source
	 * <strong>Should</strong> be case insensitive
	 * <strong>Should</strong> return null if no concept reference term is found
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public ConceptReferenceTerm getConceptReferenceTermByName(String name, ConceptSource conceptSource) throws APIException;
	
	/**
	 * Gets a concept reference term with the specified code from the specified concept source
	 * 
	 * @param code the code to match against
	 * @param conceptSource the concept source to match against
	 * @return concept reference term object
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return a concept reference term that matches the given code from the given source
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public ConceptReferenceTerm getConceptReferenceTermByCode(String code, ConceptSource conceptSource) throws APIException;

	/**
	 * Gets a list of concept reference terms with the specified code from the specified concept source
	 *
	 * @param code the code to match against
	 * @param conceptSource the concept source to match against
	 * @param includeRetired specifies if retired concept reference terms should be included
	 * @return concept reference term object
	 * @since 2.7
	 * @throws APIException
	 * <strong>Should</strong> return a list of concept reference terms that matches the given code from the given source
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public List<ConceptReferenceTerm> getConceptReferenceTermByCode(String code, ConceptSource conceptSource, boolean includeRetired) throws APIException;


	/**
	 * Stores the specified concept reference term to the database
	 * 
	 * @param conceptReferenceTerm the concept reference term object to save
	 * @return the saved concept reference term object
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> add a concept reference term to the database and assign an id to it
	 * <strong>Should</strong> update changes to the concept reference term in the database
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_REFERENCE_TERMS)
	public ConceptReferenceTerm saveConceptReferenceTerm(ConceptReferenceTerm conceptReferenceTerm) throws APIException;
	
	/**
	 * Retiring a concept reference term essentially removes it from circulation
	 * 
	 * @param conceptReferenceTerm the concept reference term object to retire
	 * @param retireReason the reason why the concept reference term is being retired
	 * @return the retired concept reference term object
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> retire the specified concept reference term with the given retire reason
	 * <strong>Should</strong> should set the default retire reason if none is given
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_REFERENCE_TERMS)
	public ConceptReferenceTerm retireConceptReferenceTerm(ConceptReferenceTerm conceptReferenceTerm, String retireReason)
	        throws APIException;
			
	/**
	 * Marks a concept reference term that is currently retired as not retired.
	 * 
	 * @param conceptReferenceTerm the concept reference term to unretire
	 * @return the unretired concept reference term
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> unretire the specified concept reference term and drop all retire related fields
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_REFERENCE_TERMS)
	public ConceptReferenceTerm unretireConceptReferenceTerm(ConceptReferenceTerm conceptReferenceTerm) throws APIException;
	
	/**
	 * Purges the specified concept reference term from the database
	 * 
	 * @param conceptReferenceTerm the concept reference term object to purge
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> purge the given concept reference term
	 * <strong>Should</strong> fail if given concept reference term is in use
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPT_REFERENCE_TERMS)
	public void purgeConceptReferenceTerm(ConceptReferenceTerm conceptReferenceTerm) throws APIException;
	
	/**
	 * Finds the concept reference term in the database that have a code or name that contains the
	 * specified search phrase.
	 * 
	 * @param query the string to match against the reference term names or codes
	 * @param conceptSource the concept source from which the terms should be looked up
	 * @param start beginning index for the batch
	 * @param length number of terms to return in the batch
	 * @param includeRetired specifies if the retired terms should be included
	 * @return a list if {@link ConceptReferenceTerm}s
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return unique terms with a code or name containing the search phrase
	 * <strong>Should</strong> return only the concept reference terms from the given concept source
	 */
	@Authorized({ PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS })
	public List<ConceptReferenceTerm> getConceptReferenceTerms(String query, ConceptSource conceptSource, Integer start,
	        Integer length, boolean includeRetired) throws APIException;
			
	/**
	 * Returns the count of concept reference terms that match the specified arguments
	 * 
	 * @param query the string to match against the reference term names
	 * @param conceptSource the concept source from which the terms should be looked up
	 * @param includeRetired specifies if retired concept reference terms should be included
	 * @return the count of matching concept reference terms
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> include retired terms if includeRetired is set to true
	 * <strong>Should</strong> not include retired terms if includeRetired is set to false
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public Integer getCountOfConceptReferenceTerms(String query, ConceptSource conceptSource, boolean includeRetired)
	        throws APIException;
			
	/**
	 * Fetches all the {@link ConceptReferenceTermMap} where the specified reference term is the
	 * termB i.e mappings added to other terms pointing to it
	 * 
	 * @param term the term to match against
	 * @return a list of {@link ConceptReferenceTermMap}s
	 * @since 1.9
	 * @throws APIException
	 * <strong>Should</strong> return all concept reference term maps where the specified term is the termB
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_REFERENCE_TERMS)
	public List<ConceptReferenceTermMap> getReferenceTermMappingsTo(ConceptReferenceTerm term) throws APIException;
	
	/**
	 * Returns a list of concepts with the same name in the given locale.
	 * <p>
	 * This method is case insensitive. It searches for exactly matching names and close matching
	 * locales (if exactLocale = false). It considers only non-voided names and all concepts.
	 * 
	 * @param name
	 * @param locale <code>null</code> = all locales
	 * @param exactLocale <code>false</code> if search for both global and country specific,
	 *            <code>true</code> if <code>null</code>
	 * @return the list of concepts
	 * @throws APIException
	 * @since 1.9, 1.8.4
	 * <strong>Should</strong> return concepts for all countries and global language given language only locale
	 * <strong>Should</strong> return concepts for specific country and global language given language and country
	 *         locale
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Concept> getConceptsByName(String name, Locale locale, Boolean exactLocale) throws APIException;
	
	/**
	 * Gets the concept map type to be used as the default. It is specified by the
	 * <code>concept.defaultConceptMapType</code> global property.
	 * 
	 * @since 1.9
	 * @return the {@link ConceptMapType}
	 * @throws APIException
	 * <strong>Should</strong> return same as by default
	 * <strong>Should</strong> return type as set in gp
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_MAP_TYPES)
	public ConceptMapType getDefaultConceptMapType() throws APIException;
	
	/**
	 * Determines if the given concept name is a duplicate.
	 * <p>
	 * Concept name is considered duplicate if it is a default non-retired name for a non-voided
	 * concept and there is another name, which is:
	 * <ol>
	 * <li>equal ignoring case</li>
	 * <li>non voided</li>
	 * <li>in same locale or in same general language</li>
	 * <li>non-retired and different concept</li>
	 * <li>default name for concept</li>
	 * </ol>
	 * 
	 * @param name
	 * @return true if it is a duplicate name
	 * @since 1.11
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public boolean isConceptNameDuplicate(ConceptName name);
	
	/**
	 * Fetches un retired drugs that match the specified search phrase. The logic matches on drug
	 * names, concept names of the associated concepts or the concept reference term codes of the
	 * drug reference term mappings
	 * 
	 * @param searchPhrase The string to match against
	 * @param locale The locale to match against when searching in drug concept names
	 * @param exactLocale If false then concepts with names in a broader locale will be matched e.g
	 *            in case en_GB is passed in then en will be matched
	 * @param includeRetired Specifies if retired drugs that match should be included or not
	 * @return A list of matching drugs
	 * @since 1.10
	 * <strong>Should</strong> get drugs with names matching the search phrase
	 * <strong>Should</strong> include retired drugs if includeRetired is set to true
	 * <strong>Should</strong> get drugs linked to concepts with names that match the phrase
	 * <strong>Should</strong> get drugs linked to concepts with names that match the phrase and locale
	 * <strong>Should</strong> get drugs linked to concepts with names that match the phrase and related locales
	 * <strong>Should</strong> get drugs that have mappings with reference term codes that match the phrase
	 * <strong>Should</strong> return unique drugs
	 * <strong>Should</strong> return all drugs with a matching term code or drug name or concept name
	 * <strong>Should</strong> reject a null search phrase
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Drug> getDrugs(String searchPhrase, Locale locale, boolean exactLocale, boolean includeRetired);
	
	/**
	 * Fetches all drugs with reference mappings to the specified concept source that match the
	 * specified code and concept map types
	 * 
	 * @param code the code the reference term code to match on
	 * @param conceptSource the concept source on which to match on
	 * @param withAnyOfTheseTypes the ConceptMapTypes to match on
	 * @param includeRetired specifies if retired drugs should be included or not
	 * @since 1.10
	 * @return the list of {@link Drug}
	 * @throws APIException
	 * <strong>Should</strong> get a list of all drugs that match on all the parameter values
	 * <strong>Should</strong> return retired and non-retired drugs if includeRetired is set to true
	 * <strong>Should</strong> return empty list if no matches are found
	 * <strong>Should</strong> match on the code
	 * <strong>Should</strong> match on the concept source
	 * <strong>Should</strong> match on the map types
	 * <strong>Should</strong> fail if no code and concept source and withAnyOfTheseTypes are provided
	 * <strong>Should</strong> exclude duplicate matches
	 * <strong>Should</strong> fail if source is null
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<Drug> getDrugsByMapping(String code, ConceptSource conceptSource,
	        Collection<ConceptMapType> withAnyOfTheseTypes, boolean includeRetired) throws APIException;
			
	/**
	 * Gets the "best" matching drug, i.e. matching the earliest ConceptMapType passed in e.g.
	 * getDrugByMapping("12345", rxNorm, Arrays.asList(sameAs, narrowerThan)) If there are multiple
	 * matches for the highest-priority ConceptMapType, throw an exception
	 * 
	 * @param code the code the reference term code to match on
	 * @param conceptSource the concept source to match on
	 * @param withAnyOfTheseTypesOrOrderOfPreference the ConceptMapTypes to match on
	 * @since 1.10
	 * @return the {@link Drug}
	 * @throws APIException
	 * <strong>Should</strong> return a drug that matches the code and source
	 * <strong>Should</strong> return a drug that matches the code and source and the best map type
	 * <strong>Should</strong> fail if multiple drugs are found matching the best map type
	 * <strong>Should</strong> return null if no match found
	 * <strong>Should</strong> fail if no code and concept source and withAnyOfTheseTypes are provided
	 * <strong>Should</strong> fail if source is null
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public Drug getDrugByMapping(String code, ConceptSource conceptSource,
	        Collection<ConceptMapType> withAnyOfTheseTypesOrOrderOfPreference) throws APIException;
			
	/**
	 * An Orderable concept is one where its conceptClass has a mapping in the order_type_class_map
	 * table. This method searches for orderable concepts which match the specified arguments
	 * 
	 * @param phrase the phrase to match on
	 * @param locales list of locales to match on
	 * @param includeRetired include retired concepts or not
	 * @param start start index of search results
	 * @param length number of concept results to be returned
	 * @return List of ConceptSearchResults
	 * @since 1.10
	 * <strong>Should</strong> get orderable concepts
	 * <strong>Should</strong> return an empty list if no concept search result is found
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	public List<ConceptSearchResult> getOrderableConcepts(String phrase, List<Locale> locales, boolean includeRetired,
	        Integer start, Integer length);
			
	/**
	 * @return all {@link ConceptAttributeType}s
	 * @since 2.0
	 * <strong>Should</strong> return all concept attribute types including retired ones
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_ATTRIBUTE_TYPES)
	public List<ConceptAttributeType> getAllConceptAttributeTypes();
	
	/**
	 * Creates or updates the given concept attribute type in the database
	 *
	 * @param conceptAttributeType
	 * @return the ConceptAttributeType created/saved
	 * @since 2.0
	 * <strong>Should</strong> create a new concept attribute type
	 * <strong>Should</strong> edit an existing concept attribute type
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_ATTRIBUTE_TYPES)
	public ConceptAttributeType saveConceptAttributeType(ConceptAttributeType conceptAttributeType);
	
	/**
	 * @param id
	 * @return the {@link ConceptAttributeType} with the given internal id
	 * @since 2.0
	 * <strong>Should</strong> return the concept attribute type with the given id
	 * <strong>Should</strong> return null if no concept attribute type exists with the given id
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_ATTRIBUTE_TYPES)
	public ConceptAttributeType getConceptAttributeType(Integer id);
	
	/**
	 * @param uuid
	 * @return the {@link ConceptAttributeType} with the given uuid
	 * @since 2.0
	 * <strong>Should</strong> return the concept attribute type with the given uuid
	 * <strong>Should</strong> return null if no concept attribute type exists with the given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPT_ATTRIBUTE_TYPES)
	public ConceptAttributeType getConceptAttributeTypeByUuid(String uuid);
	
	/**
	 * Completely removes a concept attribute type from the database
	 *
	 * @param conceptAttributeType
	 * @since 2.0
	 * <strong>Should</strong> completely remove a concept attribute type
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPT_ATTRIBUTE_TYPES)
	void purgeConceptAttributeType(ConceptAttributeType conceptAttributeType);
	
	/**
	 * Find concept attribute types matching the given parameters. Retired types are included in the
	 * results
	 *
	 * @param name (optional) The name of type
	 * @return list of ConceptAttributeTypes that matches <em>name</em> partially or completely
	 * @since 2.0
	 * @throws APIException
	 * <strong>Should</strong> return concept attribute types performing fuzzy match on given name
	 * <strong>Should</strong> return empty list when no concept attribute types match given name
	 */
	@Authorized({ PrivilegeConstants.GET_CONCEPT_ATTRIBUTE_TYPES })
	public List<ConceptAttributeType> getConceptAttributeTypes(String name) throws APIException;
	
	/**
	 * Retrieves a ConceptAttributeType object based on the name provided
	 *
	 * @param exactName
	 * @return the {@link ConceptAttributeType} with the specified name
	 * @since 2.0
	 * <strong>Should</strong> return the concept attribute type with the exact specified name
	 * <strong>Should</strong> return null if no concept attribute type exists with the exact specified name
	 */
	@Authorized({ PrivilegeConstants.GET_CONCEPT_ATTRIBUTE_TYPES })
	public ConceptAttributeType getConceptAttributeTypeByName(String exactName);
	
	/**
	 * Retire a concept attribute type
	 *
	 * @param conceptAttributeType the concept attribute type to be retired
	 * @param reason for retiring the concept attribute type
	 * @return the retired concept attribute type
	 * @since 2.0
	 * <strong>Should</strong> retire concept type attribute
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_ATTRIBUTE_TYPES)
	public ConceptAttributeType retireConceptAttributeType(ConceptAttributeType conceptAttributeType, String reason);
	
	/**
	 * Un-Retire a concept attribute type
	 *
	 * @param conceptAttributeType the concept type attribute to unretire
	 * @return the unretire concept attribute type
	 * @since 2.0
	 * <strong>Should</strong> unretire a concept attribute type
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPT_ATTRIBUTE_TYPES)
	public ConceptAttributeType unretireConceptAttributeType(ConceptAttributeType conceptAttributeType);
	
	/**
	 * @param uuid
	 * @return the {@link ConceptAttribute} with the given uuid
	 * @since 2.0
	 * <strong>Should</strong> get the concept attribute with the given uuid
	 * <strong>Should</strong> return null if no concept attribute has the given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	ConceptAttribute getConceptAttributeByUuid(String uuid);
	
	/**
	 * @param conceptAttributeType
	 * @since 2.0 Checks if there are any concept attributes (including voided attributes) for a
	 *        concept attribute type.
	 * @return boolean true if the concept attribute type is used by a concept
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	boolean hasAnyConceptAttribute(ConceptAttributeType conceptAttributeType);

	/**
	 * Creates or updates the given {@link ConceptReferenceRange} in the database
	 *
	 * @param conceptReferenceRange ConceptReferenceRange to save
	 * @return the created ConceptReferenceRange
	 * 
	 * @since 2.7.0
	 * 
	 * <strong>Should</strong> create a new concept reference range
	 * <strong>Should</strong> edit an existing concept reference range
	 */
	@Authorized(PrivilegeConstants.MANAGE_CONCEPTS)
	ConceptReferenceRange saveConceptReferenceRange(ConceptReferenceRange conceptReferenceRange);

	/**
	 * This method gets ConceptReferenceRange by concept id
	 * 
	 * @param conceptId conceptId 
	 * @return list of {@link ConceptReferenceRange}
	 *
	 * @since 2.7.0
	 *
	 * <strong>Should</strong> get a list of conceptReferenceRanges with the given conceptId
	 * <strong>Should</strong> return empty list if none of conceptReferenceRanges has the given conceptId
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	List<ConceptReferenceRange> getConceptReferenceRangesByConceptId(Integer conceptId);

	/**
	 * This method gets ConceptReferenceRange by uuid
	 *
	 * @param uuid uuid 
	 * @return {@link ConceptReferenceRange}
	 *
	 * @since 2.7.0
	 *
	 * <strong>Should</strong> get a conceptReferenceRange if found
	 * <strong>Should</strong> return null if no conceptReferenceRange was found with the given uuid
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	ConceptReferenceRange getConceptReferenceRangeByUuid(String uuid);
	
	/**
	 * Get the appropriate concept reference range for a concept and person
	 * 
	 * @param person The person
	 * @param concept The concept
	 * @return the matching ConceptReferenceRange object
	 * 
	 * @since 2.7.0
	 */
	@Authorized(PrivilegeConstants.GET_CONCEPTS)
	ConceptReferenceRange getConceptReferenceRange(Person person, Concept concept);
	
	/**
	 * Completely purge a <code>ConceptReferenceRange</code> from the database.
	 * 
	 * @param conceptReferenceRange The <code>ConceptReferenceRange</code> to remove from the system
	 * 
	 * @since 2.7.0
	 */
	@Authorized(PrivilegeConstants.PURGE_CONCEPTS)
	void purgeConceptReferenceRange(ConceptReferenceRange conceptReferenceRange);
}
