package processing.app.syntax.im;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;

import processing.app.syntax.JEditTextArea;


/**
 * Paint texts from input method. Text via input method are transmitted by
 * AttributedCaharacterIterator. This class helps the PDE's TextAreaPainter
 * to handle AttributedCaharacterIterator.
 *
 * For practical purposes, paint to textarea is done by TextLayout class.
 * Because TextLayout class is easy to draw composing texts. (For example,
 * draw underline composing texts, focus when select from candidates text.)
 *
 * @author Takashi Maekawa (takachin@generative.info)
 */
public class CompositionTextPainter {
  private TextLayout composedTextLayout;
  private int composedBeginCaretPosition = 0;
  private JEditTextArea textArea;
  private boolean caretColorFlag;
  private TextHitInfo caret;

  /**
   * Constructor for painter.
   * @param textArea textarea used by PDE.
   */
  public CompositionTextPainter(JEditTextArea textArea) {
    this.textArea = textArea;
    composedTextLayout = null;
  }


  /**
   * Check the painter has TextLayout.
   * If a user input via InputMethod, this result will return true.
   */
  public boolean hasComposedTextLayout() {
    return (composedTextLayout != null);
  }


  /**
   * Set TextLayout to the painter.
   * TextLayout will be created and set by CompositionTextManager.
   *
   * @see CompositionTextManager
   */
  public void setComposedTextLayout(TextLayout composedTextLayout, int composedStartCaretPosition) {
    this.composedTextLayout = composedTextLayout;
    this.composedBeginCaretPosition = composedStartCaretPosition;
  }


  /**
   * Invalidate this TextLayout to set null.
   * If a user end input via InputMethod, this method will called from CompositionTextManager.endCompositionText
   */
  public void invalidateComposedTextLayout(int composedEndCaretPosition) {
    this.composedTextLayout = null;
    this.composedBeginCaretPosition = composedEndCaretPosition;
    //this.composedBeginCaretPosition = textArea.getCaretPosition();
  }


  /**
   * Draw text via input method with composed text information.
   * This method can draw texts with some underlines to illustrate converting characters.
   *
   * This method is workaround for TextAreaPainter.
   * Because, TextAreaPainter can't treat AttributedCharacterIterator directly.
   * AttributedCharacterIterator has very important information when composing text.
   * It has a map where are converted characters and committed characters.
   * Ideally, changing TextAreaPainter method can treat AttributedCharacterIterator is better. But it's very tough!!
   * So I choose to write some code as a workaround.
   *
   * This draw method is proceeded with the following steps.
   * 1. Original TextAreaPainter draws characters.
   * 2. CompositionTextPainter.draw method paints composed text. It was actually drawn by TextLayout.
   *
   * @param gfx set TextAreaPainter's Graphics object.
   * @param fillBackGroundColor set textarea's background.
   */
//  public void draw(Graphics gfx, Color fillBackGroundColor) {
//	assert(composedTextLayout != null);
//	Point composedLoc = getCaretLocation();
//	//refillComposedArea(fillBackGroundColor, composedLoc.x, composedLoc.y);
//	composedTextLayout.draw((Graphics2D) gfx, composedLoc.x, composedLoc.y);
//  }
  public void draw(Graphics gfx, Color fillBackGroundColor) {
    //assert(composedTextLayout != null);
    if (composedTextLayout != null) {
      Point composedLoc = getCaretLocation();
      //refillComposedArea(fillBackGroundColor, composedLoc.x, composedLoc.y);
      composedTextLayout.draw((Graphics2D) gfx, composedLoc.x, composedLoc.y);

      // draw caret.
      if (this.caret != null) {
        int caretLocation = Math.round(composedTextLayout.getCaretInfo(caret)[0]);
        Rectangle rect = new Rectangle(composedLoc.x  + caretLocation,
                                       composedLoc.y - (int)composedTextLayout.getAscent(),
                                       1,
                                       (int)composedTextLayout.getAscent() + (int)composedTextLayout.getDescent());
        Color c = gfx.getColor(); // save
        if (caretColorFlag) {
          caretColorFlag = false;
          gfx.setColor(Color.WHITE);
        } else {
          caretColorFlag = true;
          gfx.setColor(Color.BLACK);
        }
        gfx.fillRect(rect.x, rect.y, rect.width, rect.height);
        gfx.setColor(c); // restore
      }
    }
  }

//  /**
//   * Fill color to erase characters drawn by original TextAreaPainter.
//   *
//   * @param fillColor fill color to erase characters drawn by original TextAreaPainter method.
//   * @param x x-coordinate where to fill.
//   * @param y y-coordinate where to fill.
//   */
//  private void refillComposedArea(Color fillColor, int x, int y) {
//    Graphics gfx = textArea.getPainter().getGraphics();
//    gfx.setColor(fillColor);
//    FontMetrics fm = textArea.getPainter().getFontMetrics();
//    int newY = y - (fm.getHeight() - CompositionTextManager.COMPOSING_UNDERBAR_HEIGHT);
//    int paintHeight = fm.getHeight();
//    int paintWidth = (int) composedTextLayout.getBounds().getWidth();
//    gfx.fillRect(x, newY, paintWidth, paintHeight);
//  }


  private Point getCaretLocation() {
    int line = textArea.getCaretLine();
    int offsetX = composedBeginCaretPosition - textArea.getLineStartOffset(line);
    // '+1' mean textArea.lineToY(line) + textArea.getPainter().getFontMetrics().getHeight().
    // TextLayout#draw method need at least one height of font.
    return new Point(textArea.offsetToX(line, offsetX), textArea.lineToY(line + 1));
  }

  public void setCaret(TextHitInfo caret) {
    this.caret = caret;
  }
}
