package faqapp.struts.actions;

import java.rmi.RemoteException;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;

import faqapp.AppSessionFacade;
import faqapp.AreaTO;
import faqapp.FAQTO;
import faqapp.TopicTO;
import faqapp.struts.forms.FAQForm;
import faqapp.struts.forms.FAQQueryForm;

/**
 * Implementation of FAQAction. This FAQApp specific action implementation implements
 * the business logic execution methods defined and invoked by FAQAbstractAction,
 * which are validate, create, update, saveCreate, saveUpdate, and query.
 */
public final class FAQAction extends FAQAbstractAction {
    public ActionErrors validate(ActionForm form, HttpServletRequest request,
                                 AppSessionFacade asf) throws RemoteException {
        /* Validate from data and return validation errors if necessary */
        FAQForm ff = (FAQForm)form;
        readAllAreasAndAssociatedTopics(ff, request, asf);
        ActionErrors errors = new ActionErrors();
        if ((ff.getQuestion() == null) || (ff.getQuestion().length() < 1))
            errors.add("question", new ActionError("errors.question.required"));
        if ((ff.getAnswer() == null) || (ff.getAnswer().length() < 1))
            errors.add("answer", new ActionError("errors.answer.required"));
        if ((ff.getProductVersion() == null) || (ff.getProductVersion().length() < 1))
            errors.add("productVersion", new ActionError("errors.productVersion.required"));
        if (!ff.getAreaFlags().containsValue((new Boolean(true))))
            errors.add("areaSelection", new ActionError("errors.areaSelection.required"));
        return errors;
    }

    public String create(ActionForm form, HttpServletRequest request,
                         AppSessionFacade asf) throws RemoteException {
        /**
         * Read all Areas and associated Topics and store them in FAQForm
         * for further usage in view (FAQ.jsp).
         */
        FAQForm ff = (FAQForm)form;
        /**
         * Don't compute selected areas and topics (false flag),
         * since the user is trying to create a new FAQ.
         */
        return readAllAreasAndAssociatedTopics(ff, request, asf);
    }

    public String update(ActionForm form, HttpServletRequest request,
                         AppSessionFacade asf) throws RemoteException {
        /**
         * Read FAQ based on FAQForm.id and all Areas and associated Topics
         * and store all data in FAQForm for further usage in view (FAQ.jsp).
         */
        FAQForm ff = (FAQForm)form;
        FAQTO faq = asf.findFAQByPK(ff.getId());
        ff.setId(ff.getId());
        ff.setQuestion(faq.getQuestion());
        ff.setAnswer(faq.getAnswer());
        ff.setInternalFlag(faq.isInternalFlag());
        ff.setProductVersion(faq.getProductVersion());
        /**
         * Compute selected areas and topics (true flag),
         * since the user is trying to update an existing FAQ.
         */
        String forward = readAllAreasAndAssociatedTopics(ff, request, asf);
        computeAreaAndTopicSelections(ff);
        return forward;
    }

    public String saveCreate(ActionForm form, HttpServletRequest request,
                             AppSessionFacade asf) throws RemoteException{
        /**
         * Invoke createFAQ(...) method on AppSessionFacade
         * with corresponding data from FAQForm
         */
        FAQForm ff = (FAQForm)form;
        String userName = request.getUserPrincipal().getName();
        int[] areaIDs = filterSelectedAreaIDs(ff.getAreaFlags(), ff.getAreas());
        int[] topicIDs = filterSelectedTopicIDs(ff.getAreaFlags(), ff.getTopicIDs());
        ff.setId(asf.createFAQ(ff.getQuestion(), ff.getAnswer(), ff.isInternalFlag(),
                               ff.getProductVersion(), userName, areaIDs, topicIDs));
        return SUCCESS;
    }

    public String saveUpdate(ActionForm form, HttpServletRequest request,
                             AppSessionFacade asf) throws RemoteException{
        /**
         * Invoke updateFAQ(...) method on AppSessionFacade
         * with corresponding data from FAQForm
         */
        FAQForm ff = (FAQForm)form;
        String userName = request.getUserPrincipal().getName();
        int[] areaIDs = filterSelectedAreaIDs(ff.getAreaFlags(), ff.getAreas());
        int[] topicIDs = filterSelectedTopicIDs(ff.getAreaFlags(), ff.getTopicIDs());
        asf.updateFAQ(ff.getId(), ff.getQuestion(), ff.getAnswer(), ff.isInternalFlag(),
                      ff.getProductVersion(), userName, areaIDs, topicIDs);
        return SUCCESS;
    }

    public String query(ActionForm form, HttpServletRequest request,
                        AppSessionFacade asf) throws RemoteException {
        String forward = SUCCESS;
        FAQQueryForm fqf = (FAQQueryForm)form;
        /**
         * Read all Areas and store them in FAQQuery Form for
         * further usage in view (FAQQuery.jsp).
         * Forward to "warning" page in case no areas exist.
         */
        AreaTO[] areas = asf.findAreaAll();
        if ((areas == null || areas.length < 1)) {
            ActionErrors errors = new ActionErrors();
            errors.add("warning", new ActionError("warning.noAreasExist"));
            request.setAttribute(Action.ERROR_KEY, errors);
            return WARNING;
        }
        fqf.setAreas(areas);
        /**
         * In case user has already selected a specific Area (FAQQueryForm.areaID > 0),
         * read all associated FAQs and the areaDesc for the selected Area and store
         * all data in FAQQueryForm further usage in view (FAQList.jsp or FAQReport.jsp).
         */
        if (fqf.getAreaID() > 0) {
            FAQTO[] faqs = asf.findFAQsByAreaID(fqf.getAreaID());
            AreaTO area = asf.findAreaByPK(fqf.getAreaID());
            fqf.setFAQs(faqs);
            fqf.setAreaDesc(area.getAreaDesc());
            /* Forward either to FAQList.jsp or FAQReport.jsp based on FAQQueryForm.mode */
            forward = fqf.getMode();
        }
        return forward;
    }

    private String readAllAreasAndAssociatedTopics(FAQForm form, HttpServletRequest request,
                   AppSessionFacade asf) throws RemoteException {
        /**
         * Read all Areas and all associated Topics, filter all Areas that do not
         * have any associated Topics and store the resulting Areas in FAQForm
         * for further usage in view (FAQ.jsp).
         * Forward to "warning" page in case no such Areas exist.
         */
        Collection areasList = new ArrayList();
        Collection topicsList = new ArrayList();
        AreaTO[] allAreas = asf.findAreaAll();

        /* Compute all Areas and associated Topics, filter Areas with no associated Topics */
        for (int i=0; i<allAreas.length; i++) {
            TopicTO[] assocTopics = asf.findTopicsByAreaID(allAreas[i].getAreaID());
            if (assocTopics.length > 0) {
                areasList.add(allAreas[i]);
                topicsList.add(assocTopics);
            }
        }

        /* Forward to "warning" page in case no Areas with associated Topics exist */
        if (areasList.size() < 1) {
            ActionErrors errors = new ActionErrors();
            errors.add("warning", new ActionError("warning.noAreasAndAssocTopicsExist"));
            request.setAttribute(Action.ERROR_KEY, errors);
            return WARNING;
        }

        /* Store Areas and associated Topics in FAQForm */
        AreaTO[] areas = (AreaTO[])areasList.toArray(new AreaTO[areasList.size()]);
        TopicTO[][] topics = (TopicTO[][])topicsList.toArray(new TopicTO[topicsList.size()][]);
        form.setAreas(areas);
        form.setTopics(topics);
        return SUCCESS;
    }

    private void computeAreaAndTopicSelections(FAQForm form) {
        /**
         * Compute Area and Topic selections for underlying FAQ
         * and store them in FAQForm.
         */
        AreaTO[] areas = form.getAreas();
        TopicTO[][] topics = form.getTopics();
        int id = form.getId();
        Map areaFlags = new HashMap();
        Map topicIDs = new HashMap();
        for (int i = 0; i < areas.length; i++) {
            AreaTO area = areas[i];
            int[] areaFAQIDs = area.getFAQIDs();
            boolean assoc = false;
            if (id > 0) {
                for (int j = 0; j < areaFAQIDs.length; j++) {
                    if (id == areaFAQIDs[j]) assoc = true;
                }
            }
            areaFlags.put(new Integer(i), new Boolean(assoc));
            int topicID = 0;
            TopicTO[] areaTopics = topics[i];
            if (id > 0) {
                for (int j = 0; j < areaTopics.length; j++) {
                    int[] topicFAQIDs = areaTopics[j].getFAQIDs();
                    for (int k = 0; k < topicFAQIDs.length; k++) {
                        if (id == topicFAQIDs[k]) topicID = areaTopics[j].getTopicID();
                    }
                }
            }
            topicIDs.put(new Integer(i), new Integer(topicID));
        }
        form.setAreaFlags(areaFlags);
        form.setTopicIDs(topicIDs);
    }

    private int[] filterSelectedAreaIDs(Map areaFlags, AreaTO[] areas) {
        /* Compute selected Area IDs from FAQForm, by filtering all unselected Area IDs */
        ArrayList areaIDsList = new ArrayList();
        for (int i=0; i<areas.length; i++) {
            Boolean areaFlag = (Boolean)areaFlags.get(new Integer(i));
            if (areaFlag != null && areaFlag.booleanValue() == true)
                areaIDsList.add(new Integer(areas[i].getAreaID()));
        }
        int[] areaIDs = new int[areaIDsList.size()];
        for (int i=0; i<areaIDsList.size(); i++) {
            areaIDs[i] = ((Integer)areaIDsList.get(i)).intValue();
        }
        return areaIDs;
    }

    private int[] filterSelectedTopicIDs(Map areaFlags, Map topicIDs) {
        /**
         * Compute selected Topic IDs from FAQForm, by filtering all unselected
         * Topic IDs. Only Topic IDs for Topics associated with selected Areas
         * are considered selected Topic IDs.
         */
        ArrayList topicIDsList = new ArrayList();
        Set keys = areaFlags.keySet();
        Iterator iter = keys.iterator();
        while (iter.hasNext()) {
            Integer key = (Integer)iter.next();
            Boolean areaFlag = (Boolean)areaFlags.get(key);
            if (areaFlag != null && areaFlag.booleanValue())
                topicIDsList.add(topicIDs.get(key));
        }
        int[] filteredTopicIDs = new int[topicIDsList.size()];
        for (int i=0; i<topicIDsList.size(); i++) {
            filteredTopicIDs[i] = ((Integer)topicIDsList.get(i)).intValue();
        }
        return filteredTopicIDs;
    }
}

