// 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 'dart:math' as math;
import 'dart:sky' as sky;

import 'package:sky/animation.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/theme.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/transitions.dart';

const double _kLinearProgressIndicatorHeight = 6.0;
const double _kMinCircularProgressIndicatorSize = 15.0;
const double _kCircularProgressIndicatorStrokeWidth = 3.0;

abstract class ProgressIndicator extends StatefulComponent {
  ProgressIndicator({
    Key key,
    this.value,
    this.bufferValue
  }) : super(key: key);

  double value; // Null for non-determinate progress indicator.
  double bufferValue; // TODO(hansmuller) implement the support for this.

  AnimationPerformance _performance;
  double get _performanceValue => (_performance.variable as AnimatedValue<double>).value;
  Color get _backgroundColor => Theme.of(this).primarySwatch[200];
  Color get _valueColor => Theme.of(this).primaryColor;
  Object get _customPaintToken => value != null ? value : _performanceValue;

  void initState() {
    _performance = new AnimationPerformance()
      ..duration = const Duration(milliseconds: 1500)
      ..variable = new AnimatedValue<double>(0.0, end: 1.0, curve: ease);
    _performance.addStatusListener((AnimationStatus status) {
      if (status == AnimationStatus.completed)
        _restartAnimation();
    });
    _performance.play();
  }

  void syncConstructorArguments(ProgressIndicator source) {
    value = source.value;
    bufferValue = source.bufferValue;
  }

  void _restartAnimation() {
    _performance.progress = 0.0;
    _performance.play();
  }

  Widget build() {
    if (value != null)
      return _buildIndicator();

    return new BuilderTransition(
      variables: [_performance.variable],
      performance: _performance.view,
      builder: _buildIndicator
    );
  }

  Widget _buildIndicator();
}

class LinearProgressIndicator extends ProgressIndicator {
  LinearProgressIndicator({
    Key key,
    double value,
    double bufferValue
  }) : super(key: key, value: value, bufferValue: bufferValue);

  void _paint(sky.Canvas canvas, Size size) {
    Paint paint = new Paint()
      ..color = _backgroundColor
      ..setStyle(sky.PaintingStyle.fill);
    canvas.drawRect(Point.origin & size, paint);

    paint.color = _valueColor;
    if (value != null) {
      double width = value.clamp(0.0, 1.0) * size.width;
      canvas.drawRect(Point.origin & new Size(width, size.height), paint);
    } else {
      double startX = size.width * (1.5 * _performanceValue - 0.5);
      double endX = startX + 0.5 * size.width;
      double x = startX.clamp(0.0, size.width);
      double width = endX.clamp(0.0, size.width) - x;
      canvas.drawRect(new Point(x, 0.0) & new Size(width, size.height), paint);
    }
  }

  Widget _buildIndicator() {
    return new Container(
      child: new CustomPaint(callback: _paint, token: _customPaintToken),
      constraints: new BoxConstraints.tightFor(
        width: double.INFINITY,
        height: _kLinearProgressIndicatorHeight
      )
    );
  }
}

class CircularProgressIndicator extends ProgressIndicator {
  static const _kTwoPI = math.PI * 2.0;
  static const _kEpsilon = .0000001;
  // Canavs.drawArc(r, 0, 2*PI) doesn't draw anything, so just get close.
  static const _kSweep = _kTwoPI - _kEpsilon;
  static const _kStartAngle = -math.PI / 2.0;

  CircularProgressIndicator({
    Key key,
    double value,
    double bufferValue
  }) : super(key: key, value: value, bufferValue: bufferValue);

  void _paint(sky.Canvas canvas, Size size) {
    Paint paint = new Paint()
      ..color = _valueColor
      ..strokeWidth = _kCircularProgressIndicatorStrokeWidth
      ..setStyle(sky.PaintingStyle.stroke);

    if (value != null) {
      double angle = value.clamp(0.0, 1.0) * _kSweep;
      sky.Path path = new sky.Path()
        ..arcTo(Point.origin & size, _kStartAngle, angle, false);
      canvas.drawPath(path, paint);
    } else {
      double startAngle = _kTwoPI * (1.75 * _performanceValue - 0.75);
      double endAngle = startAngle + _kTwoPI * 0.75;
      double arcAngle = startAngle.clamp(0.0, _kTwoPI);
      double arcSweep = endAngle.clamp(0.0, _kTwoPI) - arcAngle;
      sky.Path path = new sky.Path()
        ..arcTo(Point.origin & size, _kStartAngle + arcAngle, arcSweep, false);
      canvas.drawPath(path, paint);
    }
  }

  Widget _buildIndicator() {
    return new Container(
      child: new CustomPaint(callback: _paint, token: _customPaintToken),
      constraints: new BoxConstraints(
        minWidth: _kMinCircularProgressIndicatorSize,
        minHeight: _kMinCircularProgressIndicatorSize
      )
    );
  }
}