package gov.va.med.esr.service.impl;

import gov.va.med.esr.common.model.security.EEServiceRequest;
import gov.va.med.esr.common.model.security.EEServiceRequestLite;
import gov.va.med.esr.common.model.security.EEServiceUser;
import gov.va.med.esr.common.persistent.security.SecurityDAO;
import gov.va.med.esr.service.EEServiceAdminService;
import gov.va.med.esr.service.external.ExternalComponent;
import gov.va.med.esr.service.external.person.EESummary;

import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.service.ServiceException;

import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.PropertyUtilsBean;

public class EEServiceAdminServiceImpl extends AbstractRuleAwareServiceImpl
				implements EEServiceAdminService{

	private SecurityDAO         securityDAO                    = null;

    public SecurityDAO getSecurityDAO() {
		return securityDAO;
	}

	public void setSecurityDAO(SecurityDAO securityDAO) {
		this.securityDAO = securityDAO;
	}

	public List getEEServiceRequests() throws ServiceException {
    	try {
    		return securityDAO.getEEServiceRequests();
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }

	public List getEEServiceLiteRequests() throws ServiceException {
    	try {
    		return securityDAO.getEEServiceLiteRequests();
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }


    public EEServiceRequest getEEserviceRequest(EntityKey eeServiceRequestId) throws ServiceException {
    	try {
    		return securityDAO.getEEServiceRequest(eeServiceRequestId);
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }

    public Set getEEServiceRequestFieldsByRequest(EntityKey eeServiceRequestId) throws ServiceException {
    	try {
    		return securityDAO.getEEServiceRequestFieldsByRequest(eeServiceRequestId);
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}

    }

    public EEServiceRequestLite getEEserviceRequestLite(EntityKey eeServiceRequestId) throws ServiceException {
    	try {
    		return securityDAO.getEEServiceRequestLite(eeServiceRequestId);
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }


    public EEServiceRequest getEEServicerequestByName(String name) throws ServiceException{
    	try {
    		return securityDAO.getEEServiceRequestByName(name);
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }

    public EEServiceRequestLite getEEServicerequestLiteByName(String name) throws ServiceException{
    	try {
    		return securityDAO.getEEServiceRequestLiteByName(name);
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }


    public void saveEEServiceRequest(EEServiceRequest eeServiceRequest) throws ServiceException {
    	try{
    		securityDAO.saveObject(eeServiceRequest);
    	}catch( DAOException daoEx) {
    		throw new ServiceException("Exception while saving EEServiceRequest: " , daoEx);
    	}
    }
    public List getEEServiceUsers() throws ServiceException {
    	try {
    		return securityDAO.getEEServiceUsers();
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }
    public EEServiceUser getEEServiceUser(String userName) throws ServiceException {
    	try {
    		return securityDAO.getEEServiceUserByName(userName);
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }
    public void saveEEServiceUser(EEServiceUser eeServiceUser) throws ServiceException {
    	try{
    		securityDAO.saveObject(eeServiceUser);
    	}catch( DAOException daoEx) {
    		throw new ServiceException("Exception while saving EEServiceRequest: " , daoEx);
    	}
    }
    public EEServiceUser getEEserviceUser(EntityKey eeServiceUserId) throws ServiceException {
    	try {
    		return securityDAO.getEEServiceUserById(eeServiceUserId);
    	} catch ( DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}
    }

    /**
     * applies the defines request/role to the populated EESummary object
     * If the access to a field is not defined in the request then use relection
     * to set null to that field.
     */
    public void applyRequest(EESummary eeSummary, String accountName, String requestName)
    	throws ServiceException {

    	EEServiceRequestLite eeServiceRequest = null;
    	try {
    		eeServiceRequest = securityDAO.getEEServiceRequestLiteByName(requestName);
    	} catch(DAOException daoEx) {
    		throw new ServiceException("DAOException:" ,daoEx );
    	}

    	Set allowedFields = getEEServiceRequestFieldsByRequest(eeServiceRequest.getEntityKey());
		PropertyUtilsBean propUtils = new PropertyUtilsBean();
		Map map = null;
		try {
			map = propUtils.describe(eeSummary);
		} catch (Exception e) {
			throw new ServiceException(e);
		}

		String prefix = "eeSummary";
		Iterator itr = map.keySet().iterator();

		try {
			while(itr.hasNext() )
			{
				String keyName = (String)itr.next();
				if ("class".equals(keyName)) {
					continue;
				}

				Object value = map.get(keyName);
				if ( value == null ) {
					continue;
				} else if (value instanceof ExternalComponent) {
					boolean isNull = applyRoleToObject(value, prefix+"." + keyName, eeSummary, keyName, allowedFields);
					if (isNull){
						propUtils.setSimpleProperty(eeSummary, keyName, null);
					}
				} else if (value instanceof ExternalComponent[] ) {
					boolean isAllNull = true;
					ExternalComponent[] objArr = (ExternalComponent[]) value;
					for ( int i=0; i< objArr.length;i++  ) {
						Object thisObj = objArr[i];
						boolean isNull = applyRoleToObject(thisObj, prefix+"." + keyName, eeSummary, keyName, allowedFields);
						if ( isNull ) {
							thisObj = null;
							propUtils.setSimpleProperty(eeSummary, keyName, null);
						} else {
							isAllNull = false;
						}
					}
					if ( isAllNull ) {
						propUtils.setSimpleProperty(eeSummary, keyName, null);
					}

				}else {
					try {
						propUtils.setSimpleProperty(value, keyName, null);
					} catch (Exception e) {
						throw new ServiceException(e);
					}
				}
			}
		} catch (Exception e) {
			throw new ServiceException(e);
		}

		return;
    }


   



	private  boolean applyRoleToObject(Object obj, String prefix,
			Object parentObject, String parentKeyName, Set allowedFields) throws ServiceException,
			NoSuchMethodException,InvocationTargetException, IllegalAccessException {
		PropertyUtilsBean propUtils = new PropertyUtilsBean();

		Map map = null;
		try {
			map = propUtils.describe(obj);
		} catch (Exception e) {
			throw new ServiceException(e);
		}

		boolean allisNull = true;
		Iterator itr = map.keySet().iterator();

		while (itr.hasNext()) {
			String keyName = (String) itr.next();
			if ("class".equals(keyName)) {
				continue;
			}

			Object value = map.get(keyName);
			if (value == null) {
				continue;
			} else if (value instanceof ExternalComponent) {
				boolean isNull = applyRoleToObject(value, prefix + "." + keyName, obj, keyName, allowedFields);
				if ( ! isNull ) {
					allisNull = false;
				}else {
					value=null;
					propUtils.setSimpleProperty(obj, keyName, null);
				}
			} else if (value instanceof ExternalComponent[]) {
				ExternalComponent[] objArr = (ExternalComponent[]) value;
				boolean isArrayNull = true;

				for (int i = 0; objArr != null && i < objArr.length; i++) {
					Object thisObj = objArr[i];
					boolean isNull = applyRoleToObject(thisObj, prefix + "." + keyName, obj,
							keyName, allowedFields);
					if ( ! isNull ) {
						allisNull = false;
						isArrayNull = false;
					}else {
						thisObj = null;
						propUtils.setSimpleProperty(obj, keyName, null);
					}
				}
				if ( isArrayNull ) {
					value = null;
					propUtils.setSimpleProperty(parentObject, parentKeyName, null);

				}
			} else {
				try {

					/* If this field has no access then set it to null **/
					String fieldName = prefix + "." + keyName;
					if ( ! allowedFields.contains(fieldName)) {
						propUtils.setSimpleProperty(obj, keyName, null);
					} else {
						//Some field here has access.
						allisNull = false;
					}

				} catch (Exception e) {
					throw new ServiceException(e);
				}
			}

		}
		return allisNull;
	}
}
