/**
 * ***************************************************************************
 * Copyright (c) 2010 Qcadoo Limited
 * Project: Qcadoo MES
 * Version: 1.4
 *
 * This file is part of Qcadoo.
 *
 * Qcadoo 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * ***************************************************************************
 */
package com.qcadoo.mes.cmmsMachineParts.hooks;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.qcadoo.localization.api.TranslationService;
import com.qcadoo.mes.basic.ParameterService;
import com.qcadoo.mes.cmmsMachineParts.constants.ParameterFieldsCMP;
import com.qcadoo.mes.cmmsMachineParts.constants.PlannedEventBasedOn;
import com.qcadoo.mes.cmmsMachineParts.constants.PlannedEventFields;
import com.qcadoo.mes.cmmsMachineParts.constants.PlannedEventType;
import com.qcadoo.mes.cmmsMachineParts.plannedEvents.factory.EventFieldsForTypeFactory;
import com.qcadoo.mes.cmmsMachineParts.plannedEvents.fieldsForType.FieldsForType;
import com.qcadoo.mes.cmmsMachineParts.roles.PlannedEventRoles;
import com.qcadoo.mes.cmmsMachineParts.states.constants.PlannedEventState;
import com.qcadoo.mes.cmmsMachineParts.states.constants.PlannedEventStateStringValues;
import com.qcadoo.model.api.Entity;
import com.qcadoo.security.api.SecurityService;
import com.qcadoo.security.api.UserService;
import com.qcadoo.view.api.ComponentState;
import com.qcadoo.view.api.ViewDefinitionState;
import com.qcadoo.view.api.components.*;
import com.qcadoo.view.api.components.lookup.FilterValueHolder;
import com.qcadoo.view.api.ribbon.Ribbon;
import com.qcadoo.view.api.ribbon.RibbonActionItem;
import com.qcadoo.view.api.ribbon.RibbonGroup;
import com.qcadoo.view.constants.QcadooViewConstants;
import com.qcadoo.view.internal.components.select.SelectComponentState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

@Service
public class PlannedEventDetailsHooks {







    private static final String L_ACTIONS = "actions";

    private static final String L_STATUS = "status";

    private static final String L_REALIZED_EVENT = "realizedEvent";

    private static final String L_RELATED_EVENT_LOOKUP = "relatedEventLookup";

    private static final List<String> L_GRIDS = Lists.newArrayList(PlannedEventFields.RELATED_EVENTS, PlannedEventFields.ACTIONS,
            PlannedEventFields.RESPONSIBLE_WORKERS, PlannedEventFields.REALIZATIONS, PlannedEventFields.MACHINE_PARTS_FOR_EVENT);
    public static final String L_EVENT_ID_FOR_MULTI_UPLOAD = "eventIdForMultiUpload";
    public static final String L_EVENT_MULTI_UPLOAD_LOCALE = "eventMultiUploadLocale";

    private List<String> previouslyHiddenTabs = Lists.newArrayList();

    @Autowired
    private EventFieldsForTypeFactory eventFieldsForTypeFactory;

    @Autowired
    private EventHooks eventHooks;

    @Autowired
    private UserService userService;

    @Autowired
    private SecurityService securityService;

    @Autowired
    private TranslationService translationService;

    @Autowired
    private ParameterService parameterService;

    public void plannedEventBeforeRender(final ViewDefinitionState view) {
        FormComponent plannedEventForm = (FormComponent) view.getComponentByReference(QcadooViewConstants.L_FORM);
        plannedEventForm.setFormEnabled(true);

        FieldComponent acceptanceEventsField = (FieldComponent) view.getComponentByReference(PlannedEventFields.ACCEPTANCE_EVENTS);
        acceptanceEventsField.setFieldValue(parameterService.getParameter().getBooleanField(ParameterFieldsCMP.ACCEPTANCE_EVENTS));

        eventHooks.plannedEventBeforeRender(view);

        Entity plannedEvent = plannedEventForm.getEntity();

        FieldComponent typeField = (FieldComponent) view.getComponentByReference(PlannedEventFields.TYPE);

        typeField.setEnabled(plannedEvent.getId() == null);

        setCriteriaModifiers(view, plannedEvent);

        // TODO dev_team - very very ugly way to fix issue GOODFOOD-742, should be fixed more properly
        // if problem with manyToMany in related entity will happen once more
        Object relatedEvents = plannedEvent.getField(PlannedEventFields.RELATED_EVENTS);

        if ((relatedEvents != null) && relatedEvents.getClass().equals(Integer.class)) {
            lockView(view);
        }

        processRoles(view);
        disableFieldsForState(view);
        setUnit(view);
    }

    private void lockView(final ViewDefinitionState view) {
        FormComponent plannedEventForm = (FormComponent) view.getComponentByReference(QcadooViewConstants.L_FORM);
        WindowComponent window = (WindowComponent) view.getComponentByReference(QcadooViewConstants.L_WINDOW);

        Ribbon ribbon = window.getRibbon();
        RibbonGroup actionsRibbonGroup = ribbon.getGroupByName(L_ACTIONS);
        RibbonGroup statusRibbonGroup = ribbon.getGroupByName(L_STATUS);

        List<RibbonActionItem> ribbonActionItems = actionsRibbonGroup.getItems();

        ribbonActionItems.addAll(statusRibbonGroup.getItems());

        for (RibbonActionItem ribbonActionItem : ribbonActionItems) {
            ribbonActionItem.setEnabled(false);
            ribbonActionItem.requestUpdate(true);
        }

        for (String referenceName : L_GRIDS) {
            lockGrid(view, referenceName);
        }

        plannedEventForm.setFormEnabled(false);
    }

    private void lockGrid(final ViewDefinitionState view, final String referenceName) {
        GridComponent gridComponent = (GridComponent) view.getComponentByReference(referenceName);
        gridComponent.setEnabled(false);
    }

    private void setCriteriaModifiers(final ViewDefinitionState view, final Entity plannedEvent) {
        LookupComponent relatedEventLookup = (LookupComponent) view.getComponentByReference(L_RELATED_EVENT_LOOKUP);

        FilterValueHolder filterValueHolder = relatedEventLookup.getFilterValue();
        filterValueHolder.put(PlannedEventFields.NUMBER, plannedEvent.getStringField(PlannedEventFields.NUMBER));

        relatedEventLookup.setFilterValue(filterValueHolder);
    }

    public void toggleFieldsVisible(final ViewDefinitionState view) {
        FormComponent plannedEventForm = (FormComponent) view.getComponentByReference(QcadooViewConstants.L_FORM);

        Entity plannedEvent = plannedEventForm.getPersistedEntityWithIncludedFormValues();

        PlannedEventType type = PlannedEventType.from(plannedEvent);

        FieldsForType fieldsForType = eventFieldsForTypeFactory.createFieldsForType(type);

        if (fieldsForType == null) {
            return;
        }

        hideFields(view, plannedEvent, fieldsForType);
        hideTabs(view, fieldsForType);
        clearGrids(view, fieldsForType);
        setAndLockBasedOn(view, fieldsForType);
        actionsButtonProcess(plannedEvent, view, fieldsForType);
    }

    private void actionsButtonProcess(Entity plannedEvent, ViewDefinitionState view, FieldsForType ftype) {
        WindowComponent window = (WindowComponent) view.getComponentByReference(QcadooViewConstants.L_WINDOW);
        PlannedEventType type = PlannedEventType.from(plannedEvent);
        Ribbon ribbon = window.getRibbon();
        RibbonGroup actionsGroup = ribbon.getGroupByName("actionsGroup");
        RibbonActionItem addActionsItem = actionsGroup.getItemByName("actions");
        if(plannedEvent.getId() == null){
            addActionsItem.setEnabled(false);
            addActionsItem.requestUpdate(true);
            window.requestRibbonRender();
            return;
        }

        boolean enable = false;

        if (type.compareTo(PlannedEventType.REVIEW) == 0) {
            enable = true;
        } else if (type.compareTo(PlannedEventType.REPAIRS) == 0) {
            enable = true;
        } else if (type.compareTo(PlannedEventType.EXTERNAL_SERVICE) == 0) {
            enable = false;
        } else if (type.compareTo(PlannedEventType.ADDITIONAL_WORK) == 0) {
            enable = true;
        } else if (type.compareTo(PlannedEventType.MANUAL) == 0) {
            enable = true;
        } else if (type.compareTo(PlannedEventType.METER_READING) == 0) {
            enable = false;
        } else if (type.compareTo(PlannedEventType.UDT_REVIEW) == 0) {
            enable = false;
        } else if (type.compareTo(PlannedEventType.AFTER_REVIEW) == 0) {
            enable = false;
        }

        addActionsItem.setEnabled(enable);
        addActionsItem.requestUpdate(true);
        window.requestRibbonRender();
    }

    public void toggleActionsFieldsVisible(final ViewDefinitionState view) {
        FormComponent plannedEventForm = (FormComponent) view.getComponentByReference(QcadooViewConstants.L_FORM);

        Entity plannedEvent = plannedEventForm.getPersistedEntityWithIncludedFormValues();

        PlannedEventType type = PlannedEventType.from(plannedEvent);

        FieldsForType fieldsForType = eventFieldsForTypeFactory.createFieldsForType(type);

        if (fieldsForType == null) {
            return;
        }
        toggleAddMultipleActions(view, fieldsForType, plannedEvent);
    }
    public void hideFields(final ViewDefinitionState view, final Entity plannedEvent, final FieldsForType fieldsForType) {
        Set<String> allFields = plannedEvent.getDataDefinition().getFields().keySet();

        List<String> hiddenFields = fieldsForType.getHiddenFields();

        for (String fieldName : allFields) {
            Optional<ComponentState> maybeFieldComponent = view.tryFindComponentByReference(fieldName);

            if (maybeFieldComponent.isPresent() && !PlannedEventFields.STATE.equals(fieldName)) {
                ComponentState fieldComponent = maybeFieldComponent.get();

                if (hiddenFields.contains(fieldName)) {
                    fieldComponent.setVisible(false);
                } else {
                    fieldComponent.setVisible(true);
                }
            }
        }
    }

    private void hideTabs(final ViewDefinitionState view, final FieldsForType fieldsForType) {
        List<String> hiddenTabs = fieldsForType.getHiddenTabs();

        for (String tab : previouslyHiddenTabs) {
            ComponentState tabComponent = view.getComponentByReference(tab);

            if (tabComponent != null) {
                tabComponent.setVisible(true);
            }
        }

        for (String tab : hiddenTabs) {
            ComponentState tabComponent = view.getComponentByReference(tab);

            if (tabComponent != null) {
                tabComponent.setVisible(false);
            }
        }

        previouslyHiddenTabs = hiddenTabs;
    }

    private void clearGrids(final ViewDefinitionState view, final FieldsForType fieldsForType) {
        List<String> referenceNames = fieldsForType.getGridsToClear();

        referenceNames.stream().forEach(referenceName -> {
            GridComponent gridComponent = (GridComponent) view.getComponentByReference(referenceName);

            if (gridComponent != null) {
                gridComponent.setEntities(Lists.newArrayList());
            }
        });
    }

    public void setAndLockBasedOn(final ViewDefinitionState view, final FieldsForType fieldsForType) {
        FieldComponent basedOnField = (FieldComponent) view.getComponentByReference(PlannedEventFields.BASED_ON);

        if (fieldsForType.shouldLockBasedOn()) {
            basedOnField.setFieldValue(PlannedEventBasedOn.DATE.getStringValue());
            basedOnField.setEnabled(false);
        } else {
            basedOnField.setEnabled(true);
        }
    }

    private void disableFieldsForState(final ViewDefinitionState view) {
        FormComponent plannedEventForm = (FormComponent) view.getComponentByReference(QcadooViewConstants.L_FORM);

        Entity event = plannedEventForm.getPersistedEntityWithIncludedFormValues();

        PlannedEventState state = PlannedEventState.of(event);

        if (PlannedEventState.CANCELED.equals(state) || PlannedEventState.REALIZED.equals(state)) {
            plannedEventForm.setFormEnabled(false);

            lockGrids(view, Lists.newArrayList(PlannedEventFields.RESPONSIBLE_WORKERS, PlannedEventFields.RELATED_EVENTS,
                    PlannedEventFields.REALIZATIONS, PlannedEventFields.MACHINE_PARTS_FOR_EVENT));
        }
    }

    private void lockGrids(final ViewDefinitionState view, final List<String> referenceNames) {
        referenceNames.stream().forEach(referenceName -> {
            GridComponent gridComponent = (GridComponent) view.getComponentByReference(referenceName);

            if (gridComponent != null) {
                gridComponent.setEnabled(false);
            }
        });
    }

    public void setEventIdForMultiUploadField(final ViewDefinitionState view) {
        FormComponent plannedEventForm = (FormComponent) view.getComponentByReference(QcadooViewConstants.L_FORM);
        FieldComponent plannedEventIdForMultiUpload = (FieldComponent) view.getComponentByReference(L_EVENT_ID_FOR_MULTI_UPLOAD);
        FieldComponent plannedEventMultiUploadLocale = (FieldComponent) view.getComponentByReference(L_EVENT_MULTI_UPLOAD_LOCALE);

        Long plannedEventId = plannedEventForm.getEntityId();

        if (plannedEventId == null) {
            plannedEventIdForMultiUpload.setFieldValue("");
            plannedEventIdForMultiUpload.requestComponentUpdateState();
        } else {
            plannedEventIdForMultiUpload.setFieldValue(plannedEventId);
            plannedEventIdForMultiUpload.requestComponentUpdateState();
        }

        plannedEventMultiUploadLocale.setFieldValue(LocaleContextHolder.getLocale());
        plannedEventMultiUploadLocale.requestComponentUpdateState();
    }

    public void processRoles(final ViewDefinitionState view) {
        Entity user = userService.getCurrentUserEntity();

        for (PlannedEventRoles role : PlannedEventRoles.values()) {
            if (!securityService.hasRole(user, role.toString())) {
                role.disableFieldsWhenNotInRole(view);
            }
        }

        if (!parameterService.getParameter().getBooleanField(ParameterFieldsCMP.ACCEPTANCE_EVENTS)) {
            if (!securityService.hasRole(user, PlannedEventRoles.ROLE_PLANNED_EVENTS_STATES_START_STOP.toString())) {
                enableRealizationEvents(view, false);
            } else {
                enableRealizationEvents(view, true);
            }
        }
    }

    private void enableRealizationEvents(final ViewDefinitionState view, final boolean enable) {
        if (eventInState(view, PlannedEventState.IN_EDITING) || eventInState(view, PlannedEventState.ACCEPTED)) {
            enableFromRibbonGroup(view, enable, L_STATUS, L_REALIZED_EVENT);
        }
    }

    private boolean eventInState(final ViewDefinitionState view, final PlannedEventState state) {
        FormComponent plannedEventForm = (FormComponent) view.getComponentByReference(QcadooViewConstants.L_FORM);

        Entity event = plannedEventForm.getEntity();

        String eventState = event.getStringField(PlannedEventFields.STATE);

        if (eventState == null) {
            GridComponent gridComponent = (GridComponent) view.getComponentByReference(QcadooViewConstants.L_GRID);

            List<Entity> entities = gridComponent.getSelectedEntities();

            if (entities.isEmpty()) {
                return false;
            }

            return entities.stream().allMatch(e -> state.getStringValue().equals(e.getStringField(PlannedEventFields.STATE)));
        }

        return state.getStringValue().equals(eventState);
    }

    private void enableFromRibbonGroup(final ViewDefinitionState view, final boolean enable, final String groupName,
            String... items) {
        WindowComponent window = (WindowComponent) view.getComponentByReference(QcadooViewConstants.L_WINDOW);

        Ribbon ribbon = window.getRibbon();
        RibbonGroup ribbonGroup = ribbon.getGroupByName(groupName);

        if (ribbonGroup != null) {
            for (String item : items) {
                RibbonActionItem ribbonActionItem = ribbonGroup.getItemByName(item);

                if (ribbonActionItem != null) {
                    ribbonActionItem.setEnabled(enable);
                    ribbonActionItem.requestUpdate(true);
                }
            }
        }
    }

    public void setUnit(final ViewDefinitionState view) {
        SelectComponentState basedOnSelect = (SelectComponentState) view.getComponentByReference(PlannedEventFields.BASED_ON);
        FieldComponent toleranceUnitField = (FieldComponent) view.getComponentByReference("toleranceUnit");

        switch (PlannedEventBasedOn.parseString((String) basedOnSelect.getFieldValue())) {
            case COUNTER:
                toleranceUnitField.setFieldValue(translationService.translate("cmmsMachineParts.plannedEvent.toleranceUnit.mh",
                        view.getLocale()));

                break;
            case DATE:
                toleranceUnitField.setFieldValue(translationService.translate("cmmsMachineParts.plannedEvent.toleranceUnit.days",
                        view.getLocale()));

                break;
        }
    }

    private void toggleAddMultipleActions(ViewDefinitionState view, FieldsForType fieldsForType, Entity plannedEvent) {
        WindowComponent window = (WindowComponent) view.getComponentByReference(QcadooViewConstants.L_WINDOW);

        Ribbon ribbon = window.getRibbon();
        RibbonGroup actionsGroup = ribbon.getGroupByName("actionsGroup");
        RibbonActionItem addActionsItem = actionsGroup.getItemByName("addActions");

        String state = plannedEvent.getStringField(PlannedEventFields.STATE);

        List<String> statesToDisable = Arrays.asList(PlannedEventStateStringValues.REALIZED, PlannedEventStateStringValues.CANCELED);

        boolean enableAddActions = plannedEvent.getId() != null && !statesToDisable.contains(state) && !fieldsForType.getHiddenTabs().contains(PlannedEventFields.ACTIONS_TAB);

        addActionsItem.setEnabled(enableAddActions);
        addActionsItem.requestUpdate(true);
    }

}
