package gov.va.vinci.dart.service;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import gov.va.vinci.dart.biz.EventType;
import gov.va.vinci.dart.biz.Group;
import gov.va.vinci.dart.biz.Role;
import gov.va.vinci.dart.db.ActivityDAO;
import gov.va.vinci.dart.db.CommentDAO;
import gov.va.vinci.dart.db.DartRequestDAO;
import gov.va.vinci.dart.db.DataSourceDAO;
import gov.va.vinci.dart.db.DataSourceDisplayHeaderDAO;
import gov.va.vinci.dart.db.DocumentReviewNoteDAO;
import gov.va.vinci.dart.db.DocumentReviewStatusDAO;
import gov.va.vinci.dart.db.DocumentTemplateDAO;
import gov.va.vinci.dart.db.EventDAO;
import gov.va.vinci.dart.db.EventTypeDAO;
import gov.va.vinci.dart.db.FacilityDAO;
import gov.va.vinci.dart.db.GroupDAO;
import gov.va.vinci.dart.db.GroupTaskDAO;
import gov.va.vinci.dart.db.LocationDAO;
import gov.va.vinci.dart.db.NarrativeDAO;
import gov.va.vinci.dart.db.OperationalRequestDAO;
import gov.va.vinci.dart.db.OperationalStudyDAO;
import gov.va.vinci.dart.db.ParticipantDAO;
import gov.va.vinci.dart.db.PersonDAO;
import gov.va.vinci.dart.db.PersonTaskDAO;
import gov.va.vinci.dart.db.PreparatoryRequestDAO;
import gov.va.vinci.dart.db.PreparatoryStudyDAO;
import gov.va.vinci.dart.db.RequestAdminLocationDocumentDAO;
import gov.va.vinci.dart.db.RequestAdminParticipantDocumentDAO;
import gov.va.vinci.dart.db.RequestDAO;
import gov.va.vinci.dart.db.RequestLocationDocumentDAO;
import gov.va.vinci.dart.db.RequestParticipantDocumentDAO;
import gov.va.vinci.dart.db.ResearchStudyDAO;
import gov.va.vinci.dart.db.ReviewDAO;
import gov.va.vinci.dart.db.ReviewTemplateDAO;
import gov.va.vinci.dart.db.RoleDAO;
import gov.va.vinci.dart.db.SoftwareRequestDAO;
import gov.va.vinci.dart.db.TaskDAO;
import gov.va.vinci.dart.db.RequestWorkflowDAO;
import gov.va.vinci.dart.db.WorkflowTemplateDAO;
import gov.va.vinci.dart.db.WorkflowTypeDAO;
import gov.va.vinci.dart.db.util.ISessionManager;
import gov.va.vinci.dart.dms.db.AttributeDAO;
import gov.va.vinci.dart.dms.db.ContentDAO;
import gov.va.vinci.dart.dms.db.DocumentDAO;
import gov.va.vinci.dart.dms.db.LabelDAO;
import gov.va.vinci.dart.dms.db.RepositoryDAO;
import gov.va.vinci.dart.mail.MailManager;
import gov.va.vinci.dart.usr.UserManager;
import gov.va.vinci.dart.wf2.WorkflowResolver;

public class DartObjectFactory implements DataLayerFactory, DMSDataLayerFactory, ApplicationContextAware {

    private static Log log = LogFactory.getLog(DartObjectFactory.class);

    private static final String USERMANAGER_BEAN = "userManager";
    private static final String EMAILMANAGER_BEAN = "mailManager";
    private static final String SESSIONMANAGER_BEAN = "dbSessionManager";
    private static final String WORKFLOWRESOLVER_BEAN = "workflowResolver";

    private static final String ACTIVITYDAO_BEAN = "ActivityDAO";
    private static final String ATTRIBUTEDAO_BEAN = "AttributeDAO";
    private static final String COMMENTDAO_BEAN = "CommentDAO";
    private static final String CONTENTDAO_BEAN = "ContentDAO";
    private static final String DARTREQUESTDAO_BEAN = "DartRequestDAO";
    private static final String PREPARATORYREQUESTDAO_BEAN = "PreparatoryRequestDAO";
    private static final String DATASOURCEDAO_BEAN = "DataSourceDAO";
    private static final String DATASOURCEDISPLAYHEADERDAO_BEAN = "DataSourceDisplayHeaderDAO";
    private static final String DOCUMENTDAO_BEAN = "DocumentDAO";
    private static final String DOCUMENTREVIEWNOTEDAO_BEAN = "DocumentReviewNoteDAO";
    private static final String DOCUMENTREVIEWSTATUSDAO_BEAN = "DocumentReviewStatusDAO";
    private static final String DOCUMENTTEMPLATEDAO_BEAN = "DocumentTemplateDAO";
    private static final String EVENTDAO_BEAN = "EventDAO";
    private static final String EVENTTYPEDAO_BEAN = "EventTypeDAO";
    private static final String FACILITYDAO_BEAN = "FacilityDAO";
    private static final String GROUPDAO_BEAN = "GroupDAO";
    private static final String GROUPTASKDAO_BEAN = "GroupTaskDAO";
    private static final String LABELDAO_BEAN = "LabelDAO";
    private static final String LOCATIONDAO_BEAN = "LocationDAO";
    private static final String NARRATIVEDAO_BEAN = "NarrativeDAO";
    private static final String OPERATIONALSTUDYDAO_BEAN = "OperationalStudyDAO";
    private static final String OPERATIONALREQUESTDAO_BEAN = "OperationalRequestDAO";
    private static final String PARTICIPANTDAO_BEAN = "ParticipantDAO";
    private static final String PERSONDAO_BEAN = "PersonDAO";
    private static final String PERSONTASKDAO_BEAN = "PersonTaskDAO";
    private static final String REPOSITORYDAO_BEAN = "RepositoryDAO";
    private static final String REQUESTADMINLOCATIONDOCUMENTDAO_BEAN = "RequestAdminLocationDocumentDAO";
    private static final String REQUESTADMINPARTICIPANTDOCUMENTDAO_BEAN = "RequestAdminParticipantDocumentDAO";
    private static final String REQUESTLOCATIONDOCUMENTDAO_BEAN = "RequestLocationDocumentDAO";
    private static final String REQUESTPARTICIPANTDOCUMENTDAO_BEAN = "RequestParticipantDocumentDAO";
    private static final String RESEARCHSTUDYDAO_BEAN = "ResearchStudyDAO";
    private static final String PREPARATORYSTUDYDAO_BEAN = "PreparatoryStudyDAO";
    private static final String REQUESTDAO_BEAN = "RequestDAO";
    private static final String REVIEWDAO_BEAN = "ReviewDAO";
    private static final String REVIEWTEMPLATEDAO_BEAN = "ReviewTemplateDAO";
    private static final String ROLEDAO_BEAN = "RoleDAO";
    private static final String SOFTWAREREQUESTDAO_BEAN = "SoftwareRequestDAO";
    private static final String TASKDAO_BEAN = "TaskDAO";
    private static final String REQUESTWORKFLOWDAO_BEAN = "RequestWorkflowDAO";
    private static final String WORKFLOWTYPEDAO_BEAN = "WorkflowTypeDAO";
    private static final String WORKFLOWTEMPLATEDAO_BEAN = "WorkflowTemplateDAO";

    private static BeanFactory beanFactory = null;
    private static DartObjectFactory instance = null;
    private static ApplicationContext springContext = null;

    public static DartObjectFactory getInstance() {
        if (instance == null) {
            initializeInstance();
        }
        return instance;
    }

    private synchronized static void initializeInstance() {
        if (instance == null) {
            log.debug("initalizing DART object factory");
            instance = new DartObjectFactory();

            // initialize reference objects that need it.
            Role.initialize();
            EventType.initialize();
            Group.initialize();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        springContext = applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return springContext;
    }

    // This uses the existing application context to avoid loading the context twice
    private static BeanFactory getBeanFactory() {
        // if (beanFactory == null) {
        synchronized (DartObjectFactory.class) {
            if (beanFactory == null) {
                log.debug("bean factory not initialized, get app context");
                ApplicationContext appContext = springContext;
                beanFactory = appContext;
                log.debug("bean factory now set");
            }
        }
        // }
        return beanFactory;
    }

    @SuppressWarnings("unchecked")
    public <T> T getBean(String beanName) {
        T bean = (T) getBeanFactory().getBean(beanName);
        log.debug(String.format("loaded bean (without type safety) [beanname='%s']=>[bean=%s]", beanName, bean));
        return bean;
    }

    public <T> T getBean(String beanName, Class<T> requiredType) {
        T bean = (T) getBeanFactory().getBean(beanName, requiredType);
        log.debug(String.format("loaded bean (with type safety) [beanname='%s']=>[bean=%s]", beanName, bean));
        return bean;
    }

    @Override
    public UserManager getUserManager() {
        return getBean(USERMANAGER_BEAN, UserManager.class);
    }

    @Override
    public MailManager getMailManager() {
        return getBean(EMAILMANAGER_BEAN, MailManager.class);
    }

    @Override
    public ISessionManager getSessionManager() {
        return getBean(SESSIONMANAGER_BEAN, ISessionManager.class);
    }

    @Override
    public WorkflowResolver getWorkflowResolver() {
        return getBean(WORKFLOWRESOLVER_BEAN, WorkflowResolver.class);
    }

    @Override
    public ActivityDAO getActivityDAO() {
        return getBean(ACTIVITYDAO_BEAN, ActivityDAO.class);
    }

    @Override
    public AttributeDAO getAttributeDAO() {
        return getBean(ATTRIBUTEDAO_BEAN, AttributeDAO.class);
    }

    @Override
    public CommentDAO getCommentDAO() {
        return getBean(COMMENTDAO_BEAN, CommentDAO.class);
    }

    @Override
    public ContentDAO getContentDAO() {
        return getBean(CONTENTDAO_BEAN, ContentDAO.class);
    }

    @Override
    public DocumentDAO getDocumentDAO() {
        return getBean(DOCUMENTDAO_BEAN, DocumentDAO.class);
    }

    @Override
    public DocumentReviewNoteDAO getDocumentReviewNoteDAO() {
        return getBean(DOCUMENTREVIEWNOTEDAO_BEAN, DocumentReviewNoteDAO.class);
    }

    @Override
    public DocumentReviewStatusDAO getDocumentReviewStatusDAO() {
        return getBean(DOCUMENTREVIEWSTATUSDAO_BEAN, DocumentReviewStatusDAO.class);
    }

    @Override
    public DartRequestDAO getDartRequestDAO() {
        return getBean(DARTREQUESTDAO_BEAN, DartRequestDAO.class);
    }
    
    @Override
    public PreparatoryRequestDAO getPreparatoryRequestDAO() {
        return getBean(PREPARATORYREQUESTDAO_BEAN, PreparatoryRequestDAO.class);
    }

    @Override
    public DataSourceDAO getDataSourceDAO() {
        return getBean(DATASOURCEDAO_BEAN, DataSourceDAO.class);
    }

    @Override
    public DataSourceDisplayHeaderDAO getDataSourceDisplayHeaderDAO() {
        return getBean(DATASOURCEDISPLAYHEADERDAO_BEAN, DataSourceDisplayHeaderDAO.class);
    }

    @Override
    public DocumentTemplateDAO getDocumentTemplateDAO() {
        return getBean(DOCUMENTTEMPLATEDAO_BEAN, DocumentTemplateDAO.class);
    }

    @Override
    public EventDAO getEventDAO() {
        return getBean(EVENTDAO_BEAN, EventDAO.class);
    }

    @Override
    public EventTypeDAO getEventTypeDAO() {
        return getBean(EVENTTYPEDAO_BEAN, EventTypeDAO.class);
    }

    @Override
    public FacilityDAO getFacilityDAO() {
        return getBean(FACILITYDAO_BEAN, FacilityDAO.class);
    }

    @Override
    public GroupDAO getGroupDAO() {
        return getBean(GROUPDAO_BEAN, GroupDAO.class);
    }

    @Override
    public GroupTaskDAO getGroupTaskDAO() {
        return getBean(GROUPTASKDAO_BEAN, GroupTaskDAO.class);
    }

    @Override
    public LabelDAO getLabelDAO() {
        return getBean(LABELDAO_BEAN, LabelDAO.class);
    }

    @Override
    public LocationDAO getLocationDAO() {
        return getBean(LOCATIONDAO_BEAN, LocationDAO.class);
    }

    @Override
    public NarrativeDAO getNarrativeDAO() {
        return getBean(NARRATIVEDAO_BEAN, NarrativeDAO.class);
    }

    @Override
    public OperationalStudyDAO getOperationalStudyDAO() {
        return getBean(OPERATIONALSTUDYDAO_BEAN, OperationalStudyDAO.class);
    }

    @Override
    public OperationalRequestDAO getOperationalRequestDAO() {
        return getBean(OPERATIONALREQUESTDAO_BEAN, OperationalRequestDAO.class);
    }

    @Override
    public ParticipantDAO getParticipantDAO() {
        return getBean(PARTICIPANTDAO_BEAN, ParticipantDAO.class);
    }

    @Override
    public PersonDAO getPersonDAO() {
        return getBean(PERSONDAO_BEAN, PersonDAO.class);
    }

    @Override
    public PersonTaskDAO getPersonTaskDAO() {
        return getBean(PERSONTASKDAO_BEAN, PersonTaskDAO.class);
    }

    @Override
    public RepositoryDAO getRepositoryDAO() {
        return getBean(REPOSITORYDAO_BEAN, RepositoryDAO.class);
    }

    @Override
    public RequestDAO getRequestDAO() {
        return getBean(REQUESTDAO_BEAN, RequestDAO.class);
    }

    @Override
    public RequestAdminLocationDocumentDAO getRequestAdminLocationDocumentDAO() {
        return getBean(REQUESTADMINLOCATIONDOCUMENTDAO_BEAN, RequestAdminLocationDocumentDAO.class);
    }

    @Override
    public RequestAdminParticipantDocumentDAO getRequestAdminParticipantDocumentDAO() {
        return getBean(REQUESTADMINPARTICIPANTDOCUMENTDAO_BEAN, RequestAdminParticipantDocumentDAO.class);
    }

    @Override
    public RequestLocationDocumentDAO getRequestLocationDocumentDAO() {
        return getBean(REQUESTLOCATIONDOCUMENTDAO_BEAN, RequestLocationDocumentDAO.class);
    }

    @Override
    public RequestParticipantDocumentDAO getRequestParticipantDocumentDAO() {
        return getBean(REQUESTPARTICIPANTDOCUMENTDAO_BEAN, RequestParticipantDocumentDAO.class);
    }

    @Override
    public ResearchStudyDAO getResearchStudyDAO() {
        return getBean(RESEARCHSTUDYDAO_BEAN, ResearchStudyDAO.class);
    }

    @Override
    public PreparatoryStudyDAO getPreparatoryStudyDAO() {
        return getBean(PREPARATORYSTUDYDAO_BEAN, PreparatoryStudyDAO.class);
    }

    @Override
    public ReviewDAO getReviewDAO() {
        return getBean(REVIEWDAO_BEAN, ReviewDAO.class);
    }

    @Override
    public ReviewTemplateDAO getReviewTemplateDAO() {
        return getBean(REVIEWTEMPLATEDAO_BEAN, ReviewTemplateDAO.class);
    }

    @Override
    public RoleDAO getRoleDAO() {
        return getBean(ROLEDAO_BEAN, RoleDAO.class);
    }

    @Override
    public SoftwareRequestDAO getSoftwareRequestDAO() {
        return getBean(SOFTWAREREQUESTDAO_BEAN, SoftwareRequestDAO.class);
    }

    @Override
    public TaskDAO getTaskDAO() {
        return getBean(TASKDAO_BEAN, TaskDAO.class);
    }

    @Override
    public RequestWorkflowDAO getRequestWorkflowDAO() {
        return getBean(REQUESTWORKFLOWDAO_BEAN, RequestWorkflowDAO.class);
    }

    @Override
    public WorkflowTypeDAO getWorkflowTypeDAO() {
        return getBean(WORKFLOWTYPEDAO_BEAN, WorkflowTypeDAO.class);
    }

    @Override
    public WorkflowTemplateDAO getWorkflowTemplateDAO() {
        return getBean(WORKFLOWTEMPLATEDAO_BEAN, WorkflowTemplateDAO.class);
    }
}
