/*
 * Copyright 2000-2014 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 com.intellij.openapi.editor.impl.softwrap;

import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.impl.ArrowPainter;
import com.intellij.openapi.editor.impl.ColorProvider;
import com.intellij.openapi.util.Computable;
import org.jetbrains.annotations.NotNull;

import java.awt.*;

/**
 * {@link SoftWrapPainter} implementation that draws arrows in soft wrap location.
 * <p/>
 * Primary idea is to use dedicated unicode symbols as soft wrap drawings and this class is introduced only as a part
 * of defensive programming - there is unlikely case that local client environment doesn't have a font that is able to
 * represent target unicode symbol. We draw an arrow manually then (platform-independent approach).
 *
 * @author Denis Zhdanov
 * @since Jul 2, 2010 11:31:36 AM
 */
public class ArrowSoftWrapPainter implements SoftWrapPainter {

  private final HeightProvider myHeightProvider = new HeightProvider();
  private final Editor myEditor;
  private final ArrowPainter myArrowPainter;
  private int myMinWidth = -1;

  public ArrowSoftWrapPainter(Editor editor) {
    myEditor = editor;
    myArrowPainter = new ArrowPainter(ColorProvider.byColor(myEditor.getColorsScheme().getDefaultForeground()), new WidthProvider(), myHeightProvider);
  }

  @Override
  public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) {
    myHeightProvider.myHeight = lineHeight / 2;

    int start;
    int end;
    int result;
    switch (drawingType) {
      case BEFORE_SOFT_WRAP_LINE_FEED:
        start = x;
        end = myEditor.getScrollingModel().getVisibleArea().width;
        result = end - start;
        break;
      case AFTER_SOFT_WRAP:
        start = 0;
        end = x;
        result = 0;
        break;
      default: throw new IllegalStateException("Soft wrap arrow painting is not set up for drawing type " + drawingType);
    }
    myArrowPainter.paint(g, y + lineHeight - g.getFontMetrics().getDescent(), start, end);
    return result;
  }

  @Override
  public int getDrawingHorizontalOffset(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) {
    switch (drawingType) {
      case BEFORE_SOFT_WRAP_LINE_FEED: return myEditor.getScrollingModel().getVisibleArea().width - x;
      case AFTER_SOFT_WRAP: return 0;
      default: throw new IllegalStateException("Soft wrap arrow painting is not set up for drawing type " + drawingType);
    }
  }

  @Override
  public int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType) {
    if (myMinWidth < 0) {
      // We need to reserve a minimal space required for representing arrow before soft wrap-introduced line feed.
      myMinWidth = EditorUtil.charWidth('a', Font.PLAIN, myEditor);
    }
    return myMinWidth;
  }

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

  @Override
  public void reinit() {
    myMinWidth = -1;
  }

  private static class HeightProvider implements Computable<Integer> {

    public int myHeight;

    @Override
    public Integer compute() {
      return myHeight;
    }
  }

  private class WidthProvider implements Computable<Integer> {
    @Override
    public Integer compute() {
      return EditorUtil.getSpaceWidth(Font.PLAIN, myEditor);
    }
  }
}
