/*************************************************************
 *
 *  Copyright (c) 2022-2026 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * @file  Implements the LineBBox class
 *
 * @author dpvc@mathjax.org (Davide Cervone)
 */

import { MmlNode } from '../../core/MmlTree/MmlNode.js';
import { BBox, BBoxData } from '../../util/BBox.js';
import { StringMap } from './Wrapper.js';

/*****************************************************************/

/**
 * align and indent values
 */
export type IndentData = [string, string];

/**
 * A BBox with extra data about line breaking
 */
export class LineBBox extends BBox {
  /**
   * Indentation data for the line break
   */
  public indentData: IndentData[] = null;

  /**
   * the lineleading for this break
   */
  public lineLeading: number;

  /**
   * The original BBox.L value
   */
  public originalL: number;

  /**
   * True if this bbox is at the beginning of a line
   */
  public isFirst: boolean = false;

  /**
   * The index of the child node containing this starting line break and the line within that child
   */
  public start: [number, number];
  /**
   * The index of the child node containing the ending line break and the line within that child
   */
  public end: [number, number];

  /**
   * @param {BBox} bbox           The bbox to extend
   * @param {number} leading      The lineleading value for the break
   * @param {IndentData} indent   The align/shift information
   * @returns {LineBBox}          The extended bbox
   */
  public static from(
    bbox: BBox,
    leading: number,
    indent: IndentData[] = null
  ): LineBBox {
    const nbox = new this();
    Object.assign(nbox, bbox);
    nbox.lineLeading = leading;
    if (indent) {
      nbox.indentData = indent;
    }
    return nbox;
  }

  /**
   * @override
   */
  constructor(def?: BBoxData, start: [number, number] = null) {
    super(def);
    this.originalL = this.L;
    if (start) {
      this.start = start;
    }
  }

  /**
   * @override
   */
  public append(cbox: LineBBox) {
    if (this.isFirst) {
      cbox.originalL += cbox.L;
      cbox.L = 0; // remove spacing after an operator with a linebreak after it
    }
    if (cbox.indentData) {
      this.indentData = cbox.indentData;
    }
    this.lineLeading = cbox.lineLeading;
    super.append(cbox);
    this.isFirst = cbox.isFirst;
  }

  /**
   * @override
   */
  public copy(): LineBBox {
    const bbox = LineBBox.from(this, this.lineLeading);
    bbox.indentData = this.indentData;
    bbox.lineLeading = this.lineLeading;
    return bbox;
  }

  /**
   * @param {MmlNode} node    The MmlNode to get attributes from
   */
  public getIndentData(node: MmlNode) {
    let {
      indentalign,
      indentshift,
      indentalignfirst,
      indentshiftfirst,
      indentalignlast,
      indentshiftlast,
    } = node.attributes.getAllAttributes() as StringMap;
    if (indentalignfirst === 'indentalign') {
      indentalignfirst = node.attributes.getInherited('indentalign') as string;
    }
    if (indentshiftfirst === 'indentshift') {
      indentshiftfirst = node.attributes.getInherited('indentshift') as string;
    }
    if (indentalignlast === 'indentalign') {
      indentalignlast = indentalign;
    }
    if (indentshiftlast === 'indentshift') {
      indentshiftlast = indentshift;
    }
    this.indentData = [
      [indentalignfirst, indentshiftfirst],
      [indentalign, indentshift],
      [indentalignlast, indentshiftlast],
    ] as IndentData[];
  }

  /**
   * @param {LineBBox} bbox   The LineBBox whose indentData is to be copied
   * @returns {IndentData[]}   The copied array
   */
  protected copyIndentData(bbox: LineBBox): IndentData[] {
    return bbox.indentData.map(([align, indent]) => [align, indent]);
  }
}
