/*
 * 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;
import org.jetbrains.annotations.NotNull;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

/**
 * User: Alexander Shatalin
 * Date: 02.03.2010
 */
public class HighlighterBracket {
  private static final int BRACKET_LINE_THICKNESS = 2;
  private static final int ADDITIONAL_HORIZONTAL_SHIFT = 3;

  private BracketEdge myBegginingEdge = new BracketEdge(0, true);
  private BracketEdge myEndingEdge = new BracketEdge(0, false);
  private Color myColor;
  private CellInfo myCellInfo;
  private EditorComponent myEditor;
  private CellInfo mySecondCellInfo;
  // level should be at least 1 
  private int myLevel = 1;
  private boolean myRightToLeft;

  public static int getBracketWidth(int level) {
    return 2 * BRACKET_LINE_THICKNESS + (level - 1) * BRACKET_LINE_THICKNESS * 3 / 2 + ADDITIONAL_HORIZONTAL_SHIFT;
  }

  public HighlighterBracket(CellInfo cellInfo, CellInfo secondCellInfo, Color c, EditorComponent editorComponent, boolean rightToLeft) {
    myColor = c;
    myEditor = editorComponent;
    myCellInfo = cellInfo;
    mySecondCellInfo = secondCellInfo;
    myRightToLeft = rightToLeft;
    relayout();
  }

  public boolean relayout() {
    EditorCell cell1 = myCellInfo.findCell(myEditor);
    EditorCell cell2 = mySecondCellInfo.findCell(myEditor);
    if (cell1 != null && cell2 != null) {
      if (cell1.getY() <= cell2.getY()) {
        setY1(cell1.getY());
        setY2(cell2.getY() + cell2.getHeight());
      } else {
        setY1(cell2.getY());
        setY2(cell1.getY() + cell1.getHeight());
      }
      return true;
    } else {
      return false;
    }
  }

  public BracketEdge getBeginningEdge() {
    return myBegginingEdge;
  }

  public BracketEdge getEndingEdge() {
    return myEndingEdge;
  }

  public void paint(Graphics g, Rectangle clipBounds) {
    if (clipBounds.y + clipBounds.height < getY1() || clipBounds.y > getY2()) {
      return;
    }
    int bracketWidth = getBracketWidth(getLevel());
    if (clipBounds.x + clipBounds.width < (myRightToLeft ? 0 : -bracketWidth) || clipBounds.x > (myRightToLeft ? bracketWidth : 0)) {
      return;
    }
    g.setColor(myColor);
    int horizontalSegmentLength = bracketWidth - BRACKET_LINE_THICKNESS - ADDITIONAL_HORIZONTAL_SHIFT;
    g.fillRect(getHorizontalSegmentStartX(horizontalSegmentLength), getY1(), horizontalSegmentLength, BRACKET_LINE_THICKNESS);
    g.fillRect(getVerticalSegmentStartX(bracketWidth), getY1(), BRACKET_LINE_THICKNESS, getY2() - getY1());
    g.fillRect(getHorizontalSegmentStartX(horizontalSegmentLength), getY2() - BRACKET_LINE_THICKNESS, horizontalSegmentLength, BRACKET_LINE_THICKNESS);
  }

  private int getHorizontalSegmentStartX(int horizontalSegmentLength) {
    // +1 is added here because of Graphics.fillRect() specific see javadoc for details
    return myRightToLeft ? ADDITIONAL_HORIZONTAL_SHIFT + 1 : -horizontalSegmentLength - ADDITIONAL_HORIZONTAL_SHIFT;
  }

  private int getVerticalSegmentStartX(int bracketWidth) {
    // +1 is added here because of Graphics.fillRect() specific see javadoc for details
    return myRightToLeft ? bracketWidth - BRACKET_LINE_THICKNESS + 1 : -bracketWidth;
  }

  public CellInfo getCell() {
    return myCellInfo;
  }

  private int getY1() {
    return myBegginingEdge.myY;
  }

  private void setY1(int y1) {
    myBegginingEdge.myY = y1;
  }

  private int getY2() {
    return myEndingEdge.myY;
  }

  private void setY2(int y2) {
    myEndingEdge.myY = y2;
  }

  public void setLevel(int level) {
    myLevel = level;
  }

  public int getLevel() {
    return myLevel;
  }

  public class BracketEdge implements Comparable<BracketEdge> {
    public int myY;
    private boolean myBeginning;

    public BracketEdge(int y, boolean beginning) {
      myY = y;
      myBeginning = beginning;
    }

    @NotNull
    public HighlighterBracket getBracket() {
      return HighlighterBracket.this;
    }

    public boolean isBeggining() {
      return myBeginning;
    }

    @Override
    public int compareTo(BracketEdge o) {
      if (myY == o.myY) {
        if (isBeggining() && !o.isBeggining()) {
          return 1;
        } else if (isBeggining() && o.isBeggining()) {
          return o.getBracket().getY2() - getBracket().getY2();
        } else if (!isBeggining() && o.isBeggining()) {
          return -1;
        } else if (!isBeggining() && !o.isBeggining()) {
          return o.getBracket().getY1() - getBracket().getY1();
        }
      }
      return myY - o.myY;
    }
  }
}
