///
/// Created by @Haoyi on 2021/7/16
///

part of '../visual_feast_engine.dart';

class RenderQueue extends Render {
  bool get isEmpty => _next == this;

  bool get isNotEmpty => !isEmpty;

  RenderQueue() {
    _initRenderNode();
  }

  void reset() {
    _next = _prev = this;
  }

  void _addAllToBack(RenderQueue queue) {
    if (queue.isNotEmpty) {
      final tail = queue._prev;
      final head = queue._next;
      tail._next = this;
      head._prev = _prev;
      _prev._next = head;
      _prev = tail;
    }
  }

  void _addAllToFront(RenderQueue queue) {
    if (queue.isNotEmpty) {
      final tail = queue._prev;
      final head = queue._next;
      tail._next = _next;
      head._prev = this;
      _next._prev = tail;
      _next = head;
    }
  }

  void addBack(RenderElement _new) {
    _new._next = this;
    _new._prev = _prev;
    _prev._next = _new;
    _prev = _new;
  }

  void addFront(RenderElement _new) {
    _new._prev = this;
    _new._next = _next;
    _next._prev = _new;
    _next = _new;
  }

  RenderElement? popFront() {
    if (isNotEmpty) {
      final node = _next;
      node._unlink();
      return node as RenderElement;
    }
    return null;
  }

  RenderElement? removeBack() {
    if (isNotEmpty) {
      final node = _prev;
      node._unlink();
      return node as RenderElement;
    }
    return null;
  }

  @override
  void onAttach() {
    propagate((render) {
      render.onAttach();
    });
  }

  @override
  void onDetach() {
    propagate((render) {
      render.onDetach();
    });
  }

  @override
  void onStart() {
    propagate((render) {
      Log.d("onStart ${(render as RenderElement).renderId}");
      render.onStart();
    });
  } //开始渲染第一帧

  @override
  void onRender(Canvas canvas) {
    propagate((render) {
      render.onRender(canvas);
    });
  } // 渲染

  @override
  void onFrameUpdate(double t) {
    propagate((render) {
      render.onFrameUpdate(t);
    });
  } // 更新

  void propagate(void Function(Render) callback) {
    for (var render = _next, n = render._next; render != this; render = n, n = n._next) {
      callback.call(render);
    }
  }

  T propagateWithResult<T>(T Function(Render) callback,
      {required bool Function(T) interrupt, required T defaultValue}) {
    for (var renderObj = _next; renderObj != this; renderObj = renderObj._next) {
      final result = callback.call(renderObj);
      if (interrupt.call(result)) {
        return result;
      }
    }
    return defaultValue;
  }
}
