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

import gnu.trove.TIntArrayList;
import jetbrains.mps.editor.runtime.TextBuilderImpl;
import jetbrains.mps.editor.runtime.style.CellAlign;
import jetbrains.mps.editor.runtime.style.DefaultBaseLine;
import jetbrains.mps.editor.runtime.style.StyleAttributes;
import jetbrains.mps.openapi.editor.TextBuilder;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.openapi.editor.cells.EditorCell_Collection;

import java.util.Deque;
import java.util.LinkedList;

/**
 * User: Sergey Dmitriev
 * Date: Jan 19, 2005
 */
public class CellLayout_Vertical extends AbstractCellLayout {
  private boolean myGridLayout = false;

  public void setGridLayout(boolean gridLayout) {
    myGridLayout = gridLayout;
  }

  boolean isGridLayout() {
    return myGridLayout;
  }

  @Override
  public boolean canBeFolded() {
    return true;
  }

  @Override
  public void doLayout(EditorCell_Collection editorCells) {
    Iterable<EditorCell> cells = editorCells.getContentCells();
    EditorCell closingBrace = editorCells.getClosingBrace();
    EditorCell openingBrace = editorCells.getOpeningBrace();
    boolean usesBraces = editorCells.usesBraces();
    if (usesBraces) {
      closingBrace.relayout();
      openingBrace.moveTo(editorCells.getX(), editorCells.getY());
      openingBrace.relayout();
    }

    final int x = usesBraces ? editorCells.getX() + openingBrace.getWidth() : editorCells.getX();
    final int y = editorCells.getY();
    int lastCellWidth;
    int braceIndent = 0;
    int width = 0;
    int height = 0;
    for (EditorCell editorCell : cells) {
      editorCell.moveTo(x, y + height + editorCell.getTopInset());
      editorCell.relayout();
      int cellHeight = editorCell.getHeight();
      int indent = getBracesIndent(editorCell);
      braceIndent = Math.max(indent, braceIndent);
      height += cellHeight /*+ editorCell.getTopInset() + editorCell.getBottomInset()*/;
    }
    for (EditorCell editorCell : cells) {
      lastCellWidth = editorCell.getWidth();
      int indent = getBracesIndent(editorCell);
      int delta = braceIndent - indent;
      width = Math.max(width, lastCellWidth + delta);
    }

    for (EditorCell editorCell : cells) {
      int cellX = editorCell.getX();
      int cellY = editorCell.getY();
      int newCellX = cellX;
      CellAlign cellAlign = editorCell.getStyle().get(StyleAttributes.HORIZONTAL_ALIGN);
      if (cellAlign == CellAlign.CENTER && !myGridLayout) {
        newCellX = cellX + (width - editorCell.getWidth()) / 2;
      } else if (cellAlign == CellAlign.RIGHT && !myGridLayout) {
        newCellX = cellX + width - editorCell.getWidth();
      }
      if (newCellX != cellX) {
        editorCell.moveTo(newCellX, cellY);
        editorCell.relayout();
      }
    }

    editorCells.setArtificialBracesIndent(braceIndent);
    for (EditorCell editorCell : cells) {
      int cellX = editorCell.getX();
      int cellY = editorCell.getY();
      int indent = getBracesIndent(editorCell);
      int newCellX = cellX - indent + braceIndent;
      if (newCellX != cellX) {
        editorCell.moveTo(newCellX, cellY);
        editorCell.relayout();
      }
    }

    if (myGridLayout) {
      TIntArrayList columnWidths = new TIntArrayList();
      for (EditorCell editorCell : cells) {
        if (editorCell instanceof EditorCell_Collection && ((EditorCell_Collection) editorCell).getCellLayout() instanceof CellLayout_Horizontal) {
          EditorCell_Collection collectionCell = (EditorCell_Collection) editorCell;
          int columnNumber = 0;
          for (EditorCell columnCell : collectionCell) {
            if (columnNumber < columnWidths.size()) {
              columnWidths.set(columnNumber, Math.max(columnWidths.get(columnNumber), columnCell.getWidth()));
            } else {
              columnWidths.add(columnCell.getWidth());
            }
            columnNumber++;
          }
        }
      }

      for (EditorCell editorCell : cells) {
        if (editorCell instanceof EditorCell_Collection && ((EditorCell_Collection) editorCell).getCellLayout() instanceof CellLayout_Horizontal) {
          EditorCell_Collection collectionCell = (EditorCell_Collection) editorCell;
          int lineWidth = 0;
          int columnNumber = 0;
          for (EditorCell columnCell : collectionCell) {
            setX(columnCell, x + lineWidth);
            int columnWidth = columnWidths.get(columnNumber);
            columnCell.setWidth(columnWidth);
            lineWidth += columnWidth;
            columnNumber++;
          }
          editorCell.setWidth(lineWidth);
          width = Math.max(width, lineWidth);
        }
      }
    }

    if (usesBraces) {
      closingBrace.setY(y + height - closingBrace.getHeight());
      if (myGridLayout) {
        closingBrace.setX(x + width);
        width += closingBrace.getWidth();
      } else {
        EditorCell lastCell = editorCells.lastContentCell();
        while ((lastCell instanceof EditorCell_Collection) && !((EditorCell_Collection) lastCell).isCollapsed()) {
          lastCell = ((EditorCell_Collection) lastCell).lastCell();
        }
        if (lastCell == null) {
          lastCell = editorCells.lastContentCell() != null ? editorCells.lastContentCell() : openingBrace;
        }
        closingBrace.setX(lastCell.getX() + lastCell.getWidth()/*x + lastCellWidth*/);
        width = Math.max(width, (closingBrace.getX() - x) + closingBrace.getWidth());
      }
      width += openingBrace.getWidth();
    }
    editorCells.setWidth(width);
    editorCells.setHeight(height);
  }

  private void setX(EditorCell cell, int newX) {
    int deltaX = newX - cell.getX();
    Deque<EditorCell> cellsToMove = new LinkedList<EditorCell>();
    cellsToMove.add(cell);
    while (!cellsToMove.isEmpty()) {
      EditorCell nextCell = cellsToMove.removeFirst();
      nextCell.setX(nextCell.getX() + deltaX);
      if (nextCell instanceof EditorCell_Collection) {
        for (EditorCell childCell : ((EditorCell_Collection) nextCell)) {
          cellsToMove.addLast(childCell);
        }
      }
    }
  }

  private int getBracesIndent(EditorCell cell) {
    return cell instanceof EditorCell_Collection ? ((EditorCell_Collection) cell).getBracesIndent() : 0;
  }

  @Override
  public int getRightInternalInset(EditorCell_Collection editorCell_collection) {
    return editorCell_collection.isEmpty() ? 0 : editorCell_collection.firstCell().getRightInset();
  }

  @Override
  public TextBuilder doLayoutText(Iterable<EditorCell> editorCells) {
    TextBuilder result = new TextBuilderImpl();
    for (EditorCell editorCell : editorCells) {
      result.appendToTheBottom(editorCell.renderText());
    }
    return result;
  }

  @Override
  public int getAscent(EditorCell_Collection editorCells) {
    for (EditorCell cell : editorCells) {
      if (cell.getStyle().get(StyleAttributes.BASE_LINE_CELL)) {
        return cell.getY() - editorCells.getY() + cell.getAscent();
      }
    }

    DefaultBaseLine bL = editorCells.getStyle().get(StyleAttributes.DEFAULT_BASE_LINE);

    int result = 0;
    for (EditorCell cell : editorCells) {
      result = cell.getAscent();
      if (result > 0) {
        break;
      }
    }

    switch (bL) {
      case FIRST: // default behavior
        return result;
      case CENTER:
        return Math.max(result, editorCells.getHeight() / 2);
      case LAST:
        if (!editorCells.isEmpty()) {
          EditorCell lastCell = editorCells.lastCell();
          return lastCell.getY() - editorCells.getY() + lastCell.getAscent();
        }
    }

    return 0;
  }

  public String toString() {
    return "Vertical";
  }

  @Override
  public void requestRelayout(EditorCell_Collection editorCells) {
    super.requestRelayout(editorCells);
    if (myGridLayout) {
      for (EditorCell childCell : editorCells) {
        if (childCell instanceof EditorCell_Collection) {
          for (EditorCell innerCell : (EditorCell_Collection) childCell) {
            innerCell.requestRelayout();
          }
        } else {
          childCell.requestRelayout();
        }
      }
    }
  }
}
