/*
 * jNPad v0.3 - jNPad's an Simple Text Editor written in Java
 *
 * Copyright (C) 2014-2017  rgs
 *
 * Require JDK 1.6 (or later)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 *
 * Info, Questions, Suggestions & Bugs Report to rgsevero@gmail.com
 */

package jnpad.text;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;

import jnpad.config.Config;
import jnpad.text.highlighter.IHighlightPainter;

/**
 * The Class ActiveLine.
 *
 * @version 0.3
 * @since   jNPad v0.1
 * @see IHighlightPainter
 */
public class ActiveLine implements IHighlightPainter {
  private JTextArea           textArea;

  private Color               color;

  private Rectangle           lastView;

  private Object              highlightTag;

  /** Logger */
  private static final Logger LOGGER = Logger.getLogger(ActiveLine.class.getName());

  /**
   * Instantiates a new active line.
   *
   * @param textArea the text area
   */
  public ActiveLine(JTextArea textArea) {
    this.textArea = textArea;

    configure(CFG_COLOR);

    // Add a listener to get events important to the component.
    final Handler handler = new Handler();
    textArea.addCaretListener(handler);
    textArea.addMouseListener(handler);
    textArea.addMouseMotionListener(handler);

    setVisible(true);
  }

  /**
   * Configure.
   *
   * @param cfg int
   * @see jnpad.config.Configurable#configure(int)
   */
  @Override
  public void configure(final int cfg) {
    if ( (cfg & CFG_COLOR) != 0) {
      setColor(Config.ACTIVE_LINE_COLOR.getValue());
    }
  }

  /**
   * Sets the visible.
   *
   * @param b the new visible
   */
  public void setVisible(boolean b) {
    try {
      if (b) {
        highlightTag = textArea.getHighlighter().addHighlight(0, 0, this);
      }
      else {
        textArea.getHighlighter().removeHighlight(highlightTag);
      }
    }
    catch (Exception ex) {
      LOGGER.log(Level.WARNING, ex.getMessage(), ex);
    }
  }

  /**
   * Gets the color.
   *
   * @return Color
   */
  public Color getColor() {
    return color;
  }

  /**
   * Sets the color.
   *
   * @param color Color
   */
  public void setColor(Color color) {
    if (color != null) {
      this.color = color;
    }
  }

  /**
   * Paint.
   *
   * @param g the Graphics
   * @param p0 the p0
   * @param p1 the p1
   * @param bounds the bounds
   * @param c the JTextComponent
   * @see javax.swing.text.Highlighter.HighlightPainter#paint(java.awt.Graphics, int, int, java.awt.Shape, javax.swing.text.JTextComponent)
   */
  @Override
  public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c) {
    try {
      Rectangle r = c.modelToView(c.getCaretPosition());
      g.setColor(color);
      g.fillRect(0, r.y, c.getWidth(), r.height);

      if (lastView == null) {
        lastView = r;
      }
    }
    catch (BadLocationException ex) {
      LOGGER.log(Level.WARNING, ex.getMessage(), ex);
    }
  }

  /**
   * Reset highlight.
   */
  private void resetHighlight() {
    // Use invokeLater to make sure updates to the Document are completed,
    // otherwise Undo processing causes the modelToView method to loop.
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        if (lastView == null) {
          return; // [ADDED]
        }
        try {
          int offset = textArea.getCaretPosition();
          Rectangle currentView = textArea.modelToView(offset);

          // Removes the highlighting from the previously highlighted line
          if (lastView.y != currentView.y) {
            textArea.repaint(0, lastView.y, textArea.getWidth(), lastView.height);
            lastView = currentView;
            textArea.repaint(0, currentView.y, textArea.getWidth(), currentView.height);
          }
        }
        catch (BadLocationException ex) {
          LOGGER.log(Level.WARNING, ex.getMessage(), ex);
        }
      }
    });
  }

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class Handler.
   */
  private class Handler extends MouseInputAdapter implements CaretListener {
    /**
     * Mouse pressed.
     *
     * @param e MouseEvent
     * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)
     */
    @Override
    public void mousePressed(final MouseEvent e) {
      resetHighlight();
    }

    /**
     * Mouse dragged.
     *
     * @param e MouseEvent
     * @see java.awt.event.MouseAdapter#mouseDragged(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseDragged(final MouseEvent e) {
      resetHighlight();
    }

    /**
     * Caret update.
     *
     * @param e CaretEvent
     * @see javax.swing.event.CaretListener#caretUpdate(javax.swing.event.CaretEvent)
     */
    @Override
    public void caretUpdate(final CaretEvent e) {
      resetHighlight();
    }
  }
  //////////////////////////////////////////////////////////////////////////////

} // fin de ActiveLine
