package gov.va.med.cds.clinicaldata;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Element;

/**
 * Class used to locate a clinical record's recordIdentifier.identity element value as well
 * as any child element or elements' recordIdentifier.identity values.
 * 
 * This class is a necessary work-around to the Hibernate framework's limitations in mapping the <id> element to our <recordIdentifier.identity> element.
 * When performing an update using the dynamic model, Hibernate requires the <id> to be present in the xml in order to pre-select and locate the record.
 * 
 * This utility will locate through dom4j api calls the owning parent and if configured its child elements' recordIdentifier.identity and insert an <id> with the identity value
 * into the XML before forwarding an UPDATE to the Hibernate framework.
 * 
 * @author vhaisltalbom
 *
 */
public class DomainClinicalRecordElementHelper implements DomainClinicalRecordElementHelperInterface
{
    //used to find the domain's or clinical record's recordIdentifier.identity - which may be found within an inner component.element of the clinical record owner element
    private String domainIdentityOwner;
  
    //used to obtain assigningFacility and assigningAuthority within the clinical record
    private String facilityOwner;
    private String authorityOwner;
    
    /*
     * the keyset is the collection name/names if there is more than one child collection, 
     * the value is the path to the identity of the child element
     */
    private Map<String, String> childCollectionsAndIdentityOwners;
    
    public void setDomainIdentityOwner( String domainIdentityOwner )
    {
        this.domainIdentityOwner = domainIdentityOwner;
    }

    public Map<String, String> getChildCollectionsAndIdentityOwners( )
    {
        return childCollectionsAndIdentityOwners;
    }

    public void setChildCollectionsAndIdentityOwners( Map<String, String> childCollectionsAndIdentityOwners )
    {
        this.childCollectionsAndIdentityOwners = childCollectionsAndIdentityOwners;
    }
    
    public void setFacilityOwner( String facilityOwner )
    {
        this.facilityOwner = facilityOwner;
    }

    public void setAuthorityOwner( String authorityOwner )
    {
        this.authorityOwner = authorityOwner;
    }

    
    public String addIdToClinicalRecordElement(Element clinicalRecordElement)
    {
        
        String parentResultId = null;
        //add <id> to clinical recrod element - a Hibernate Entity -  with the discovered identity element's value from an XPath like element name.
        Element recordId = discoverOwnerElement(this.domainIdentityOwner, clinicalRecordElement);
        if(recordId != null)
        {
            parentResultId = recordId.getStringValue();
            clinicalRecordElement.addElement( "id" ).addText( parentResultId);         
        }
        
        return parentResultId; 
    }
    
    public String addIdToParentAndChildElements(Element parentElement)
    {
        
        String parentResultId = null;
        //First add <id> to parent element - a Hibernate Entity -  with the discovered identity element's value from an XPath like element name.
        Element recordId = discoverOwnerElement(this.domainIdentityOwner, parentElement);
        if(recordId != null)
        {
            parentResultId = recordId.getStringValue();
            parentElement.addElement( "id" ).addText( parentResultId);
            
            if(this.childCollectionsAndIdentityOwners != null)
            {
                /* next add an id to each child element of each child collection if a recordIdentifier.identity exists
                 * some child element recordIdentifers may be empty which means they are additions to the parent and not updates.
                 */
                
                //1. get the list of child collection names - the keysets are a list of the collections, which may be contained inside complex components of the parentElement
                Set<String> childCollections = this.childCollectionsAndIdentityOwners.keySet();
                for(String collectionName : childCollections){
                    
                    //2. get the list of child collection elements and loop these to locate each child element in the collection
                    Element collectionsElement = discoverOwnerElement(collectionName, parentElement);
                    @SuppressWarnings( "unchecked" )
                    List<Element> childCollectionElements = collectionsElement.elements( );
                    for(Element childElement :childCollectionElements ){
                     
                        //3. discover the child recordIdentifier/Identity element - use the value of the map which defines the owner componen.element of the identity element
                        String childRecordIdentityPath = this.childCollectionsAndIdentityOwners.get( collectionName );
                        Element childRecordIdentity = discoverOwnerElement(childRecordIdentityPath, childElement);
                        if(childRecordIdentity != null){
                             //4. add the <id> to the childElement - a Hibernate Entity - with the discovered identity element's value
                             childElement.addElement( "id" ).addText( childRecordIdentity.getStringValue() );
                         }
                        
                    }
                }
            }
            
        
        }
        
        return parentResultId; 
    }

   
    
    /**
     * Will locate the last element in the hierarchy.
     * 
     * @param componentName
     * @param ownerElement
     * @return
     */
    private Element discoverOwnerElement(String componentName, Element ownerElement){
        Element element = null;
        
        if(!componentName.contains( "." )){
            return ownerElement.element(componentName);
        }
        String compName = componentName.substring( 0, componentName.indexOf('.') );
        element = ownerElement.element(compName);
        if(element != null)
        {
            String elementName = componentName.substring( componentName.indexOf('.')+1 );
            if(elementName.contains(".")){
                element = discoverOwnerElement(elementName, element);
            }
            else{
                element=element.element(elementName);
            }
        }
        
        return element; 
    }

    @Override
    public String getAssigningFacilityValue( Element clinicalRecord )
    {  
        return getElementValue(clinicalRecord, this.facilityOwner);   
    }

    @Override
    public String getAssigningAuthorityValue( Element clinicalRecord)
    {
        return getElementValue(clinicalRecord, this.authorityOwner);  
    }
    
    protected String getElementValue(Element clinicalRecord, String elementOwnerName)
    {             
        String value = null;
        
        Element owner = discoverOwnerElement( elementOwnerName, clinicalRecord );
        if(owner != null)
        {
            value = owner.getStringValue();           
        }
           
        return value;       
    }
    
}   