package gov.va.med.esr.messaging.parser;

import java.util.List;

import gov.va.med.fw.hl7.InvalidMessageException;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.hl7.constants.SegmentConstants;
import gov.va.med.fw.hl7.segment.MSH;
import gov.va.med.fw.hl7.segment.PID;
import gov.va.med.fw.hl7.segment.RF1;
import gov.va.med.fw.hl7.segment.ZGD;
import gov.va.med.fw.hl7.segment.ZPD;
import gov.va.med.fw.util.ObjectUtils;
import gov.va.med.fw.util.StringUtils;
import gov.va.med.fw.util.builder.Builder;
import gov.va.med.fw.util.builder.BuilderException;

import gov.va.med.esr.common.builder.datatype.metadata.XAD;
import gov.va.med.esr.common.builder.entity.AddressMetaData;
import gov.va.med.esr.common.builder.entity.EntityBuilder;
import gov.va.med.esr.common.builder.entity.PersonMetaData;
import gov.va.med.esr.common.builder.entity.metaData.AddressMetaDataFromPID;
import gov.va.med.esr.common.builder.entity.metaData.AssociationMetaDataFromZGD;
import gov.va.med.esr.common.builder.entity.metaData.PersonMetaDataFromPID;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.person.Association;
import gov.va.med.esr.common.model.person.Person;

/**
 * @author Rajiv Patnaik Created on Sep 27, 2005
 * @version 1.0 Copyright © 2005 VHA. All rights reserved
 */
public class Z05Parser extends EntityBuilder {
	/**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = 7744201993182841661L;

	private Builder addressBuilder;

	private Builder addressTypeBuilder;

	private Builder associationBuilder;

	private Builder personBuilder;

	private Builder phoneBuilder;

	/**
	 * @return Returns the addressBuilder.
	 */
	public Builder getAddressBuilder() {
		return addressBuilder;
	}

	/**
	 * @param addressBuilder
	 *           The addressBuilder to set.
	 */
	public void setAddressBuilder(Builder addressBuilder) {
		this.addressBuilder = addressBuilder;
	}

	public Builder getAddressTypeBuilder() {
		return addressTypeBuilder;
	}

	public void setAddressTypeBuilder(Builder addressTypeBuilder) {
		this.addressTypeBuilder = addressTypeBuilder;
	}

	/**
	 * @return Returns the associationBuilder.
	 */
	public Builder getAssociationBuilder() {
		return associationBuilder;
	}

	/**
	 * @param associationBuilder
	 *           The associationBuilder to set.
	 */
	public void setAssociationBuilder(Builder associationBuilder) {
		this.associationBuilder = associationBuilder;
	}

	/**
	 * @return Returns the personBuilder.
	 */
	public Builder getPersonBuilder() {
		return personBuilder;
	}

	/**
	 * @param personBuilder
	 *           The personBuilder to set.
	 */
	public void setPersonBuilder(Builder personBuilder) {
		this.personBuilder = personBuilder;
	}

	/**
	 * @return Returns the phoneBuilder.
	 */
	public Builder getPhoneBuilder() {
		return phoneBuilder;
	}

	/**
	 * @param phoneBuilder
	 *           The phoneBuilder to set.
	 */
	public void setPhoneBuilder(Builder phoneBuilder) {
		this.phoneBuilder = phoneBuilder;
	}

	public Person build(Object[] args) throws BuilderException {
		return this.build((Person) args[0], (Message) args[1]);
	}

	/**
	 * Main method to build the BOM from the Z05 message.
	 * 
	 * @param input
	 * @param message
	 * @return Person
	 * @throws BuilderException
	 */
	public Person build(Person input, Message message) throws BuilderException {
		try {
			Person person = this.buildPerson(input, message);

			buildCorrespondenceAddresses(person, message);
			buildGuardianAssociation(person, message);

			return person;
		}
		catch (InvalidMessageException e) {
			throw new BuilderException(e);
		}
	}

	private Person buildPerson(Person input, Message message)
			throws BuilderException, InvalidMessageException {

		// Pass null for zem, zen, zel, zmh, zio
		PersonMetaData metaData = new PersonMetaDataFromPID((PID) message
				.getSegment(SegmentConstants.PID), (ZPD) message
				.getSegment(SegmentConstants.ZPD), null, null, null, (MSH) message
				.getSegment(SegmentConstants.MSH), null, null);
		metaData.setEntity(input);

		return (Person) this.personBuilder.build(metaData);
	}

	/**
	 * Build only the CorrespondenceAddresses as the Z05 contains only the PID
	 * segment.
	 * 
	 * @param input
	 * @param message
	 * @throws BuilderException
	 * @throws InvalidMessageException
	 */
	private void buildCorrespondenceAddresses(Person input, Message message)
			throws BuilderException, InvalidMessageException {
		PID pid = (PID) message.getSegment(SegmentConstants.PID);
		RF1 rf1 = (RF1) message.getSegment(SegmentConstants.RF1);

		XAD[] xads = XAD.create(pid.getPatientAddress(),
				pid.getRepeatDelimiter(), pid.getComponentDelimiter());

		AddressMetaData metaData = null;
					
		if (xads != null && xads.length > 0) {
			metaData = new AddressMetaDataFromPID(xads[0], pid, rf1);	
		
			Address address = (Address) addressBuilder.build(metaData);
			// ESR CodeCR7305: Z05 always come from VBA, and may not contain Address Type.
			// Set address type explicitly, to Permanent 
			AddressType addressType = (AddressType) addressTypeBuilder.build(
					AddressType.CODE_PERMANENT_ADDRESS.getCode());
			address.setType(addressType);
			
			// Remove the existing permanent address and then add the one from message
			Address addressOnFile = Address.getAddressOfType(input.getAddresses(),
					AddressType.CODE_PERMANENT_ADDRESS.getName());
			
			if (addressOnFile != null) {
				input.removeAddress(addressOnFile);
			}
			input.addAddress(address);
		}
	}

	/**
	 * Builds Guardian associations from ZGD segments
	 * 
	 * @param person
	 * @param message
	 * @throws BuilderException
	 * @throws InvalidMessageException
	 */
	private void buildGuardianAssociation(Person person, Message message)
			throws BuilderException, InvalidMessageException {
		List zgds = message.getSegments(SegmentConstants.ZGD);

		for (int index = 0; index < ((zgds == null) ? 0 : zgds.size()); index++) {
			ZGD zgd = (ZGD) zgds.get(index);
			Association association = (Association) associationBuilder
					.build(new AssociationMetaDataFromZGD(zgd));

			if (association != null) {
				// Remove the existing association and then add the one from message
				Association associationOnFile = Association.getAssociationOfType(
						person.getAssociations(), association.getType().getCode());
				if (associationOnFile != null) {
					person.removeAssociation(associationOnFile);
				}
				person.addAssociation(association);
			}
		}
	}

}