/*
 * 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.nodeEditor.leftHighlighter;

import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.openapi.editor.cells.CellInfo;
import jetbrains.mps.openapi.editor.cells.EditorCell_Collection;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

/**
 * User: Alexander Shatalin
 * Date: 02.03.2010
 */
public class FoldingButtonsPainter extends AbstractFoldingAreaPainter {
  private Map<CellInfo, FoldingButton> myFoldingButtons = new HashMap<CellInfo, FoldingButton>();
  private FoldingButton myButtonUnderMouse;
  private boolean myNeedsRelayout = false;

  public FoldingButtonsPainter(LeftEditorHighlighter leftHighlighter) {
    super(leftHighlighter);
  }

  @Override
  public void editorRebuilt() {
    myFoldingButtons.clear();
    EditorComponent editorComponent = getEditorComponent();
    for (EditorCell_Collection foldable : editorComponent.getCellTracker().getFoldableCells()) {
      assert foldable.getEditorComponent() == editorComponent : "cell must be from my editor";
      myFoldingButtons.put(foldable.getCellInfo(), new FoldingButton(foldable, getLeftHighlighter().getBackground()));
    }
    myNeedsRelayout = true;
  }

  @Override
  public void relayout() {
    myNeedsRelayout = true;
  }

  private void doRelayout() {
    for (Iterator<Entry<CellInfo, FoldingButton>> it = myFoldingButtons.entrySet().iterator(); it.hasNext(); ) {
      Entry<CellInfo, FoldingButton> nextEntry = it.next();
      if (!nextEntry.getValue().relayout()) {
        it.remove();
      }
    }
  }

  @Override
  public int getLeftAreaWidth() {
    return myFoldingButtons.isEmpty() ? 0 : FoldingButton.HALF_WIDTH;
  }

  @Override
  public int getRightAreaWidth() {
    // same as leftAreaWidth
    return getLeftAreaWidth();
  }

  @Override
  public void paintInLocalCoordinates(Graphics g) {
    if (myNeedsRelayout) {
      myNeedsRelayout = false;
      doRelayout();
    }
    Rectangle clipBounds = g.getClipBounds();
    // Painting mouse over feedback "below" all other folding buttons
    for (FoldingButton button : myFoldingButtons.values()) {
      if (isVisible(button, clipBounds)) {
        button.paintFeedback(g);
      }
    }
    for (FoldingButton button : myFoldingButtons.values()) {
      if (isVisible(button, clipBounds)) {
        button.paint(g);
      }
    }
  }

  private boolean isVisible(FoldingButton button, Rectangle clipBounds) {
    return !(clipBounds.y + clipBounds.height < button.getY() ||
        clipBounds.y > button.getY() + button.getHeight() ||
        clipBounds.x + clipBounds.width < -FoldingButton.HALF_WIDTH ||
        clipBounds.x > FoldingButton.HALF_WIDTH);
  }

  @Override
  public void mouseMoved(MouseEvent e) {
    FoldingButton newButtonUnderMouse = getFoldingButtonUnderMouse(e);
    if (newButtonUnderMouse != null) {
      e.consume();
    }
    if (newButtonUnderMouse == myButtonUnderMouse) {
      return;
    }
    if (myButtonUnderMouse != null) {
      myButtonUnderMouse.mouseExited(getLeftHighlighter());
      repaint(myButtonUnderMouse);
    }
    if (newButtonUnderMouse != null) {
      newButtonUnderMouse.mouseEntered(getLeftHighlighter());
      repaint(newButtonUnderMouse);
    }
    myButtonUnderMouse = newButtonUnderMouse;
  }

  @Override
  public void mouseExited(MouseEvent e) {
    if (myButtonUnderMouse != null) {
      myButtonUnderMouse.mouseExited(getLeftHighlighter());
      repaint(myButtonUnderMouse);
      myButtonUnderMouse = null;
    }
  }

  @Override
  public void mousePressed(MouseEvent e) {
    if (e.getButton() != MouseEvent.BUTTON1) {
      return;
    }
    FoldingButton buttonUnderMouse = getFoldingButtonUnderMouse(e);
    if (buttonUnderMouse != null) {
      if (e.getID() == MouseEvent.MOUSE_CLICKED) {
        buttonUnderMouse.activate(getLocalXCoordinate(e), e.getY());
      }
      e.consume();
    }
  }

  @Override
  public int getWeight() {
    return 2;
  }

  private FoldingButton getFoldingButtonUnderMouse(MouseEvent e) {
    int x = getLocalXCoordinate(e);
    int y = e.getY();
    for (FoldingButton button : myFoldingButtons.values()) {
      if (button.isInside(x, y)) {
        return button;
      }
    }
    return null;
  }

  private void repaint(FoldingButton buttonUnderMouse) {
    repaint(buttonUnderMouse.getY(), buttonUnderMouse.getHeight());
  }
}
