// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.google.collide.client.ui;

import collide.client.util.Elements;

import com.google.common.base.Preconditions;

import elemental.css.CSSStyleDeclaration;
import elemental.events.Event;
import elemental.events.EventListener;
import elemental.html.TextAreaElement;

/**
 * Creates a GrowableTextArea, it isn't perfect but it does a fairly decent job
 * of growing as the user types in new text.
 *
 */
/*
 * How it works:
 *
 * This class creates two text areas, one which is returned to the user and a
 * second which is hidden and placed off-screen. As users type into the main
 * text area, the text is also put into the hidden text area which has an
 * explicit row height of 1. We use the scrollHeight of the hidden text area to
 * determine the number of row necessary to display the text in the main area
 * without scrolling. It is off a little bit due to the scrollbar width changing
 * the width of the hidden text area a little bit, but generally it is good
 * enough.
 */
public class GrowableTextArea {

  public static GrowableTextArea create(String className) {
    return createWithMinimumRows(className, 1);
  }

  /**
   * @param minRows The minimum number of rows for this {@link GrowableTextArea}
   *        to be.
   */
  public static GrowableTextArea createWithMinimumRows(String className, int minRows) {
    Preconditions.checkArgument(minRows >= 1, "Minimum rows must be >= 1");
    
    // Create the base text element and pass it in
    TextAreaElement element = Elements.createTextAreaElement();
    element.setClassName(className);
    element.setRows(minRows);
    element.getStyle().setOverflowY(CSSStyleDeclaration.OverflowY.HIDDEN);

    return new GrowableTextArea(element, minRows);
  }

  private final TextAreaElement element;
  private final TextAreaElement clone;
  private final int minimumRows;

  public GrowableTextArea(TextAreaElement element, int minimumRows) {
    this.element = element;
    this.minimumRows = minimumRows;
    this.clone = Elements.createTextAreaElement();
    
    setupClone();
    attachEvents();
  }
  
  private void setupClone() {
    clone.setClassName(element.getClassName());
    CSSStyleDeclaration style = clone.getStyle();
    style.setVisibility(CSSStyleDeclaration.Visibility.HIDDEN);
    style.setPosition(CSSStyleDeclaration.Position.ABSOLUTE);
    style.setOverflow(CSSStyleDeclaration.Overflow.AUTO);
    style.setLeft("-5000px");
    style.setTop("-5000px");
    
    clone.setRows(1);
  }

  private void attachEvents() {
    element.addEventListener(Event.INPUT, new EventListener() {
      @Override
      public void handleEvent(Event evt) {
        if (clone.getParentElement() == null) {
          element.getParentElement().appendChild(clone);
        }
        
        clone.setValue(element.getValue());
        int requiredRows =
            (int) Math.ceil((double) clone.getScrollHeight() / (double) clone.getClientHeight());
        int rows = Math.max(requiredRows, minimumRows);
        
        element.setRows(rows);
      }
    }, false);
  }

  public TextAreaElement asTextArea() {
    return element;
  }
}
