# DO NOT EDIT THIS FILE!
#
# This file is generated from the CDP specification. If you need to make
# changes, edit the generator and regenerate all of the modules.
#
# CDP domain: LayerTree (experimental)
from __future__ import annotations
from .util import event_class, T_JSON_DICT
from dataclasses import dataclass
import enum
import typing
from . import dom


class LayerId(str):
    '''
    Unique Layer identifier.
    '''
    def to_json(self) -> str:
        return self

    @classmethod
    def from_json(cls, json: str) -> LayerId:
        return cls(json)

    def __repr__(self):
        return 'LayerId({})'.format(super().__repr__())


class SnapshotId(str):
    '''
    Unique snapshot identifier.
    '''
    def to_json(self) -> str:
        return self

    @classmethod
    def from_json(cls, json: str) -> SnapshotId:
        return cls(json)

    def __repr__(self):
        return 'SnapshotId({})'.format(super().__repr__())


@dataclass
class ScrollRect:
    '''
    Rectangle where scrolling happens on the main thread.
    '''
    #: Rectangle itself.
    rect: dom.Rect

    #: Reason for rectangle to force scrolling on the main thread
    type_: str

    def to_json(self):
        json = dict()
        json['rect'] = self.rect.to_json()
        json['type'] = self.type_
        return json

    @classmethod
    def from_json(cls, json):
        return cls(
            rect=dom.Rect.from_json(json['rect']),
            type_=str(json['type']),
        )


@dataclass
class StickyPositionConstraint:
    '''
    Sticky position constraints.
    '''
    #: Layout rectangle of the sticky element before being shifted
    sticky_box_rect: dom.Rect

    #: Layout rectangle of the containing block of the sticky element
    containing_block_rect: dom.Rect

    #: The nearest sticky layer that shifts the sticky box
    nearest_layer_shifting_sticky_box: typing.Optional[LayerId] = None

    #: The nearest sticky layer that shifts the containing block
    nearest_layer_shifting_containing_block: typing.Optional[LayerId] = None

    def to_json(self):
        json = dict()
        json['stickyBoxRect'] = self.sticky_box_rect.to_json()
        json['containingBlockRect'] = self.containing_block_rect.to_json()
        if self.nearest_layer_shifting_sticky_box is not None:
            json['nearestLayerShiftingStickyBox'] = self.nearest_layer_shifting_sticky_box.to_json()
        if self.nearest_layer_shifting_containing_block is not None:
            json['nearestLayerShiftingContainingBlock'] = self.nearest_layer_shifting_containing_block.to_json()
        return json

    @classmethod
    def from_json(cls, json):
        return cls(
            sticky_box_rect=dom.Rect.from_json(json['stickyBoxRect']),
            containing_block_rect=dom.Rect.from_json(json['containingBlockRect']),
            nearest_layer_shifting_sticky_box=LayerId.from_json(json['nearestLayerShiftingStickyBox']) if 'nearestLayerShiftingStickyBox' in json else None,
            nearest_layer_shifting_containing_block=LayerId.from_json(json['nearestLayerShiftingContainingBlock']) if 'nearestLayerShiftingContainingBlock' in json else None,
        )


@dataclass
class PictureTile:
    '''
    Serialized fragment of layer picture along with its offset within the layer.
    '''
    #: Offset from owning layer left boundary
    x: float

    #: Offset from owning layer top boundary
    y: float

    #: Base64-encoded snapshot data.
    picture: str

    def to_json(self):
        json = dict()
        json['x'] = self.x
        json['y'] = self.y
        json['picture'] = self.picture
        return json

    @classmethod
    def from_json(cls, json):
        return cls(
            x=float(json['x']),
            y=float(json['y']),
            picture=str(json['picture']),
        )


@dataclass
class Layer:
    '''
    Information about a compositing layer.
    '''
    #: The unique id for this layer.
    layer_id: LayerId

    #: Offset from parent layer, X coordinate.
    offset_x: float

    #: Offset from parent layer, Y coordinate.
    offset_y: float

    #: Layer width.
    width: float

    #: Layer height.
    height: float

    #: Indicates how many time this layer has painted.
    paint_count: int

    #: Indicates whether this layer hosts any content, rather than being used for
    #: transform/scrolling purposes only.
    draws_content: bool

    #: The id of parent (not present for root).
    parent_layer_id: typing.Optional[LayerId] = None

    #: The backend id for the node associated with this layer.
    backend_node_id: typing.Optional[dom.BackendNodeId] = None

    #: Transformation matrix for layer, default is identity matrix
    transform: typing.Optional[typing.List[float]] = None

    #: Transform anchor point X, absent if no transform specified
    anchor_x: typing.Optional[float] = None

    #: Transform anchor point Y, absent if no transform specified
    anchor_y: typing.Optional[float] = None

    #: Transform anchor point Z, absent if no transform specified
    anchor_z: typing.Optional[float] = None

    #: Set if layer is not visible.
    invisible: typing.Optional[bool] = None

    #: Rectangles scrolling on main thread only.
    scroll_rects: typing.Optional[typing.List[ScrollRect]] = None

    #: Sticky position constraint information
    sticky_position_constraint: typing.Optional[StickyPositionConstraint] = None

    def to_json(self):
        json = dict()
        json['layerId'] = self.layer_id.to_json()
        json['offsetX'] = self.offset_x
        json['offsetY'] = self.offset_y
        json['width'] = self.width
        json['height'] = self.height
        json['paintCount'] = self.paint_count
        json['drawsContent'] = self.draws_content
        if self.parent_layer_id is not None:
            json['parentLayerId'] = self.parent_layer_id.to_json()
        if self.backend_node_id is not None:
            json['backendNodeId'] = self.backend_node_id.to_json()
        if self.transform is not None:
            json['transform'] = [i for i in self.transform]
        if self.anchor_x is not None:
            json['anchorX'] = self.anchor_x
        if self.anchor_y is not None:
            json['anchorY'] = self.anchor_y
        if self.anchor_z is not None:
            json['anchorZ'] = self.anchor_z
        if self.invisible is not None:
            json['invisible'] = self.invisible
        if self.scroll_rects is not None:
            json['scrollRects'] = [i.to_json() for i in self.scroll_rects]
        if self.sticky_position_constraint is not None:
            json['stickyPositionConstraint'] = self.sticky_position_constraint.to_json()
        return json

    @classmethod
    def from_json(cls, json):
        return cls(
            layer_id=LayerId.from_json(json['layerId']),
            offset_x=float(json['offsetX']),
            offset_y=float(json['offsetY']),
            width=float(json['width']),
            height=float(json['height']),
            paint_count=int(json['paintCount']),
            draws_content=bool(json['drawsContent']),
            parent_layer_id=LayerId.from_json(json['parentLayerId']) if 'parentLayerId' in json else None,
            backend_node_id=dom.BackendNodeId.from_json(json['backendNodeId']) if 'backendNodeId' in json else None,
            transform=[float(i) for i in json['transform']] if 'transform' in json else None,
            anchor_x=float(json['anchorX']) if 'anchorX' in json else None,
            anchor_y=float(json['anchorY']) if 'anchorY' in json else None,
            anchor_z=float(json['anchorZ']) if 'anchorZ' in json else None,
            invisible=bool(json['invisible']) if 'invisible' in json else None,
            scroll_rects=[ScrollRect.from_json(i) for i in json['scrollRects']] if 'scrollRects' in json else None,
            sticky_position_constraint=StickyPositionConstraint.from_json(json['stickyPositionConstraint']) if 'stickyPositionConstraint' in json else None,
        )


class PaintProfile(list):
    '''
    Array of timings, one per paint step.
    '''
    def to_json(self) -> typing.List[float]:
        return self

    @classmethod
    def from_json(cls, json: typing.List[float]) -> PaintProfile:
        return cls(json)

    def __repr__(self):
        return 'PaintProfile({})'.format(super().__repr__())


def compositing_reasons(
        layer_id: LayerId
    ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.Tuple[typing.List[str], typing.List[str]]]:
    '''
    Provides the reasons why the given layer was composited.

    :param layer_id: The id of the layer for which we want to get the reasons it was composited.
    :returns: A tuple with the following items:

        0. **compositingReasons** - A list of strings specifying reasons for the given layer to become composited.
        1. **compositingReasonIds** - A list of strings specifying reason IDs for the given layer to become composited.
    '''
    params: T_JSON_DICT = dict()
    params['layerId'] = layer_id.to_json()
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.compositingReasons',
        'params': params,
    }
    json = yield cmd_dict
    return (
        [str(i) for i in json['compositingReasons']],
        [str(i) for i in json['compositingReasonIds']]
    )


def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
    '''
    Disables compositing tree inspection.
    '''
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.disable',
    }
    json = yield cmd_dict


def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
    '''
    Enables compositing tree inspection.
    '''
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.enable',
    }
    json = yield cmd_dict


def load_snapshot(
        tiles: typing.List[PictureTile]
    ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,SnapshotId]:
    '''
    Returns the snapshot identifier.

    :param tiles: An array of tiles composing the snapshot.
    :returns: The id of the snapshot.
    '''
    params: T_JSON_DICT = dict()
    params['tiles'] = [i.to_json() for i in tiles]
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.loadSnapshot',
        'params': params,
    }
    json = yield cmd_dict
    return SnapshotId.from_json(json['snapshotId'])


def make_snapshot(
        layer_id: LayerId
    ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,SnapshotId]:
    '''
    Returns the layer snapshot identifier.

    :param layer_id: The id of the layer.
    :returns: The id of the layer snapshot.
    '''
    params: T_JSON_DICT = dict()
    params['layerId'] = layer_id.to_json()
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.makeSnapshot',
        'params': params,
    }
    json = yield cmd_dict
    return SnapshotId.from_json(json['snapshotId'])


def profile_snapshot(
        snapshot_id: SnapshotId,
        min_repeat_count: typing.Optional[int] = None,
        min_duration: typing.Optional[float] = None,
        clip_rect: typing.Optional[dom.Rect] = None
    ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[PaintProfile]]:
    '''
    :param snapshot_id: The id of the layer snapshot.
    :param min_repeat_count: *(Optional)* The maximum number of times to replay the snapshot (1, if not specified).
    :param min_duration: *(Optional)* The minimum duration (in seconds) to replay the snapshot.
    :param clip_rect: *(Optional)* The clip rectangle to apply when replaying the snapshot.
    :returns: The array of paint profiles, one per run.
    '''
    params: T_JSON_DICT = dict()
    params['snapshotId'] = snapshot_id.to_json()
    if min_repeat_count is not None:
        params['minRepeatCount'] = min_repeat_count
    if min_duration is not None:
        params['minDuration'] = min_duration
    if clip_rect is not None:
        params['clipRect'] = clip_rect.to_json()
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.profileSnapshot',
        'params': params,
    }
    json = yield cmd_dict
    return [PaintProfile.from_json(i) for i in json['timings']]


def release_snapshot(
        snapshot_id: SnapshotId
    ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
    '''
    Releases layer snapshot captured by the back-end.

    :param snapshot_id: The id of the layer snapshot.
    '''
    params: T_JSON_DICT = dict()
    params['snapshotId'] = snapshot_id.to_json()
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.releaseSnapshot',
        'params': params,
    }
    json = yield cmd_dict


def replay_snapshot(
        snapshot_id: SnapshotId,
        from_step: typing.Optional[int] = None,
        to_step: typing.Optional[int] = None,
        scale: typing.Optional[float] = None
    ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,str]:
    '''
    Replays the layer snapshot and returns the resulting bitmap.

    :param snapshot_id: The id of the layer snapshot.
    :param from_step: *(Optional)* The first step to replay from (replay from the very start if not specified).
    :param to_step: *(Optional)* The last step to replay to (replay till the end if not specified).
    :param scale: *(Optional)* The scale to apply while replaying (defaults to 1).
    :returns: A data: URL for resulting image.
    '''
    params: T_JSON_DICT = dict()
    params['snapshotId'] = snapshot_id.to_json()
    if from_step is not None:
        params['fromStep'] = from_step
    if to_step is not None:
        params['toStep'] = to_step
    if scale is not None:
        params['scale'] = scale
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.replaySnapshot',
        'params': params,
    }
    json = yield cmd_dict
    return str(json['dataURL'])


def snapshot_command_log(
        snapshot_id: SnapshotId
    ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[dict]]:
    '''
    Replays the layer snapshot and returns canvas log.

    :param snapshot_id: The id of the layer snapshot.
    :returns: The array of canvas function calls.
    '''
    params: T_JSON_DICT = dict()
    params['snapshotId'] = snapshot_id.to_json()
    cmd_dict: T_JSON_DICT = {
        'method': 'LayerTree.snapshotCommandLog',
        'params': params,
    }
    json = yield cmd_dict
    return [dict(i) for i in json['commandLog']]


@event_class('LayerTree.layerPainted')
@dataclass
class LayerPainted:
    #: The id of the painted layer.
    layer_id: LayerId
    #: Clip rectangle.
    clip: dom.Rect

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> LayerPainted:
        return cls(
            layer_id=LayerId.from_json(json['layerId']),
            clip=dom.Rect.from_json(json['clip'])
        )


@event_class('LayerTree.layerTreeDidChange')
@dataclass
class LayerTreeDidChange:
    #: Layer tree, absent if not in the comspositing mode.
    layers: typing.Optional[typing.List[Layer]]

    @classmethod
    def from_json(cls, json: T_JSON_DICT) -> LayerTreeDidChange:
        return cls(
            layers=[Layer.from_json(i) for i in json['layers']] if 'layers' in json else None
        )
