/*
 * Copyright 2010-2015 Institut Pasteur.
 * 
 * This file is part of Icy.
 * 
 * Icy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Icy 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
 */
package icy.gui.component.renderer;

import java.awt.Color;
import java.awt.Component;
import java.awt.Insets;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.ListUI;

import org.pushingpixels.substance.api.ColorSchemeAssociationKind;
import org.pushingpixels.substance.api.ComponentState;
import org.pushingpixels.substance.api.ComponentStateFacet;
import org.pushingpixels.substance.api.SubstanceColorScheme;
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
import org.pushingpixels.substance.api.renderers.SubstanceDefaultComboBoxRenderer;
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
import org.pushingpixels.substance.internal.animation.StateTransitionTracker.ModelStateInfo;
import org.pushingpixels.substance.internal.animation.StateTransitionTracker.StateContributionInfo;
import org.pushingpixels.substance.internal.ui.SubstanceComboBoxUI;
import org.pushingpixels.substance.internal.ui.SubstanceListUI;
import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
import org.pushingpixels.substance.internal.utils.SubstanceStripingUtils;
import org.pushingpixels.substance.internal.utils.SubstanceTextUtilities;
import org.pushingpixels.substance.internal.utils.UpdateOptimizationInfo;

/**
 * CustomComboBox renderer, based on Substance look and feel code.<br>
 * Override the getListCellRendererComponent() or updateItem() methods to do your own rendering.
 * 
 * @author Stephane
 */
public class CustomComboBoxRenderer extends SubstanceDefaultComboBoxRenderer
{
    /**
     * 
     */
    private static final long serialVersionUID = -3392789033779535472L;

    private final JComboBox combo;

    public CustomComboBoxRenderer(JComboBox combo)
    {
        super(combo);

        this.combo = combo;
    }

    public JComboBox getComboBox()
    {
        return combo;
    }

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
            boolean cellHasFocus)
    {
        JComponent result = (JComponent) getRendererComponent(list, value, index, isSelected);

        ListUI baseListUI = list.getUI();
        ComboBoxUI baseComboUI = combo.getUI();

        if ((baseListUI instanceof SubstanceListUI) && (baseComboUI instanceof SubstanceComboBoxUI))
        {
            SubstanceListUI listUI = (SubstanceListUI) baseListUI;
            SubstanceComboBoxUI comboUI = (SubstanceComboBoxUI) baseComboUI;

            // special case for the combobox. The selected value is
            // painted using the renderer of the list, and the index
            // is -1.
            if (index == -1)
            {
                StateTransitionTracker stateTransitionTracker = comboUI.getTransitionTracker();
                ModelStateInfo modelStateInfo = stateTransitionTracker.getModelStateInfo();
                ComponentState currState = modelStateInfo.getCurrModelState();
                float comboAlpha = SubstanceColorSchemeUtilities.getAlpha(combo, currState);
                Color fg = SubstanceTextUtilities.getForegroundColor(combo, ((JLabel) result).getText(),
                        modelStateInfo, comboAlpha);
                result.setForeground(fg);
            }
            else
            {
                // use highlight color scheme for selected and rollover
                // elements in the drop down list
                StateTransitionTracker.ModelStateInfo modelStateInfo = listUI.getModelStateInfo(index, result);
                ComponentState currState = listUI.getCellState(index, result);

                if (modelStateInfo == null)
                {
                    SubstanceColorScheme scheme = getColorSchemeForState(list, index, listUI, currState);
                    result.setForeground(new ColorUIResource(scheme.getForegroundColor()));
                }
                else
                {
                    Map<ComponentState, StateContributionInfo> activeStates = modelStateInfo.getStateContributionMap();
                    SubstanceColorScheme colorScheme = getColorSchemeForState(list, index, listUI, currState);

                    if (currState.isDisabled() || (activeStates == null) || (activeStates.size() == 1))
                    {
                        super.setForeground(new ColorUIResource(colorScheme.getForegroundColor()));
                    }
                    else
                    {
                        float aggrRed = 0.0f;
                        float aggrGreen = 0.0f;
                        float aggrBlue = 0.0f;

                        for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : modelStateInfo
                                .getStateContributionMap().entrySet())
                        {
                            ComponentState activeState = activeEntry.getKey();
                            float activeContribution = activeEntry.getValue().getContribution();
                            if (activeContribution == 0.0f)
                                continue;

                            SubstanceColorScheme scheme = getColorSchemeForState(list, index, listUI, activeState);
                            Color schemeFg = scheme.getForegroundColor();
                            aggrRed += schemeFg.getRed() * activeContribution;
                            aggrGreen += schemeFg.getGreen() * activeContribution;
                            aggrBlue += schemeFg.getBlue() * activeContribution;
                        }

                        result.setForeground(new ColorUIResource(new Color((int) aggrRed, (int) aggrGreen,
                                (int) aggrBlue)));
                    }
                }
            }

            SubstanceStripingUtils.applyStripedBackground(list, index, this);
        }

        result.setEnabled(combo.isEnabled() & isEnabled());

        return result;
    }

    protected Component getRendererComponent(JList list, Object value, int index, boolean isSelected)
    {
        setComponentOrientation(list.getComponentOrientation());

        ListUI listUI = list.getUI();

        if (listUI instanceof SubstanceListUI)
        {
            SubstanceListUI ui = (SubstanceListUI) listUI;

            StateTransitionTracker.ModelStateInfo modelStateInfo = ui.getModelStateInfo(index, this);
            ComponentState currState = ui.getCellState(index, this);

            // special case for drop location
            JList.DropLocation dropLocation = list.getDropLocation();
            boolean isDropLocation = (dropLocation != null && !dropLocation.isInsert() && dropLocation.getIndex() == index);

            if (!isDropLocation && (modelStateInfo != null))
            {
                Map<ComponentState, StateContributionInfo> activeStates = modelStateInfo.getStateContributionMap();
                SubstanceColorScheme colorScheme = getColorSchemeForState(list, ui, currState);

                if (currState.isDisabled() || (activeStates == null) || (activeStates.size() == 1))
                    super.setForeground(new ColorUIResource(colorScheme.getForegroundColor()));
                else
                {
                    float aggrRed = 0;
                    float aggrGreen = 0;
                    float aggrBlue = 0;

                    for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : modelStateInfo
                            .getStateContributionMap().entrySet())
                    {
                        ComponentState activeState = activeEntry.getKey();
                        SubstanceColorScheme scheme = getColorSchemeForState(list, ui, activeState);
                        Color schemeFg = scheme.getForegroundColor();
                        float contribution = activeEntry.getValue().getContribution();

                        aggrRed += schemeFg.getRed() * contribution;
                        aggrGreen += schemeFg.getGreen() * contribution;
                        aggrBlue += schemeFg.getBlue() * contribution;
                    }

                    super.setForeground(new ColorUIResource(new Color((int) aggrRed, (int) aggrGreen, (int) aggrBlue)));
                }
            }
            else
            {
                SubstanceColorScheme scheme = getColorSchemeForState(list, ui, currState);

                if (isDropLocation)
                {
                    scheme = SubstanceColorSchemeUtilities.getColorScheme(list,
                            ColorSchemeAssociationKind.TEXT_HIGHLIGHT, currState);
                }

                super.setForeground(new ColorUIResource(scheme.getForegroundColor()));
            }
        }
        else
        {
            if (isSelected)
                setForeground(list.getSelectionForeground());
            else
                setForeground(list.getForeground());
        }

        if (SubstanceLookAndFeel.isCurrentLookAndFeel() && (list.getLayoutOrientation() == JList.VERTICAL))
            SubstanceStripingUtils.applyStripedBackground(list, index, this);

        updateItem(list, value);

        Insets ins = SubstanceSizeUtils.getListCellRendererInsets(SubstanceSizeUtils.getComponentFontSize(list));
        setBorder(new EmptyBorder(ins.top, ins.left, ins.bottom, ins.right));
        setOpaque(false);

        return this;
    }

    protected void updateItem(JList list, Object value)
    {
        if (value instanceof Icon)
        {
            setIcon((Icon) value);
            setText("");
        }
        else
        {
            setIcon(null);
            setText((value == null) ? "" : value.toString());
            setToolTipText((value == null) ? "" : value.toString());
        }

        setEnabled(list.isEnabled());
        setFont(list.getFont());
    }

    private SubstanceColorScheme getColorSchemeForState(JList list, SubstanceListUI ui, ComponentState state)
    {
        final UpdateOptimizationInfo updateOptimizationInfo = ui.getUpdateOptimizationInfo();

        if (state == ComponentState.ENABLED)
        {
            if (updateOptimizationInfo == null)
                return SubstanceColorSchemeUtilities.getColorScheme(list, state);

            return updateOptimizationInfo.getDefaultScheme();
        }

        if (updateOptimizationInfo == null)
            return SubstanceColorSchemeUtilities.getColorScheme(list, ColorSchemeAssociationKind.HIGHLIGHT, state);

        return updateOptimizationInfo.getHighlightColorScheme(state);
    }

    private SubstanceColorScheme getColorSchemeForState(JList list, int index, SubstanceListUI listUI,
            ComponentState state)
    {
        final boolean toUseHighlightKindForCurrState = (index >= 0)
                && (state.isFacetActive(ComponentStateFacet.ROLLOVER) || state
                        .isFacetActive(ComponentStateFacet.SELECTION));
        final UpdateOptimizationInfo updateOptimizationInfo = listUI.getUpdateOptimizationInfo();

        if (toUseHighlightKindForCurrState)
        {
            if (updateOptimizationInfo == null)
                return SubstanceColorSchemeUtilities.getColorScheme(list, ColorSchemeAssociationKind.HIGHLIGHT, state);

            return updateOptimizationInfo.getHighlightColorScheme(state);
        }

        if (updateOptimizationInfo == null)
            return SubstanceColorSchemeUtilities.getColorScheme(list, state);

        return updateOptimizationInfo.getDefaultScheme();
    }
}