// 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/animation.dart';
import 'package:flutter/widgets.dart';

import 'colors.dart';
import 'shadows.dart';
import 'theme.dart';

// TODO(eseidel): Draw width should vary based on device size:
// http://www.google.com/design/spec/layout/structure.html#structure-side-nav

// Mobile:
// Width = Screen width − 56 dp
// Maximum width: 320dp
// Maximum width applies only when using a left nav. When using a right nav,
// the panel can cover the full width of the screen.

// Desktop/Tablet:
// Maximum width for a left nav is 400dp.
// The right nav can vary depending on content.

const double _kWidth = 304.0;
const double _kMinFlingVelocity = 365.0;
const double _kFlingVelocityScale = 1.0 / 300.0;
const Duration _kBaseSettleDuration = const Duration(milliseconds: 246);
const Duration _kThemeChangeDuration = const Duration(milliseconds: 200);
const Point _kOpenPosition = Point.origin;
const Point _kClosedPosition = const Point(-_kWidth, 0.0);

class _Drawer extends StatelessComponent {

  _Drawer({
    Key key,
    this.child,
    this.level: 3,
    this.performance,
    this.interactive,
    this.route
  }) : super(key: key);

  final Widget child;
  final int level;
  final PerformanceView performance;
  final bool interactive;
  final _DrawerRoute route;

  Widget build(BuildContext context) {
    return new GestureDetector(
      onHorizontalDragStart: () {
        if (interactive)
          route._takeControl();
      },
      onHorizontalDragUpdate: (double delta) {
        if (interactive)
          route._moveDrawer(delta);
      },
      onHorizontalDragEnd: (Offset velocity) {
        if (interactive)
          route._settle(velocity);
      },
      child: new Stack(<Widget>[
        // mask
        new GestureDetector(
          onTap: () {
            if (interactive)
              route._close();
          },
          child: new ColorTransition(
            performance: performance,
            color: new AnimatedColorValue(Colors.transparent, end: Colors.black54),
            child: new Container()
          )
        ),
        // drawer
        new Positioned(
          top: 0.0,
          left: 0.0,
          bottom: 0.0,
          child: new SlideTransition(
            performance: performance,
            position: new AnimatedValue<Point>(_kClosedPosition, end: _kOpenPosition),
            child: new AnimatedContainer(
              curve: Curves.ease,
              duration: _kThemeChangeDuration,
              decoration: new BoxDecoration(
                backgroundColor: Theme.of(context).canvasColor,
                boxShadow: shadows[level]),
              width: _kWidth,
              child: child
            )
          )
        )
      ])
    );
  }

}

class _DrawerRoute extends Route {
  _DrawerRoute({ this.child, this.level });

  final Widget child;
  final int level;

  PerformanceView get performance => _performance?.view;
  Performance _performance = new Performance(duration: _kBaseSettleDuration, debugLabel: 'Drawer');

  bool get opaque => false;

  bool _interactive = true;

  Widget build(RouteArguments args) {
    return new Focus(
      key: new GlobalObjectKey(this),
      autofocus: true,
      child: new _Drawer(
        child: child,
        level: level,
        performance: performance,
        interactive: _interactive,
        route: this
      )
    );
  }

  void didPush(NavigatorState navigator) {
    super.didPush(navigator);
    _performance.forward();
  }

  void didPop([dynamic result]) {
    assert(result == null); // because we don't do anything with it, so otherwise it'd be lost
    super.didPop(result);
    if (_performance.status != PerformanceStatus.dismissed)
      _performance.reverse();
    setState(() {
      _interactive = false;
      // TODO(ianh): https://github.com/flutter/engine/issues/1539
    });
  }

  void _takeControl() {
    assert(_interactive);
    _performance.stop();
  }

  void _moveDrawer(double delta) {
    assert(_interactive);
    _performance.progress += delta / _kWidth;
  }

  void _settle(Offset velocity) {
    assert(_interactive);
    if (velocity.dx.abs() >= _kMinFlingVelocity) {
      _performance.fling(velocity: velocity.dx * _kFlingVelocityScale);
    } else if (_performance.progress < 0.5) {
      _close();
    } else {
      _performance.fling(velocity: 1.0);
    }
  }

  void _close() {
    assert(_interactive);
    _performance.fling(velocity: -1.0);
  }
}

void showDrawer({ NavigatorState navigator, Widget child, int level: 3 }) {
  assert(navigator != null);
  navigator.push(new _DrawerRoute(child: child, level: level));
}
