// 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:async';

import 'package:sky/mojo/asset_bundle.dart';
import 'package:sky/theme/colors.dart' as colors;
import 'package:sky/rendering/object.dart';
import 'package:sky/painting/text_style.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/button_base.dart';
import 'package:sky/widgets/navigator.dart';
import 'package:sky/widgets/framework.dart';
import 'package:sky/widgets/title.dart';
import 'package:sky/widgets/theme.dart';

import 'game_demo.dart';
import 'sprites.dart';

AssetBundle _initBundle() {
  if (rootBundle != null)
    return rootBundle;
  return new NetworkAssetBundle(new Uri.directory(Uri.base.origin));
}

final AssetBundle _bundle = _initBundle();

ImageMap _imageMap;
SpriteSheet _spriteSheet;
SpriteSheet _spriteSheetUI;
GameDemoApp _app;
Map<String,SoundEffect> _sounds = {};

main() async {
  _imageMap = new ImageMap(_bundle);

  // Use a list to wait on all loads in parallel just before starting the app.
  List loads = [];

  loads.add(_imageMap.load([
    'assets/nebula.png',
    'assets/sprites.png',
    'assets/starfield.png',
    'assets/game_ui.png',
  ]));

  // TODO(eseidel): SoundEffect doesn't really do anything except hold a future.
  _sounds['explosion'] = new SoundEffect(_bundle.load('assets/explosion.wav'));
  _sounds['laser'] = new SoundEffect(_bundle.load('assets/laser.wav'));

  loads.addAll([
    _sounds['explosion'].load(),
    _sounds['laser'].load(),
  ]);

  await Future.wait(loads);

  // TODO(eseidel): These load in serial which is bad for startup!
  String json = await _bundle.loadString('assets/sprites.json');
  _spriteSheet = new SpriteSheet(_imageMap['assets/sprites.png'], json);

  json = await _bundle.loadString('assets/game_ui.json');
  _spriteSheetUI = new SpriteSheet(_imageMap['assets/game_ui.png'], json);

  _app = new GameDemoApp();

  assert(_spriteSheet.image != null);

  SoundTrackPlayer stPlayer = SoundTrackPlayer.sharedInstance();
  SoundTrack music = await stPlayer.load(_bundle.load('assets/temp_music.aac'));
  stPlayer.play(music);

  runApp(_app);
}

class GameDemoApp extends App {

  NavigationState _navigationState;
  NodeWithSize _game;
  int _lastScore = 0;

  void initState() {
    _navigationState = new NavigationState([
      new Route(
        name: '/',
        builder: _buildMainScene
      ),
      new Route(
        name: '/game',
        builder: _buildGameScene
      ),
    ]);
    super.initState();
  }

  Widget build() {
    // TODO(viktork): The task bar purple is the wrong purple, we may need
    // a custom theme swatch to match the purples in the sprites.
    ThemeData theme = new ThemeData(
      brightness: ThemeBrightness.light,
      primarySwatch: colors.Purple
    );

    return new Theme(
      data: theme,
      child: new Title(
        title: 'Asteroids',
        child: new Navigator(_navigationState)
      )
    );
  }

  Widget _buildGameScene(navigator, route) {
    return new SpriteWidget(_game, SpriteBoxTransformMode.fixedWidth);
  }

  Widget _buildMainScene(navigator, route) {
    return new Stack([
      new SpriteWidget(new MainScreenBackground(), SpriteBoxTransformMode.fixedWidth),
      new Flex([
        new TextureButton(
          onPressed: () {
            _game = new GameDemoNode(
              _imageMap,
              _spriteSheet,
              _spriteSheetUI,
              _sounds,
              (lastScore) {
                setState(() {_lastScore = lastScore;});
                navigator.pop();
              }
            );
            navigator.pushNamed('/game');
          },
          texture: _spriteSheetUI['btn_play_up.png'],
          textureDown: _spriteSheetUI['btn_play_down.png'],
          width: 128.0,
          height: 128.0
        ),
        new Text(
          "Last Score: $_lastScore",
          style: new TextStyle(fontSize:20.0)
        )
      ],
      direction: FlexDirection.vertical,
      justifyContent: FlexJustifyContent.center)
    ]);
  }
}

class TextureButton extends ButtonBase {
  TextureButton({
    Key key,
    this.onPressed,
    this.texture,
    this.textureDown,
    this.width: 128.0,
    this.height: 128.0
  }) : super(key: key);

  final Function onPressed;
  final Texture texture;
  final Texture textureDown;
  final double width;
  final double height;

  Widget buildContent() {
    return new Listener(
      child: new Container(
        width: width,
        height: height,
        child: new CustomPaint(
          callback: paintCallback,
          token: new _TextureButtonToken(
            highlight,
            texture,
            textureDown,
            width,
            height
          )
        )
      ),
      onPointerUp: (_) {
        if (onPressed != null) onPressed();
      }
    );
  }

  void paintCallback(PaintingCanvas canvas, Size size) {
    if (texture == null)
      return;

    canvas.save();
    if (highlight && textureDown != null) {
      // Draw down state
      canvas.scale(size.width / textureDown.size.width, size.height / textureDown.size.height);
      textureDown.drawTexture(canvas, Point.origin, new Paint());
    } else {
      // Draw up state
      canvas.scale(size.width / texture.size.width, size.height / texture.size.height);
      texture.drawTexture(canvas, Point.origin, new Paint());
    }
    canvas.restore();
  }
}

class _TextureButtonToken {
  _TextureButtonToken(
    this._highlight,
    this._texture,
    this._textureDown,
    this._width,
    this._height
  );

  final bool _highlight;
  final Texture _texture;
  final Texture _textureDown;
  final double _width;
  final double _height;

  bool operator== (other) {
    return
      other is _TextureButtonToken &&
      _highlight == other._highlight &&
      _texture == other._texture &&
      _textureDown == other._textureDown &&
      _width == other._width &&
      _height == other._height;
  }

  int get hashCode {
    int value = 373;
    value = 37 * value * _highlight.hashCode;
    value = 37 * value * _texture.hashCode;
    value = 37 * value * _textureDown.hashCode;
    value = 37 * value * _width.hashCode;
    value = 37 * value * _height.hashCode;
    return value;
  }
}

class MainScreenBackground extends NodeWithSize {
  MainScreenBackground() : super(new Size(320.0, 320.0)) {
    assert(_spriteSheet.image != null);

    StarField starField = new StarField(_spriteSheet, 200, true);
    addChild(starField);
  }

  void paint(PaintingCanvas canvas) {
    canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, 320.0, 320.0), new Paint()..color=new Color(0xff000000));
    super.paint(canvas);
  }
}