import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_sprites/flutter_sprites.dart';

class GalleryHeader extends StatefulWidget {
  @override
  _GalleryHeaderState createState() => new _GalleryHeaderState();
}

class _GalleryHeaderState extends State<GalleryHeader> {
  _FlutterHeaderNode _headerNode;
  ImageMap _images;

  Future<Null> _loadAssets() async {
    final AssetBundle bundle = DefaultAssetBundle.of(context);
    _images = new ImageMap(bundle);
    await _images.load(<String>[
      'packages/flutter_gallery_assets/grain.png',
    ]);
  }

  @override
  void initState() {
    super.initState();
    _loadAssets().then((_) {
      setState(() {
        _headerNode = new _FlutterHeaderNode(_images);
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return _headerNode == null ? new Container() : new SpriteWidget(_headerNode);
  }
}

const Size _kCanvasSize = const Size(1024.0, 1024.0);
const Point _kCenterPoint = const Point(512.0, 512.0);

class _FlutterHeaderNode extends NodeWithSize {
  _FlutterHeaderNode(this._images) : super(_kCanvasSize) {
    clippingLayer.opacity = 0.0;
    clippingLayer.actions.run(new ActionTween((double a) => clippingLayer.opacity = a, 0.0, 1.0, 0.5));
    addChild(clippingLayer);

    clippingLayer.addChild(new _BackgroundBox());

    paperAnimation.position = _kCenterPoint;
    clippingLayer.addChild(paperAnimation);

    final Sprite grain = new Sprite.fromImage(_images['packages/flutter_gallery_assets/grain.png'])
      ..position = _kCenterPoint;
    clippingLayer.addChild(grain);

    userInteractionEnabled = true;
  }

  final ImageMap _images;
  final Layer clippingLayer = new Layer();
  final _PaperAnimation paperAnimation = new _PaperAnimation();

  @override
  void spriteBoxPerformedLayout() {
    clippingLayer.layerRect = spriteBox.visibleArea;
  }
}

final List<_PaperConfig> _kPaperConfigs = <_PaperConfig>[
  new _PaperConfig(
    color: Colors.deepPurple[500],
    startPosition: const Point(-300.0, -300.0),
    startRotation: -10.0,
    rotationSpeed: -1.0,
    parallaxDepth: 0.0,
    rect: new Rect.fromLTRB(-1024.0, -280.0, 1024.0, 280.0)
  ),
  new _PaperConfig(
    color: Colors.purple[400],
    startPosition: const Point(550.0, 0.0),
    startRotation: 45.0,
    rotationSpeed: 0.7,
    parallaxDepth: 1.0,
    rect: new Rect.fromLTRB(-512.0, -512.0, 512.0, 512.0)
  ),
  new _PaperConfig(
    color: Colors.purple[600],
    startPosition: const Point(550.0, 0.0),
    startRotation: 55.0,
    rotationSpeed: 0.9,
    parallaxDepth: 2.0,
    rect: new Rect.fromLTRB(-512.0, -512.0, 512.0, 512.0)
  ),
  new _PaperConfig(
    color: Colors.purple[700],
    startPosition: const Point(550.0, 0.0),
    startRotation: 65.0,
    rotationSpeed: 1.1,
    parallaxDepth: 3.0,
    rect: new Rect.fromLTRB(-512.0, -512.0, 512.0, 512.0)
  )
];

class _PaperAnimation extends Node {
  _PaperAnimation() {
    for (_PaperConfig config in _kPaperConfigs) {

      final _PaperSheet sheet = new _PaperSheet(config);
      final _PaperSheetShadow shadow = new _PaperSheetShadow(config);

      addChild(shadow);
      addChild(sheet);
      _sheets.add(sheet);

      shadow.constraints = <Constraint>[
        new ConstraintRotationToNodeRotation(sheet),
        new ConstraintPositionToNode(sheet, offset: const Offset(0.0, 10.0))
      ];
    }
  }

  final List<_PaperSheet> _sheets = <_PaperSheet>[];
}

class _PaperConfig {
  _PaperConfig({
    this.color,
    this.startPosition,
    this.startRotation,
    this.rotationSpeed,
    this.parallaxDepth,
    this.rect
  });

  final Color color;
  final Point startPosition;
  final double startRotation;
  final double rotationSpeed;
  final double parallaxDepth;
  final Rect rect;
}

class _PaperSheet extends Node {
  _PaperSheet(this._config) {
    _paperPaint.color = _config.color;

    position = _config.startPosition;
    rotation = _config.startRotation;
  }

  final _PaperConfig _config;
  final Paint _paperPaint = new Paint();

  @override
  void paint(Canvas canvas) {
    canvas.drawRect(_config.rect, _paperPaint);
  }

  @override
  void update(double dt) {
    rotation += _config.rotationSpeed * dt;
  }
}

class _PaperSheetShadow extends Node {
  _PaperSheetShadow(this._config) {
    _paperPaint.color = Colors.black45;
    _paperPaint.maskFilter = new MaskFilter.blur(BlurStyle.normal, 10.0);
  }

  final _PaperConfig _config;
  final Paint _paperPaint = new Paint();

  @override
  void paint(Canvas canvas) {
    canvas.drawRect(_config.rect, _paperPaint);
  }
}

class _BackgroundBox extends Node {
  final Paint _boxPaint = new Paint()..color = Colors.purple[500];

  @override
  void paint(Canvas canvas) {
    canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, _kCanvasSize.width, _kCanvasSize.height), _boxPaint);
  }
}