/// Tree view widget library
library tree_view;

import 'dart:async';

import 'package:aitrainer_app/util/common.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class TreeViewStream {
  static final TreeViewStream _singleton = TreeViewStream._internal();
  final StreamController<bool> streamController = StreamController<bool>.broadcast();
  double positionY = 0;

  Stream get stream => streamController.stream;
  StreamController getStreamController() => streamController;

  factory TreeViewStream() => _singleton;

  TreeViewStream._internal();

  void dispose() {
    streamController.close();
  }
}

class TreeView extends InheritedWidget {
  final List<Widget> children;
  final bool startExpanded;

  TreeView({
    Key? key,
    required List<Widget> children,
    bool startExpanded = false,
  })  : this.children = children,
        this.startExpanded = startExpanded,
        super(
          key: key,
          child: _TreeViewData(
            children: children,
          ),
        );

  static TreeView? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType(aspect: TreeView);
  }

  @override
  bool updateShouldNotify(TreeView oldWidget) {
    if (oldWidget.children == this.children && oldWidget.startExpanded == this.startExpanded) {
      return false;
    }
    return true;
  }
}

class _TreeViewData extends StatefulWidget {
  final List<Widget> children;

  _TreeViewData({
    required this.children,
  });

  @override
  __TreeViewDataState createState() => __TreeViewDataState();
}

class __TreeViewDataState extends State<_TreeViewData> {
  final ScrollController _controller = ScrollController();
  final Stream stream = TreeViewStream().stream;
  late var subscription;

  @override
  void initState() {
    super.initState();

    /// We require the initializers to run after the loading screen is rendered
    SchedulerBinding.instance!.addPostFrameCallback((_) {
      final double cHeight = MediaQuery.of(context).size.height;
      subscription = stream.listen((value) {
        if (value) {
          final double positionY = TreeViewStream().positionY;
          /* print("pos " +
              positionY.toString() +
              " height: " +
              cHeight.toString() +
              " controller offset " +
              _controller.offset.toString() +
              " controller initial " +
              _controller.initialScrollOffset.toString()); */
          if (positionY > cHeight - 190) {
            final double offset = positionY + 40;
            //print("antimateTo " + offset.toString());
            _controller.animateTo(offset, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
          }
        }
      });
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    subscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      scrollDirection: Axis.vertical,
      controller: _controller,
      itemCount: widget.children.length,
      itemBuilder: (context, index) {
        return widget.children.elementAt(index);
      },
    );
  }
}

class TreeViewChild extends StatefulWidget {
  final bool? startExpanded;
  final Widget parent;
  final List<Widget> children;
  final VoidCallback? onTap;

  TreeViewChild({
    required this.parent,
    required this.children,
    this.startExpanded,
    this.onTap,
    Key? key,
  }) : super(key: key);

  @override
  TreeViewChildState createState() => TreeViewChildState();

  TreeViewChild copyWith(
    TreeViewChild source, {
    bool? startExpanded,
    Widget? parent,
    List<Widget>? children,
    VoidCallback? onTap,
  }) {
    return TreeViewChild(
      parent: parent ?? source.parent,
      children: children ?? source.children,
      startExpanded: startExpanded ?? source.startExpanded,
      onTap: onTap ?? source.onTap,
      key: key,
    );
  }
}

class TreeViewChildState extends State<TreeViewChild> with Common {
  late bool? isExpanded;
  final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();

  @override
  void initState() {
    super.initState();
    isExpanded = widget.startExpanded;
  }

  @override
  void didChangeDependencies() {
    isExpanded = widget.startExpanded ?? TreeView.of(context)!.startExpanded;
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    return (Column(
      key: listKey,
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        GestureDetector(child: widget.parent, onTap: widget.onTap ?? () => toggleExpanded()),
        Flexible(
          child: Container(
            child: AnimatedSwitcher(
              duration: Duration(milliseconds: 200),
              reverseDuration: Duration(milliseconds: 200),
              switchInCurve: Curves.easeIn,
              child: isExpanded!
                  ? Column(
                      mainAxisSize: MainAxisSize.min,
                      children: widget.children,
                    )
                  : Offstage(),
            ),
          ),
        )
      ],
    ));
  }

  void toggleExpanded() {
    setState(() {
      this.isExpanded = !this.isExpanded!;
      TreeViewStream().positionY = getPosition();
      TreeViewStream().getStreamController().add(this.isExpanded);
    });
  }

  double getPosition() {
    RenderBox? box = listKey.currentContext!.findRenderObject() as RenderBox;
    Offset position = box.localToGlobal(Offset.zero); //this is global position
    double y = position.dy;
    return y;
  }
}
