// 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:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';

abstract class GlobalKeyWatcher extends StatefulComponent {
  GlobalKeyWatcher({
    Key key,
    this.watchedKey
  });

  GlobalKey watchedKey;

  void syncConstructorArguments(GlobalKeyWatcher source) {
    if (source != source.watchedKey) {
      _removeListeners();
      watchedKey = source.watchedKey;
      _addListeners();
    }
  }

  void didMount() {
    super.didMount();
    _addListeners();
  }

  void didUnmount() {
    super.didUnmount();
    _removeListeners();
  }

  void didSyncWatchedKey(GlobalKey key, Widget widget) {
    assert(key == watchedKey);
  }

  void didRemoveWatchedKey(GlobalKey key) {
    assert(key == watchedKey);
  }

  void _addListeners() {
    GlobalKey.registerSyncListener(watchedKey, didSyncWatchedKey);
    GlobalKey.registerRemoveListener(watchedKey, didRemoveWatchedKey);
  }

  void _removeListeners() {
    GlobalKey.unregisterSyncListener(watchedKey, didSyncWatchedKey);
    GlobalKey.unregisterRemoveListener(watchedKey, didRemoveWatchedKey);
  }
}

typedef MimicReadyCallback();

class Mimic extends GlobalKeyWatcher {
  Mimic({
    Key key,
    GlobalKey original,
    this.onMimicReady
  }) : super(key: key, watchedKey: original);

  MimicReadyCallback onMimicReady;

  void syncConstructorArguments(Mimic source) {
    onMimicReady = source.onMimicReady;
    super.syncConstructorArguments(source);
  }

  Mimicable _mimicable;

  void didMount() {
    super.didMount();
    if (_mimicable == null)
      _setMimicable(GlobalKey.getWidget(watchedKey));
  }

  void didUnmount() {
    super.didUnmount();
    _stopMimic();
  }

  void didSyncWatchedKey(GlobalKey key, Widget widget) {
    super.didSyncWatchedKey(key, widget);
    _setMimicable(widget);
  }

  void didRemoveWatchedKey(GlobalKey key) {
    super.didRemoveWatchedKey(key);
    _setMimicable(null);
  }

  void _stopMimic() {
    if (_mimicable != null) {
      _mimicable.stopMimic();
      _mimicable = null;
    }
  }

  void _setMimicable(widget) {
    if (_mimicable != widget) {
      _stopMimic();
      if (widget != null) {
        widget.startMimic();
      }
    }
    setState(() {
      _mimicable = widget;
    });
    if (onMimicReady != null && _mimicable != null && _mimicable._didBuildPlaceholder)
      onMimicReady();
  }

  Widget build() {
    if (_mimicable == null || !_mimicable._didBuildPlaceholder)
      return new Container();
    return _mimicable.child;
  }
}

class Mimicable extends StatefulComponent {
  Mimicable({ GlobalKey key, this.child }) : super(key: key);

  Widget child;

  Size _size;
  Size get size => _size;

  void syncConstructorArguments(Mimicable source) {
    child = source.child;
  }

  bool _didBuildPlaceholder = false;

  Rect get globalBounds {
    if (_size == null)
      return null;
    return localToGlobal(Point.origin) & _size;
  }

  bool _mimicRequested = false;
  void startMimic() {
    assert(!_mimicRequested);
    setState(() {
      _mimicRequested = true;
    });
  }

  void stopMimic() {
    assert(_mimicRequested);
    setState(() {
      _mimicRequested = false;
    });
  }

  void _handleSizeChanged(Size size) {
    setState(() {
      _size = size;
    });
  }

  Widget build() {
    _didBuildPlaceholder = _mimicRequested && _size != null;
    if (_didBuildPlaceholder) {
      return new ConstrainedBox(
        constraints: new BoxConstraints.tight(_size)
      );
    }
    return new SizeObserver(
      callback: _handleSizeChanged,
      child: child
    );
  }
}