// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import type * as Protocol from '../../generated/protocol.js';
import * as TextUtils from '../../models/text_utils/text_utils.js';

import type {CSSModel} from './CSSModel.js';
import {CSSQuery} from './CSSQuery.js';
import type {DOMNode} from './DOMModel.js';

export class CSSContainerQuery extends CSSQuery {
  name?: string;
  physicalAxes?: Protocol.DOM.PhysicalAxes;
  logicalAxes?: Protocol.DOM.LogicalAxes;
  queriesScrollState?: boolean;
  queriesAnchored?: boolean;

  static parseContainerQueriesPayload(cssModel: CSSModel, payload: Protocol.CSS.CSSContainerQuery[]):
      CSSContainerQuery[] {
    return payload.map(cq => new CSSContainerQuery(cssModel, cq));
  }

  constructor(cssModel: CSSModel, payload: Protocol.CSS.CSSContainerQuery) {
    super(cssModel);
    this.reinitialize(payload);
  }

  reinitialize(payload: Protocol.CSS.CSSContainerQuery): void {
    this.text = payload.text;
    this.range = payload.range ? TextUtils.TextRange.TextRange.fromObject(payload.range) : null;
    this.styleSheetId = payload.styleSheetId;
    this.name = payload.name;
    this.physicalAxes = payload.physicalAxes;
    this.logicalAxes = payload.logicalAxes;
    this.queriesScrollState = payload.queriesScrollState;
    this.queriesAnchored = payload.queriesAnchored;
  }

  active(): boolean {
    return true;
  }

  async getContainerForNode(nodeId: Protocol.DOM.NodeId): Promise<CSSContainerQueryContainer|undefined> {
    const containerNode = await this.cssModel.domModel().getContainerForNode(
        nodeId, this.name, this.physicalAxes, this.logicalAxes, this.queriesScrollState, this.queriesAnchored);
    if (!containerNode) {
      return;
    }
    return new CSSContainerQueryContainer(containerNode);
  }
}

export class CSSContainerQueryContainer {
  readonly containerNode: DOMNode;

  constructor(containerNode: DOMNode) {
    this.containerNode = containerNode;
  }

  async getContainerSizeDetails(): Promise<ContainerQueriedSizeDetails|undefined> {
    const styles = await this.containerNode.domModel().cssModel().getComputedStyle(this.containerNode.id);
    if (!styles) {
      return;
    }
    const containerType = styles.get('container-type');
    const writingMode = styles.get('writing-mode');
    if (!containerType || !writingMode) {
      return;
    }

    const queryAxis = getQueryAxisFromContainerType(`${containerType}`);
    const physicalAxis = getPhysicalAxisFromQueryAxis(queryAxis, writingMode);
    let width, height;
    if (physicalAxis === PhysicalAxis.BOTH || physicalAxis === PhysicalAxis.HORIZONTAL) {
      width = styles.get('width');
    }
    if (physicalAxis === PhysicalAxis.BOTH || physicalAxis === PhysicalAxis.VERTICAL) {
      height = styles.get('height');
    }

    return {
      queryAxis,
      physicalAxis,
      width,
      height,
    };
  }
}

export const getQueryAxisFromContainerType = (propertyValue: string): QueryAxis => {
  const segments = propertyValue.split(' ');
  let isInline = false;
  for (const segment of segments) {
    if (segment === 'size') {
      return QueryAxis.BOTH;
    }
    isInline = isInline || segment === 'inline-size';
  }
  if (isInline) {
    return QueryAxis.INLINE;
  }
  return QueryAxis.NONE;
};

export const getPhysicalAxisFromQueryAxis = (queryAxis: QueryAxis, writingMode: string): PhysicalAxis => {
  const isVerticalWritingMode = writingMode.startsWith('vertical');
  switch (queryAxis) {
    case QueryAxis.NONE:
      return PhysicalAxis.NONE;
    case QueryAxis.BOTH:
      return PhysicalAxis.BOTH;
    case QueryAxis.INLINE:
      return isVerticalWritingMode ? PhysicalAxis.VERTICAL : PhysicalAxis.HORIZONTAL;
    case QueryAxis.BLOCK:
      return isVerticalWritingMode ? PhysicalAxis.HORIZONTAL : PhysicalAxis.VERTICAL;
  }
};

export interface ContainerQueriedSizeDetails {
  queryAxis: QueryAxis;
  physicalAxis: PhysicalAxis;
  width?: string;
  height?: string;
}

export const enum QueryAxis {
  NONE = '',
  INLINE = 'inline-size',
  BLOCK = 'block-size',
  BOTH = 'size',
}

export const enum PhysicalAxis {
  NONE = '',
  HORIZONTAL = 'Horizontal',
  VERTICAL = 'Vertical',
  BOTH = 'Both',
}
