// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.components;

import com.intellij.ui.ComponentUtil;
import com.intellij.ui.DocumentAdapter;
import com.intellij.util.BooleanFunction;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ui.ComponentWithEmptyText;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.StatusText;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import java.awt.*;

public class JBTextArea extends JTextArea implements ComponentWithEmptyText {
  //private final DefaultBoundedRangeModel visibility;

  private final TextComponentEmptyText myEmptyText;

  public JBTextArea() {
    this(null, null, 0, 0);
  }

  public JBTextArea(String text) {
    this(null, text, 0, 0);
  }

  public JBTextArea(int rows, int columns) {
    this(null, null, rows, columns);
  }

  public JBTextArea(String text, int rows, int columns) {
    this(null, text, rows, columns);
  }

  public JBTextArea(Document doc) {
    this(doc, null, 0, 0);
  }

  public JBTextArea(Document doc, String text, int rows, int columns) {
    super(doc, text, rows, columns);

    myEmptyText = new TextComponentEmptyText(this) {
      @Override
      protected boolean isStatusVisible() {
        Object function = getClientProperty("StatusVisibleFunction");
        if (function instanceof BooleanFunction) {
          //noinspection unchecked
          return ((BooleanFunction<JTextComponent>)function).fun(JBTextArea.this);
        }
        return super.isStatusVisible();
      }

      @Override
      protected Rectangle getTextComponentBound() {
        Insets insets = ObjectUtils.notNull(getInsets(), JBUI.emptyInsets());
        Insets margin = ObjectUtils.notNull(getMargin(), JBUI.emptyInsets());
        Insets ipad = getComponent().getIpad();
        Dimension size = getSize();
        size.height = getPreferredSize().height;
        int left = insets.left + margin.left - ipad.left;
        int top = insets.top + margin.top - ipad.top;
        int right = size.width - (insets.right + margin.right - ipad.right);
        int bottom = size.height - (insets.bottom + margin.bottom - ipad.bottom);
        return new Rectangle(left, top, right - left, bottom - top);
      }
    };

    getDocument().addDocumentListener(new DocumentAdapter() {
      @Override
      protected void textChanged(@NotNull DocumentEvent e) {
        invalidate();
        revalidate();
        repaint();
      }
    });
  }

  @Override
  public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    Insets insets = getInsets();
    String[] lines = getText().split("\n");
    int columns = 0;
    int rows = lines.length;
    for (String line : lines) {
      columns = Math.max(columns, line.length());
    }
    if (columns != 0) {
      d.width = Math.max(d.width, columns * getColumnWidth() +
                                  insets.left + insets.right);
    }
    if (rows != 0) {
      d.height = Math.max(d.height, rows * getRowHeight() +
                                    insets.top + insets.bottom);
    }
    return d;
  }

  @Override
  public void scrollRectToVisible(Rectangle r) {
    JViewport viewport = ComponentUtil.getParentOfType((Class<? extends JViewport>)JViewport.class, (Component)this);
    if (viewport != null) {
      r = SwingUtilities.convertRectangle(this, r, viewport);
      viewport.scrollRectToVisible(r);
    }
  }

  @NotNull
  @Override
  public StatusText getEmptyText() {
    return myEmptyText;
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (!myEmptyText.getStatusTriggerText().isEmpty() && myEmptyText.isStatusVisible()) {
      g.setColor(getBackground());

      Rectangle rect = new Rectangle(getSize());
      JBInsets.removeFrom(rect, getInsets());
      JBInsets.removeFrom(rect, getMargin());
      ((Graphics2D)g).fill(rect);

      g.setColor(getForeground());
    }
    myEmptyText.paintStatusText(g);
  }
}

@@ -2092,57 +2092,8 @@
 ();%0A
-        size.height = getPreferredSize().height;%0A
     
@@ -2281,90 +2281,8 @@
 t);%0A
-        int bottom = size.height - (insets.bottom + margin.bottom - ipad.bottom);%0A
     
@@ -2335,20 +2335,22 @@
 ft, 
-bottom - top
+getRowHeight()
 );%0A 

Use getRowHeight() for better 'empty text' positioning (part of IDEA-222899)