/*
 * Copyright 2003-2011 JetBrains s.r.o.
 *
 * Licensed 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
 *
 * http://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 jetbrains.mps.ide.platform.ui;

import jetbrains.mps.smodel.ModelAccess;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

public class CheckBoxNodeRenderer implements TreeCellRenderer {
  private final JPanelWithCheckBox myPanel;

  public CheckBoxNodeRenderer() {
    this(false);
  }

  public CheckBoxNodeRenderer(boolean isCheckboxLeft) {
    myPanel = new JPanelWithCheckBox(isCheckboxLeft);
  }

  public boolean isSelected() {
    return myPanel.myCheckBox.isSelected();
  }

  @Override
  public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, final boolean expanded, boolean leaf, int row, boolean hasFocus) {
    String text = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus);
    Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
    myPanel.updateView(tree.isEnabled(), selected, expanded, userObject, text);
    return myPanel;
  }

  private static class JPanelWithCheckBox extends JPanel {
    private final JLabel myIconLabel = new JLabel();
    private final JLabel myTextLabel = new JLabel();
    private final JCheckBox myCheckBox = new JCheckBox();

    public JPanelWithCheckBox(boolean isCheckboxLeft) {
      super(new BorderLayout());
      Font font = UIManager.getFont("Tree.font");
      if (font != null) {
        myTextLabel.setFont(font);
      }
      Boolean iconBorder = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
      setOpaque(true);
      myCheckBox.setFocusPainted((iconBorder != null) && iconBorder);
      add(myCheckBox, isCheckboxLeft ? BorderLayout.WEST : BorderLayout.CENTER);
      add(myIconLabel, isCheckboxLeft ? BorderLayout.CENTER : BorderLayout.WEST);
      add(myTextLabel, BorderLayout.EAST);
    }

    public void addItemListener(ItemListener listener) {
      myCheckBox.addItemListener(listener);
    }

    public void updateView(boolean enabled, boolean selected, final boolean expanded, final Object userObject, final String text) {

      myCheckBox.setEnabled(enabled);

      ModelAccess.instance().runReadAction(new Runnable() {
        @Override
        public void run() {
          if (userObject instanceof NodeData) {
            NodeData data = (NodeData) userObject;
            myIconLabel.setIcon(data.getIcon(expanded));
            Color color = data.getColor();
            if (color != null) {
              myCheckBox.setForeground(color);
              myTextLabel.setForeground(color);
            }
            myTextLabel.setText(data.getText());
            myCheckBox.setSelected(data.isSelected());
          } else {
            myIconLabel.setIcon(null);
            myTextLabel.setText(text);
            myCheckBox.setSelected(false);
          }
        }
      });

      myCheckBox.setForeground(getForeground(selected));
      myCheckBox.setBackground(getBackground(selected));
      setForeground(getForeground(selected));
      setBackground(getBackground(selected));

    }

    private Color getBackground(boolean selected) {
      return selected ? UIManager.getColor("Tree.selectionBackground") : UIManager.getColor("Tree.textBackground");
    }

    private Color getForeground(boolean selected) {
      return selected ? UIManager.getColor("Tree.selectionForeground") : UIManager.getColor("Tree.textForeground");
    }

  }

  public static interface NodeData {
    Icon getIcon(boolean expanded);

    Color getColor();

    String getText();

    boolean isSelected();

    void setSelected(boolean selected); 
  }

  public static abstract class CheckBoxNodeEditor<N extends NodeData> extends AbstractCellEditor implements TreeCellEditor {
    private final CheckBoxNodeRenderer myRenderer;
    private N myObject;

    public CheckBoxNodeEditor() {
      this(false);
    }

    public CheckBoxNodeEditor(boolean isCheckboxLeft) {
      myRenderer = new CheckBoxNodeRenderer(isCheckboxLeft);
    }

    @Override
    public Object getCellEditorValue() {
      return createNodeData(myRenderer.isSelected());
    }

    protected abstract N createNodeData(boolean selected);

    @Override
    public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
      Component editor = myRenderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);

      DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
      myObject = (N) node.getUserObject();

      ItemListener itemListener = new ItemListener() {
        @Override
        public void itemStateChanged(ItemEvent itemEvent) {
          if (stopCellEditing()) {
            fireEditingStopped();
          }
        }
      };
      if (editor instanceof JPanelWithCheckBox) {
        ((JPanelWithCheckBox) editor).addItemListener(itemListener);
      }
      return editor;
    }

    public N getObject() {
      return myObject;
    }
  }
}
