/*
 * Copyright (C) 2017 LibreCCM Foundation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */
package com.arsdigita.ui.admin.pagemodels;

import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.ParameterSingleSelectionModel;
import com.arsdigita.bebop.PropertySheet;
import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.form.Option;
import com.arsdigita.bebop.form.SingleSelect;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.ui.admin.AdminUiConstants;

import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.pagemodel.ComponentModel;
import org.libreccm.pagemodel.ComponentModels;
import org.libreccm.pagemodel.PageModel;
import org.libreccm.pagemodel.PageModelComponentModel;
import org.libreccm.pagemodel.PageModelManager;
import org.libreccm.pagemodel.PageModelRepository;

import java.util.List;
import java.util.TooManyListenersException;

/**
 * Shows the details about a {@link PageModel} including the
 * {@link ComponentModel}s assigned to the {@link PageModel}.
 *
 * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
 */
class PageModelDetails extends BoxPanel {

    private final PageModelsTab pageModelTab;
    private final ParameterSingleSelectionModel<String> selectedModelId;
    private final ParameterSingleSelectionModel<String> selectedComponentId;

    public PageModelDetails(
        final PageModelsTab pageModelTab,
        final ParameterSingleSelectionModel<String> selectedModelId,
        final ParameterSingleSelectionModel<String> selectedComponentId) {

        super(BoxPanel.VERTICAL);

        this.pageModelTab = pageModelTab;
        this.selectedModelId = selectedModelId;
        this.selectedComponentId = selectedComponentId;

        final ActionLink backLink = new ActionLink(new GlobalizedMessage(
            "ui.admin.pagemodels.details.back",
            AdminUiConstants.ADMIN_BUNDLE));
        backLink.setClassAttr("back-link");
        backLink.addActionListener(event -> {
            selectedModelId.clearSelection(event.getPageState());
            pageModelTab.showPageModelsTable(event.getPageState());
        });
        super.add(backLink);

        final Label heading = new Label();
        heading.setClassAttr("heading");
        heading.addPrintListener(this::printHeading);
        super.add(heading);

        final PropertySheet propertySheet = new PropertySheet(
            new PageModelPropertySheetModelBuilder(selectedModelId));
        super.add(propertySheet);

        final ActionLink editProperties = new ActionLink(new GlobalizedMessage(
            "ui.admin.pagemodels.details.edit_properties",
            AdminUiConstants.ADMIN_BUNDLE));
        editProperties.addActionListener(event -> {
            pageModelTab.showPageModelForm(event.getPageState());
        });

        final ActionLink publishLink = new ActionLink(new GlobalizedMessage(
            "ui.admin.pagemodels.details.publish",
            AdminUiConstants.ADMIN_BUNDLE));
        publishLink.addActionListener(this::publishPageModel);

        final BoxPanel actionsPanel = new BoxPanel(BoxPanel.HORIZONTAL);
        actionsPanel.add(editProperties);
        actionsPanel.add(publishLink);
        super.add(actionsPanel);

        final AddComponentForm addComponentForm = new AddComponentForm(
            pageModelTab);
        super.add(addComponentForm);

        final ComponentsTable componentsTable
                                  = new ComponentsTable(
                pageModelTab, selectedModelId, selectedComponentId);
        super.add(componentsTable);
    }

    private void printHeading(final PrintEvent event) {
        final PageState state = event.getPageState();
        final Label target = (Label) event.getTarget();
        final PageModelRepository pageModelRepo = CdiUtil
            .createCdiUtil()
            .findBean(PageModelRepository.class);
        final PageModel pageModel = pageModelRepo
            .findById(Long.parseLong(selectedModelId.getSelectedKey(state)))
            .get();
        target.setLabel(new GlobalizedMessage(
            "ui.admin.pagemodels.details.heading",
            AdminUiConstants.ADMIN_BUNDLE,
            new String[]{pageModel.getName()}));
    }

    private void publishPageModel(final ActionEvent event) {

        final PageState state = event.getPageState();

        final String selectedPageModelIdStr = selectedModelId
            .getSelectedKey(state);

        final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
        final PageModelManager pageModelManager = cdiUtil
            .findBean(PageModelManager.class);
        final PageModelRepository pageModelRepo = cdiUtil
            .findBean(PageModelRepository.class);

        final PageModel pageModel = pageModelRepo
            .findById(Long.parseLong(selectedPageModelIdStr))
            .orElseThrow(() -> new UnexpectedErrorException(String
            .format("No PageModel with ID %s in the database.",
                    selectedPageModelIdStr)));

        final PageModel draftModel = pageModelManager.getDraftVersion(pageModel);
        pageModelManager.publish(draftModel);
    }

    /**
     * Form for selecting the type of {@link ComponentModel} to add to the
     * {@link PageModel}.
     */
    private class AddComponentForm extends Form {

        private final PageModelsTab pageModelTab;
        private final SingleSelect selectType;

        public AddComponentForm(final PageModelsTab pageModelTab) {

            super("pagemodel_add_component_form",
                  new BoxPanel(BoxPanel.HORIZONTAL));

            this.pageModelTab = pageModelTab;

            final Label formLabel = new Label(new GlobalizedMessage(
                "ui.admin.pagemodels.add_new_component",
                AdminUiConstants.ADMIN_BUNDLE));
            super.add(formLabel);

            selectType = new SingleSelect("select_component_type");
            selectType.setLabel(new GlobalizedMessage(
                "ui.admin.pagemodels.add_new_component.type",
                AdminUiConstants.ADMIN_BUNDLE));
            try {
                selectType
                    .addPrintListener(new ComponentModelSelectPrintListener());
            } catch (TooManyListenersException ex) {
                throw new UnexpectedErrorException(ex);
            }
            super.add(selectType);

            final Submit submit = new Submit(new GlobalizedMessage(
                "ui.admin.pagemodels.add_new_component.submit",
                AdminUiConstants.ADMIN_BUNDLE));
            super.add(submit);

            super.addProcessListener(this::process);
        }

        public void process(final FormSectionEvent event)
            throws FormProcessException {

            final PageState state = event.getPageState();
            final String type = (String) selectType.getValue(state);
            final Class<? extends ComponentModel> clazz = getClass(type);

            pageModelTab.showNewComponentForm(state, clazz);
        }

        @SuppressWarnings("unchecked")
        private Class<? extends ComponentModel> getClass(final String type) {
            try {
                return (Class<? extends ComponentModel>) Class.forName(type);
            } catch (ClassNotFoundException ex) {
                throw new UnexpectedErrorException(ex);
            }
        }

    }

    /**
     * {@link PrintListener} implementation for the select box in the
     * {@link AddComponentForm}.
     */
    private class ComponentModelSelectPrintListener implements PrintListener {

        @Override
        public void prepare(final PrintEvent event) {

            final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
            final ComponentModels componentModels = cdiUtil
                .findBean(ComponentModels.class);

            final SingleSelect target = (SingleSelect) event.getTarget();
            target.clearOptions();

            final List<PageModelComponentModel> models = componentModels
                .findAvailableComponentModels();
            for (final PageModelComponentModel model : models) {
                target.addOption(createOption(model));
            }
        }

        private Option createOption(final PageModelComponentModel model) {

            final GlobalizedMessage title = new GlobalizedMessage(
                model.titleKey(), model.descBundle());

            return new Option(model.modelClass().getName(),
                              new Label(title));
        }

    }

}
