/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/

package org.apache.cayenne.modeler.editor.cgen;

import org.apache.cayenne.configuration.ConfigurationNode;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.Embeddable;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.modeler.util.CayenneController;
import org.apache.cayenne.modeler.util.CellRenderers;
import org.apache.cayenne.modeler.util.CheckBoxHeader;
import org.apache.cayenne.modeler.util.ModelerUtil;
import org.apache.cayenne.swing.BindingBuilder;
import org.apache.cayenne.swing.ImageRendererColumn;
import org.apache.cayenne.swing.ObjectBinding;
import org.apache.cayenne.swing.TableBindingBuilder;
import org.apache.cayenne.validation.ValidationFailure;
import org.apache.cayenne.validation.ValidationResult;

import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.table.TableColumnModel;
import java.awt.Component;
import java.util.Collection;
import java.util.List;

/**
 * @since 4.1
 */
public class CgenArtefactSelectorController extends CayenneController {

    private static final Icon ERROR_ICON = ModelerUtil.buildIcon("icon-error.png");
    protected CgenArtefactSelectorPanel view;
    protected ObjectBinding tableBinding;
    private ValidationResult lastValidationResult;
    private final BindingBuilder builder;
    private final CheckBoxHeader checkBoxHeader;

    public CgenArtefactSelectorController(CgenController parent) {
        super(parent);
        this.checkBoxHeader = new CheckBoxHeader();
        this.view = new CgenArtefactSelectorPanel();
        this.builder = new BindingBuilder(getApplication().getBindingFactory(), this);
    }

    public void startup() {
        initBindings();
        classSelectedAction();
    }

    protected CgenController getParentController() {
        return (CgenController) getParent();
    }

    public Component getView() {
        return view;
    }

    protected void initBindings() {
        builder.bindToAction(checkBoxHeader, "checkAllAction()");
        TableBindingBuilder tableBuilder = new TableBindingBuilder(builder);

        tableBuilder.addColumn(
                "",
                "parent.setCurrentClass(#item), selected",
                Boolean.class,
                true,
                Boolean.TRUE);

        tableBuilder.addColumn(
                "  Class",
                "getItemName(#item)",
                JLabel.class,
                false,
                "XXXXXXXXXXXXXXXXXXXXXXXXXX");

        tableBuilder.addColumn(
                "",
                "getProblem(#item)",
                String.class,
                false,
                "XX");

        this.tableBinding = tableBuilder.bindToTable(view.getTable(), "parent.classes");
        TableColumnModel columnModel = view.getTable().getColumnModel();
        columnModel.getColumn(0).setHeaderRenderer(checkBoxHeader);
        columnModel.getColumn(1).setCellRenderer(new ImageRendererColumn());
        columnModel.getColumn(2).setCellRenderer(new ImageRendererColumn());
    }

    public boolean isSelected() {
        return getParentController().isSelected();
    }

    public void setSelected(boolean selected) {
        getParentController().setSelected(selected);
        classSelectedAction();
    }

    /**
     * A callback action that updates the state of Select All checkbox.
     */
    public void classSelectedAction() {
        int selectedCount = getParentController().getSelectedEntitiesSize()
                + getParentController().getSelectedEmbeddablesSize()
                + (getParentController().isDataMapSelected() ? 1 : 0);
        int totalClasses = getParentController().getClasses().size();
        checkBoxHeader.setSelected(selectedCount >= totalClasses);
        getParentController().updateGenerateButton();
        getParentController().updateSelectedEntities();
        getParentController().getStandardModeController().updateTemplateEditorButtons();
        view.repaint();
    }


    /**
     * An action that updates entity check boxes in response to the Select All state
     * change.
     */
    @SuppressWarnings("unused")
    public void checkAllAction() {
        if (getParentController().updateSelection(checkBoxHeader.isSelected() ? o -> true : o -> false)) {
            tableBinding.updateView();
            getParentController().updateSelectedEntities();
            getParentController().updateGenerateButton();
            getParentController().getStandardModeController().updateTemplateEditorButtons();
        }
    }

    public void validate(Collection<? extends ConfigurationNode> classes) {
        CgenValidator validator = new CgenValidator();
        this.lastValidationResult = validator.getValidationResult(classes);
    }

    /**
     * Returns the first encountered validation problem for an antity matching the name or
     * null if the entity is valid or the entity is not present.
     */
    public JLabel getProblem(Object obj) {
        String name = null;
        if (obj instanceof ObjEntity) {
            name = ((ObjEntity) obj).getName();
        } else if (obj instanceof Embeddable) {
            name = ((Embeddable) obj).getClassName();
        }

        ValidationFailure validationFailure = null;
        if (lastValidationResult != null) {
            List<ValidationFailure> failures = lastValidationResult.getFailures(name);
            if (!failures.isEmpty()) {
                validationFailure = failures.get(0);
            }
        }

        JLabel labelIcon = new JLabel();
        labelIcon.setVisible(true);
        if (validationFailure != null) {
            labelIcon.setIcon(ERROR_ICON);
            labelIcon.setToolTipText(validationFailure.getDescription());
        }
        return labelIcon;
    }

    @SuppressWarnings("unused")
    public JLabel getItemName(Object obj) {
        String className;
        Icon icon;
        if (obj instanceof Embeddable) {
            className = ((Embeddable) obj).getClassName();
            icon = CellRenderers.iconForObject(new Embeddable());
        } else if(obj instanceof ObjEntity) {
            className = ((ObjEntity) obj).getName();
            icon = CellRenderers.iconForObject(new ObjEntity());
        } else {
            className = ((DataMap) obj).getName();
            icon = CellRenderers.iconForObject(new DataMap());
        }
        JLabel labelIcon = new JLabel();
        labelIcon.setIcon(icon);
        labelIcon.setVisible(true);
        labelIcon.setText(className);
        return labelIcon;
    }

}
