package gov.va.med.esr.common.batchprocess;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.Validate;

import gov.va.med.fw.batchprocess.DataQueryProcessExecutionContext;
import gov.va.med.fw.io.RawFileDataList;
import gov.va.med.fw.io.writer.FormattedFileWriter;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.DateUtils;

import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.service.PSDelegateService;
import gov.va.med.esr.service.PersonIdentityTraits;

/**
 * Base class to produce the OPP Extract output files.
 * 
 * @author Rajiv Patnaik Created on Apr 29, 2006
 * @version 1.0
 * 
 * Copyright © 2006 VHA. All rights reserved
 */
public abstract class AbstractOPPExtractOutputFileProducer extends
        AbstractComponent implements OPPExtractOutputFileProducer
{
    private FormattedFileWriter outputFileWriter;

    private PSDelegateService psDelegateService;

    private boolean lastRowStatisticalData;

    /**
     * @return Returns the lastRowStatisticalData.
     */
    public boolean isLastRowStatisticalData()
    {
        return lastRowStatisticalData;
    }

    /**
     * @param lastRowStatisticalData
     *            The lastRowStatisticalData to set.
     */
    public void setLastRowStatisticalData(boolean lastRowStatisticalData)
    {
        this.lastRowStatisticalData = lastRowStatisticalData;
    }

    /**
     * @return Returns the outputFileWriter.
     */
    public FormattedFileWriter getOutputFileWriter()
    {
        return outputFileWriter;
    }

    /**
     * @param outputFileWriter
     *            The outputFileWriter to set.
     */
    public void setOutputFileWriter(FormattedFileWriter outputFileWriter)
    {
        this.outputFileWriter = outputFileWriter;
    }

    /**
     * @return Returns the psDelegateService.
     */
    public PSDelegateService getPsDelegateService()
    {
        return psDelegateService;
    }

    /**
     * @param psDelegateService
     *            The psDelegateService to set.
     */
    public void setPsDelegateService(PSDelegateService psDelegateService)
    {
        this.psDelegateService = psDelegateService;
    }

    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.esr.common.batchprocess.OPPExtractOutputFileProducer#generateOutputFile(gov.va.med.fw.batchprocess.DataQueryProcessExecutionContext,
     *      java.util.List)
     */
    /* (non-Javadoc)
     * @see gov.va.med.esr.common.batchprocess.OPPExtractOutputFileProducer#generateOutputFile(gov.va.med.fw.batchprocess.DataQueryProcessExecutionContext, java.util.List)
     */
    public void generateOutputFile(DataQueryProcessExecutionContext context,
            List acquiredData)
    {
        List outputData = new ArrayList();

        try
        {
            RawFileDataList rawFileData = null;

            for (Iterator iter = acquiredData.iterator(); iter.hasNext();)
            {
                Object[] queryData = (Object[]) iter.next();

                rawFileData = new RawFileDataList();

                rawFileData.addAll(Arrays.asList(queryData));

                outputData.add(rawFileData);
            }
            
            //Write all data to an output file.
            outputFileWriter.appendData(outputData);

            OPPExtractExecutionContext oppContext = (OPPExtractExecutionContext) context;
            //Increment process specific stats
            incrementStatistics(oppContext, outputData);
            
        } catch (Exception e)
        {
            context.getProcessStatistics().incrementNumberOfErrorRecords(
                    acquiredData.size());

            String errorMessage = "Error while executing OPP Extract Process  "
                    + e.getMessage();

            context.getExceptionData().add(errorMessage);

            logger.info(errorMessage, e);
        }
    }

    protected abstract RawFileDataList setIdentityTraitsOnOutputData(
            RawFileDataList rawFileDataRow, PersonIdentityTraits traits);

    protected abstract void incrementStatistics(
            OPPExtractExecutionContext context, List outputFileData);

    protected PersonIdentityTraits getIdentityTraits(String vpidValue,
            Map personTraitsMap) throws ServiceException
    {
        VPIDEntityKey vpidEntityKey = CommonEntityKeyFactory
                .createVPIDEntityKey(vpidValue);
        PersonIdentityTraits traits = null;
        if (personTraitsMap.containsKey(vpidEntityKey.getKeyValueAsString()))
        {
            traits = (PersonIdentityTraits) personTraitsMap.get(vpidEntityKey
                    .getKeyValueAsString());
            logger.info("Retrieving cached person Identity traits");
        } else
        {
            long startTime = Calendar.getInstance().getTimeInMillis();
            traits = psDelegateService.getIdentityTraits(vpidEntityKey);
            personTraitsMap.put(vpidEntityKey.getKeyValueAsString(), traits);

            logger.info("Calling PSIM to get Identity Traits - time for call "
                    + (Calendar.getInstance().getTimeInMillis() - startTime));
        }

        return traits;
    }

    protected String getCenturyFormattedDate(Date date)
    {
        String CENTURY_FORMAT = "CyyMMdd";
        String CENTURY_1800 = "1";
        String CENTURY_1900 = "2";
        String CENTURY_2000 = "3";

        if (date == null)
            return null;

        Calendar cal = DateUtils.createCalendar(date);
        String century = null;
        if (cal.get(Calendar.YEAR) >= 1800 && cal.get(Calendar.YEAR) < 1900)
        {
            century = CENTURY_1800;
        } else if (cal.get(Calendar.YEAR) >= 1900
                && cal.get(Calendar.YEAR) < 2000)
        {
            century = CENTURY_1900;
        } else if (cal.get(Calendar.YEAR) > 2000)
        {
            century = CENTURY_2000;
        }

        return century
                + (new SimpleDateFormat(CENTURY_FORMAT.substring(1))
                        .format(date));

    }

    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.fw.batchprocess.AbstractDataQueryIncrementalProcess#afterPropertiesSet()
     */
    public void afterPropertiesSet()
    {
        Validate.notNull(psDelegateService, "psDelegateService cannot be null");
        Validate.notNull(outputFileWriter, "outputFileWriter cannot be null");
    }

}