/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsMathMLFrame_h_
#define nsMathMLFrame_h_

#include "mozilla/dom/MathMLElement.h"
#include "nsBoundingMetrics.h"
#include "nsFontMetrics.h"
#include "nsIFrame.h"
#include "nsIMathMLFrame.h"
#include "nsMathMLOperators.h"

class nsMathMLChar;
class nsCSSValue;

namespace mozilla {
class nsDisplayListBuilder;
class nsDisplayListSet;
}  // namespace mozilla

// Concrete base class with default methods that derived MathML frames can
// override
class nsMathMLFrame : public nsIMathMLFrame {
 public:
  // nsIMathMLFrame ---

  bool IsSpaceLike() override {
    return mPresentationData.flags.contains(MathMLPresentationFlag::SpaceLike);
  }

  NS_IMETHOD
  GetBoundingMetrics(nsBoundingMetrics& aBoundingMetrics) override {
    aBoundingMetrics = mBoundingMetrics;
    return NS_OK;
  }

  NS_IMETHOD
  SetBoundingMetrics(const nsBoundingMetrics& aBoundingMetrics) override {
    mBoundingMetrics = aBoundingMetrics;
    return NS_OK;
  }

  NS_IMETHOD
  SetReference(const nsPoint& aReference) override {
    mReference = aReference;
    return NS_OK;
  }

  MathMLFrameType GetMathMLFrameType() override;

  NS_IMETHOD
  Stretch(mozilla::gfx::DrawTarget* aDrawTarget,
          StretchDirection aStretchDirection, nsBoundingMetrics& aContainerSize,
          mozilla::ReflowOutput& aDesiredStretchSize) override {
    return NS_OK;
  }

  NS_IMETHOD
  GetEmbellishData(nsEmbellishData& aEmbellishData) override {
    aEmbellishData = mEmbellishData;
    return NS_OK;
  }

  NS_IMETHOD
  GetPresentationData(nsPresentationData& aPresentationData) override {
    aPresentationData = mPresentationData;
    return NS_OK;
  }

  NS_IMETHOD
  InheritAutomaticData(nsIFrame* aParent) override;

  NS_IMETHOD
  TransmitAutomaticData() override { return NS_OK; }

  NS_IMETHOD
  UpdatePresentationData(MathMLPresentationFlags aFlagsValues,
                         MathMLPresentationFlags aFlagsToUpdate) override;

  NS_IMETHOD
  UpdatePresentationDataFromChildAt(
      int32_t aFirstIndex, int32_t aLastIndex,
      MathMLPresentationFlags aFlagsValues,
      MathMLPresentationFlags aFlagsToUpdate) override {
    return NS_OK;
  }

  uint8_t ScriptIncrement(nsIFrame* aFrame) override { return 0; }

  bool IsMrowLike() override { return false; }

  // TODO: Implement italic correction for mrow-like and mtext-like elements.
  nscoord ItalicCorrection() override { return 0; }

  // helper to get the mEmbellishData of a frame
  // The MathML REC precisely defines an "embellished operator" as:
  // - an <mo> element;
  // - or one of the elements <msub>, <msup>, <msubsup>, <munder>, <mover>,
  //   <munderover>, <mmultiscripts>, <mfrac>, or <semantics>, whose first
  //   argument exists and is an embellished operator;
  //- or one of the elements <mstyle>, <mphantom>, or <mpadded>, such that
  //   an <mrow> containing the same arguments would be an embellished
  //   operator;
  // - or an <maction> element whose selected subexpression exists and is an
  //   embellished operator;
  // - or an <mrow> whose arguments consist (in any order) of one embellished
  //   operator and zero or more spacelike elements.
  static void GetEmbellishDataFrom(nsIFrame* aFrame,
                                   nsEmbellishData& aEmbellishData);

  // utilities to parse and retrieve numeric values in CSS units
  // All values are stored in twips.
  // @pre  aLengthValue is the default length value of the attribute.
  // @post aLengthValue is the length value computed from the attribute.
  static void ParseAndCalcNumericValue(
      const nsString& aString, nscoord* aLengthValue, float aFontSizeInflation,
      nsIFrame* aFrame,
      mozilla::dom::MathMLElement::ParseFlags aFlags =
          mozilla::dom::MathMLElement::ParseFlags());

  static nscoord CalcLength(const nsCSSValue& aCSSValue,
                            float aFontSizeInflation, nsIFrame* aFrame);

  static MathMLFrameType GetMathMLFrameTypeFor(nsIFrame* aFrame) {
    if (aFrame->IsMathMLFrame()) {
      if (nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame)) {
        return mathMLFrame->GetMathMLFrameType();
      }
    }
    return MathMLFrameType::Unknown;
  }

  // estimate of the italic correction
  static void GetItalicCorrection(nsBoundingMetrics& aBoundingMetrics,
                                  nscoord& aItalicCorrection) {
    aItalicCorrection = aBoundingMetrics.rightBearing - aBoundingMetrics.width;
    if (0 > aItalicCorrection) {
      aItalicCorrection = 0;
    }
  }

  static void GetItalicCorrection(nsBoundingMetrics& aBoundingMetrics,
                                  nscoord& aLeftItalicCorrection,
                                  nscoord& aRightItalicCorrection) {
    aRightItalicCorrection =
        aBoundingMetrics.rightBearing - aBoundingMetrics.width;
    if (0 > aRightItalicCorrection) {
      aRightItalicCorrection = 0;
    }
    aLeftItalicCorrection = -aBoundingMetrics.leftBearing;
    if (0 > aLeftItalicCorrection) {
      aLeftItalicCorrection = 0;
    }
  }

  // helper methods for getting sup/subdrop's from a child
  static void GetSubDropFromChild(nsIFrame* aChild, nscoord& aSubDrop,
                                  float aFontSizeInflation);

  static void GetSupDropFromChild(nsIFrame* aChild, nscoord& aSupDrop,
                                  float aFontSizeInflation);

  static void GetSkewCorrectionFromChild(nsIFrame* aChild,
                                         nscoord& aSkewCorrection) {
    // default is 0
    // individual classes should over-ride this method if necessary
    aSkewCorrection = 0;
  }

  // 2 levels of subscript shifts
  static void GetSubScriptShifts(nsFontMetrics* fm, nscoord& aSubScriptShift1,
                                 nscoord& aSubScriptShift2) {
    nscoord xHeight = fm->XHeight();
    aSubScriptShift1 = NSToCoordRound(150.000f / 430.556f * xHeight);
    aSubScriptShift2 = NSToCoordRound(247.217f / 430.556f * xHeight);
  }

  // 3 levels of superscript shifts
  static void GetSupScriptShifts(nsFontMetrics* fm, nscoord& aSupScriptShift1,
                                 nscoord& aSupScriptShift2,
                                 nscoord& aSupScriptShift3) {
    nscoord xHeight = fm->XHeight();
    aSupScriptShift1 = NSToCoordRound(412.892f / 430.556f * xHeight);
    aSupScriptShift2 = NSToCoordRound(362.892f / 430.556f * xHeight);
    aSupScriptShift3 = NSToCoordRound(288.889f / 430.556f * xHeight);
  }

  // these are TeX specific params not found in ordinary fonts

  static void GetSubDrop(nsFontMetrics* fm, nscoord& aSubDrop) {
    nscoord xHeight = fm->XHeight();
    aSubDrop = NSToCoordRound(50.000f / 430.556f * xHeight);
  }

  static void GetSupDrop(nsFontMetrics* fm, nscoord& aSupDrop) {
    nscoord xHeight = fm->XHeight();
    aSupDrop = NSToCoordRound(386.108f / 430.556f * xHeight);
  }

  static void GetNumeratorShifts(nsFontMetrics* fm, nscoord& numShift1,
                                 nscoord& numShift2, nscoord& numShift3) {
    nscoord xHeight = fm->XHeight();
    numShift1 = NSToCoordRound(676.508f / 430.556f * xHeight);
    numShift2 = NSToCoordRound(393.732f / 430.556f * xHeight);
    numShift3 = NSToCoordRound(443.731f / 430.556f * xHeight);
  }

  static void GetDenominatorShifts(nsFontMetrics* fm, nscoord& denShift1,
                                   nscoord& denShift2) {
    nscoord xHeight = fm->XHeight();
    denShift1 = NSToCoordRound(685.951f / 430.556f * xHeight);
    denShift2 = NSToCoordRound(344.841f / 430.556f * xHeight);
  }

  static void GetEmHeight(nsFontMetrics* fm, nscoord& emHeight) {
#if 0
    // should switch to this API in order to scale with changes of TextZoom
    emHeight = fm->EmHeight();
#else
    emHeight = fm->Font().size.ToAppUnits();
#endif
  }

  static void GetAxisHeight(nsFontMetrics* fm, nscoord& axisHeight) {
    axisHeight = NSToCoordRound(250.000f / 430.556f * fm->XHeight());
  }

  static void GetBigOpSpacings(nsFontMetrics* fm, nscoord& bigOpSpacing1,
                               nscoord& bigOpSpacing2, nscoord& bigOpSpacing3,
                               nscoord& bigOpSpacing4, nscoord& bigOpSpacing5) {
    nscoord xHeight = fm->XHeight();
    bigOpSpacing1 = NSToCoordRound(111.111f / 430.556f * xHeight);
    bigOpSpacing2 = NSToCoordRound(166.667f / 430.556f * xHeight);
    bigOpSpacing3 = NSToCoordRound(200.000f / 430.556f * xHeight);
    bigOpSpacing4 = NSToCoordRound(600.000f / 430.556f * xHeight);
    bigOpSpacing5 = NSToCoordRound(100.000f / 430.556f * xHeight);
  }

  static void GetRuleThickness(nsFontMetrics* fm, nscoord& ruleThickness) {
    nscoord xHeight = fm->XHeight();
    ruleThickness = NSToCoordRound(40.000f / 430.556f * xHeight);
  }

  // Some parameters are not accurately obtained using the x-height.
  // Here are some slower variants to obtain the desired metrics
  // by actually measuring some characters
  static void GetRuleThickness(mozilla::gfx::DrawTarget* aDrawTarget,
                               nsFontMetrics* aFontMetrics,
                               nscoord& aRuleThickness);

  static void GetAxisHeight(mozilla::gfx::DrawTarget* aDrawTarget,
                            nsFontMetrics* aFontMetrics, nscoord& aAxisHeight);

  static void GetRadicalParameters(nsFontMetrics* aFontMetrics,
                                   bool aDisplayStyle,
                                   nscoord& aRadicalRuleThickness,
                                   nscoord& aRadicalExtraAscender,
                                   nscoord& aRadicalVerticalGap);

 protected:
  /**
   * Display a solid rectangle in the frame's text color. Used for drawing
   * fraction separators and root/sqrt overbars.
   */
  void DisplayBar(mozilla::nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                  const nsRect& aRect, const mozilla::nsDisplayListSet& aLists,
                  uint16_t aIndex = 0);

  // information about the presentation policy of the frame
  nsPresentationData mPresentationData;

  // information about a container that is an embellished operator
  nsEmbellishData mEmbellishData;

  // Metrics that _exactly_ enclose the text of the frame
  nsBoundingMetrics mBoundingMetrics;

  // Reference point of the frame: mReference.y is the baseline
  nsPoint mReference;
};

#endif /* nsMathMLFrame_h_ */
