/**
   * Transform associated with a node. This helps to reduce the size of nodes.
   */
export class Transform {

    /**
     * Node that this transform belongs to.
     */
    protected _node: Node;

    /**
     * Transform matrix.
     */
    protected _matrix: Matrix;

    /**
     * Transform color matrix.
     */
    protected _colorMatrix: ColorMatrix;

    /**
     * Concatenated matrix. This is not frequently used.
     */
    protected _concatenatedMatrix: Matrix;

    /**
     * Inverted concatenated matrix. This is not frequently used.
     */
    protected _invertedConcatenatedMatrix: Matrix;

    /**
     * Concatenated color matrix. This is not frequently used.
     */
    protected _concatenatedColorMatrix: ColorMatrix;

    constructor(node: Node) {
      this._node = node;
      this._matrix = Matrix.createIdentity(); // MEMORY: Lazify construction.
      this._colorMatrix = ColorMatrix.createIdentity(); // MEMORY: Lazify construction.
      this._concatenatedMatrix = Matrix.createIdentity(); // MEMORY: Lazify construction.
      this._invertedConcatenatedMatrix = Matrix.createIdentity(); // MEMORY: Lazify construction.
      this._concatenatedColorMatrix = ColorMatrix.createIdentity(); // MEMORY: Lazify construction.
    }

//    public get x(): number {
//      return this._matrix.tx;
//    }
//
//    public set x(value: number) {
//      this._matrix.tx = value;
//      this._node._propagateFlagsUp(NodeFlags.UpOnMoved);
//      this._node._propagateFlagsDown(NodeFlags.DownOnMoved);
//    }
//
//    public get y(): number {
//      return this._matrix.ty;
//    }
//
//    public set y(value: number) {
//      this._matrix.ty = value;
//      this._node._propagateFlagsUp(NodeFlags.UpOnMoved);
//      this._node._propagateFlagsDown(NodeFlags.DownOnMoved);
//    }

    /**
     * Set a node's transform matrix. You should never mutate the matrix object directly.
     */
    public setMatrix(value: Matrix) {
      if (this._matrix.isEqual(value)) {
        return;
      }
      this._node._markCurrentBoundsAsDirtyRegion();
      this._matrix.set(value);
      this._node._propagateFlagsUp(NodeFlags.UpOnMoved);
      this._node._propagateFlagsDown(NodeFlags.DownOnMoved);
      this._node._markCurrentBoundsAsDirtyRegion();
    }

    public setColorMatrix(value: ColorMatrix) {
      this._colorMatrix.set(value);
      this._node._propagateFlagsUp(NodeFlags.UpOnColorMatrixChanged);
      this._node._propagateFlagsDown(NodeFlags.DownOnColorMatrixChanged);
    }

    public getMatrix(clone: boolean = false): Matrix {
      if (clone) {
        return this._matrix.clone();
      }
      return this._matrix;
    }

    public hasColorMatrix() {
      return this._colorMatrix !== null;
    }

    public getColorMatrix(clone: boolean = false): ColorMatrix {
      if (this._colorMatrix === null) {
        this._colorMatrix = ColorMatrix.createIdentity();
      }
      if (clone) {
        return this._colorMatrix.clone();
      }
      return this._colorMatrix;
    }

    /**
     * Compute the concatenated transforms for this node and all of its ancestors since we're already doing
     * all the work.
     */
    public getConcatenatedMatrix(clone: boolean = false): Matrix {
      if (this._node.hasFlags(NodeFlags.InvalidConcatenatedMatrix)) {
        var ancestor = this._node._findClosestAncestor(NodeFlags.InvalidConcatenatedMatrix, false);
        var path = Node._getAncestors(this._node, ancestor);
        var m = ancestor ? ancestor.getTransform()._concatenatedMatrix.clone() : Matrix.createIdentity();
        for (var i = path.length - 1; i >= 0; i--) {
          var ancestor = path[i];
          var ancestorTransform = ancestor.getTransform();
          release || assert (ancestor.hasFlags(NodeFlags.InvalidConcatenatedMatrix));
          m.preMultiply(ancestorTransform._matrix);
          ancestorTransform._concatenatedMatrix.set(m);
          ancestor.removeFlags(NodeFlags.InvalidConcatenatedMatrix);
        }
      }
      if (clone) {
        return this._concatenatedMatrix.clone();
      }
      return this._concatenatedMatrix;
    }

    public getInvertedConcatenatedMatrix(clone: boolean = false): Matrix {
      if (this._node.hasFlags(NodeFlags.InvalidInvertedConcatenatedMatrix)) {
        this.getConcatenatedMatrix().inverse(this._invertedConcatenatedMatrix);
        this._node.removeFlags(NodeFlags.InvalidInvertedConcatenatedMatrix);
      }
      if (clone) {
        return this._invertedConcatenatedMatrix.clone();
      }
      return this._invertedConcatenatedMatrix;
    }

//    public getConcatenatedColorMatrix(clone: boolean = false): ColorMatrix {
//      // Compute the concatenated color transforms for this node and all of its ancestors.
//      if (this.hasFlags(NodeFlags.InvalidConcatenatedColorMatrix)) {
//        var ancestor = <Transform>this._findClosestAncestor(NodeFlags.InvalidConcatenatedColorMatrix, false);
//        var path = <Transform []>Node._getAncestors(this, ancestor, NodeType.Transform);
//        var m = ancestor ? ancestor._concatenatedColorMatrix.clone() : ColorMatrix.createIdentity();
//        for (var i = path.length - 1; i >= 0; i--) {
//          var ancestor = path[i];
//          release || assert (ancestor.hasFlags(NodeFlags.InvalidConcatenatedColorMatrix));
//          // TODO: Premultiply here.
//          m.multiply(ancestor._colorMatrix);
//          ancestor._concatenatedColorMatrix.set(m);
//          ancestor.removeFlags(NodeFlags.InvalidConcatenatedColorMatrix);
//        }
//      }
//      if (clone) {
//        return this._concatenatedColorMatrix.clone();
//      }
//      return this._concatenatedColorMatrix;
//    }

    public toString() {
      return this._matrix.toString();
    }
  }


/**
   * Layer information associated with a node. This helps to reduce the size of nodes.
   */
export class Layer {
    protected _node: Node;
    protected _blendMode: BlendMode;
    protected _mask: Node;
    protected _filters: Filter [];

    constructor(node: Node) {
      this._node = node;
      this._mask = null;
      this._blendMode = BlendMode.Normal;
    }

    get filters() {
      return this._filters;
    }

    set filters(value: Filter []) {
      this._filters = value;
    }

    get blendMode() {
      return this._blendMode;
    }

    set blendMode(value: BlendMode) {
      this._blendMode = value;
    }

    get mask(): Node {
      return this._mask;
    }

    set mask(value: Node) {
      if (this._mask && this._mask !== value) {
        this._mask.removeFlags(NodeFlags.IsMask);
      }
      this._mask = value;
      if (this._mask) {
        this._mask.setFlags(NodeFlags.IsMask);
      }
      // TODO: Keep track of masked object so we can propagate flags up.
    }

    public toString() {
      return BlendMode[this._blendMode];
    }
  }


/**
   * Shapes are instantiations of Renderables.
   */
export class Shape extends Node {
    private _source: Renderable;
    public ratio: number;

    constructor(source: Renderable) {
      super();
      release || assert(source);
      this._source = source;
      this._type = NodeType.Shape;
      this.ratio = 0;
    }

    public getBounds(clone: boolean = false): Rectangle {
      var bounds = this._bounds || (this._bounds = Rectangle.createEmpty());
      if (this.hasFlags(NodeFlags.InvalidBounds)) {
        bounds.set(this._source.getBounds());
        this.removeFlags(NodeFlags.InvalidBounds);
      }
      if (clone) {
        return bounds.clone();
      }
      return bounds;
    }

    get source(): Renderable {
      return this._source;
    }

    _propagateFlagsDown(flags: NodeFlags) {
      this.setFlags(flags);
    }

    public getChildren(clone: boolean = false): Node[] {
      return [this._source];
    }
  }


export class RendererOptions {
    debug: boolean = false;
    paintRenderable: boolean = true;
    paintBounds: boolean = false;
    paintDirtyRegion: boolean = false;
    paintFlashing: boolean = false;
    paintViewport: boolean = false;
    clear: boolean = true;
  }


/**
   * Base class for all renderers.
   */
export class Renderer extends NodeVisitor {
    /**
     * Everything is clipped by the viewport.
     */
    protected _viewport: Rectangle;

    protected _options: RendererOptions;

    /**
     * We can render either into a canvas element or into a div element.
     */
    protected _container: HTMLDivElement | HTMLCanvasElement;

    protected _stage: Stage;

    protected _devicePixelRatio: number;

    constructor(container: HTMLDivElement | HTMLCanvasElement, stage: Stage, options: RendererOptions) {
      super();
      this._container = container;
      this._stage = stage;
      this._options = options;
      this._viewport = Rectangle.createSquare(1024);
      this._devicePixelRatio = 1;
    }

    set viewport (viewport: Rectangle) {
      this._viewport.set(viewport);
    }

    public render() {
      throw Shumway.Debug.abstractMethod("Renderer::render");
    }

    /**
     * Notify renderer that the viewport has changed.
     */
    public resize() {
      throw Shumway.Debug.abstractMethod("Renderer::resize");
    }

    /**
     * Captures a rectangular region of the easel as a dataURL as specified by |bounds|. |stageContent| indicates if the bounds
     * should be computed by looking at the bounds of the content of the easel rather than the easel itself.
     */
    public screenShot(bounds: Rectangle, stageContent: boolean): ScreenShot {
      throw Shumway.Debug.abstractMethod("Renderer::screenShot");
    }
  }


/**
   * Node container that handles Flash style alignment and scale modes.
   */
export class Stage extends Group {
    /**
     * This is supposed to keep track of dirty regions.
     */
    private _dirtyRegion: DirtyRegion;

    public get dirtyRegion(): DirtyRegion {
      return this._dirtyRegion;
    }

    private _align: StageAlignFlags;
    private _scaleMode: StageScaleMode;

    /**
     * All stage content is added to his child node. This is so what we can set the align and scale
     * transform to all stage descendants but not affect the stage itself.
     */
    private _content: Group;

    public color: Color;

    // Using these constants initially -- they don't require knowing bounds.
    // Notice that this default values are different from ActionScript object values.
    private static DEFAULT_SCALE = StageScaleMode.NoScale;
    private static DEFAULT_ALIGN = StageAlignFlags.Left | StageAlignFlags.Top;

    private _preVisitor: PreRenderVisitor = new PreRenderVisitor();

    constructor(w: number, h: number, trackDirtyRegion: boolean = false) {
      super();
      this._flags &= ~NodeFlags.BoundsAutoCompute;
      this._type = NodeType.Stage;
      this._scaleMode = Stage.DEFAULT_SCALE;
      this._align = Stage.DEFAULT_ALIGN;
      this._content = new Group();
      this._content._flags &= ~NodeFlags.BoundsAutoCompute;
      this.addChild(this._content);
      this.setFlags(NodeFlags.Dirty);
      this.setBounds(new Rectangle(0, 0, w, h));
      if (trackDirtyRegion) {
        this._dirtyRegion = new DirtyRegion(w, h);
        this._dirtyRegion.addDirtyRectangle(new Rectangle(0, 0, w, h));
      } else {
        this._dirtyRegion = null;
      }
      this._updateContentMatrix();
    }

    public setBounds(value: Rectangle) {
      super.setBounds(value);
      this._updateContentMatrix();
      this._dispatchEvent(NodeEventType.OnStageBoundsChanged);
      if (this._dirtyRegion) {
        this._dirtyRegion = new DirtyRegion(value.w, value.h);
        this._dirtyRegion.addDirtyRectangle(value);
      }
    }

    public get content(): Group {
      return this._content;
    }

    /**
     * Checks to see if we should render and if so, clears any relevant dirty flags. Returns
     * true if rendering should commence. Flag clearing is made optional here in case there
     * is any code that needs to check if rendering is about to happen.
     */
    readyToRender(): boolean {
      this._preVisitor.isDirty = false;
      this._preVisitor.start(this, this._dirtyRegion);
      if (this._preVisitor.isDirty) {
        return true;
      }
      return false;
    }

    public get align(): StageAlignFlags {
      return this._align;
    }

    public set align(value: StageAlignFlags) {
      this._align = value;
      this._updateContentMatrix();
    }

    public get scaleMode(): StageScaleMode {
      return this._scaleMode;
    }

    public set scaleMode(value: StageScaleMode) {
      this._scaleMode = value;
      this._updateContentMatrix();
    }

    /**
     * Figure out what the content transform shuold be given the current align and scale modes.
     */
    private _updateContentMatrix() {
      if (this._scaleMode === Stage.DEFAULT_SCALE && this._align === Stage.DEFAULT_ALIGN) {
        // Shortcut and also guard to avoid using targetWidth/targetHeight.
        // ThetargetWidth/targetHeight normally set in setScaleAndAlign call.
        this._content.getTransform().setMatrix(new Matrix(1, 0, 0, 1, 0, 0));
        return;
      }

      var bounds = this.getBounds();
      var contentBounds = this._content.getBounds();

      // Debug.assert(this.targetWidth > 0 && this.targetHeight > 0);
      var wScale = bounds.w / contentBounds.w;
      var hScale = bounds.h / contentBounds.h;
      var scaleX, scaleY;
      switch (this._scaleMode) {
        case StageScaleMode.NoBorder:
          scaleX = scaleY = Math.max(wScale, hScale);
          break;
        case StageScaleMode.NoScale:
          scaleX = scaleY = 1;
          break;
        case StageScaleMode.ExactFit:
          scaleX = wScale;
          scaleY = hScale;
          break;
        // case StageScaleMode.ShowAll:
        default:
          scaleX = scaleY = Math.min(wScale, hScale);
          break;
      }

      var offsetX;
      if ((this._align & StageAlignFlags.Left)) {
        offsetX = 0;
      } else if ((this._align & StageAlignFlags.Right)) {
        offsetX = bounds.w - contentBounds.w * scaleX;
      } else {
        offsetX = (bounds.w - contentBounds.w * scaleX) / 2;
      }

      var offsetY;
      if ((this._align & StageAlignFlags.Top)) {
        offsetY = 0;
      } else if ((this._align & StageAlignFlags.Bottom)) {
        offsetY = bounds.h - contentBounds.h * scaleY;
      } else {
        offsetY = (bounds.h - contentBounds.h * scaleY) / 2;
      }

      this._content.getTransform().setMatrix(new Matrix(scaleX, 0, 0, scaleY, offsetX, offsetY));
    }
  }


export enum BlendMode {
    Normal     = 1,
    Layer      = 2,
    Multiply   = 3,
    Screen     = 4,
    Lighten    = 5,
    Darken     = 6,
    Difference = 7,
    Add        = 8,
    Subtract   = 9,
    Invert     = 10,
    Alpha      = 11,
    Erase      = 12,
    Overlay    = 13,
    HardLight  = 14
  }


export enum NodeFlags {
    None                              = 0x00000,

    /**
     * Whether this node's bounding box is automatically computed from its children. If this
     * flag is |false| then this node's bounding box can only be set via |setBounds|.
     */
    BoundsAutoCompute                 = 0x00002,

    /**
     * Whether this node acts as a mask for another node.
     */
    IsMask                            = 0x00004,

    /**
     * Whether this node needs to be repainted.
     */
    Dirty                             = 0x00010,

    /**
     * Whether this node's bounds is invalid and needs to be recomputed. Only nodes that have the
     * |BoundsAutoCompute| flag set can have this flag set.
     */
    InvalidBounds                     = 0x00100,

    /**
     * Whether this node's concatenated matrix is invalid. This happens whenever a node's ancestor
     * is moved in the node tree.
     */
    InvalidConcatenatedMatrix         = 0x00200,

    /**
     * Whether this node's inverted concatenated matrix is invalid. This happens whenever a node's ancestor
     * is moved in the node tree.
     */
    InvalidInvertedConcatenatedMatrix = 0x00400,

    /**
     * Same as above, but for colors.
     */
    InvalidConcatenatedColorMatrix    = 0x00800,

    /**
     * Flags to propagate upwards when a node is added or removed from a group.
     */
    UpOnAddedOrRemoved                = InvalidBounds | Dirty,

    /**
     * Flags to propagate upwards when a node is moved.
     */
    UpOnMoved                         = InvalidBounds | Dirty,

    /**
     * Flags to propagate downwards when a mode is added or removed from a group.
     */
    DownOnAddedOrRemoved              = InvalidConcatenatedMatrix | InvalidInvertedConcatenatedMatrix | InvalidConcatenatedColorMatrix,

    /**
     * Flags to propagate downwards when a node is moved.
     */
    DownOnMoved                       = InvalidConcatenatedMatrix | InvalidInvertedConcatenatedMatrix | InvalidConcatenatedColorMatrix,

    /**
     * Flags to propagate upwards when a node's color matrix is changed.
     */
    UpOnColorMatrixChanged            = Dirty,

    /**
     * Flags to propagate downwards when a node's color matrix is changed.
     */
    DownOnColorMatrixChanged          = InvalidConcatenatedColorMatrix,

    Visible                           = 0x10000,

    /**
     * Flags to propagate upwards when a node is invalidated.
     */
    UpOnInvalidate                    = InvalidBounds | Dirty,

    /**
     * Default node flags, however not all node types use these defaults.
     */
    Default                           = BoundsAutoCompute |
                                        InvalidBounds |
                                        InvalidConcatenatedMatrix |
                                        InvalidInvertedConcatenatedMatrix |
                                        Visible,

    /**
     * Whether this node is marked to be cached as a bitmap. This isn't just a performance optimization,
     * but also affects the way masking is performed.
     */
    CacheAsBitmap                     = 0x20000,

    /**
     * Whether this node's contents should be drawn snapped to pixel boundaries.
     */
    PixelSnapping                     = 0x40000,

    /**
     * Whether this node's contents should use higher quality image smoothing.
     */
    ImageSmoothing                    = 0x80000,

    /**
     * Whether source has dynamic content.
     */
    Dynamic                           = 0x100000,

    /**
     * Whether the source's content can be scaled and drawn at a higher resolution.
     */
    Scalable                          = 0x200000,

    /**
     * Whether the source's content should be tiled.
     */
    Tileable                          = 0x400000,

    // Delete These
    Transparent                       = 0x08000,
  }


/**
   * Scene graph object hierarchy. This enum makes it possible to write fast type checks.
   */
export enum NodeType {
    Node                   = 0x0001,
      Shape                = 0x0003, // 0x0002 | Node,
      Group                = 0x0005, // 0x0004 | Node,
        Stage              = 0x000D, // 0x0008 | Group
      Renderable           = 0x0021  // 0x0020 | Node
  }


/**
   * Basic event types. Not much here.
   */
export enum NodeEventType {
    None                        = 0x0000,
    OnStageBoundsChanged        = 0x0001,
    RemovedFromStage            = 0x0002
  }


export enum Backend {
    Canvas2D = 0,
    WebGL = 1, // Soon
    Both = 2,
    DOM = 3, // Someday
    SVG = 4 // Someday
  }


visitNode(node: Node, state: State) {
      // ...
    }


visitShape(node: Shape, state: State) {
      this.visitNode(node, state);
    }


visitGroup(node: Group, state: State) {
      this.visitNode(node, state);
      var children = node.getChildren();
      for (var i = 0; i < children.length; i++) {
        children[i].visit(this, state);
      }
    }


visitStage(node: Stage, state: State) {
      this.visitGroup(node, state);
    }


visitRenderable(node: Renderable, state: State) {
      this.visitNode(node, state);
    }


transform(transform: Transform): PreRenderState {
      var state = this.clone();
      state.matrix.preMultiply(transform.getMatrix());
      return state;
    }


static allocate(): PreRenderState {
      var dirtyStack = PreRenderState._dirtyStack;
      var state = null;
      if (dirtyStack.length) {
        state = dirtyStack.pop();
      }
      return state;
    }


public clone(): PreRenderState {
      var state = PreRenderState.allocate();
      if (!state) {
        state = new PreRenderState();
      }
      state.set(this);
      return state;
    }


set (state: PreRenderState) {
      this.matrix.set(state.matrix);
    }


free() {
      PreRenderState._dirtyStack.push(this);
    }


start(node: Group, dirtyRegion: DirtyRegion) {
      this._dirtyRegion = dirtyRegion;
      var state = new PreRenderState();
      state.matrix.setIdentity();
      node.visit(this, state);
      state.free();
    }


visitGroup(node: Group, state: PreRenderState) {
      var children = node.getChildren();
      this.visitNode(node, state);
      for (var i = 0; i < children.length; i++) {
        var child = children[i];
        var childState = state.transform(child.getTransform());
        child.visit(this, childState);
        childState.free();
      }
    }


visitNode(node: Node, state: PreRenderState) {
      if (node.hasFlags(NodeFlags.Dirty)) {
        this.isDirty = true;
      }
      node.toggleFlags(NodeFlags.Dirty, false);
      node.depth = state.depth++;
    }


visitShape(node: Shape, state: State) {
      this.writer.writeLn(node.toString());
      this.visitNode(node, state);
    }


visitGroup(node: Group, state: State) {
      this.visitNode(node, state);

      var children = node.getChildren();
      this.writer.enter(node.toString() + " " + children.length);
      for (var i = 0; i < children.length; i++) {
        children[i].visit(this, state);
      }
      this.writer.outdent();
    }


protected _dispatchEvent(type: NodeEventType) {
      if (!this._eventListeners) {
        return;
      }
      var listeners = this._eventListeners;
      for (var i = 0; i < listeners.length; i++) {
        var listener = listeners[i];
        if (listener.type === type) {
          listener.listener(this, type);
        }
      }
    }


/**
     * Adds an event listener.
     */
public addEventListener(type: NodeEventType, listener: (node: Node, type?: NodeEventType) => void) {
      if (!this._eventListeners) {
        this._eventListeners = [];
      }
      this._eventListeners.push({type: type, listener: listener});
    }


/**
     * Removes an event listener.
     */
public removeEventListener(type: NodeEventType, listener: (node: Node, type?: NodeEventType) => void) {
      var listeners = this._eventListeners;
      for (var i = 0; i < listeners.length; i++) {
        var listenerObject = listeners[i];
        if (listenerObject.type === type && listenerObject.listener === listener) {
          listeners.splice(i, 1);
          return;
        }
      }
    }


/**
     * Resets the Node to its initial state but preserves its identity.
     * It safe to call this on a child without disrupting ownership.
     */
reset() {
      this._flags = NodeFlags.Default;
      this._bounds = null;
      this._layer = null;
      this._transform = null;
      this._properties = null;
      this.depth = -1;
    }


public getTransformedBounds(target: Node): Rectangle {
      var bounds = this.getBounds(true);
      if (target === this || bounds.isEmpty()) {
        // Nop.
      } else {
        var m = this.getTransform().getConcatenatedMatrix();
        if (target) {
          var t = target.getTransform().getInvertedConcatenatedMatrix(true);
          t.preMultiply(m);
          t.transformRectangleAABB(bounds);
          t.free();
        } else {
          m.transformRectangleAABB(bounds);
        }
      }
      return bounds;
    }


_markCurrentBoundsAsDirtyRegion() {
      return;
      /*
      var stage = this.getStage();
      if (!stage) {
        return;
      }
      var bounds = this.getTransformedBounds(stage);
      stage.dirtyRegion.addDirtyRectangle(bounds);
      */
    }


public getStage(withDirtyRegion: boolean = true): Stage {
      var node = this._parent;
      while (node) {
        if (node.isType(NodeType.Stage)) {
          var stage = <Stage>node;
          if (withDirtyRegion) {
            if (stage.dirtyRegion) {
              return stage;
            }
          } else {
            return stage;
          }
        }
        node = node._parent;
      }
      return null;
    }


/**
     * This shouldn't be used on any hot path becuse it allocates.
     */
public getChildren(clone: boolean = false): Node [] {
      throw Shumway.Debug.abstractMethod("Node::getChildren");
    }


public getBounds(clone: boolean = false): Rectangle {
      throw Shumway.Debug.abstractMethod("Node::getBounds");
    }


/**
     * Can only be set on nodes without the |NodeFlags.BoundsAutoCompute| flag set.
     */
public setBounds(value: Rectangle) {
      release || assert(!(this._flags & NodeFlags.BoundsAutoCompute));
      var bounds = this._bounds || (this._bounds = Rectangle.createEmpty());
      bounds.set(value);
      this.removeFlags(NodeFlags.InvalidBounds);
    }


public clone(): Node {
      throw Shumway.Debug.abstractMethod("Node::clone");
    }


public setFlags(flags: NodeFlags) {
      this._flags |= flags;
    }


public hasFlags(flags: NodeFlags): boolean {
      return (this._flags & flags) === flags;
    }


public hasAnyFlags(flags: NodeFlags): boolean {
      return !!(this._flags & flags);
    }


public removeFlags(flags: NodeFlags) {
      this._flags &= ~flags;
    }


public toggleFlags(flags: NodeFlags, on: boolean) {
      if (on) {
        this._flags |= flags;
      } else {
        this._flags &= ~flags;
      }
    }


/**
     * Propagates flags up the tree. Propagation stops if all flags are already set.
     */
_propagateFlagsUp(flags: NodeFlags) {
      if (flags === NodeFlags.None || this.hasFlags(flags)) {
        return;
      }
      if (!this.hasFlags(NodeFlags.BoundsAutoCompute)) {
        flags &= ~NodeFlags.InvalidBounds;
      }
      this.setFlags(flags);
      var parent = this._parent;
      if (parent) {
        parent._propagateFlagsUp(flags);
      }
    }


/**
     * Propagates flags down the tree. Non-containers just set the flags on themselves.
     */
_propagateFlagsDown(flags: NodeFlags) {
      throw Shumway.Debug.abstractMethod("Node::_propagateFlagsDown");
    }


public isAncestor(node: Node): boolean {
      while (node) {
        if (node === this) {
          return true;
        }
        release || assert(node !== node._parent);
        node = node._parent;
      }
      return false;
    }


/**
     * Return's a list of ancestors excluding the |last|, the return list is reused.
     */
static _getAncestors(node: Node, last: Node): Node [] {
      var path = Node._path;
      path.length = 0;
      while (node && node !== last) {
        release || assert(node !== node._parent);
        path.push(node);
        node = node._parent;
      }
      release || assert (node === last, "Last ancestor is not an ancestor.");
      return path;
    }


/**
     * Finds the closest ancestor with a given set of flags that are either turned on or off.
     */
_findClosestAncestor(flags: NodeFlags, on: boolean): Node {
      var node = this;
      while (node) {
        if (node.hasFlags(flags) === on) {
          return node;
        }
        release || assert(node !== node._parent);
        node = node._parent;
      }
      return null;
    }


/**
     * Type check.
     */
public isType(type: NodeType): boolean {
      return this._type === type;
    }


/**
     * Subtype check.
     */
public isTypeOf(type: NodeType): boolean {
      return (this._type & type) === type;
    }


public isLeaf(): boolean {
      return this.isType(NodeType.Renderable) || this.isType(NodeType.Shape);
    }


public isLinear(): boolean {
      if (this.isLeaf()) {
        return true;
      }
      if (this.isType(NodeType.Group)) {
        var children = (<any>this)._children;
        if (children.length === 1 && children[0].isLinear()) {
          return true;
        }
      }
      return false;
    }


public getTransformMatrix(clone: boolean = false): Matrix {
      return this.getTransform().getMatrix(clone);
    }


public getTransform(): Transform {
      if (this._transform === null) {
        this._transform = new Transform(this);
      }
      return this._transform;
    }


public getLayer(): Layer {
      if (this._layer === null) {
        this._layer = new Layer(this);
      }
      return this._layer;
    }


//    public getConcatenatedMatrix(clone: boolean = false): Matrix {
//      var transform: Transform = this.getTransform(false);
//      if (transform) {
//        return transform.getConcatenatedMatrix(clone);
//      }
//      return Matrix.createIdentity();
//    }
/**
     * Dispatch on node types.
     */
public visit(visitor: NodeVisitor, state: State) {
      switch (this._type) {
        case NodeType.Node:
          visitor.visitNode(this, state);
          break;
        case NodeType.Group:
          visitor.visitGroup(<Group>this, state);
          break;
        case NodeType.Stage:
          visitor.visitStage(<Stage>this, state);
          break;
        case NodeType.Shape:
          visitor.visitShape(<Shape>this, state);
          break;
        case NodeType.Renderable:
          visitor.visitRenderable(<Renderable>this, state);
          break;
        default:
          Debug.unexpectedCase();
      }
    }


public invalidate() {
      this._markCurrentBoundsAsDirtyRegion();
      this._propagateFlagsUp(NodeFlags.UpOnInvalidate);
    }


public toString(bounds: boolean = false): string {
      var s = NodeType[this._type] + " " + this._id;
      if (bounds) {
        s += " " + this._bounds.toString();
      }
      return s;
    }


public getChildren(clone: boolean = false): Node [] {
      if (clone) {
        return this._children.slice(0);
      }
      return this._children;
    }


public childAt(index: number): Node {
      release || assert(index >= 0 && index < this._children.length);
      return this._children[index];
    }


/**
     * Adds a node and remove's it from its previous location if it has a parent and propagates
     * flags accordingly.
     */
public addChild(node: Node) {
      release || assert(node);
      release || assert(!node.isAncestor(this));
      if (node._parent) {
        node._parent.removeChildAt(node._index);
      }
      node._parent = this;
      node._index = this._children.length;
      this._children.push(node);
      this._propagateFlagsUp(NodeFlags.UpOnAddedOrRemoved);
      node._propagateFlagsDown(NodeFlags.DownOnAddedOrRemoved);
      node._markCurrentBoundsAsDirtyRegion();
    }


/**
     * Removes a child at the given index and propagates flags accordingly.
     */
public removeChildAt(index: number) {
      release || assert(index >= 0 && index < this._children.length);
      var node = this._children[index];
      release || assert(index === node._index);
      node._markCurrentBoundsAsDirtyRegion();
      this._children.splice(index, 1);
      node._index = -1;
      node._parent = null;
      this._propagateFlagsUp(NodeFlags.UpOnAddedOrRemoved);
      node._propagateFlagsDown(NodeFlags.DownOnAddedOrRemoved);
    }


public clearChildren() {
      for (var i = 0; i < this._children.length; i++) {
        var child = this._children[i];
        child._markCurrentBoundsAsDirtyRegion();
        if (child) {
          child._index = -1;
          child._parent = null;
          child._propagateFlagsDown(NodeFlags.DownOnAddedOrRemoved);
        }
      }
      this._children.length = 0;
      this._propagateFlagsUp(NodeFlags.UpOnAddedOrRemoved);
    }


/**
     * Override and propagate flags to all children.
     */
_propagateFlagsDown(flags: NodeFlags) {
      if (this.hasFlags(flags)) {
        return;
      }
      this.setFlags(flags);
      var children = this._children;
      for (var i = 0; i < children.length; i++) {
        children[i]._propagateFlagsDown(flags);
      }
    }


/**
     * Takes the inion of all child bounds and caches the bounds locally.
     */
public getBounds(clone: boolean = false): Rectangle {
      var bounds = this._bounds || (this._bounds = Rectangle.createEmpty());
      if (this.hasFlags(NodeFlags.InvalidBounds)) {
        bounds.setEmpty();
        var children = this._children;
        var childBounds = Rectangle.allocate();
        for (var i = 0; i < children.length; i++) {
          var child = children[i];
          childBounds.set(child.getBounds());
          child.getTransformMatrix().transformRectangleAABB(childBounds);
          bounds.union(childBounds);
        }
        childBounds.free();
        this.removeFlags(NodeFlags.InvalidBounds);
      }
      if (clone) {
        return bounds.clone();
      }
      return bounds;
    }


//    public get x(): number {
//      return this._matrix.tx;
//    }
//
//    public set x(value: number) {
//      this._matrix.tx = value;
//      this._node._propagateFlagsUp(NodeFlags.UpOnMoved);
//      this._node._propagateFlagsDown(NodeFlags.DownOnMoved);
//    }
//
//    public get y(): number {
//      return this._matrix.ty;
//    }
//
//    public set y(value: number) {
//      this._matrix.ty = value;
//      this._node._propagateFlagsUp(NodeFlags.UpOnMoved);
//      this._node._propagateFlagsDown(NodeFlags.DownOnMoved);
//    }
/**
     * Set a node's transform matrix. You should never mutate the matrix object directly.
     */
public setMatrix(value: Matrix) {
      if (this._matrix.isEqual(value)) {
        return;
      }
      this._node._markCurrentBoundsAsDirtyRegion();
      this._matrix.set(value);
      this._node._propagateFlagsUp(NodeFlags.UpOnMoved);
      this._node._propagateFlagsDown(NodeFlags.DownOnMoved);
      this._node._markCurrentBoundsAsDirtyRegion();
    }


public setColorMatrix(value: ColorMatrix) {
      this._colorMatrix.set(value);
      this._node._propagateFlagsUp(NodeFlags.UpOnColorMatrixChanged);
      this._node._propagateFlagsDown(NodeFlags.DownOnColorMatrixChanged);
    }


public getMatrix(clone: boolean = false): Matrix {
      if (clone) {
        return this._matrix.clone();
      }
      return this._matrix;
    }


public hasColorMatrix() {
      return this._colorMatrix !== null;
    }


public getColorMatrix(clone: boolean = false): ColorMatrix {
      if (this._colorMatrix === null) {
        this._colorMatrix = ColorMatrix.createIdentity();
      }
      if (clone) {
        return this._colorMatrix.clone();
      }
      return this._colorMatrix;
    }


/**
     * Compute the concatenated transforms for this node and all of its ancestors since we're already doing
     * all the work.
     */
public getConcatenatedMatrix(clone: boolean = false): Matrix {
      if (this._node.hasFlags(NodeFlags.InvalidConcatenatedMatrix)) {
        var ancestor = this._node._findClosestAncestor(NodeFlags.InvalidConcatenatedMatrix, false);
        var path = Node._getAncestors(this._node, ancestor);
        var m = ancestor ? ancestor.getTransform()._concatenatedMatrix.clone() : Matrix.createIdentity();
        for (var i = path.length - 1; i >= 0; i--) {
          var ancestor = path[i];
          var ancestorTransform = ancestor.getTransform();
          release || assert (ancestor.hasFlags(NodeFlags.InvalidConcatenatedMatrix));
          m.preMultiply(ancestorTransform._matrix);
          ancestorTransform._concatenatedMatrix.set(m);
          ancestor.removeFlags(NodeFlags.InvalidConcatenatedMatrix);
        }
      }
      if (clone) {
        return this._concatenatedMatrix.clone();
      }
      return this._concatenatedMatrix;
    }


public getInvertedConcatenatedMatrix(clone: boolean = false): Matrix {
      if (this._node.hasFlags(NodeFlags.InvalidInvertedConcatenatedMatrix)) {
        this.getConcatenatedMatrix().inverse(this._invertedConcatenatedMatrix);
        this._node.removeFlags(NodeFlags.InvalidInvertedConcatenatedMatrix);
      }
      if (clone) {
        return this._invertedConcatenatedMatrix.clone();
      }
      return this._invertedConcatenatedMatrix;
    }


//    public getConcatenatedColorMatrix(clone: boolean = false): ColorMatrix {
//      // Compute the concatenated color transforms for this node and all of its ancestors.
//      if (this.hasFlags(NodeFlags.InvalidConcatenatedColorMatrix)) {
//        var ancestor = <Transform>this._findClosestAncestor(NodeFlags.InvalidConcatenatedColorMatrix, false);
//        var path = <Transform []>Node._getAncestors(this, ancestor, NodeType.Transform);
//        var m = ancestor ? ancestor._concatenatedColorMatrix.clone() : ColorMatrix.createIdentity();
//        for (var i = path.length - 1; i >= 0; i--) {
//          var ancestor = path[i];
//          release || assert (ancestor.hasFlags(NodeFlags.InvalidConcatenatedColorMatrix));
//          // TODO: Premultiply here.
//          m.multiply(ancestor._colorMatrix);
//          ancestor._concatenatedColorMatrix.set(m);
//          ancestor.removeFlags(NodeFlags.InvalidConcatenatedColorMatrix);
//        }
//      }
//      if (clone) {
//        return this._concatenatedColorMatrix.clone();
//      }
//      return this._concatenatedColorMatrix;
//    }
public toString() {
      return this._matrix.toString();
    }


public toString() {
      return BlendMode[this._blendMode];
    }


public getBounds(clone: boolean = false): Rectangle {
      var bounds = this._bounds || (this._bounds = Rectangle.createEmpty());
      if (this.hasFlags(NodeFlags.InvalidBounds)) {
        bounds.set(this._source.getBounds());
        this.removeFlags(NodeFlags.InvalidBounds);
      }
      if (clone) {
        return bounds.clone();
      }
      return bounds;
    }


_propagateFlagsDown(flags: NodeFlags) {
      this.setFlags(flags);
    }


public getChildren(clone: boolean = false): Node[] {
      return [this._source];
    }


public render() {
      throw Shumway.Debug.abstractMethod("Renderer::render");
    }


/**
     * Notify renderer that the viewport has changed.
     */
public resize() {
      throw Shumway.Debug.abstractMethod("Renderer::resize");
    }


/**
     * Captures a rectangular region of the easel as a dataURL as specified by |bounds|. |stageContent| indicates if the bounds
     * should be computed by looking at the bounds of the content of the easel rather than the easel itself.
     */
public screenShot(bounds: Rectangle, stageContent: boolean): ScreenShot {
      throw Shumway.Debug.abstractMethod("Renderer::screenShot");
    }


public setBounds(value: Rectangle) {
      super.setBounds(value);
      this._updateContentMatrix();
      this._dispatchEvent(NodeEventType.OnStageBoundsChanged);
      if (this._dirtyRegion) {
        this._dirtyRegion = new DirtyRegion(value.w, value.h);
        this._dirtyRegion.addDirtyRectangle(value);
      }
    }


/**
     * Checks to see if we should render and if so, clears any relevant dirty flags. Returns
     * true if rendering should commence. Flag clearing is made optional here in case there
     * is any code that needs to check if rendering is about to happen.
     */
readyToRender(): boolean {
      this._preVisitor.isDirty = false;
      this._preVisitor.start(this, this._dirtyRegion);
      if (this._preVisitor.isDirty) {
        return true;
      }
      return false;
    }


/**
     * Figure out what the content transform shuold be given the current align and scale modes.
     */
private _updateContentMatrix() {
      if (this._scaleMode === Stage.DEFAULT_SCALE && this._align === Stage.DEFAULT_ALIGN) {
        // Shortcut and also guard to avoid using targetWidth/targetHeight.
        // ThetargetWidth/targetHeight normally set in setScaleAndAlign call.
        this._content.getTransform().setMatrix(new Matrix(1, 0, 0, 1, 0, 0));
        return;
      }

      var bounds = this.getBounds();
      var contentBounds = this._content.getBounds();

      // Debug.assert(this.targetWidth > 0 && this.targetHeight > 0);
      var wScale = bounds.w / contentBounds.w;
      var hScale = bounds.h / contentBounds.h;
      var scaleX, scaleY;
      switch (this._scaleMode) {
        case StageScaleMode.NoBorder:
          scaleX = scaleY = Math.max(wScale, hScale);
          break;
        case StageScaleMode.NoScale:
          scaleX = scaleY = 1;
          break;
        case StageScaleMode.ExactFit:
          scaleX = wScale;
          scaleY = hScale;
          break;
        // case StageScaleMode.ShowAll:
        default:
          scaleX = scaleY = Math.min(wScale, hScale);
          break;
      }

      var offsetX;
      if ((this._align & StageAlignFlags.Left)) {
        offsetX = 0;
      } else if ((this._align & StageAlignFlags.Right)) {
        offsetX = bounds.w - contentBounds.w * scaleX;
      } else {
        offsetX = (bounds.w - contentBounds.w * scaleX) / 2;
      }

      var offsetY;
      if ((this._align & StageAlignFlags.Top)) {
        offsetY = 0;
      } else if ((this._align & StageAlignFlags.Bottom)) {
        offsetY = bounds.h - contentBounds.h * scaleY;
      } else {
        offsetY = (bounds.h - contentBounds.h * scaleY) / 2;
      }

      this._content.getTransform().setMatrix(new Matrix(scaleX, 0, 0, scaleY, offsetX, offsetY));
    }


(consentState: ConsentState) =>
	(onConsentChange as jest.Mock).mockImplementation((cb: Callback) =>
		cb(consentState),
	)


(cb: Callback) =>
		cb(consentState)


() => {
	test('tcfv2', async () => {
		const consentState: ConsentState = {
			tcfv2: tcfv2ConsentState,
			canTarget: true,
			framework: 'tcfv2',
		};
		mockOnConsentChange(consentState);
		const resolvedConsentState = await onConsent();
		expect(resolvedConsentState).toEqual(consentState);
	});
	test('ccpa', async () => {
		const consentState: ConsentState = {
			ccpa: ccpaConsentState,
			canTarget: true,
			framework: 'ccpa',
		};
		mockOnConsentChange(consentState);
		const resolvedConsentState = await onConsent();
		expect(resolvedConsentState).toEqual(consentState);
	});
	test('aus', async () => {
		const consentState: ConsentState = {
			aus: ausConsentState,
			canTarget: true,
			framework: 'aus',
		};
		mockOnConsentChange(consentState);
		const resolvedConsentState = await onConsent();
		expect(resolvedConsentState).toEqual(consentState);
	});
	test('unknown region rejects', async () => {
		mockOnConsentChange({ canTarget: false, framework: null });
		await expect(onConsent()).rejects.toEqual('Unknown framework');
	});
}


async () => {
		const consentState: ConsentState = {
			tcfv2: tcfv2ConsentState,
			canTarget: true,
			framework: 'tcfv2',
		};
		mockOnConsentChange(consentState);
		const resolvedConsentState = await onConsent();
		expect(resolvedConsentState).toEqual(consentState);
	}


async () => {
		const consentState: ConsentState = {
			ccpa: ccpaConsentState,
			canTarget: true,
			framework: 'ccpa',
		};
		mockOnConsentChange(consentState);
		const resolvedConsentState = await onConsent();
		expect(resolvedConsentState).toEqual(consentState);
	}


async () => {
		const consentState: ConsentState = {
			aus: ausConsentState,
			canTarget: true,
			framework: 'aus',
		};
		mockOnConsentChange(consentState);
		const resolvedConsentState = await onConsent();
		expect(resolvedConsentState).toEqual(consentState);
	}


async () => {
		mockOnConsentChange({ canTarget: false, framework: null });
		await expect(onConsent()).rejects.toEqual('Unknown framework');
	}


(jsonValue) => InboxForwarderDtoFromJSON(jsonValue)


(jsonValue) => PageInboxForwarderDtoFromJSON(jsonValue)


(jsonValue) => InboxForwarderTestResultFromJSON(jsonValue)


export interface CreateNewInboxForwarderRequest {
    createInboxForwarderOptions: CreateInboxForwarderOptions;
    inboxId?: string;
}


export interface DeleteInboxForwarderRequest {
    id: string;
}


export interface DeleteInboxForwardersRequest {
    inboxId?: string;
}


export interface GetInboxForwarderRequest {
    id: string;
}


export interface GetInboxForwardersRequest {
    before?: Date;
    inboxId?: string;
    page?: number;
    searchFilter?: string;
    since?: Date;
    size?: number;
    sort?: GetInboxForwardersSortEnum;
}


export interface TestInboxForwarderRequest {
    id: string;
    inboxForwarderTestOptions: InboxForwarderTestOptions;
}


export interface TestInboxForwardersForInboxRequest {
    inboxId: string;
    inboxForwarderTestOptions: InboxForwarderTestOptions;
}


export interface TestNewInboxForwarderRequest {
    testNewInboxForwarderOptions: TestNewInboxForwarderOptions;
}


/**
 * InboxForwarderControllerApi - interface
 *
 * @export
 * @interface InboxForwarderControllerApiInterface
 */
export interface InboxForwarderControllerApiInterface {
    /**
     * Create a new inbox rule for forwarding, blocking, and allowing emails when sending and receiving
     * @summary Create an inbox forwarder
     * @param {CreateInboxForwarderOptions} createInboxForwarderOptions createInboxForwarderOptions
     * @param {string} [inboxId] Inbox id to attach forwarder to
     * @param {*} [options] Override http request option.
     * @throws {RequiredError}
     * @memberof InboxForwarderControllerApiInterface
     */
    createNewInboxForwarderRaw(requestParameters: CreateNewInboxForwarderRequest): Promise<runtime.ApiResponse<InboxForwarderDto>>;

    /**
     * Create a new inbox rule for forwarding, blocking, and allowing emails when sending and receiving
     * Create an inbox forwarder
     */
    createNewInboxForwarder(requestParameters: CreateNewInboxForwarderRequest): Promise<InboxForwarderDto>;

    /**
     * Delete inbox forwarder
     * @summary Delete an inbox forwarder
     * @param {string} id ID of inbox forwarder
     * @param {*} [options] Override http request option.
     * @throws {RequiredError}
     * @memberof InboxForwarderControllerApiInterface
     */
    deleteInboxForwarderRaw(requestParameters: DeleteInboxForwarderRequest): Promise<runtime.ApiResponse<void>>;

    /**
     * Delete inbox forwarder
     * Delete an inbox forwarder
     */
    deleteInboxForwarder(requestParameters: DeleteInboxForwarderRequest): Promise<void>;

    /**
     * Delete inbox forwarders. Accepts optional inboxId filter.
     * @summary Delete inbox forwarders
     * @param {string} [inboxId] Optional inbox id to attach forwarder to
     * @param {*} [options] Override http request option.
     * @throws {RequiredError}
     * @memberof InboxForwarderControllerApiInterface
     */
    deleteInboxForwardersRaw(requestParameters: DeleteInboxForwardersRequest): Promise<runtime.ApiResponse<void>>;

    /**
     * Delete inbox forwarders. Accepts optional inboxId filter.
     * Delete inbox forwarders
     */
    deleteInboxForwarders(requestParameters: DeleteInboxForwardersRequest): Promise<void>;

    /**
     * Get inbox ruleset
     * @summary Get an inbox forwarder
     * @param {string} id ID of inbox forwarder
     * @param {*} [options] Override http request option.
     * @throws {RequiredError}
     * @memberof InboxForwarderControllerApiInterface
     */
    getInboxForwarderRaw(requestParameters: GetInboxForwarderRequest): Promise<runtime.ApiResponse<InboxForwarderDto>>;

    /**
     * Get inbox ruleset
     * Get an inbox forwarder
     */
    getInboxForwarder(requestParameters: GetInboxForwarderRequest): Promise<InboxForwarderDto>;

    /**
     * List all forwarders attached to an inbox
     * @summary List inbox forwarders
     * @param {Date} [before] Filter by created at before the given timestamp
     * @param {string} [inboxId] Optional inbox id to get forwarders from
     * @param {number} [page] Optional page index in inbox forwarder list pagination
     * @param {string} [searchFilter] Optional search filter
     * @param {Date} [since] Filter by created at after the given timestamp
     * @param {number} [size] Optional page size in inbox forwarder list pagination
     * @param {'ASC' | 'DESC'} [sort] Optional createdAt sort direction ASC or DESC
     * @param {*} [options] Override http request option.
     * @throws {RequiredError}
     * @memberof InboxForwarderControllerApiInterface
     */
    getInboxForwardersRaw(requestParameters: GetInboxForwardersRequest): Promise<runtime.ApiResponse<PageInboxForwarderDto>>;

    /**
     * List all forwarders attached to an inbox
     * List inbox forwarders
     */
    getInboxForwarders(requestParameters: GetInboxForwardersRequest): Promise<PageInboxForwarderDto>;

    /**
     * Test an inbox forwarder
     * @summary Test an inbox forwarder
     * @param {string} id ID of inbox forwarder
     * @param {InboxForwarderTestOptions} inboxForwarderTestOptions inboxForwarderTestOptions
     * @param {*} [options] Override http request option.
     * @throws {RequiredError}
     * @memberof InboxForwarderControllerApiInterface
     */
    testInboxForwarderRaw(requestParameters: TestInboxForwarderRequest): Promise<runtime.ApiResponse<InboxForwarderTestResult>>;

    /**
     * Test an inbox forwarder
     * Test an inbox forwarder
     */
    testInboxForwarder(requestParameters: TestInboxForwarderRequest): Promise<InboxForwarderTestResult>;

    /**
     * Test inbox forwarders for inbox
     * @summary Test inbox forwarders for inbox
     * @param {string} inboxId ID of inbox
     * @param {InboxForwarderTestOptions} inboxForwarderTestOptions inboxForwarderTestOptions
     * @param {*} [options] Override http request option.
     * @throws {RequiredError}
     * @memberof InboxForwarderControllerApiInterface
     */
    testInboxForwardersForInboxRaw(requestParameters: TestInboxForwardersForInboxRequest): Promise<runtime.ApiResponse<InboxForwarderTestResult>>;

    /**
     * Test inbox forwarders for inbox
     * Test inbox forwarders for inbox
     */
    testInboxForwardersForInbox(requestParameters: TestInboxForwardersForInboxRequest): Promise<InboxForwarderTestResult>;

    /**
     * Test new inbox forwarder
     * @summary Test new inbox forwarder
     * @param {TestNewInboxForwarderOptions} testNewInboxForwarderOptions testNewInboxForwarderOptions
     * @param {*} [options] Override http request option.
     * @throws {RequiredError}
     * @memberof InboxForwarderControllerApiInterface
     */
    testNewInboxForwarderRaw(requestParameters: TestNewInboxForwarderRequest): Promise<runtime.ApiResponse<InboxForwarderTestResult>>;

    /**
     * Test new inbox forwarder
     * Test new inbox forwarder
     */
    testNewInboxForwarder(requestParameters: TestNewInboxForwarderRequest): Promise<InboxForwarderTestResult>;

}


/**
    * @export
    * @enum {string}
    */
export enum GetInboxForwardersSortEnum {
    ASC = 'ASC',
    DESC = 'DESC'
}


/**
     * Create a new inbox rule for forwarding, blocking, and allowing emails when sending and receiving
     * Create an inbox forwarder
     */
async createNewInboxForwarderRaw(requestParameters: CreateNewInboxForwarderRequest): Promise<runtime.ApiResponse<InboxForwarderDto>> {
        if (requestParameters.createInboxForwarderOptions === null || requestParameters.createInboxForwarderOptions === undefined) {
            throw new runtime.RequiredError('createInboxForwarderOptions','Required parameter requestParameters.createInboxForwarderOptions was null or undefined when calling createNewInboxForwarder.');
        }

        const queryParameters: runtime.HTTPQuery = {};

        if (requestParameters.inboxId !== undefined) {
            queryParameters['inboxId'] = requestParameters.inboxId;
        }

        const headerParameters: runtime.HTTPHeaders = {};

        headerParameters['Content-Type'] = 'application/json';

        if (this.configuration && this.configuration.apiKey) {
            headerParameters["x-api-key"] = this.configuration.apiKey("x-api-key"); // API_KEY authentication
        }

        const response = await this.request({
            path: `/forwarders`,
            method: 'POST',
            headers: headerParameters,
            query: queryParameters,
            body: CreateInboxForwarderOptionsToJSON(requestParameters.createInboxForwarderOptions),
        });

        return new runtime.JSONApiResponse(response, (jsonValue) => InboxForwarderDtoFromJSON(jsonValue));
    }


/**
     * Create a new inbox rule for forwarding, blocking, and allowing emails when sending and receiving
     * Create an inbox forwarder
     */
async createNewInboxForwarder(requestParameters: CreateNewInboxForwarderRequest): Promise<InboxForwarderDto> {
        const response = await this.createNewInboxForwarderRaw(requestParameters);
        return await response.value();
    }


/**
     * Delete inbox forwarder
     * Delete an inbox forwarder
     */
async deleteInboxForwarderRaw(requestParameters: DeleteInboxForwarderRequest): Promise<runtime.ApiResponse<void>> {
        if (requestParameters.id === null || requestParameters.id === undefined) {
            throw new runtime.RequiredError('id','Required parameter requestParameters.id was null or undefined when calling deleteInboxForwarder.');
        }

        const queryParameters: runtime.HTTPQuery = {};

        const headerParameters: runtime.HTTPHeaders = {};

        if (this.configuration && this.configuration.apiKey) {
            headerParameters["x-api-key"] = this.configuration.apiKey("x-api-key"); // API_KEY authentication
        }

        const response = await this.request({
            path: `/forwarders/{id}`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters.id))),
            method: 'DELETE',
            headers: headerParameters,
            query: queryParameters,
        });

        return new runtime.VoidApiResponse(response);
    }


/**
     * Delete inbox forwarder
     * Delete an inbox forwarder
     */
async deleteInboxForwarder(requestParameters: DeleteInboxForwarderRequest): Promise<void> {
        await this.deleteInboxForwarderRaw(requestParameters);
    }


/**
     * Delete inbox forwarders. Accepts optional inboxId filter.
     * Delete inbox forwarders
     */
async deleteInboxForwardersRaw(requestParameters: DeleteInboxForwardersRequest): Promise<runtime.ApiResponse<void>> {
        const queryParameters: runtime.HTTPQuery = {};

        if (requestParameters.inboxId !== undefined) {
            queryParameters['inboxId'] = requestParameters.inboxId;
        }

        const headerParameters: runtime.HTTPHeaders = {};

        if (this.configuration && this.configuration.apiKey) {
            headerParameters["x-api-key"] = this.configuration.apiKey("x-api-key"); // API_KEY authentication
        }

        const response = await this.request({
            path: `/forwarders`,
            method: 'DELETE',
            headers: headerParameters,
            query: queryParameters,
        });

        return new runtime.VoidApiResponse(response);
    }


/**
     * Delete inbox forwarders. Accepts optional inboxId filter.
     * Delete inbox forwarders
     */
async deleteInboxForwarders(requestParameters: DeleteInboxForwardersRequest): Promise<void> {
        await this.deleteInboxForwardersRaw(requestParameters);
    }


/**
     * Get inbox ruleset
     * Get an inbox forwarder
     */
async getInboxForwarderRaw(requestParameters: GetInboxForwarderRequest): Promise<runtime.ApiResponse<InboxForwarderDto>> {
        if (requestParameters.id === null || requestParameters.id === undefined) {
            throw new runtime.RequiredError('id','Required parameter requestParameters.id was null or undefined when calling getInboxForwarder.');
        }

        const queryParameters: runtime.HTTPQuery = {};

        const headerParameters: runtime.HTTPHeaders = {};

        if (this.configuration && this.configuration.apiKey) {
            headerParameters["x-api-key"] = this.configuration.apiKey("x-api-key"); // API_KEY authentication
        }

        const response = await this.request({
            path: `/forwarders/{id}`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters.id))),
            method: 'GET',
            headers: headerParameters,
            query: queryParameters,
        });

        return new runtime.JSONApiResponse(response, (jsonValue) => InboxForwarderDtoFromJSON(jsonValue));
    }


/**
     * Get inbox ruleset
     * Get an inbox forwarder
     */
async getInboxForwarder(requestParameters: GetInboxForwarderRequest): Promise<InboxForwarderDto> {
        const response = await this.getInboxForwarderRaw(requestParameters);
        return await response.value();
    }


/**
     * List all forwarders attached to an inbox
     * List inbox forwarders
     */
async getInboxForwardersRaw(requestParameters: GetInboxForwardersRequest): Promise<runtime.ApiResponse<PageInboxForwarderDto>> {
        const queryParameters: runtime.HTTPQuery = {};

        if (requestParameters.before !== undefined) {
            queryParameters['before'] = (requestParameters.before as any).toISOString();
        }

        if (requestParameters.inboxId !== undefined) {
            queryParameters['inboxId'] = requestParameters.inboxId;
        }

        if (requestParameters.page !== undefined) {
            queryParameters['page'] = requestParameters.page;
        }

        if (requestParameters.searchFilter !== undefined) {
            queryParameters['searchFilter'] = requestParameters.searchFilter;
        }

        if (requestParameters.since !== undefined) {
            queryParameters['since'] = (requestParameters.since as any).toISOString();
        }

        if (requestParameters.size !== undefined) {
            queryParameters['size'] = requestParameters.size;
        }

        if (requestParameters.sort !== undefined) {
            queryParameters['sort'] = requestParameters.sort;
        }

        const headerParameters: runtime.HTTPHeaders = {};

        if (this.configuration && this.configuration.apiKey) {
            headerParameters["x-api-key"] = this.configuration.apiKey("x-api-key"); // API_KEY authentication
        }

        const response = await this.request({
            path: `/forwarders`,
            method: 'GET',
            headers: headerParameters,
            query: queryParameters,
        });

        return new runtime.JSONApiResponse(response, (jsonValue) => PageInboxForwarderDtoFromJSON(jsonValue));
    }


/**
     * List all forwarders attached to an inbox
     * List inbox forwarders
     */
async getInboxForwarders(requestParameters: GetInboxForwardersRequest): Promise<PageInboxForwarderDto> {
        const response = await this.getInboxForwardersRaw(requestParameters);
        return await response.value();
    }


/**
     * Test an inbox forwarder
     * Test an inbox forwarder
     */
async testInboxForwarderRaw(requestParameters: TestInboxForwarderRequest): Promise<runtime.ApiResponse<InboxForwarderTestResult>> {
        if (requestParameters.id === null || requestParameters.id === undefined) {
            throw new runtime.RequiredError('id','Required parameter requestParameters.id was null or undefined when calling testInboxForwarder.');
        }

        if (requestParameters.inboxForwarderTestOptions === null || requestParameters.inboxForwarderTestOptions === undefined) {
            throw new runtime.RequiredError('inboxForwarderTestOptions','Required parameter requestParameters.inboxForwarderTestOptions was null or undefined when calling testInboxForwarder.');
        }

        const queryParameters: runtime.HTTPQuery = {};

        const headerParameters: runtime.HTTPHeaders = {};

        headerParameters['Content-Type'] = 'application/json';

        if (this.configuration && this.configuration.apiKey) {
            headerParameters["x-api-key"] = this.configuration.apiKey("x-api-key"); // API_KEY authentication
        }

        const response = await this.request({
            path: `/forwarders/{id}/test`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters.id))),
            method: 'POST',
            headers: headerParameters,
            query: queryParameters,
            body: InboxForwarderTestOptionsToJSON(requestParameters.inboxForwarderTestOptions),
        });

        return new runtime.JSONApiResponse(response, (jsonValue) => InboxForwarderTestResultFromJSON(jsonValue));
    }


/**
     * Test an inbox forwarder
     * Test an inbox forwarder
     */
async testInboxForwarder(requestParameters: TestInboxForwarderRequest): Promise<InboxForwarderTestResult> {
        const response = await this.testInboxForwarderRaw(requestParameters);
        return await response.value();
    }


/**
     * Test inbox forwarders for inbox
     * Test inbox forwarders for inbox
     */
async testInboxForwardersForInboxRaw(requestParameters: TestInboxForwardersForInboxRequest): Promise<runtime.ApiResponse<InboxForwarderTestResult>> {
        if (requestParameters.inboxId === null || requestParameters.inboxId === undefined) {
            throw new runtime.RequiredError('inboxId','Required parameter requestParameters.inboxId was null or undefined when calling testInboxForwardersForInbox.');
        }

        if (requestParameters.inboxForwarderTestOptions === null || requestParameters.inboxForwarderTestOptions === undefined) {
            throw new runtime.RequiredError('inboxForwarderTestOptions','Required parameter requestParameters.inboxForwarderTestOptions was null or undefined when calling testInboxForwardersForInbox.');
        }

        const queryParameters: runtime.HTTPQuery = {};

        if (requestParameters.inboxId !== undefined) {
            queryParameters['inboxId'] = requestParameters.inboxId;
        }

        const headerParameters: runtime.HTTPHeaders = {};

        headerParameters['Content-Type'] = 'application/json';

        if (this.configuration && this.configuration.apiKey) {
            headerParameters["x-api-key"] = this.configuration.apiKey("x-api-key"); // API_KEY authentication
        }

        const response = await this.request({
            path: `/forwarders`,
            method: 'PUT',
            headers: headerParameters,
            query: queryParameters,
            body: InboxForwarderTestOptionsToJSON(requestParameters.inboxForwarderTestOptions),
        });

        return new runtime.JSONApiResponse(response, (jsonValue) => InboxForwarderTestResultFromJSON(jsonValue));
    }


/**
     * Test inbox forwarders for inbox
     * Test inbox forwarders for inbox
     */
async testInboxForwardersForInbox(requestParameters: TestInboxForwardersForInboxRequest): Promise<InboxForwarderTestResult> {
        const response = await this.testInboxForwardersForInboxRaw(requestParameters);
        return await response.value();
    }


/**
     * Test new inbox forwarder
     * Test new inbox forwarder
     */
async testNewInboxForwarderRaw(requestParameters: TestNewInboxForwarderRequest): Promise<runtime.ApiResponse<InboxForwarderTestResult>> {
        if (requestParameters.testNewInboxForwarderOptions === null || requestParameters.testNewInboxForwarderOptions === undefined) {
            throw new runtime.RequiredError('testNewInboxForwarderOptions','Required parameter requestParameters.testNewInboxForwarderOptions was null or undefined when calling testNewInboxForwarder.');
        }

        const queryParameters: runtime.HTTPQuery = {};

        const headerParameters: runtime.HTTPHeaders = {};

        headerParameters['Content-Type'] = 'application/json';

        if (this.configuration && this.configuration.apiKey) {
            headerParameters["x-api-key"] = this.configuration.apiKey("x-api-key"); // API_KEY authentication
        }

        const response = await this.request({
            path: `/forwarders`,
            method: 'PATCH',
            headers: headerParameters,
            query: queryParameters,
            body: TestNewInboxForwarderOptionsToJSON(requestParameters.testNewInboxForwarderOptions),
        });

        return new runtime.JSONApiResponse(response, (jsonValue) => InboxForwarderTestResultFromJSON(jsonValue));
    }


/**
     * Test new inbox forwarder
     * Test new inbox forwarder
     */
async testNewInboxForwarder(requestParameters: TestNewInboxForwarderRequest): Promise<InboxForwarderTestResult> {
        const response = await this.testNewInboxForwarderRaw(requestParameters);
        return await response.value();
    }


/**
 * A block of data, to simply increase gRPC message size.
 */
export interface Payload {
  /**
   * The type of data in body.
   */
  'type'?: (_grpc_testing_PayloadType | keyof typeof _grpc_testing_PayloadType);
  /**
   * Primary contents of payload.
   */
  'body'?: (Buffer | Uint8Array | string);
}


/**
 * A block of data, to simply increase gRPC message size.
 */
export interface Payload__Output {
  /**
   * The type of data in body.
   */
  'type': (keyof typeof _grpc_testing_PayloadType);
  /**
   * Primary contents of payload.
   */
  'body': (Buffer);
}


({ classes }: WithStyles<ExecutionPartDetailsClasses>) => {
  const executionPartsStore = useStore("executionParts");

  const part = executionPartsStore.selectedPart;

  const endTimeString = part?.endTime
    ? `${part?.endTime?.toDateString()} ${part?.endTime?.toLocaleTimeString()}`
    : undefined;

  return useObserver(() =>
    <div className={classes.root}>
      <SectionHeader onClose={()


() =>
    <div className


theme => createStyles({
    root: {
      display: "flex",
      flexDirection: "column"
    },
    title: {},
    detailsContainer: {
      padding: "0 15px",
      display: "grid",
      gridTemplateColumns: "30% auto",
      gridTemplateRows: "auto",
      gridGap: "10px"
    },
    emptyRow: {
      height: "16px",
      gridColumn: "1 / span 2"
    }
  })


type ExecutionPartDetailsClasses = "root" | "title" | "detailsContainer" | "emptyRow";


export class ArcaneCircle extends RaidBuffWindow {
	static override handle = 'arcaneCircle'
	static override title = t('rpr.arcanecircle.title')`Arcane Circle`
	static override displayOrder = DISPLAY_ORDER.ARCANE_CIRCLE

	override buffStatus = this.data.statuses.ARCANE_CIRCLE

	override initialise() {
		super.initialise()

		this.addEvaluator(new ExpectedActionsEvaluator({
			expectedActions: [
				{
					action: this.data.actions.COMMUNIO,
					expectedPerWindow: 2,
				},
				{
					action: this.data.actions.PLENTIFUL_HARVEST,
					expectedPerWindow: 1,
				},
			],
			suggestionIcon: this.data.actions.ARCANE_CIRCLE.icon,
			suggestionContent: <Trans id="rpr.arcanecircle.suggestions.content">
				Each <ActionLink action="ARCANE_CIRCLE"/> window should contain 2 uses of <ActionLink action="COMMUNIO"/>
				and 1 use of <ActionLink action="PLENTIFUL_HARVEST"/>. In your opener, only 1 <ActionLink showIcon={false} action="COMMUNIO"/> is expected.
			</Trans>,
			suggestionWindowName: <ActionLink action="ARCANE_CIRCLE" showIcon={false} />,
			severityTiers: SEVERITIES,
			adjustCount: this.adjustCount.bind(this),
		}))
	}


override initialise() {
		super.initialise()

		this.addEvaluator(new ExpectedActionsEvaluator({
			expectedActions: [
				{
					action: this.data.actions.COMMUNIO,
					expectedPerWindow: 2,
				},
				{
					action: this.data.actions.PLENTIFUL_HARVEST,
					expectedPerWindow: 1,
				},
			],
			suggestionIcon: this.data.actions.ARCANE_CIRCLE.icon,
			suggestionContent: <Trans id="rpr.arcanecircle.suggestions.content">
				Each <ActionLink action="ARCANE_CIRCLE"/> window should contain 2 uses of <ActionLink action="COMMUNIO"/>
				and 1 use of <ActionLink action="PLENTIFUL_HARVEST"/>. In your opener, only 1 <ActionLink showIcon={false} action="COMMUNIO"/> is expected.
			</Trans>,
			suggestionWindowName: <ActionLink action="ARCANE_CIRCLE" showIcon={false} />,
			severityTiers: SEVERITIES,
			adjustCount: this.adjustCount.bind(this),
		}


of <ActionLink action="COMMUNIO"


(
  map: Map<TaskId, Set<TaskArgs>>
): WorkspaceStateTasks => {
  return Array.from(map.keys()).reduce(
    (workspaceStateTasks: WorkspaceStateTasks, key: string) => {
      workspaceStateTasks[key] = Array.from(map.get(key)!.values());
      return workspaceStateTasks;
    },
    {}
  );
}


(workspaceStateTasks: WorkspaceStateTasks, key: string) => {
      workspaceStateTasks[key] = Array.from(map.get(key)!.values());
      return workspaceStateTasks;
    }


(taskId: TaskId) => {
      this.setItem(taskId, new Set(pinnedTasks[taskId]), false);
    }


export class PinnedTasksStore extends TaskStore {
  constructor(private readonly context: vscode.ExtensionContext) {
    super();
    const pinnedTasks = this.context.workspaceState.get(
      'pinnedTasks',
      {}
    ) as WorkspaceStateTasks;
    if (
      !pinnedTasks ||
      Array.isArray(pinnedTasks) ||
      typeof pinnedTasks !== 'object'
    ) {
      return;
    }
    Object.keys(pinnedTasks).forEach((taskId: TaskId) => {
      this.setItem(taskId, new Set(pinnedTasks[taskId]), false);
    });
  }

  public fireOnDidChange(): void {
    const workspaceStateTasks: WorkspaceStateTasks = toWorkspaceStateTasks(
      this.getData()
    );
    this.context.workspaceState.update('pinnedTasks', workspaceStateTasks);
    super.fireOnDidChange(null);
  }
}


interface WorkspaceStateTasks {
  [key: string]: TaskArgs[];
}


public fireOnDidChange(): void {
    const workspaceStateTasks: WorkspaceStateTasks = toWorkspaceStateTasks(
      this.getData()
    );
    this.context.workspaceState.update('pinnedTasks', workspaceStateTasks);
    super.fireOnDidChange(null);
  }


data => {
          this.projects = data;
          this.adminService.projects=data;
          console.log(data);
        }


error => {
          this.errorMessage = error.message;
          console.error('There was an error!', error);
        }


({ matches }) => {
      if (matches) {
        return {
          columns :1
        };
      }
      return {
        columns:4,
      };
    }


@Component({
  selector: 'app-admin-projects',
  templateUrl: './admin-projects.component.html',
  styleUrls: ['./admin-projects.component.css']
})
export class AdminProjectsComponent   {
  projects:any[]= [];
  errorMessage: any;

  constructor(private adminService:AdminService,private loginService: LoginService,private breakpointObserver: BreakpointObserver){}

  ngOnInit() {
    this.fetchProjectList();
  }

  fetchProjectList() {
       this.adminService.getProjectList(this.loginService.currentUserValue.courseName).subscribe({
        next: data => {
          this.projects = data;
          this.adminService.projects=data;
          console.log(data);
        },
       error: error => {
          this.errorMessage = error.message;
          console.error('There was an error!', error);
        }
    })
  }

  cards = this.breakpointObserver.observe(Breakpoints.Handset).pipe(
    map(({ matches }) => {
      if (matches) {
        return {
          columns :1
        };
      }
      return {
        columns:4,
      };
    })
  );

}


ngOnInit() {
    this.fetchProjectList();
  }


fetchProjectList() {
       this.adminService.getProjectList(this.loginService.currentUserValue.courseName).subscribe({
        next: data => {
          this.projects = data;
          this.adminService.projects=data;
          console.log(data);
        },
       error: error => {
          this.errorMessage = error.message;
          console.error('There was an error!', error);
        }
    })
  }


(prev: number, curr: Vector) => {
      return Math.min(prev, curr.x);
    }


(prev: number, curr: Vector) => {
      return Math.max(prev, curr.x);
    }


(prev: number, curr: Vector) => {
      return Math.min(prev, curr.y);
    }


(prev: number, curr: Vector) => {
      return Math.max(prev, curr.y);
    }


/**
 * Creates a closed polygon drawing given a list of [[Vector]]s.
 *
 * @deprecated Use [[Polygon]]
 * @warning Use sparingly as Polygons are performance intensive
 */
@obsolete({
  message: 'Polygon will be removed in v0.26.0',
  alternateMethod: 'Use Graphics.Polygon'
})
export class Polygon implements Drawable {
  public flipVertical: boolean;
  public flipHorizontal: boolean;
  public drawWidth: number;
  public drawHeight: number;

  public width: number;
  public height: number;

  /**
   * The color to use for the lines of the polygon
   */
  public lineColor: Color;
  /**
   * The color to use for the interior of the polygon
   */
  public fillColor: Color;
  /**
   * The width of the lines of the polygon
   */
  public lineWidth: number = 5;
  /**
   * Indicates whether the polygon is filled or not.
   */
  public filled: boolean = false;

  private _points: Vector[] = [];
  public anchor = Vector.Zero;
  public offset = Vector.Zero;
  public rotation: number = 0;
  public scale = Vector.One;
  public opacity: number = 1;

  /**
   * @param points  The vectors to use to build the polygon in order
   */
  constructor(points: Vector[]) {
    this._points = points;

    const minX = this._points.reduce((prev: number, curr: Vector) => {
      return Math.min(prev, curr.x);
    }, 0);
    const maxX = this._points.reduce((prev: number, curr: Vector) => {
      return Math.max(prev, curr.x);
    }, 0);

    this.drawWidth = maxX - minX;

    const minY = this._points.reduce((prev: number, curr: Vector) => {
      return Math.min(prev, curr.y);
    }, 0);
    const maxY = this._points.reduce((prev: number, curr: Vector) => {
      return Math.max(prev, curr.y);
    }, 0);

    this.drawHeight = maxY - minY;

    this.height = this.drawHeight;
    this.width = this.drawWidth;
  }

  /**
   * @notimplemented Effects are not supported on `Polygon`
   */
  public addEffect() {
    // not supported on polygons
  }
  /**
   * @notimplemented Effects are not supported on `Polygon`
   */
  public removeEffect(index: number): void;
  /**
   * @notimplemented Effects are not supported on `Polygon`
   */
  public removeEffect(effect: Effects.SpriteEffect): void;
  /**
   * @notimplemented Effects are not supported on `Polygon`
   */
  public removeEffect() {
    // not supported on polygons
  }

  /**
   * @notimplemented Effects are not supported on `Polygon`
   */
  public clearEffects() {
    // not supported on polygons
  }

  public reset() {
    //pass
  }

  /**
   * Draws the sprite appropriately to the 2D rendering context, at an x and y coordinate.
   * @param ctx  The 2D rendering context
   * @param x    The x coordinate of where to draw
   * @param y    The y coordinate of where to draw
   */
  public draw(ctx: CanvasRenderingContext2D, x: number, y: number): void;
  /**
   * Draws the sprite with custom options to override internals without mutating them.
   * @param options
   */
  public draw(options: DrawOptions): void;
  public draw(ctxOrOptions: CanvasRenderingContext2D | DrawOptions, x?: number, y?: number): void {
    if (ctxOrOptions instanceof CanvasRenderingContext2D) {
      this._drawWithOptions({ ctx: ctxOrOptions, x, y });
    } else {
      this._drawWithOptions(ctxOrOptions);
    }
  }

  private _drawWithOptions(options: DrawOptions) {
    const { ctx, x, y, rotation, drawWidth, drawHeight, anchor, offset, opacity, flipHorizontal, flipVertical } = {
      ...options,
      rotation: options.rotation ?? this.rotation,
      drawWidth: options.drawWidth ?? this.drawWidth,
      drawHeight: options.drawHeight ?? this.drawHeight,
      flipHorizontal: options.flipHorizontal ?? this.flipHorizontal,
      flipVertical: options.flipVertical ?? this.flipVertical,
      anchor: options.anchor ?? this.anchor,
      offset: options.offset ?? this.offset,
      opacity: (options.opacity ?? 1) * (this.opacity ?? 1)
    };

    const xpoint = drawWidth * anchor.x + offset.x + x;
    const ypoint = drawHeight * anchor.y + offset.y + y;

    ctx.save();
    ctx.translate(xpoint, ypoint);
    ctx.scale(this.scale.x, this.scale.y);
    ctx.rotate(rotation);
    ctx.beginPath();
    ctx.lineWidth = this.lineWidth;

    // Iterate through the supplied points and construct a 'polygon'
    const firstPoint = this._points[0];
    ctx.moveTo(firstPoint.x, firstPoint.y);

    let i = 0;
    const len = this._points.length;

    for (i; i < len; i++) {
      ctx.lineTo(this._points[i].x, this._points[i].y);
    }

    ctx.lineTo(firstPoint.x, firstPoint.y);
    ctx.closePath();

    if (this.filled) {
      ctx.fillStyle = this.fillColor.toString();
      ctx.fill();
    }

    ctx.strokeStyle = this.lineColor.toString();

    if (flipHorizontal) {
      ctx.translate(drawWidth, 0);
      ctx.scale(-1, 1);
    }

    if (flipVertical) {
      ctx.translate(0, drawHeight);
      ctx.scale(1, -1);
    }

    const oldAlpha = ctx.globalAlpha;
    ctx.globalAlpha = opacity;
    ctx.stroke();
    ctx.globalAlpha = oldAlpha;

    ctx.restore();
  }
}


/**
   * @notimplemented Effects are not supported on `Polygon`
   */
public addEffect() {
    // not supported on polygons
  }


/**
   * @notimplemented Effects are not supported on `Polygon`
   */
public removeEffect(index: number): void;


/**
   * @notimplemented Effects are not supported on `Polygon`
   */
public removeEffect(effect: Effects.SpriteEffect): void;


/**
   * @notimplemented Effects are not supported on `Polygon`
   */
public removeEffect() {
    // not supported on polygons
  }


/**
   * @notimplemented Effects are not supported on `Polygon`
   */
public clearEffects() {
    // not supported on polygons
  }


public reset() {
    //pass
  }


/**
   * Draws the sprite appropriately to the 2D rendering context, at an x and y coordinate.
   * @param ctx  The 2D rendering context
   * @param x    The x coordinate of where to draw
   * @param y    The y coordinate of where to draw
   */
public draw(ctx: CanvasRenderingContext2D, x: number, y: number): void;


/**
   * Draws the sprite with custom options to override internals without mutating them.
   * @param options
   */
public draw(options: DrawOptions): void;


public draw(ctxOrOptions: CanvasRenderingContext2D | DrawOptions, x?: number, y?: number): void {
    if (ctxOrOptions instanceof CanvasRenderingContext2D) {
      this._drawWithOptions({ ctx: ctxOrOptions, x, y });
    } else {
      this._drawWithOptions(ctxOrOptions);
    }
  }


private _drawWithOptions(options: DrawOptions) {
    const { ctx, x, y, rotation, drawWidth, drawHeight, anchor, offset, opacity, flipHorizontal, flipVertical } = {
      ...options,
      rotation: options.rotation ?? this.rotation,
      drawWidth: options.drawWidth ?? this.drawWidth,
      drawHeight: options.drawHeight ?? this.drawHeight,
      flipHorizontal: options.flipHorizontal ?? this.flipHorizontal,
      flipVertical: options.flipVertical ?? this.flipVertical,
      anchor: options.anchor ?? this.anchor,
      offset: options.offset ?? this.offset,
      opacity: (options.opacity ?? 1) * (this.opacity ?? 1)
    };

    const xpoint = drawWidth * anchor.x + offset.x + x;
    const ypoint = drawHeight * anchor.y + offset.y + y;

    ctx.save();
    ctx.translate(xpoint, ypoint);
    ctx.scale(this.scale.x, this.scale.y);
    ctx.rotate(rotation);
    ctx.beginPath();
    ctx.lineWidth = this.lineWidth;

    // Iterate through the supplied points and construct a 'polygon'
    const firstPoint = this._points[0];
    ctx.moveTo(firstPoint.x, firstPoint.y);

    let i = 0;
    const len = this._points.length;

    for (i; i < len; i++) {
      ctx.lineTo(this._points[i].x, this._points[i].y);
    }

    ctx.lineTo(firstPoint.x, firstPoint.y);
    ctx.closePath();

    if (this.filled) {
      ctx.fillStyle = this.fillColor.toString();
      ctx.fill();
    }

    ctx.strokeStyle = this.lineColor.toString();

    if (flipHorizontal) {
      ctx.translate(drawWidth, 0);
      ctx.scale(-1, 1);
    }

    if (flipVertical) {
      ctx.translate(0, drawHeight);
      ctx.scale(1, -1);
    }

    const oldAlpha = ctx.globalAlpha;
    ctx.globalAlpha = opacity;
    ctx.stroke();
    ctx.globalAlpha = oldAlpha;

    ctx.restore();
  }


({
  socketTransactions,
  pendingSocketTransactions,
}) => {
  const { t } = useTranslation();
  const [knownExchangesList, setKnownExchangesList] = React.useState<
    undefined | string[]
  >();
  const { account } = React.useContext(AccountInfoContext);
  const {
    pending: { blocks = {} } = {},
    isLoading: isAccountHistoryLoading,
  } = usePending(
    typeof knownExchangesList?.length === "number" ? account : "",
    {
      count: String(MAX_PENDING_TRANSACTIONS),
      sorting: true,
      source: true,
      threshold: knownExchangesList?.includes(account)
        ? PENDING_MIN_EXCHANGE_THRESHOLD
        : PENDING_MIN_THRESHOLD,
      include_only_confirmed: false,
    },
  );
  const {
    knownExchangeAccounts,
    isLoading: isKnownAccountsLoading,
  } = React.useContext(KnownAccountsContext);

  React.useEffect(() => {
    if (!isKnownAccountsLoading) {
      setKnownExchangesList(
        knownExchangeAccounts.map(({ account }) => account),
      );
    }
  }, [knownExchangeAccounts, isKnownAccountsLoading]);

  const [hashes, setHashes] = React.useState<string[]>([]);
  const { blocks: blocksInfo } = useBlocksInfo(hashes);

  const totalPending = Object.keys(blocks).length;
  const isPaginated = true;
  const showPaginate = totalPending > TRANSACTIONS_PER_PAGE;
  const [currentPage, setCurrentPage] = React.useState<number>(1);

  let pendingHistory: PendingHistoryBlock[] | undefined = undefined;

  if (totalPending) {
    pendingHistory = Object.entries(blocks).map(
      // @ts-ignore
      ([block, { amount, source }]): PendingHistoryBlock => ({
        hash: block,
        // @NOTE "hack" to easily filter out socketTransactions
        // @ts-ignore
        link: block,
        amount,
        local_timestamp: blocksInfo.blocks?.[block]?.local_timestamp,
        confirmed: toBoolean(blocksInfo.blocks?.[block]?.confirmed),
        account: source,
        subtype: "pending",
      }),
    );

    pendingHistory = differenceBy(pendingHistory, socketTransactions, "link");
  }

  const start = 0 + (currentPage - 1) * TRANSACTIONS_PER_PAGE;
  let data = pendingHistory?.slice(start, start + TRANSACTIONS_PER_PAGE) || [];
  if (currentPage === 1) {
    // @ts-ignore
    data = pendingSocketTransactions.concat(data);
  }

  React.useEffect(() => {
    const hashes = Object.keys(blocks);
    if (!hashes.length) return;
    setHashes(hashes?.slice(start, start + TRANSACTIONS_PER_PAGE));
  }, [blocks, start]);

  // const accountPending = accountInfo?.pending
  //   ? new BigNumber(rawToRai(accountInfo?.pending)).toNumber()
  //   : 0;

  // return accountPending > PENDING_MIN_THRESHOLD ? (

  let count = (pendingHistory?.length || 0) + pendingSocketTransactions.length;

  return count ? (
    <>
      <div
        style={{
          display: "flex",
          alignItems: "baseline",
        }}


() => {
    if (!isKnownAccountsLoading) {
      setKnownExchangesList(
        knownExchangeAccounts.map(({ account }) => account),
      );
    }
  }


({ account }) => account


// @ts-ignore
([block, { amount, source }]): PendingHistoryBlock => ({
        hash: block,
        // @NOTE "hack" to easily filter out socketTransactions
        // @ts-ignore
        link: block,
        amount,
        local_timestamp: blocksInfo.blocks?.[block]?.local_timestamp,
        confirmed: toBoolean(blocksInfo.blocks?.[block]?.confirmed),
        account: source,
        subtype: "pending",
      })


() => {
    const hashes = Object.keys(blocks);
    if (!hashes.length) return;
    setHashes(hashes?.slice(start, start + TRANSACTIONS_PER_PAGE));
  }


interface PendingHistoryBlock extends PendingBlock {
  hash: string;
  account: string;
  subtype: Subtype;
  confirmed: boolean;
  local_timestamp: String;
}


interface Props {
  socketTransactions: Transaction[];
  pendingSocketTransactions: Transaction[];
}


t("tooltips.pendingTransaction")


t("tooltips.pendingTransactionCount", {
              count: MAX_PENDING_TRANSACTIONS,
            })


/**
     * Tests an optional parameter on a method
     */
export class MethodParamOptional {
        /**
         * method bar
         * @param x bar param x
         */
        bar(x?: string): void;
        /**
         * method foo
         * @param x foo param x
         */
        foo(x?: number): void;
    }


/**
         * method bar
         * @param x bar param x
         */
bar(x?: string): void;


/**
         * method foo
         * @param x foo param x
         */
foo(x?: number): void;


@RouteConfig([
  {
    path: '/heroes',
    name: 'Heroes',
    component: HeroesComponent
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: DashboardComponent,
    useAsDefault: true
  },
  {
    path: '/detail/:id',
    name: 'HeroDetail',
    component: HeroDetailComponent
  },

])

@Component({
  selector: 'my-app',
  template: `

{{title}}

    Dashboard
    Heroes

    <router-outlet></router-outlet>
  `,
  styleUrls: ['app/app.component.css'],
  directives: [ROUTER_DIRECTIVES],
  providers: [
    ROUTER_PROVIDERS,
    HeroService
  ]
})
export class AppComponent {
  title = 'Tour of Heroes';
}


() =>
        handleComponent === null ? 0 : handleHeight || DEFAULT_HANDLE_HEIGHT


() => _providedContainerHeight || containerHeight || WINDOW_HEIGHT


() => _providedContainerHeight === undefined


() =>
        _providedHandleHeight === undefined &&
        handleComponent !== undefined &&
        handleComponent !== null


() => {
        return didSetHandleHeight.current && didSetContainerHeight.current;
      }


() =>
        safeContainerHeight -
        snapPoints[snapPoints.length - 1] -
        safeHandleHeight


() => {
      return currentIndexRef.current < 0 || animateOnMount
        ? safeContainerHeight
        : snapPoints[currentIndexRef.current];
    }


(fromIndex: number, toIndex: number) => {
        if (_providedOnAnimate) {
          _providedOnAnimate(fromIndex, toIndex);
        }
      }


() => {
      const adjustedSnapPoints = snapPoints.slice().reverse();
      const adjustedSnapPointsIndexes = snapPoints
        .slice()
        .map((_, index) => index)
        .reverse();

      /**
       * this been added to resolve issues when provide
       * one snap point.
       */
      if (snapPoints.length === 1) {
        adjustedSnapPoints.push(safeContainerHeight);
        adjustedSnapPointsIndexes.push(-1);
      }

      return cond(
        animatedIsLayoutReady,
        interpolate(position, {
          inputRange: adjustedSnapPoints,
          outputRange: adjustedSnapPointsIndexes,
          extrapolate: Extrapolate.CLAMP,
        }),
        0
      );
    }


(_, index) => index


() =>
        cond(
          animatedIsLayoutReady,
          abs(sub(safeContainerHeight, position)),
          safeContainerHeight
        )


() =>
        cond(
          greaterThan(position, snapPoints[snapPoints.length - 1]),
          0.001,
          NORMAL_DECELERATION_RATE
        )


(height: number) => {
      // console.log('BottomSheet', 'handleOnContainerMeasureHeight', height);
      didSetContainerHeight.current = true;
      setContainerHeight(height);
    }


(height: number) => {
      // console.log('BottomSheet', 'handleOnHandleMeasureHeight', height);
      didSetHandleHeight.current = true;
      setHandleHeight(height);
    }


() => {
      const currentPositionIndex = Math.max(currentIndexRef.current, 0);
      if (containerTapGestureRef.current) {
        // @ts-ignore
        containerTapGestureRef.current.setNativeProps({
          maxDeltaY: Math.abs(
            snapPoints[snapPoints.length - 1] - snapPoints[currentPositionIndex]
          ),
        });
      }

      if (currentPositionIndex === snapPoints.length - 1) {
        flashScrollableIndicators();
      }
    }


(index: number) => {
      if (_providedOnChange) {
        _providedOnChange(index);
      }

      if (isClosing.current && (index === 0 || index === -1)) {
        isClosing.current = false;
      }
    }


(scrollableRef: ScrollableRef) => {
        setScrollableRef(scrollableRef);
        refreshUIElements();
      }


(index: number, force: boolean = false) => {
        invariant(
          index >= 0 && index <= snapPoints.length - 1,
          `'index' was provided but out of the provided snap points range! expected value to be between -1, ${
            snapPoints.length - 1
          }`
        );
        if (isClosing.current && !force) {
          return;
        }
        manualSnapToPoint.setValue(snapPoints[index]);
      }


() => {
      const currentIndexValue = currentIndexRef.current;
      if (currentIndexValue === -1 || isClosing.current) {
        return;
      }
      isClosing.current = true;
      manualSnapToPoint.setValue(safeContainerHeight);
    }


() => {
      if (isClosing.current) {
        return;
      }
      manualSnapToPoint.setValue(snapPoints[snapPoints.length - 1]);
    }


() => {
      if (isClosing.current) {
        return;
      }
      manualSnapToPoint.setValue(snapPoints[0]);
    }


() => ({
        enableContentPanningGesture,
        containerTapGestureRef,
        handlePanGestureState,
        handlePanGestureTranslationY,
        handlePanGestureVelocityY,
        contentPanGestureState,
        contentPanGestureTranslationY,
        contentPanGestureVelocityY,
        scrollableContentOffsetY,
        decelerationRate,
        setScrollableRef: handleSettingScrollableRef,
        removeScrollableRef,
      })


() => ({
        snapTo: handleSnapTo,
        expand: handleExpand,
        collapse: handleCollapse,
        close: handleClose,
      })


() => ({
      snapTo: handleSnapTo,
      expand: handleExpand,
      collapse: handleCollapse,
      close: handleClose,
    })


() => [
        _providedStyle,
        styles.container,
        {
          opacity: animatedIsLayoutReady,
          transform: [
            {
              translateY: cond(
                animatedIsLayoutReady,
                position,
                safeContainerHeight
              ),
            },
          ],
        },
      ]


() => ({
        ...styles.contentContainer,
        height: sheetHeight,
      })


() => ({
        ...styles.contentMaskContainer,
        paddingBottom: animatedIsLayoutReady ? sheetHeight : 0,
      })


() => {
      if (
        animateOnMount &&
        isLayoutCalculated &&
        didMountOnAnimate.current === false &&
        isClosing.current === false
      ) {
        manualSnapToPoint.setValue(snapPoints[_providedIndex]);
        didMountOnAnimate.current = true;
      }
    }


() => {
      if (
        isLayoutCalculated &&
        currentIndexRef.current !== -1 &&
        isClosing.current === false
      ) {
        manualSnapToPoint.setValue(snapPoints[currentIndexRef.current]);
      }
    }


() =>
        onChange(currentPosition, [
          call([currentPosition], args => {
            const currentPositionIndex = snapPoints.indexOf(args[0]);

            /**
             * if animation was interrupted, we ignore the change.
             */
            if (
              currentPositionIndex === -1 &&
              args[0] !== safeContainerHeight - topInset
            ) {
              return;
            }

            /**
             * Here we announce the bottom sheet position
             * for accessibility service.
             */
            AccessibilityInfo.isScreenReaderEnabled().then(isEnabled => {
              if (!isEnabled) {
                return;
              }
              const positionInScreen = Math.max(
                Math.floor(
                  ((WINDOW_HEIGHT - snapPoints[currentPositionIndex] || 1) /
                    WINDOW_HEIGHT) *
                    100
                ),
                0
              ).toFixed(0);
              AccessibilityInfo.announceForAccessibility(
                `Bottom sheet snapped to ${positionInScreen}% of the screen`
              );
            });

            currentIndexRef.current = currentPositionIndex;
            refreshUIElements();
            handleOnChange(currentPositionIndex);
          }),
        ])


args => {
            const currentPositionIndex = snapPoints.indexOf(args[0]);

            /**
             * if animation was interrupted, we ignore the change.
             */
            if (
              currentPositionIndex === -1 &&
              args[0] !== safeContainerHeight - topInset
            ) {
              return;
            }

            /**
             * Here we announce the bottom sheet position
             * for accessibility service.
             */
            AccessibilityInfo.isScreenReaderEnabled().then(isEnabled => {
              if (!isEnabled) {
                return;
              }
              const positionInScreen = Math.max(
                Math.floor(
                  ((WINDOW_HEIGHT - snapPoints[currentPositionIndex] || 1) /
                    WINDOW_HEIGHT) *
                    100
                ),
                0
              ).toFixed(0);
              AccessibilityInfo.announceForAccessibility(
                `Bottom sheet snapped to ${positionInScreen}% of the screen`
              );
            });

            currentIndexRef.current = currentPositionIndex;
            refreshUIElements();
            handleOnChange(currentPositionIndex);
          }


isEnabled => {
              if (!isEnabled) {
                return;
              }
              const positionInScreen = Math.max(
                Math.floor(
                  ((WINDOW_HEIGHT - snapPoints[currentPositionIndex] || 1) /
                    WINDOW_HEIGHT) *
                    100
                ),
                0
              ).toFixed(0);
              AccessibilityInfo.announceForAccessibility(
                `Bottom sheet snapped to ${positionInScreen}% of the screen`
              );
            }


() =>
        cond(
          and(
            eq(containerTapGestureState, State.FAILED),
            eq(currentGesture, GESTURE.CONTENT),
            neq(position, snapPoints[snapPoints.length - 1])
          ),
          call([], () => {
            scrollToTop();
          })
        )


() => {
            scrollToTop();
          }


type BottomSheet = BottomSheetMethods;


export class TicketCreatedListener extends Listener<TicketCreatedEvent> {
	readonly subject = Subjects.TicketCreated;
	queueGroupName = "payments-service";

	onMessage(data: TicketCreatedEvent["data"], msg: Message) {
		console.log("Event data!", data);

		msg.ack();
	}
}


onMessage(data: TicketCreatedEvent["data"], msg: Message) {
		console.log("Event data!", data);

		msg.ack();
	}


() => {
      return [
        NAME_CLASS,
        {
          [BREAK_LINE_CLASS]: props.excessTagsDisplayType === 'break-line',
        },
      ];
    }


() => {
      return isHoverRef.value || !tagValue.value?.length ? props.placeholder : '';
    }


() => {
      return Boolean(
        !props.readonly && !props.disabled && props.clearable && isHoverRef.value && tagValue.value?.length,
      );
    }


(value: InputValue, context: { e: KeyboardEvent }) => {
      inputValueRef.value = '';
      onInnerEnter(value, context);
      nextTick(() => {
        scrollFunctions.scrollToRight();
      });
    }


() => {
        scrollFunctions.scrollToRight();
      }


(item, index) => {
          const tagContent = renderTNodeJSX(this, 'tag', { params: { value: item } });
          return (
            <Tag
              size={this.size}
              disabled={this.disabled}
              onClose={(e)


setup(props: TdTagInputProps, context) {
    const root = ref(null);
    const inputValueRef = ref<InputValue>();
    const { isHoverRef, addHover, cancelHover } = useHover(props);
    const scrollFunctions = useTagScroll(props, root);
    // handle tag add and remove
    const { tagValue, onClose, onInnerEnter, onInputBackspaceKeyUp, clearAll } = useTagList(props, context);

    const classes = computed(() => {
      return [
        NAME_CLASS,
        {
          [BREAK_LINE_CLASS]: props.excessTagsDisplayType === 'break-line',
        },
      ];
    });

    const tagInputPlaceholder = computed(() => {
      return isHoverRef.value || !tagValue.value?.length ? props.placeholder : '';
    });

    const showClearIcon = computed(() => {
      return Boolean(
        !props.readonly && !props.disabled && props.clearable && isHoverRef.value && tagValue.value?.length,
      );
    });

    const onInputEnter = (value: InputValue, context: { e: KeyboardEvent }) => {
      inputValueRef.value = '';
      onInnerEnter(value, context);
      nextTick(() => {
        scrollFunctions.scrollToRight();
      });
    };

    return {
      tagValue,
      root,
      inputValueRef,
      isHoverRef,
      tagInputPlaceholder,
      showClearIcon,
      addHover,
      cancelHover,
      ...scrollFunctions,
      onInputEnter,
      onClose,
      onInnerEnter,
      onInputBackspaceKeyUp,
      clearAll,
      classes,
    };
  }


renderLabel() {
      const displayNode = renderTNodeJSX(this, 'valueDisplay', { params: { value: this.tagValue } });
      const newList = this.minCollapsedNum ? this.tagValue.slice(0, this.minCollapsedNum) : this.tagValue;
      const list =
        displayNode ??
        newList?.map((item, index) => {
          const tagContent = renderTNodeJSX(this, 'tag', { params: { value: item } });
          return (
            <Tag
              size={this.size}
              disabled={this.disabled}
              onClose={(e) => this.onClose({ e, item, index })}


export interface ErrorOther<T = string> {
    _error?: T;
}


export interface WarningOther<T = void> {
    _warning?: T;
}


export interface RegisteredFieldState {
    name: string;
    type: FieldType;
}


export type FieldType = "Field" | "FieldArray";


export type FormErrors<FormData = {}, T = string> = {
    [P in keyof FormData]?: ReactElement | T;
} & ErrorOther<T>;


export type FormWarnings<FormData = {}, T = void> = {
    [P in keyof FormData]?: ReactElement | string | WarningOther<T>;
};


export type Omit<T, K extends keyof T> = Pick<T, ({ [P in keyof T]: P } & { [P in K]: never } & { [x: string]: never, [x: number]: never })[keyof T]>;


async (_: object, { id }: { id: string }, context: any): Promise<Todo> => {
  const db = new InMemoryDatabase()
  return db.getTodo(id)
}


(props) => {
  const { className, ...rest } = props;
  useDeprecationWarning({
    shouldWarn: true,
    message: `Built-in icons are being deprecated in the next major release. You can use the react-icons (https://react-icons.github.io/react-icons) package with the Material Icon set in place of these icons or any other React Icon library.
import { IconErrorOutline } from '@aws-amplify/ui-react'; → import { MdErrorOutline } from 'react-icons/md';`,
  });
  return (
    <View
      as="span"
      width="1em"
      height="1em"
      className={classNames(ComponentClassNames.Icon, className)}
      {...rest}
    >
      <svg
        width="24"
        height="24"
        viewBox="0 0 24 24"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      >
        <path
          d="M11 15H13V17H11V15ZM11 7H13V13H11V7ZM11.99 2C6.47 2 2 6.48 2 12C2 17.52 6.47 22 11.99 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 11.99 2ZM12 20C7.58 20 4 16.42 4 12C4 7.58 7.58 4 12 4C16.42 4 20 7.58 20 12C20 16.42 16.42 20 12 20Z"
          fill="currentColor"
        />
      </svg>
    </View>