package gov.va.med.esr.ui.report.action;

// Java Classes
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// Library Classes
import net.mlw.vlh.ValueList;
import org.apache.commons.lang.Validate;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.springframework.web.context.ServletContextAware;

// Framework Classes
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.report.ReportExportedType;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.ui.struts.ValueListActionUtils;
import gov.va.med.fw.util.StringUtils;

// ESR Classes
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.lookup.StandardReport;
import gov.va.med.esr.common.model.report.CompletedReport;
import gov.va.med.esr.common.report.data.ReportParameterConfig;
import gov.va.med.esr.service.StandardReportService;
import gov.va.med.esr.ui.common.action.AbstractAction;
import gov.va.med.esr.ui.message.action.MessageConstants;

/**
 * Base action for all the report related action classes.
 *
 * @author Muddaiah Ranga
 * @author Andrew Pach
 * @version 3.0
 */
public class ReportAction extends AbstractAction implements ReportConstants, ServletContextAware
{
    private ServletContext servletContext;
    private ReportParameterConfig reportParameterConfig;
    private StandardReportService standardReportService;

    /**
     * Displays available report list.
     *
     * @param mapping Struts action mapping for this action
     * @param actionForm Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward displayAvailableReports(ActionMapping mapping,
        ActionForm actionForm,
        HttpServletRequest request,
        HttpServletResponse response) throws Exception
    {
        List reports = this.getStandardReports(request);
        if (reports != null && reports.size() > 0)
        {
            // Get the value list information
            ValueList valueList = ValueListActionUtils
                .getValueList(actionForm, request, STD_REPORT_TABLE_ID, STD_REPORTS_VALUE_LIST_ADAPTER, reports);
            // Backup the ValueListInfo into session and store the resultant ValueList on the request
            ValueListActionUtils.setValueList(request, valueList, MessageConstants.VALUE_LIST);
        }
        return mapping.findForward(DISPLAY_AVAILABLE_REPORTS);
    }

    /**
     * Displays a report.
     *
     * @param mapping Struts action mapping for this action
     * @param actionForm Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward displayReport(ActionMapping mapping,
        ActionForm actionForm,
        HttpServletRequest request,
        HttpServletResponse response) throws Exception
    {
        String reportId = request.getParameter(REPORT_ID);
        CompletedReport compltReport = this.getCompletedReport(request, reportId);
        if (compltReport != null)
        {
            byte buffer[] = compltReport.getReportFileContent();
            String fileType = (compltReport.getFileType() != null) ? compltReport.getFileType().getCode() : null;
            if (StringUtils.equals(fileType, ReportExportedType.PDF.getCode()))
            {
                response.setHeader("Content-Disposition", "inline; filename=report.pdf");
                this.writeReportToReponse(buffer, CONTENT_TYPE_APPLICATION_PDF, response);
            }
            else
            {
                if (StringUtils.equals(fileType, ReportExportedType.CSV.getCode()))
                {
                    response.setHeader("Content-Disposition", "inline; filename=report.csv");
                    this.writeReportToReponse(buffer, CONTENT_TYPE_APPLICATION_CSV, response);
                }
            }
        }
        else
        {
            this.writeMessageToReponse("Report/report id is empty", response);
        }
        return null;
    }

    /**
     * Gets a list of standard reports sorted by description.
     *
     * @param request The HttpServletRequest.
     *
     * @return The list of StandardReport objects.
     * @throws ServiceException if any problems were encountered.
     */
    protected List getStandardReports(HttpServletRequest request) throws ServiceException
    {
        List standardReports = (List)request.getSession().getAttribute(STANDARD_REPORT_LIST);
        if (standardReports == null)
        {
            List reportIds = this.getReportParameterConfig().getReportIds();
            List stdReports = this.getStandardReportService().getStandardReports(this.getLoggedInUser());

            // Cache the standard reports (sorted by description)
            standardReports = this.getFilteredStandardReports(stdReports, reportIds);
            Comparator comparator = new Comparator()
            {
                public int compare(Object pObject1, Object pObject2)
                {
                    String name1 = (pObject1 != null && pObject1 instanceof StandardReport) ?
                        ((StandardReport)pObject1).getDescription() : null;
                    String name2 = (pObject2 != null && pObject2 instanceof StandardReport) ?
                        ((StandardReport)pObject2).getDescription() : null;
                    return (name1 != null && name2 != null) ? name1.compareTo(name2) : 0;
                }
            };
            Collections.sort(standardReports, comparator);
            request.getSession().setAttribute(STANDARD_REPORT_LIST, standardReports);
        }
        return standardReports;
    }

    /**
     * Gets a filtered list of standard reports.  This is a UI specific filter since the database list of standard
     * reports contains more than we currently support.
     *
     * @param stdReports the list of standard reports.
     * @param reportIds the filtered list of report Ids.
     *
     * @return the filtered list of standard reports.
     */
    private List getFilteredStandardReports(List stdReports, List reportIds)
    {
        List standardReports = new ArrayList();
        if (reportIds != null && !reportIds.isEmpty())
        {
            for (Iterator iter = stdReports.iterator(); iter.hasNext();)
            {
                StandardReport stdReport = (StandardReport)iter.next();
                if(stdReport.isActive()){
                    if (reportIds.contains(ReportActionHelper.getRemoveSpaceAndDash(stdReport.getCode())))
                    {
                        /* Changes made to meet the requirement to Disable/Retire ES reports QM26 & QM27 - CCR12924
                         Instead of changing the database view, this code change is filtering the report list from database view not to include QM26 & QM27  */
                      if (!stdReport.getCode().equals(stdReport.CODE_QM_26.getCode())) {
                          if (!stdReport.getCode().equals(stdReport.CODE_QM_27.getCode()))
                         standardReports.add(stdReport);
                      }
                    }
                    
                }
            }
        }
        return standardReports;
    }

    protected StandardReport getStandardReport(HttpServletRequest request, String reportId) throws ServiceException
    {
        Map standardReportsMap = this.getStandardReportsMap(request);
        return (standardReportsMap != null) ? (StandardReport)standardReportsMap.get(reportId) : null;
    }

    protected CompletedReport getCompletedReport(HttpServletRequest request, String reportId) throws ServiceException
    {
        EntityKey key =
            StringUtils.isNotEmpty(reportId) ? CommonEntityKeyFactory.createCompletedReportEntityKey(reportId) : null;
        return (key != null) ? getStandardReportService().getCompletedReport(key) : null;
    }

    private Map getStandardReportsMap(HttpServletRequest request) throws ServiceException
    {
        Map standardReportsMap = (Map)request.getSession().getAttribute(STANDARD_REPORT_MAP);
        if (standardReportsMap == null)
        {
            List standardReports = this.getStandardReports(request);
            if (standardReports != null)
            {
                standardReportsMap = new HashMap();
                StandardReport stdReport = null;
                for (Iterator iter = standardReports.iterator(); iter.hasNext();)
                {
                    stdReport = (StandardReport)iter.next();
                    standardReportsMap.put(stdReport.getCode(), stdReport);
                }
            }
            request.setAttribute(STANDARD_REPORT_MAP, standardReportsMap);
        }
        return standardReportsMap;
    }

    protected Map getKeyMethodMap()
    {
        if (keyMethodMap == null)
        {
            keyMethodMap = new HashMap();
            keyMethodMap.put("button.displayAvailableReports", "displayAvailableReports");
            keyMethodMap.put("button.displayScheduledReports", "resetFilter");
            keyMethodMap.put("button.displayCompletedReports", "resetFilter");
            keyMethodMap.put("button.displayArchivedReports", "resetFilter");
            keyMethodMap.put("button.displayReport", "displayReport");
            keyMethodMap.put("button.displayReportParameters", "displayReportParameters");
            keyMethodMap.put("button.runReport", "generateReport");
            keyMethodMap.put("button.displayReportSelection", "displayReportSelection");
            keyMethodMap.put("button.displayAddReportSchedule", "displayAddReportSchedule");
            keyMethodMap.put("button.add", "addReportSchedule");
            keyMethodMap.put("button.displayUpdateReportSchedule", "displayUpdateReportSchedule");
            keyMethodMap.put("button.update", "updateReportSchedule");
            keyMethodMap.put("button.cancel", "cancel");
            keyMethodMap.put("button.applyFilter", "applyFilter");
            keyMethodMap.put("button.goFilter", "applyFilter");
            keyMethodMap.put("button.resetFilter", "resetFilter");
            keyMethodMap.put("button.display", "displayReportData");
            keyMethodMap.put("button.cancelScheduledReport", "cancelScheduledReport");
            keyMethodMap.put("button.deleteCompletedReport", "deleteCompletedReport");
            keyMethodMap.put("button.inactivateCompletedReport", "inactivateCompletedReport");
            keyMethodMap.put("button.refresh", "refreshData");
        }
        return keyMethodMap;
    }

    private void writeReportToReponse(byte buffer[], String contentType, HttpServletResponse response)
        throws IOException
    {
        response.setContentType(contentType);
        OutputStream outStream = response.getOutputStream();
        outStream.write(buffer);
        outStream.close();
    }

    private void writeMessageToReponse(String msg, HttpServletResponse response) throws IOException
    {
        PrintWriter writer = response.getWriter();
        writer.write(msg);
        writer.close();
    }

    public ReportParameterConfig getReportParameterConfig()
    {
        return reportParameterConfig;
    }

    public void setReportParameterConfig(ReportParameterConfig reportParameterConfig)
    {
        this.reportParameterConfig = reportParameterConfig;
    }

    public StandardReportService getStandardReportService()
    {
        return standardReportService;
    }

    public void setStandardReportService(StandardReportService standardReportService)
    {
        this.standardReportService = standardReportService;
    }

    public void setServletContext(ServletContext servletContext)
    {
        this.servletContext = servletContext;
    }

    public void afterPropertiesSet()
    {
        Validate.notNull(reportParameterConfig, "Report Parameter configuration cannot be null");
        Validate.notNull(standardReportService, "standardReportService cannot be null");
        if (servletContext != null && reportParameterConfig != null)
        {
            servletContext.setAttribute(REPORT_PARAMETER_CONFIG, reportParameterConfig.getParameterMap());
        }
    }
}
