/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2016 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.module.tem.document.web.struts;

import org.apache.commons.lang.StringUtils;
import org.apache.struts.upload.FormFile;
import org.kuali.kfs.kns.web.ui.ExtraButton;
import org.kuali.kfs.kns.web.ui.HeaderField;
import org.kuali.kfs.krad.bo.DocumentHeader;
import org.kuali.kfs.krad.service.DataDictionaryService;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.module.tem.TemConstants;
import org.kuali.kfs.module.tem.TemConstants.TravelAuthorizationParameters;
import org.kuali.kfs.module.tem.TemConstants.TravelParameters;
import org.kuali.kfs.module.tem.TemKeyConstants;
import org.kuali.kfs.module.tem.TemParameterConstants;
import org.kuali.kfs.module.tem.TemPropertyConstants;
import org.kuali.kfs.module.tem.businessobject.AccountingDistribution;
import org.kuali.kfs.module.tem.businessobject.TemSourceAccountingLine;
import org.kuali.kfs.module.tem.businessobject.TransportationModeDetail;
import org.kuali.kfs.module.tem.businessobject.TravelerDetail;
import org.kuali.kfs.module.tem.businessobject.TravelerDetailEmergencyContact;
import org.kuali.kfs.module.tem.document.TravelAuthorizationDocument;
import org.kuali.kfs.module.tem.document.TravelDocument;
import org.kuali.kfs.module.tem.document.TravelDocumentBase;
import org.kuali.kfs.module.tem.document.service.TravelReimbursementService;
import org.kuali.kfs.module.tem.document.web.bean.TravelAuthorizationMvcWrapperBean;
import org.kuali.kfs.module.tem.service.TravelService;
import org.kuali.kfs.module.tem.util.MessageUtils;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.AccountingDocument;
import org.kuali.rice.core.web.format.CurrencyFormatter;
import org.kuali.rice.kew.api.WorkflowDocument;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TravelAuthorizationForm extends TravelFormBase implements TravelAuthorizationMvcWrapperBean {
    private TravelerDetailEmergencyContact newEmergencyContactLine;
    private TravelerDetail newTraveler;
    private boolean canCloseTA;
    private boolean canAmend;
    private boolean canCancelTA;
    private boolean canHold;
    private boolean canRemoveHold;
    private boolean canUnmask = false;
    private boolean travelArranger;

    private boolean allowIncidentals = true;
    private String policyURL;
    private Boolean multipleAdvances;
    private boolean showPolicy;
    private boolean waitingOnTraveler;
    private Boolean showCorporateCardTotal;
    private boolean showTravelAdvancesForTrip;

    protected transient FormFile advanceAccountingFile;

    private TemSourceAccountingLine newAdvanceAccountingLine;

    // parameters that affect the UI
    private List<String> tempSelectedTransportationModes = new ArrayList<String>();

    /**
     * Constructs a TravelAuthorizationForm.java.
     */
    public TravelAuthorizationForm() {
        super();
        this.setShowPerDiem(false);
    }

    /**
     * @see org.kuali.kfs.module.tem.document.web.bean.TravelMvcWrapperBean#setDistribution(java.util.List)
     */
    @Override
    public void setDistribution(final List<AccountingDistribution> distribution) {
        super.setDistribution(distribution);

        TravelDocument travelDocument = (TravelDocument) getDocument();

        //TA doc - always deselected the expense type (ENCUMBRANCE) so it does not get distributed automatically
        for (AccountingDistribution accountdistribution : this.distribution) {
            if (accountdistribution.getCardType().equals(travelDocument.getDefaultCardTypeCode())) {
                accountdistribution.setSelected(Boolean.FALSE);
                accountdistribution.setDisabled(Boolean.TRUE);
            }
        }
    }


    @Override
    public boolean isDisplayAccountingLines() {

        boolean display = super.isDisplayAccountingLines();

        TravelAuthorizationDocument document = (TravelAuthorizationDocument) getTravelDocument();
        display = document.isTripGenerateEncumbrance(); // trips which do not generate encumbrances won't have accounting lines
        return display;

    }

    /**
     * Display imported expense related tab base on Travel Authorization document base on system parameter
     *
     * @return
     */
    public boolean isDisplayImportedExpenseRelatedTab() {
        boolean display = getParameterService().getParameterValueAsBoolean(TravelAuthorizationDocument.class, TravelAuthorizationParameters.DISPLAY_IMPORTED_EXPENSE_IND);
        return display;
    }

    /**
     * @see org.kuali.kfs.module.tem.document.web.struts.TravelFormBase#getPerDiemLabel()
     */
    @Override
    public String getPerDiemLabel() {
        return TemConstants.ENCUMBRANCE_PREFIX + super.getPerDiemLabel();
    }

    /**
     * @see org.kuali.kfs.module.tem.document.web.struts.TravelFormBase#getExpenseLabel()
     */
    @Override
    public String getExpenseLabel() {
        return TemConstants.ENCUMBRANCE_PREFIX + StringUtils.substringAfter(super.getExpenseLabel(), " ");
    }

    /**
     * @see org.kuali.kfs.module.tem.document.web.struts.TravelFormBase#getExpenseTabLabel()
     */
    @Override
    public String getExpenseTabLabel() {
        return TemConstants.GENERAL_EXPENSES_LABEL;
    }

    /**
     * Gets the newEmergencyContactLine attribute.
     *
     * @return Returns the newEmergencyContactLine.
     */
    @Override
    public TravelerDetailEmergencyContact getNewEmergencyContactLine() {
        return newEmergencyContactLine;
    }

    /**
     * Sets the newEmergencyContactLine attribute value.
     *
     * @param newEmergencyContactLine The newEmergencyContactLine to set.
     */
    @Override
    public void setNewEmergencyContactLine(TravelerDetailEmergencyContact newEmergencyContactLine) {
        this.newEmergencyContactLine = newEmergencyContactLine;
    }

    /**
     * @return new accounting line associated with travel advance
     */
    public TemSourceAccountingLine getNewAdvanceAccountingLine() {
        if (getTravelAuthorizationDocument() == null) {
            throw new IllegalArgumentException("TravelAuthorizationDocument cannot be null when creating new accounting line for advance");
        }
        if (newAdvanceAccountingLine == null) {
            newAdvanceAccountingLine = getTravelAuthorizationDocument().createNewAdvanceAccountingLine();
        }
        return newAdvanceAccountingLine;
    }

    /**
     * Sets new accounting line associated with travel advance
     *
     * @param newTravelAdvanceAccountingLine new accounting line associated with travel advance
     */
    public void setNewAdvanceAccountingLine(TemSourceAccountingLine newTravelAdvanceAccountingLine) {
        this.newAdvanceAccountingLine = newTravelAdvanceAccountingLine;
    }

    /**
     * Gets the selectedTransportationModes attribute.
     *
     * @return Returns the selectedTransportationModes.
     */
    @Override
    public List<String> getSelectedTransportationModes() {

        List<String> mySelectedTransportationModes = new ArrayList<String>();
        List<TransportationModeDetail> details = this.getTravelAuthorizationDocument().getTransportationModes();
        for (TransportationModeDetail detail : details) {
            mySelectedTransportationModes.add(detail.getTransportationModeCode());

        }
        return mySelectedTransportationModes;
    }

    /**
     * Sets the selectedTransportationModes attribute value.
     *
     * @param selectedTransportationModes The selectedTransportationModes to set.
     */
    @Override
    public void setSelectedTransportationModes(List<String> selectedTransportationModes) {
        this.tempSelectedTransportationModes = selectedTransportationModes;
    }

    @Override
    public List<String> getTempSelectedTransporationModes() {
        return this.tempSelectedTransportationModes;
    }

    /**
     * Retrieve the name of the document identifier field for data dictionary queries
     *
     * @return String with the field name of the document identifier
     */
    @Override
    protected String getDocumentIdentifierFieldName() {
        return "travelDocumentIdentifier";
    }

    /**
     * @return TravelAuthorizationDocument
     */
    @Override
    public TravelAuthorizationDocument getTravelAuthorizationDocument() {
        return (TravelAuthorizationDocument) getDocument();
    }


    @Override
    public void setNewTraveler(final TravelerDetail traveler) {
        this.newTraveler = traveler;
    }

    @Override
    public TravelerDetail getNewTraveler() {
        return this.newTraveler;
    }

    @Override
    protected String getDefaultDocumentTypeName() {
        return "TA";
    }

    /**
     * Creates a MAP for all the buttons to appear on the Travel Authorization Form, and sets the attributes of these buttons.
     *
     * @return the button map created.
     */
    protected Map<String, ExtraButton> createButtonsMap() {
        HashMap<String, ExtraButton> result = new HashMap<String, ExtraButton>();

        // Amend button
        ExtraButton amendButton = new ExtraButton();
        amendButton.setExtraButtonProperty("methodToCall.amendTa");
        amendButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_amend.gif");
        amendButton.setExtraButtonAltText("Amend");

        // Hold button
        ExtraButton holdButton = new ExtraButton();
        holdButton.setExtraButtonProperty("methodToCall.holdTa");
        holdButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_hold.gif");
        holdButton.setExtraButtonAltText("Hold");

        // Remove Hold button
        ExtraButton removeHoldButton = new ExtraButton();
        removeHoldButton.setExtraButtonProperty("methodToCall.removeHoldTa");
        removeHoldButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_removehold.gif");
        removeHoldButton.setExtraButtonAltText("Remove Hold");

        // Cancel Travel button
        ExtraButton cancelTravelButton = new ExtraButton();
        cancelTravelButton.setExtraButtonProperty("methodToCall.cancelTa");
        cancelTravelButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_cancelta.gif");
        cancelTravelButton.setExtraButtonAltText("Cancel Travel Authorization");

        // Close Travel button
        ExtraButton closeTAButton = new ExtraButton();
        closeTAButton.setExtraButtonProperty("methodToCall.closeTa");
        closeTAButton.setExtraButtonSource("${" + KFSConstants.EXTERNALIZABLE_IMAGES_URL_KEY + "}buttonsmall_closeta.gif");
        closeTAButton.setExtraButtonAltText("Close Travel Authorization");

        result.put(amendButton.getExtraButtonProperty(), amendButton);
        result.put(holdButton.getExtraButtonProperty(), holdButton);
        result.put(removeHoldButton.getExtraButtonProperty(), removeHoldButton);
        result.put(cancelTravelButton.getExtraButtonProperty(), cancelTravelButton);
        result.put(closeTAButton.getExtraButtonProperty(), closeTAButton);

        result.putAll(createNewReimbursementButtonMap());
        result.putAll(createPaymentExtraButtonMap());

        return result;
    }

    @Override
    public List<ExtraButton> getExtraButtons() {
        super.getExtraButtons();
        Map<String, ExtraButton> buttonsMap = createButtonsMap();

        if (getDocumentActions().keySet().contains(TemConstants.TravelAuthorizationActions.CAN_AMEND)) {
            extraButtons.add(buttonsMap.get("methodToCall.amendTa"));
        }
        if (getDocumentActions().keySet().contains(TemConstants.TravelAuthorizationActions.CAN_HOLD)) {
            extraButtons.add(buttonsMap.get("methodToCall.holdTa"));
        }
        if (getDocumentActions().keySet().contains(TemConstants.TravelAuthorizationActions.CAN_REMOVE_HOLD)) {
            extraButtons.add(buttonsMap.get("methodToCall.removeHoldTa"));
        }
        if (getDocumentActions().keySet().contains(TemConstants.TravelAuthorizationActions.CAN_CLOSE_TA)) {
            extraButtons.add(buttonsMap.get("methodToCall.closeTa"));
        }
        if (getDocumentActions().keySet().contains(TemConstants.TravelAuthorizationActions.CAN_CANCEL_TA)) {
            extraButtons.add(buttonsMap.get("methodToCall.cancelTa"));
        }
        if (getDocumentActions().keySet().contains(TemConstants.TravelAuthorizationActions.CAN_NEW_REIMBURSEMENT)) {
            extraButtons.add(buttonsMap.get("methodToCall.newReimbursement"));
        }
        if (getDocumentActions().keySet().contains(TemConstants.TravelAuthorizationActions.CAN_PAY_VENDOR)) {
            extraButtons.add(buttonsMap.get("methodToCall.payDVToVendor"));
        }

        return extraButtons;
    }

    @Override
    public boolean isCanUnmask() {
        return canUnmask;
    }

    @Override
    public void setCanUnmask(boolean canUnmask) {
        this.canUnmask = canUnmask;
    }

    protected TravelReimbursementService getTravelReimbursementService() {
        return SpringContext.getBean(TravelReimbursementService.class);
    }

    protected TravelService getTravelService() {
        return SpringContext.getBean(TravelService.class);
    }

    public boolean isAllowIncidentals() {
        return allowIncidentals;
    }

    public void setAllowIncidentals(boolean allowIncidentals) {
        this.allowIncidentals = allowIncidentals;
    }

    /**
     * Gets the policyURL attribute.
     *
     * @return Returns the policyURL.
     */
    public String getPolicyURL() {
        if (policyURL == null) {
            policyURL = getParameterService().getParameterValueAsString(TravelAuthorizationDocument.class, TravelAuthorizationParameters.TRAVEL_ADVANCES_POLICY_URL);
        }
        return MessageUtils.getMessage(TemKeyConstants.MESSAGE_TA_ADVANCE_POLICY, policyURL);
    }

    /**
     * @return true if travel advances for trip should be shown, false otherwise
     */
    public boolean isShowTravelAdvancesForTrip() {
        return showTravelAdvancesForTrip;
    }

    /**
     * Sets whether the travel advances for the rest of the trip (ie, not this document's travel advance, but other completed advances related to this trip)
     * should be shown
     *
     * @param showTravelAdvancesForTrip true if travel advances for trip should be shown, false otherwise
     */
    public void setShowTravelAdvancesForTrip(boolean showTravelAdvancesForTrip) {
        this.showTravelAdvancesForTrip = showTravelAdvancesForTrip;
    }

    /**
     * Gets the waitingOnTraveler attribute.
     *
     * @return Returns the waitingOnTraveler.
     */
    public boolean isWaitingOnTraveler() {
        return waitingOnTraveler;
    }

    /**
     * Sets the waitingOnTraveler attribute value.
     *
     * @param waitingOnTraveler The waitingOnTraveler to set.
     */
    public void setWaitingOnTraveler(boolean waitingOnTraveler) {
        this.waitingOnTraveler = waitingOnTraveler;
    }

    /**
     * Gets the showCorporateCardTotal attribute.
     *
     * @return Returns the showCorporateCardTotal.
     */
    public boolean getShowCorporateCardTotal() {
        if (showCorporateCardTotal == null) {
            showCorporateCardTotal = Boolean.valueOf(getParameterService().getParameterValueAsBoolean(TemParameterConstants.TEM_DOCUMENT.class, TravelParameters.AMOUNT_DUE_CORPORATE_CARD_TOTAL_LINE_IND, false));
        }
        return showCorporateCardTotal.booleanValue();
    }

    /**
     * Gets the showPolicy attribute.
     *
     * @return Returns the showPolicy.
     */
    public boolean isShowPolicy() {
        return showPolicy;
    }

    /**
     * Sets the showPolicy attribute value.
     *
     * @param showPolicy The showPolicy to set.
     */
    public void setShowPolicy(boolean showPolicy) {
        this.showPolicy = showPolicy;
    }

    protected DataDictionaryService getDataDictionaryService() {
        return SpringContext.getBean(DataDictionaryService.class);
    }

    /**
     * @see org.kuali.kfs.module.tem.document.web.struts.TravelFormBase#getTravelPaymentFormAction()
     */
    @Override
    public String getTravelPaymentFormAction() {
        return TemConstants.TRAVEL_AUTHORIZATION_ACTION_NAME;
    }

    /**
     * @return the encapsulation of an uploaded file of accounting lines to associate with the travel advance
     */
    public FormFile getAdvanceAccountingFile() {
        return advanceAccountingFile;
    }

    /**
     * Sets the form file for advance accounting line uploads
     *
     * @param advanceFile a file of accounting lines to associate with paying for a travel advance
     */
    public void setAdvanceAccountingFile(FormFile advanceFile) {
        this.advanceAccountingFile = advanceFile;
    }

    /**
     * Retrieves the advance accounting lines total in a currency format with commas.
     *
     * @return the currency formatted total
     */
    public String getCurrencyFormattedAdvanceTotal() {
        return (String) new CurrencyFormatter().format(getTravelAuthorizationDocument().getAdvanceTotal());
    }

    public boolean isAdvancePdpStatusTabShown() {
        if (getTravelDocument() != null) {
            return getTravelAuthorizationDocument().shouldProcessAdvanceForDocument();
        }
        return false;
    }


    public boolean isDefaultTravelAdvanceTab() {
        if (getTravelDocument() != null && getTravelAuthorizationDocument().shouldProcessAdvanceForDocument()) {

            if (TemConstants.TravelAuthorizationStatusCodeKeys.IN_PROCESS.equals(getTravelDocument().getAppDocStatus()) ||
                TemConstants.TravelAuthorizationStatusCodeKeys.AWAIT_TRVLR.equals(getTravelDocument().getAppDocStatus()) ||
                TemConstants.TravelAuthorizationStatusCodeKeys.AWAIT_FISCAL.equals(getTravelDocument().getAppDocStatus()) ||
                TemConstants.TravelAuthorizationStatusCodeKeys.AWAIT_TRVL_MGR.equals(getTravelDocument().getAppDocStatus())) {
                return true;
            }

        }

        return false;
    }

    /**
     * Overridden to pull the object code from the trip type if possible
     *
     * @see org.kuali.kfs.module.tem.document.web.struts.TravelFormBase#createNewSourceAccountingLine(org.kuali.kfs.sys.document.AccountingDocument)
     */
    @Override
    protected SourceAccountingLine createNewSourceAccountingLine(AccountingDocument financialDocument) {
        TemSourceAccountingLine accountingLine = (TemSourceAccountingLine) super.createNewSourceAccountingLine(financialDocument);
        TravelDocumentBase travelDoc = (TravelDocumentBase) financialDocument;
        travelDoc.refreshReferenceObject(TemPropertyConstants.TRIP_TYPE);
        if (ObjectUtils.isNotNull(travelDoc.getTripType())) {
            // set object code based on trip type
            accountingLine.setFinancialObjectCode(travelDoc.getTripType().getEncumbranceObjCode());
        }
        return accountingLine;
    }

    @Override
    public void populateHeaderFields(WorkflowDocument workflowDocument) {
        super.populateHeaderFields(workflowDocument);
        boolean vendorPaymentAllowedBeforeFinal = getParameterService().getParameterValueAsBoolean(TravelAuthorizationDocument.class, TemConstants.TravelAuthorizationParameters.VENDOR_PAYMENT_ALLOWED_BEFORE_FINAL_APPROVAL_IND);
        String travelDocumentIdentifier = getTravelDocument().getTravelDocumentIdentifier();
        String organizationDocumentNumber = getTravelDocument().getDocumentHeader().getOrganizationDocumentNumber();
        TravelAuthorizationDocument document = (TravelAuthorizationDocument) getTravelDocument();

        if (document.maskTravelDocumentIdentifierAndOrganizationDocNumber()) {

            DataDictionaryService dataDictionaryService = SpringContext.getBean(DataDictionaryService.class);
            if (ObjectUtils.isNotNull(travelDocumentIdentifier)) {
                travelDocumentIdentifier = "";
                int strLength = dataDictionaryService.getAttributeMaxLength(TravelAuthorizationDocument.class.getName(), TemPropertyConstants.TRAVEL_DOCUMENT_IDENTIFIER);
                for (int i = 0; i < strLength; i++) {
                    travelDocumentIdentifier = travelDocumentIdentifier.concat("*");
                }
            }

            if (ObjectUtils.isNotNull(organizationDocumentNumber)) {
                organizationDocumentNumber = "";
                int strLength = dataDictionaryService.getAttributeMaxLength(DocumentHeader.class.getName(), "organizationDocumentNumber");

                for (int i = 0; i < strLength; i++) {
                    organizationDocumentNumber = organizationDocumentNumber.concat("*");
                }
            }

            for (HeaderField headerField : getDocInfo()) {
                String ddAttributeEntryName = headerField.getDdAttributeEntryName();
                if (ObjectUtils.isNotNull(ddAttributeEntryName) && ddAttributeEntryName.equals("DataDictionary.TravelAuthorizationDocument.attributes.travelDocumentIdentifier")) {
                    headerField.setDisplayValue(travelDocumentIdentifier);
                }

                if (ObjectUtils.isNotNull(ddAttributeEntryName) && ddAttributeEntryName.equals("DataDictionary.DocumentHeader.attributes.organizationDocumentNumber")) {
                    headerField.setDisplayValue(organizationDocumentNumber);
                }
            }
        }


    }

}
