// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/painting.dart';

import 'box.dart';
import 'object.dart';
import 'paragraph.dart';
import 'proxy_box.dart' show SizeChangedCallback;

const _kCursorGap = 1.0; // pixels
const _kCursorHeightOffset = 2.0; // pixels
const _kCursorWidth = 1.0; // pixels

/// A render object used by EditableText widgets.  This is similar to
/// RenderParagraph but also renders a cursor and provides support for
/// scrolling.
class RenderEditableParagraph extends RenderParagraph {

  RenderEditableParagraph({
    TextSpan text,
    Color cursorColor,
    bool showCursor,
    this.onContentSizeChanged,
    Offset scrollOffset
  }) : _cursorColor = cursorColor,
       _showCursor = showCursor,
       _scrollOffset = scrollOffset,
       super(text);

  Color _cursorColor;
  bool _showCursor;
  SizeChangedCallback onContentSizeChanged;
  Offset _scrollOffset;

  Size _contentSize;

  Color get cursorColor => _cursorColor;
  void set cursorColor(Color value) {
    if (_cursorColor == value)
      return;
    _cursorColor = value;
    markNeedsPaint();
  }

  bool get showCursor => _showCursor;
  void set showCursor(bool value) {
    if (_showCursor == value)
      return;
    _showCursor = value;
    markNeedsPaint();
  }

  Offset get scrollOffset => _scrollOffset;
  void set scrollOffset(Offset value) {
    if (_scrollOffset == value)
      return;
    _scrollOffset = value;
    markNeedsPaint();
  }

  // Editable text does not support line wrap.
  bool get allowLineWrap => false;

  double _getIntrinsicWidth(BoxConstraints constraints) {
    // There should be no difference between the minimum and maximum width
    // because we only support single-line text.
    layoutText(constraints);
    return constraints.constrainWidth(
      textPainter.maxContentWidth + _kCursorGap + _kCursorWidth
    );
  }

  double getMinIntrinsicWidth(BoxConstraints constraints) {
    return _getIntrinsicWidth(constraints);
  }

  double getMaxIntrinsicWidth(BoxConstraints constraints) {
    return _getIntrinsicWidth(constraints);
  }

  void performLayout() {
    layoutText(constraints);

    Size newContentSize = new Size(
      textPainter.maxContentWidth + _kCursorGap + _kCursorWidth,
      textPainter.height
    );
    size = constraints.constrain(newContentSize);

    if (_contentSize == null || _contentSize != newContentSize) {
      _contentSize = newContentSize;
      if (onContentSizeChanged != null)
        onContentSizeChanged(newContentSize);
    }
  }

  void paint(PaintingContext context, Offset offset) {
    layoutText(constraints);

    bool needsClipping = (_contentSize.width > size.width);
    if (needsClipping) {
      context.canvas.save();
      context.canvas.clipRect(offset & size);
    }

    textPainter.paint(context.canvas, offset - _scrollOffset);

    if (_showCursor) {
      Rect cursorRect =  new Rect.fromLTWH(
        textPainter.maxContentWidth + _kCursorGap,
        _kCursorHeightOffset,
        _kCursorWidth,
        size.height - 2.0 * _kCursorHeightOffset
      );
      context.canvas.drawRect(
        cursorRect.shift(offset - _scrollOffset),
        new Paint()..color = _cursorColor
      );
    }

    if (needsClipping)
      context.canvas.restore();
  }

}
