text: /* Tencent is pleased to support the open source community by making Hippy available.
   * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
   *
   * 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.
   */
  
  #include "HPNode.h"
  #include <algorithm>
  #include <string.h>
  #include <string>
  
  //the layout progress refers https://www.w3.org/TR/css-flexbox-1/#layout-algorithm
  
  std::string getIndentString(int indent) {
    std::string str;
    for (int i = 0; i < indent; i++) {
      str += " ";
    }
    return str;
  }
  
  std::string toString(float value) {
    if (isUndefined(value)) {
      return "NAN";
    } else {
      char str[10] = { 0 };
      snprintf(str, 9, "%0.f", PixelRoundInt(value));
      return str;
    }
  }
  
  void HPNode::printNode(uint32_t indent) {
    std::string indentStr = getIndentString(indent);
    std::string startStr;
    startStr = indentStr
        + "<div layout=\"width:%s; height:%s; left:%s; top:%s;\" style=\"%s\">\n";
    HPLogd(startStr.c_str(), toString(result.dim[0]).c_str(),
           toString(result.dim[1]).c_str(), toString(result.position[0]).c_str(),
           toString(result.position[1]).c_str(), style.toString().c_str());
  
    std::vector<HPNodeRef>& items = children;
    for (size_t i = 0; i < items.size(); i++) {
      HPNodeRef item = items[i];
      item->printNode(indent + 4);
    }
    std::string endStr = indentStr + "</div>\n";
    HPLogd(endStr.c_str());
  }
  
  HPNode::HPNode() {
    context = nullptr;
    parent = nullptr;
    measure = nullptr;
    dirtiedFunc = nullptr;
  
    initLayoutResult();
    inInitailState = true;
  }
  
  HPNode::~HPNode() {
    //remove from parent
    if (parent != nullptr) {
      parent->removeChild(this);
      parent = nullptr;
    }
  
    //set child's parent as null
    for (size_t i = 0; i < children.size(); i++) {
      HPNodeRef item = children[i];
      if (item != nullptr) {
        item->setParent(nullptr);
      }
    }
  
    children.clear();
  }
  
  void HPNode::initLayoutResult() {
  #ifdef  LAYOUT_TIME_ANALYZE
    fetchCount = 0;
  #endif
    isFrozen = false;
    isDirty = true;
    _hasNewLayout = false;
    result.dim[DimWidth] = 0;
    result.dim[DimHeight] = 0;
  
    memset((void *) result.position, 0, sizeof(float) * 4);
    memset((void *) result.cachedPosition, 0, sizeof(float) * 4);
    memset((void *) result.margin, 0, sizeof(float) * 4);
    memset((void *) result.padding, 0, sizeof(float) * 4);
    memset((void *) result.border, 0, sizeof(float) * 4);
  
    result.hadOverflow = false;
    result.direction = DirectionInherit;
  }
  
  bool HPNode::reset() {
    if (childCount() != 0 || getParent() != nullptr)
      return false;
    children.clear();
    children.shrink_to_fit();
    initLayoutResult();
    inInitailState = true;
    return true;
  }
  
  void HPNode::resetLayoutRecursive(bool isDisplayNone) {
    if (inInitailState && isDisplayNone) {
      return;
    }
    initLayoutResult();
    if (!isDisplayNone) {  //see HPNode::removeChild
      //set result as undefined.see HPNodeChildTest.cpp
      //in tests/folder
      result.dim[DimWidth] = VALUE_UNDEFINED;
      result.dim[DimHeight] = VALUE_UNDEFINED;
    } else {
      inInitailState = true;	//prevent resetLayoutRecursive run many times in recursive
      //in DisplayNone state, set hasNewLayout as true;
      //set dirty false;
      setHasNewLayout(true);
      setDirty(false);
    }
    //if just because parent's display type change,
    //not to clear child layout cache, can be reused.
    layoutCache.clearCache();
    for (size_t i = 0; i < children.size(); i++) {
      HPNodeRef item = children[i];
      item->resetLayoutRecursive(isDisplayNone);
    }
  }
  
  HPStyle HPNode::getStyle() {
    return style;
  }
  
  void HPNode::setStyle(const HPStyle& st) {
    style = st;
    //TODO:: layout if needed???
  }
  
  bool HPNode::setMeasureFunc(HPMeasureFunc _measure) {
    if (measure == _measure) {
      return true;
    }
  
    //not leaf node , not set measure
    if (childCount() > 0) {
      return false;
    }
  
    measure = _measure;
    style.nodeType = _measure ? NodeTypeText : NodeTypeDefault;
    markAsDirty();
    return true;
  }
  
  void HPNode::setParent(HPNodeRef _parent) {
    parent = _parent;
  }
  
  HPNodeRef HPNode::getParent() {
    return parent;
  }
  
  void HPNode::addChild(HPNodeRef item) {
    if (item == nullptr) {
      return;
    }
    item->setParent(this);
    children.push_back(item);
    markAsDirty();
  }
  
  bool HPNode::insertChild(HPNodeRef item, uint32_t index) {
    //measure node cannot have child.
    if (item == nullptr || measure != nullptr) {
      return false;
    }
    item->setParent(this);
    children.insert(children.begin() + index, item);
    markAsDirty();
    return true;
  }
  
  HPNodeRef HPNode::getChild(uint32_t index) {
    if (index > children.size() - 1) {
      return nullptr;
    }
    return children[index];
  }
  
  bool HPNode::removeChild(HPNodeRef child) {
    std::vector<HPNodeRef>::iterator p = std::find(children.begin(),
                                                   children.end(), child);
    if (p != children.end()) {
      children.erase(p);
      child->setParent(nullptr);
      child->resetLayoutRecursive(false);
      markAsDirty();
      return true;
    }
    return false;
  }
  
  bool HPNode::removeChild(uint32_t index) {
    if (index > children.size() - 1) {
      return false;
    }
    HPNodeRef child = getChild(index);
    if (child != nullptr) {
      child->setParent(nullptr);
      child->resetLayoutRecursive(false);
    }
    children.erase(children.begin() + index);
    markAsDirty();
    return true;
  }
  
  uint32_t HPNode::childCount() {
    return children.size();
  }
  
  void HPNode::setDisplayType(DisplayType displayType) {
    if (style.displayType == displayType)
      return;
    style.displayType = displayType;
    isDirty = false;  //force following markAsDirty did effect to its parent
    markAsDirty();
  }
  
  void HPNode::markAsDirty() {
    if (!isDirty) {
      setDirty(true);
      if (parent) {
        parent->markAsDirty();
      }
    }
  }
  
  void HPNode::setHasNewLayout(bool hasNewLayoutOrNot) {
    _hasNewLayout = hasNewLayoutOrNot;
  }
  
  bool HPNode::hasNewLayout() {
    return _hasNewLayout;
  }
  
  void HPNode::setDirty(bool dirtyOrNot) {
    if (isDirty == dirtyOrNot) {
      return;
    }
    isDirty = dirtyOrNot;
    if (isDirty) {
      //reset layout direction to initial state
      //need to calculated again
      setLayoutDirection(DirectionInherit);
      //if is dirty, reset frozen.
      isFrozen = false;
      //if is dirty, layout cache muse be in clear state.
      layoutCache.clearCache();
      if (dirtiedFunc != nullptr) {
        dirtiedFunc(this);
      }
    }
  }
  
  void HPNode::setDirtiedFunc(HPDirtiedFunc _dirtiedFunc) {
    dirtiedFunc = _dirtiedFunc;
  }
  
  void HPNode::setContext(void * _context) {
    context = _context;
  }
  
  void * HPNode::getContext() {
    return context;
  }
  
  bool HPNode::isLayoutDimDefined(FlexDirection axis) {
    return isDefined(result.dim[axisDim[axis]]);
  }
  
  void HPNode::setLayoutDim(FlexDirection axis, float value) {
    result.dim[axisDim[axis]] = value;
  }
  
  inline void HPNode::setLayoutDirection(HPDirection direction) {
    result.direction = direction;
  }
  
  inline HPDirection HPNode::getLayoutDirection() {
    return result.direction;
  }
  
  float HPNode::getLayoutDim(FlexDirection axis) {
    if (!isLayoutDimDefined(axis)) {
      return VALUE_UNDEFINED;
    }
    return result.dim[axisDim[axis]];
  }
  
  float HPNode::getMainAxisDim() {
    FlexDirection mainAxis = style.flexDirection;
    if (!isLayoutDimDefined(mainAxis)) {
      return VALUE_UNDEFINED;
    }
    return result.dim[axisDim[mainAxis]];
  }
  
  float HPNode::getStartBorder(FlexDirection axis) {
    return style.getStartBorder(axis);
  }
  
  float HPNode::getEndBorder(FlexDirection axis) {
    return style.getEndBorder(axis);
  }
  
  float HPNode::getStartPaddingAndBorder(FlexDirection axis) {
    return style.getStartPadding(axis) + style.getStartBorder(axis);
  }
  
  float HPNode::getEndPaddingAndBorder(FlexDirection axis) {
    return style.getEndPadding(axis) + style.getEndBorder(axis);
  }
  
  float HPNode::getPaddingAndBorder(FlexDirection axis) {
    return getStartPaddingAndBorder(axis) + getEndPaddingAndBorder(axis);
  }
  
  float HPNode::getStartMargin(FlexDirection axis) {
    return style.getStartMargin(axis);
  }
  
  float HPNode::getEndMargin(FlexDirection axis) {
    return style.getEndMargin(axis);
  }
  
  float HPNode::getMargin(FlexDirection axis) {
    return style.getStartMargin(axis) + style.getEndMargin(axis);
  }
  
  bool HPNode::isAutoStartMargin(FlexDirection axis) {
    return style.isAutoStartMargin(axis);
  }
  
  bool HPNode::isAutoEndMargin(FlexDirection axis) {
    return style.isAutoEndMargin(axis);
  }
  
  void HPNode::setLayoutStartMargin(FlexDirection axis, float value) {
    result.margin[axisStart[axis]] = value;
  }
  
  void HPNode::setLayoutEndMargin(FlexDirection axis, float value) {
    result.margin[axisEnd[axis]] = value;
  }
  
  inline float HPNode::getLayoutMargin(FlexDirection axis) {
    return getLayoutStartMargin(axis) + getLayoutEndMargin(axis);
  }
  
  float HPNode::getLayoutStartMargin(FlexDirection axis) {
    return
    isDefined(result.margin[axisStart[axis]]) ? result.margin[axisStart[axis]] : 0;
  }
  
  float HPNode::getLayoutEndMargin(FlexDirection axis) {
    return
    isDefined(result.margin[axisEnd[axis]]) ? result.margin[axisEnd[axis]] : 0;
  }
  
  /* If both axisStart and axisEnd are defined,
   * then use axisStart. Otherwise use which is defined.
   * @param axis flex direction
   * @param forAxisStart
   * 		  true  get relative value for axis start
   * 		  false get relative value for axis end
   */
  float HPNode::resolveRelativePosition(FlexDirection axis, bool forAxisStart) {
    if (style.positionType != PositionTypeRelative) {
      return 0.0f;
    }
  
    if (isDefined(style.getStartPosition(axis))) {
      float value = style.getStartPosition(axis);
      return forAxisStart ? value : -value;
    } else if (isDefined(style.getEndPosition(axis))) {
      float value = style.getEndPosition(axis);
      return forAxisStart ? -value : value;
    }
  
    return 0.0f;
  }
  
  void HPNode::setLayoutStartPosition(FlexDirection axis, float value,
                                      bool addRelativePosition) {
    if (addRelativePosition && style.positionType == PositionTypeRelative) {
      value += resolveRelativePosition(axis, true);
    }
  
    if (!FloatIsEqual(result.cachedPosition[axisStart[axis]], value)) {
      result.cachedPosition[axisStart[axis]] = value;
      setHasNewLayout(true);
    }
  
    result.position[axisStart[axis]] = value;
  }
  
  void HPNode::setLayoutEndPosition(FlexDirection axis, float value,
                                    bool addRelativePosition) {
    if (addRelativePosition && style.positionType == PositionTypeRelative) {
      value += resolveRelativePosition(axis, false);
    }
  
    if (!FloatIsEqual(result.cachedPosition[axisEnd[axis]], value)) {
      result.cachedPosition[axisEnd[axis]] = value;
      setHasNewLayout(true);
    }
  
    result.position[axisEnd[axis]] = value;
  }
  
  float HPNode::getLayoutStartPosition(FlexDirection axis) {
    return result.position[axisStart[axis]];
  }
  
  float HPNode::getLayoutEndPosition(FlexDirection axis) {
    return result.position[axisEnd[axis]];
  }
  
  //calculate main axis by refer this node's flex direction
  //and layout direction which resolved in resolveDirection.
  FlexDirection HPNode::resolveMainAxis() {
    FlexDirection mainAxis = style.flexDirection;
    HPDirection direction = getLayoutDirection();
    if (direction == DirectionRTL) {
      if (mainAxis == FLexDirectionRow) {
        return FLexDirectionRowReverse;
      } else if (mainAxis == FLexDirectionRowReverse) {
        return FLexDirectionRow;
      }
    }
    return mainAxis;
  }
  
  /* calculate cross axis by refer this node's flex properties
   * and layout direction which resolved in resolveDirection.
   * must use this method when cross axis alignment
   * to determine cross start & cross end
   */
  FlexDirection HPNode::resolveCrossAxis() {
    FlexDirection mainAxis = style.flexDirection;
    FlexDirection crossAxis;
    //cross axis's direction rely on flex wrap mode.
    if (isRowDirection(mainAxis)) {
      if (style.flexWrap == FlexWrapReverse) {
        crossAxis = FLexDirectionColumnReverse;
      } else {
        crossAxis = FLexDirectionColumn;
      }
    } else {
      if (style.flexWrap == FlexWrapReverse) {
        crossAxis = FLexDirectionRowReverse;
      } else {
        crossAxis = FLexDirectionRow;
      }
      //in this situation crossAxis is Row Direction
      //should think about node's layout direction(HPDirection)
      HPDirection direction = getLayoutDirection();
      if (direction == DirectionRTL) {
        if (crossAxis == FLexDirectionRow) {
          crossAxis = FLexDirectionRowReverse;
        } else if (crossAxis == FLexDirectionRowReverse) {
          crossAxis = FLexDirectionRow;
        }
      }
    }
    return crossAxis;
  }
  
  FlexAlign HPNode::getNodeAlign(HPNodeRef item) {
    ASSERT(item != nullptr);
    if (item->style.alignSelf == FlexAlignAuto) {
      return style.alignItems;
    }
    return item->style.alignSelf;
  }
  
  float HPNode::boundAxis(FlexDirection axis, float value) {
    float min = style.minDim[axisDim[axis]];
    float max = style.maxDim[axisDim[axis]];
    float boundValue = value;
    if (!isUndefined(max) && max >= 0.0 && boundValue > max) {
      boundValue = max;
    }
    if (!isUndefined(min) && min >= 0.0 && boundValue < min) {
      boundValue = min;
    }
    return boundValue;
  }
  
  inline HPDirection HPNode::resolveDirection(HPDirection parentDirection) {
    return
        style.direction == DirectionInherit ?
            (parentDirection > DirectionInherit ? parentDirection : DirectionLTR) :
            style.direction;
  }
  
  //called after resolveDirection
  void HPNode::resolveStyleValues() {
  //  if (!isDirty) {
  //    return;
  //  }
    FlexDirection mainAxis = resolveMainAxis();
    FlexDirection crossAxis = resolveCrossAxis();
    //set layout margin value
    //auto margins are treated as zero. may be modified during layout process
    setLayoutStartMargin(mainAxis, getStartMargin(mainAxis));
    setLayoutEndMargin(mainAxis, getEndMargin(mainAxis));
    setLayoutStartMargin(crossAxis, getStartMargin(crossAxis));
    setLayoutEndMargin(crossAxis, getEndMargin(crossAxis));
  
    //set layout padding value
    result.padding[axisStart[mainAxis]] = style.getStartPadding(mainAxis);
    result.padding[axisEnd[mainAxis]] = style.getEndPadding(mainAxis);
    result.padding[axisStart[crossAxis]] = style.getStartPadding(crossAxis);
    result.padding[axisEnd[crossAxis]] = style.getEndPadding(crossAxis);
  
    //set layout border value;
    result.border[axisStart[mainAxis]] = style.getStartBorder(mainAxis);
    result.border[axisEnd[mainAxis]] = style.getEndBorder(mainAxis);
    result.border[axisStart[crossAxis]] = style.getStartBorder(crossAxis);
    result.border[axisEnd[crossAxis]] = style.getEndBorder(crossAxis);
  }
  
  #ifdef  LAYOUT_TIME_ANALYZE
  static int layoutCount = 0;
  static int layoutCacheCount = 0;
  static int measureCount = 0;
  static int measureCacheCount = 0;
  #endif
  
  void HPNode::layout(float parentWidth, float parentHeight,
                      HPDirection parentDirection,
                      void * layoutContext) {
  #ifdef  LAYOUT_TIME_ANALYZE
    layoutCount = 0;
    layoutCacheCount = 0;
    measureCount = 0;
    measureCacheCount = 0;
  #endif
    if (isUndefined(
        style.flexBasis) && !isUndefined(style.dim[axisDim[style.flexDirection]])) {
      style.flexBasis = style.dim[axisDim[style.flexDirection]];
    }
  
    //if container not set itself width and parent width is set,
    //set container width  as parentWidth subtract margin
    bool styleWidthReset = false;
    if (isUndefined(style.dim[DimWidth]) && isDefined(parentWidth)) {
      float containerWidth = parentWidth - getMargin(FLexDirectionRow);
      style.setDim(DimWidth, containerWidth > 0.0f ? containerWidth : 0.0f);
      styleWidthReset = true;
    }
  
    bool styleHeightReset = false;
    if (isUndefined(style.dim[DimHeight]) && isDefined(parentHeight)) {
      float containerHeight = parentHeight - getMargin(FLexDirectionColumn);
      style.setDim(DimHeight, containerHeight > 0.0f ? containerHeight : 0.0f);
      styleHeightReset = true;
    }
    layoutImpl(parentWidth, parentHeight, parentDirection, LayoutActionLayout, layoutContext);
    if (styleWidthReset) {
      style.setDim(DimWidth, VALUE_UNDEFINED);
    }
    if (styleHeightReset) {
      style.setDim(DimHeight, VALUE_UNDEFINED);
    }
  
    //calculate container's position
    FlexDirection mainAxis = resolveMainAxis();
    FlexDirection crossAxis = resolveCrossAxis();
    setLayoutStartPosition(mainAxis, getStartMargin(mainAxis), true);
    setLayoutEndPosition(mainAxis, getEndMargin(mainAxis), true);
    setLayoutStartPosition(crossAxis, getStartMargin(crossAxis), true);
    setLayoutEndPosition(crossAxis, getEndMargin(crossAxis), true);
  
    //node 's layout is complete
    //convert its and its descendants position and size to a integer value.
  #ifndef ANDROID
    convertLayoutResult(0.0f, 0.0f);  //layout result convert has been taken in java . 3.8.2018. ianwang..
  #endif
  
  #ifdef  LAYOUT_TIME_ANALYZE
    HPLog(LogLevelDebug,"HippyLayoutTime layout: count %d cache %d, measure: count %d cache %d",
        layoutCount,layoutCacheCount,measureCount,measureCacheCount);
  #endif
  }
  
  //3.Determine the flex base size and hypothetical main size of each item
  void HPNode::calculateItemsFlexBasis(HPSize availableSize, void * layoutContext) {
    FlexDirection mainAxis = style.flexDirection;
    std::vector<HPNodeRef>& items = children;
    for (size_t i = 0; i < items.size(); i++) {
      HPNodeRef item = items[i];
      //for display none item, reset its and its descendants layout result.
      if (item->style.displayType == DisplayTypeNone) {
        item->resetLayoutRecursive();
        continue;
      }
      //https://stackoverflow.com/questions/34352140/what-are-the-differences-between-flex-basis-and-width
      //flex-basis has no effect on absolutely-positioned flex items. width and height properties
      //would be necessary.
      //Absolutely-positioned flex items do not participate in flex layout.
      if (item->style.positionType == PositionTypeAbsolute) {
        continue;
      }
      //3.Determine the flex base size and hypothetical main size of each item:
      //3.1 If the item has a definite used flex basis, that's the flex base size.
      if (isDefined(
          item->style.getFlexBasis()) && isDefined(style.dim[axisDim[mainAxis]])) {
        item->result.flexBaseSize = item->style.getFlexBasis();
      } else if (isDefined(item->style.dim[axisDim[mainAxis]])) {
        //flex-basis:auto:
        //When specified on a flex item, the auto keyword retrieves the value
        //of the main size property as the used flex-basis.
        //If that value is itself auto, then the used value is content.
        item->result.flexBaseSize = item->style.dim[axisDim[mainAxis]];
      } else {
        //3.2 Otherwise, size the item into the available space using its used flex basis
        //in place of its main size,
        float oldMainDim = item->style.getDim(mainAxis);
        //item->style.flexBasis is auto value
        item->style.setDim(mainAxis, item->style.flexBasis);
        item->layoutImpl(
            availableSize.width,
            availableSize.height,
            getLayoutDirection(),
            isRowDirection(mainAxis) ?
                LayoutActionMeasureWidth : LayoutActionMeasureHeight,
            layoutContext);
        item->style.setDim(mainAxis, oldMainDim);
  
        item->result.flexBaseSize =
        isDefined(item->result.dim[axisDim[mainAxis]]) ?
            item->result.dim[axisDim[mainAxis]] : 0;
      }
  
      //item->result.dim[axisDim[mainAxis]] = item->boundAxis(mainAxis, item->result.flexBasis);
      //The hypothetical main size is the item's flex base size clamped
      //according to its min and max main size properties (and flooring the content box size at zero).
      item->result.hypotheticalMainAxisSize = item->boundAxis(
          mainAxis, item->result.flexBaseSize);
      item->result.hypotheticalMainAxisMarginBoxSize = item->result
          .hypotheticalMainAxisSize + item->getMargin(mainAxis);
    }
  }
  
  bool HPNode::collectFlexLines(std::vector<FlexLine*>& flexLines,
                                HPSize availableSize) {
    std::vector<HPNodeRef>& items = children;
    bool sumHypotheticalMainSizeOverflow = false;
    float availableWidth =
        axisDim[style.flexDirection] == DimWidth ?
            availableSize.width : availableSize.height;
    if (isUndefined(availableWidth)) {
      availableWidth = INFINITY;
    }
  
    FlexLine * line = nullptr;
    int itemsSize = items.size();
    int i = 0;
    while (i < itemsSize) {
      HPNodeRef item = items[i];
      if (item->style.positionType == PositionTypeAbsolute
          || item->style.displayType == DisplayTypeNone) {
        //see HippyTest.dirty_mark_all_children_as_dirty_when_display_changes
        //when display changes.
        if (i == itemsSize - 1 && line != nullptr) {
          flexLines.push_back(line);
          break;
        }
        //
        i++;
        continue;
      }
  
      if (line == nullptr) {
        line = new FlexLine(this);
      }
  
      float leftSpace = availableWidth
          - (line->sumHypotheticalMainSize
              + item->result.hypotheticalMainAxisMarginBoxSize);
      if (leftSpace < 0) {
        //may be line wrap happened
        sumHypotheticalMainSizeOverflow = true;
      }
  
      if (style.flexWrap == FlexNoWrap) {
        line->addItem(item);
        if (i == itemsSize - 1) {
          flexLines.push_back(line);
          break;
        }
        i++;
      } else {
        if (leftSpace >= 0 || line->isEmpty()) {
          line->addItem(item);
          if (i == itemsSize - 1) {
            flexLines.push_back(line);
            line = nullptr;
          }
          i++;
        } else {
  
          flexLines.push_back(line);
          line = nullptr;
        }
      }
    }
  
    return sumHypotheticalMainSizeOverflow;
  }
  
  void HPNode::cacheLayoutOrMeasureResult(HPSize availableSize,
                                          HPSizeMode measureMode,
                                          FlexLayoutAction layoutAction) {
  
    HPSize resultSize = { result.dim[DimWidth], result.dim[DimHeight] };
    layoutCache.cacheResult(availableSize, resultSize, measureMode, layoutAction);
    if (layoutAction == LayoutActionLayout) {
      setDirty(false);
      setHasNewLayout(true);
      inInitailState = false;
    }
  }
  
  /*
   * availableWidth/availableHeight  has subtract its margin and padding.
   */
  void HPNode::layoutSingleNode(float availableWidth,
                                MeasureMode widthMeasureMode,
                                float availableHeight,
                                MeasureMode heightMeasureMode,
                                FlexLayoutAction layoutAction,
                                void * layoutContext) {
  
    if (widthMeasureMode == MeasureModeExactly
        && heightMeasureMode == MeasureModeExactly) {
      result.dim[DimWidth] = availableWidth
          + getPaddingAndBorder(FLexDirectionRow);
      result.dim[DimHeight] = availableHeight
          + getPaddingAndBorder(FLexDirectionColumn);
    } else {
      //measure text, image etc. content node;
      HPSize dim = { 0, 0 };
      bool needMeasure = true;
      if (style.flexGrow > 0 && style.flexShrink > 0 && parent
          && parent->childCount() == 1
          && !parent->style.isDimensionAuto(FLexDirectionRow)
          && !parent->style.isDimensionAuto(FLexDirectionColumn)) {
        //don't measure single grow shrink child
        //see HPMeasureTest.cpp dont_measure_single_grow_shrink_child
        needMeasure = false;
      }
  
      if (!needMeasure) {
        dim.width = availableWidth;
        dim.height = availableHeight;
      } else if (measure != nullptr && needMeasure) {
        dim = measure(this, availableWidth, widthMeasureMode, availableHeight,
                      heightMeasureMode, layoutContext);
      }
  
      result.dim[DimWidth] = boundAxis(
          FLexDirectionRow,
          widthMeasureMode == MeasureModeExactly ?
              (availableWidth + getPaddingAndBorder(FLexDirectionRow)) :
              (dim.width + getPaddingAndBorder(FLexDirectionRow)));
  
      result.dim[DimHeight] = boundAxis(
          FLexDirectionColumn,
          heightMeasureMode == MeasureModeExactly ?
              (availableHeight + getPaddingAndBorder(FLexDirectionColumn)) :
              (dim.height + getPaddingAndBorder(FLexDirectionColumn)));
    }
  
    HPSize availableSize = { availableWidth, availableHeight };
    HPSizeMode measureMode = { widthMeasureMode, heightMeasureMode };
    cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
  }
  
  //reference: https://www.w3.org/TR/css-flexbox-1/#layout-algorithm
  void HPNode::layoutImpl(float parentWidth, float parentHeight,
                          HPDirection parentDirection,
                          FlexLayoutAction layoutAction,
                          void * layoutContext) {
  #ifdef  LAYOUT_TIME_ANALYZE
    if(layoutAction == LayoutActionLayout) {
      layoutCount++;
    } else {
      measureCount++;
    }
  #endif
  
    HPDirection direction = resolveDirection(parentDirection);
    if (getLayoutDirection() != direction) {
      setLayoutDirection(direction);
      layoutCache.clearCache();
      resolveStyleValues();
    }
  
    FlexDirection mainAxis = style.flexDirection;
    bool performLayout = layoutAction == LayoutActionLayout;
    if (isDefined(parentWidth)) {
      parentWidth -= getMargin(FLexDirectionRow);
      parentWidth = parentWidth >= 0.0f ? parentWidth : 0.0f;
    }
  
    if (isDefined(parentHeight)) {
      parentHeight -= getMargin(FLexDirectionColumn);
      parentHeight = parentHeight >= 0.0f ? parentHeight : 0.0f;
    }
  
    //get node dim from style
    float nodeWidth =
    isDefined(style.dim[DimWidth]) ?
        boundAxis(FLexDirectionRow, style.dim[DimWidth]) : VALUE_UNDEFINED;
  
    float nodeHeight =
    isDefined(style.dim[DimHeight]) ?
        boundAxis(FLexDirectionColumn, style.dim[DimHeight]) :
        VALUE_UNDEFINED;
  
    //layoutMeasuredWidth  layoutMeasuredHeight used in
    //"Determine the flex base size and hypothetical main size of each item"
    if (layoutAction == LayoutActionMeasureWidth && isDefined(nodeWidth)) {
  #ifdef  LAYOUT_TIME_ANALYZE
      measureCacheCount++;
  #endif
      result.dim[DimWidth] = nodeWidth;
      return;
    } else if (layoutAction == LayoutActionMeasureHeight && isDefined(nodeHeight)) {
  #ifdef  LAYOUT_TIME_ANALYZE
      measureCacheCount++;
  #endif
      result.dim[DimHeight] = nodeHeight;
      return;
    }
  
    //9.2.Line Length Determination
    //Determine the available main and cross space for the flex items.
    //For each dimension, if that dimension of the flex container's content box is a definite size,
    //use that; if that dimension of the flex container is being sized under a min or max-content constraint,
    //the available space in that dimension is that constraint;
    //otherwise, subtract the flex container's margin, border, and padding
    //from the space available to the flex container in that dimension and use that value.
    //This might result in an infinite value.
  
    //get available size for layout or measure
    float availableWidth = VALUE_UNDEFINED;
    if (isDefined(nodeWidth)) {
      availableWidth = nodeWidth - getPaddingAndBorder(FLexDirectionRow);
    } else if (isDefined(parentWidth)) {
      availableWidth = parentWidth - getPaddingAndBorder(FLexDirectionRow);
    }
  
    float availableHeight = VALUE_UNDEFINED;
    if (isDefined(nodeHeight)) {
      availableHeight = nodeHeight - getPaddingAndBorder(FLexDirectionColumn);
    } else if (isDefined(parentHeight)) {
      availableHeight = parentHeight - getPaddingAndBorder(FLexDirectionColumn);
    }
  
    if (isDefined(style.maxDim[DimWidth])) {
      if (FloatIsEqual(style.maxDim[DimWidth], style.minDim[DimWidth])) {
        style.dim[DimWidth] = style.minDim[DimWidth];
      }
      float maxDimWidth = style.maxDim[DimWidth]
          - getPaddingAndBorder(FLexDirectionRow);
      if (maxDimWidth >= 0.0f && maxDimWidth < NanAsINF(availableWidth)) {
        availableWidth = maxDimWidth;
      }
    }
  
    if (isDefined(style.maxDim[DimHeight])) {
      if (FloatIsEqual(style.maxDim[DimHeight], style.minDim[DimHeight])) {
        style.dim[DimHeight] = style.minDim[DimHeight];
      }
      float maxDimHeight = style.maxDim[DimHeight]
          - getPaddingAndBorder(FLexDirectionColumn);
      if (maxDimHeight >= 0.0f && maxDimHeight < NanAsINF(availableHeight)) {
        availableHeight = maxDimHeight;
      }
    }
  
    //available size to layout...
    availableWidth = availableWidth < 0.0f ? 0.0f : availableWidth;
    availableHeight = availableHeight < 0.0f ? 0.0f : availableHeight;
  
    MeasureMode widthMeasureMode = MeasureModeUndefined;
    if (isDefined(style.dim[DimWidth])) {
      widthMeasureMode = MeasureModeExactly;
    } else if (isDefined(availableWidth)) {
      if (parent && parent->style.isOverflowScroll()
          && isRowDirection(parent->style.flexDirection)) {
        widthMeasureMode = MeasureModeUndefined;
        availableWidth = VALUE_AUTO;
      } else {
        widthMeasureMode = MeasureModeAtMost;
      }
    }
  
    MeasureMode heightMeasureMode = MeasureModeUndefined;
    if (isDefined(style.dim[DimHeight])) {
      heightMeasureMode = MeasureModeExactly;
    } else if (isDefined(availableHeight)) {
      if (parent && parent->style.isOverflowScroll()
          && isColumnDirection(parent->style.flexDirection)) {
        heightMeasureMode = MeasureModeUndefined;
        availableHeight = VALUE_AUTO;
      } else {
        heightMeasureMode = MeasureModeAtMost;
      }
    }
  
    HPSize availableSize = { availableWidth, availableHeight };
    HPSizeMode measureMode = { widthMeasureMode, heightMeasureMode };
    MeasureResult* cacheResult = layoutCache.getCachedMeasureResult(
        availableSize, measureMode, layoutAction, measure != nullptr);
    if (cacheResult != nullptr) {
      //set Result....
      switch (layoutAction) {
        case LayoutActionMeasureWidth:
  #ifdef  LAYOUT_TIME_ANALYZE
          measureCacheCount++;
  #endif
          ASSERT(isDefined(cacheResult->resultSize.width));
          result.dim[DimWidth] = cacheResult->resultSize.width;
          break;
        case LayoutActionMeasureHeight:
  #ifdef  LAYOUT_TIME_ANALYZE
          measureCacheCount++;
  #endif
          ASSERT(isDefined(cacheResult->resultSize.height));
          result.dim[DimHeight] = cacheResult->resultSize.height;
          break;
        case LayoutActionLayout:
  
          //if it's a measure node and cache result cached by LayoutActionMeasureWidth or LayoutActionMeasureHeight
          //so this is first layout for current Measure Node, set hasNewLayout as true,
          //if not, this node's layout result value has been fetched to java and set hasNewLayout false in FlexNode.cc
          //so not set hasNewLayout as true to avoid JNI call again in FLexNode.cc
          if (cacheResult->layoutAction
              != LayoutActionLayout&& measure != nullptr) {
            //need assign result size if layoutAction is different 3.14.2018
            result.dim[DimWidth] = cacheResult->resultSize.width;
            result.dim[DimHeight] = cacheResult->resultSize.height;
            cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
          } else {
  #ifdef  LAYOUT_TIME_ANALYZE
            layoutCacheCount++;
  #endif
            //do nothing..
            //layoutCache.cachedLayout object is last layout result.
            //used to determine need layout or not.
          }
  
          //if it's a measure node , layout could be cache by LayoutActionMeasureWidth
          //or LayoutActionMeasureHeight,so in this case, we need set dirty as false;
          setDirty(false);
  
          break;
        default:
          break;
      }
      return;
    }
    //before layout set result's hadOverflow as false.
    if (layoutAction == LayoutActionLayout) {
      result.hadOverflow = false;
    }
    //single element measure width and height
    if ((children.size() == 0)) {
      layoutSingleNode(availableWidth, widthMeasureMode, availableHeight,
                       heightMeasureMode, layoutAction, layoutContext);
      return;
    }
    //3.Determine the flex base size and hypothetical main size of each item
    calculateItemsFlexBasis(availableSize, layoutContext);
    //9.3. Main Size Determination
    // 5. Collect flex items into flex lines:
    std::vector<FlexLine *> flexLines;
    bool sumHypotheticalMainSizeOverflow = collectFlexLines(flexLines,
                                                            availableSize);
  
    //get max line's  main size
    float maxSumItemsMainSize = 0;
    for (size_t i = 0; i < flexLines.size(); i++) {
      if (flexLines[i]->sumHypotheticalMainSize > maxSumItemsMainSize) {
        maxSumItemsMainSize = flexLines[i]->sumHypotheticalMainSize;
      }
    }
  
    // 4. Determine the main size of the flex container using the rules of the formatting context
    //in which it participates. For this computation, auto margins on flex items are treated as 0.
    //TODO:: if has set , what to do for next run in determineCrossAxisSize's  layoutImpl
    float containerInnerMainSize = 0.0f;
    if (isDefined(style.dim[axisDim[mainAxis]])) {
      //MeasureModeExactly
      containerInnerMainSize = style.dim[axisDim[mainAxis]]
          - getPaddingAndBorder(mainAxis);
    } else {
      if (sumHypotheticalMainSizeOverflow) {	//MeasureModeAtMost
        //if sum of hypothetical MainSize > available size;
        float mainInnerSize =
            axisDim[mainAxis] == DimWidth ?
                availableSize.width : availableSize.height;
  
        if (maxSumItemsMainSize > mainInnerSize && !style.isOverflowScroll()) {
          if (parent && parent->getNodeAlign(this) == FlexAlignStretch
              && axisDim[mainAxis] == axisDim[parent->resolveCrossAxis()]
              && style.positionType != PositionTypeAbsolute) {
            //it this node has text child and node main axis(width) is stretch
            //,cross axis length(height) is undefined
            //text can has multi-line, text's height can affect parent's height
            //in this situation, use availableSize if possible
            containerInnerMainSize = mainInnerSize;
          } else {
            containerInnerMainSize = maxSumItemsMainSize;
          }
        } else {
          containerInnerMainSize = mainInnerSize;
        }
      } else {
        containerInnerMainSize = maxSumItemsMainSize;
      }
    }
    result.dim[axisDim[mainAxis]] = boundAxis(
        mainAxis, containerInnerMainSize + getPaddingAndBorder(mainAxis));
    //return if its just in measure
    if ((layoutAction == LayoutActionMeasureWidth && isRowDirection(mainAxis))
        || (layoutAction == LayoutActionMeasureHeight
            && isColumnDirection(mainAxis))) {
      //cache layout result & state...
      cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
      //free flexLines, allocate in collectFlexLines.
      //TODO:: opt.
      for (size_t i = 0; i < flexLines.size(); i++) {
        delete flexLines[i];
      }
      return;
    }
  
    //6. Resolving Flexible Lengths
    //To resolve the flexible lengths of the items within a flex line:
    //TODO:://this's the only place that confirm child items main axis size, see item->setLayoutDim
    determineItemsMainAxisSize(flexLines, layoutAction);
  
    //9.4. Cross Size Determination
    //calculate line's cross size in flexLines
    //TODO:: The real place that Determine
    //the flex container's used cross size is at step 15.
  
    float sumLinesCrossSize = determineCrossAxisSize(flexLines, availableSize,
                                                     layoutAction, layoutContext);
  
    if (!performLayout) {
      //TODO::for measure, I put the calculate of flex container's cross size in here..
      //TODO::why must in step 15 in W3 flex layout algorithm
      //noted by ianwang 12.30.2017.
  
      //15.Determine the flex container's used cross size:
      //If the cross size property is a definite size, use that,
      //clamped by the min and max cross size properties of the flex container.
      //Otherwise, use the sum of the flex lines' cross sizes,
      //clamped by the min and max cross size properties of the flex container.
      FlexDirection crossAxis = resolveCrossAxis();
      float crossDimSize;
      if (isDefined(style.dim[axisDim[crossAxis]])) {
        crossDimSize = style.dim[axisDim[crossAxis]];
      } else {
        crossDimSize = (sumLinesCrossSize + getPaddingAndBorder(crossAxis));
      }
      result.dim[axisDim[crossAxis]] = boundAxis(crossAxis, crossDimSize);
      //cache layout result & state...
      cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
  
      //free flexLines, allocate in collectFlexLines.
      for (size_t i = 0; i < flexLines.size(); i++) {
        delete flexLines[i];
      }
      return;
    }
  
    //9.5. Main-Axis Alignment
    mainAxisAlignment(flexLines);
  
    //9.6. Cross-Axis Alignment
    //if contianer's innerCross size not defined,
    //then it will be determined in step 15 of crossAxisAlignment
    crossAxisAlignment(flexLines);
  
    //free flexLines, allocate in collectFlexLines.
    for (size_t i = 0; i < flexLines.size(); i++) {
      delete flexLines[i];
    }
  
    //cache layout result & state...
    cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction);
    //layout fixed elements...
    layoutFixedItems(measureMode, layoutContext);
  
    return;
  }
  
  //9.4. Cross Size Determination
  float HPNode::determineCrossAxisSize(std::vector<FlexLine*>& flexLines,
                                       HPSize availableSize,
                                       FlexLayoutAction layoutAction,
                                       void * layoutContext) {
    FlexDirection mainAxis = style.flexDirection;
    FlexDirection crossAxis = resolveCrossAxis();
    float sumLinesCrossSize = 0;
    for (size_t i = 0; i < flexLines.size(); i++) {
      FlexLine * line = flexLines[i];
      float maxItemCrossSize = 0;
      for (size_t j = 0; j < line->items.size(); j++) {
        HPNodeRef item = line->items[j];
        //item's main axis size has been determined.
        //try to calculate the hypothetical cross size of each item
        //that would be stored in result.dim[crossAxis]
        //align stretch may be modify this value in the later step.
  
        //WARNING TODO::this is the only place that the Recursive flex layout happen.
        //7.Determine the hypothetical cross size of each item by performing layout with
        //the used main size and the available space, treating auto as fit-content.
        FlexLayoutAction oldLayoutAction = layoutAction;
        if (getNodeAlign(item) == FlexAlignStretch
            && item->style.isDimensionAuto(crossAxis)
            && !item->style.hasAutoMargin(crossAxis)
            && layoutAction == LayoutActionLayout) {
          //Delay layout for stretch item, do layout later in step 11.
          layoutAction =
              axisDim[crossAxis] == DimWidth ?
                  LayoutActionMeasureWidth : LayoutActionMeasureHeight;
        }
        float oldMainDim = item->style.getDim(mainAxis);
        item->style.setDim(mainAxis, item->getLayoutDim(mainAxis));
        item->layoutImpl(availableSize.width, availableSize.height,
                         getLayoutDirection(), layoutAction, layoutContext);
        item->style.setDim(mainAxis, oldMainDim);
        layoutAction = oldLayoutAction;
        //if child item had overflow , then transfer this state to its parent.
        //see HippyTest_HadOverflowTests.spacing_overflow_in_nested_nodes in ./tests/HPHadOverflowTest.cpp
        result.hadOverflow = result.hadOverflow | item->result.hadOverflow;
  
        //TODO:: if need support baseline  add here
        //8.Calculate the cross size of each flex line.
        //1)Collect all the flex items whose inline-axis is parallel to the main-axis,
        //whose align-self is baseline, and whose cross-axis margins are both non-auto.
        //Find the largest of the distances between each item's baseline and its hypothetical
        //outer cross-start edge, and the largest of the distances between each item's baseline
        //and its hypothetical outer cross-end edge, and sum these two values.
        //2)Among all the items not collected by the previous step, find the largest outer
        //hypothetical cross size.
        //3)The used cross-size of the flex line is the largest of the numbers found
        //in the previous two steps and zero.
  
        //Max item cross size
        float itemOutCrossSize = item->getLayoutDim(crossAxis)
            + item->getMargin(crossAxis);
        if (itemOutCrossSize > maxItemCrossSize) {
          maxItemCrossSize = itemOutCrossSize;
        }
      }
  
      //8.Calculate the cross size of each flex line.
      //clip current container cross axis size..
      maxItemCrossSize = boundAxis(crossAxis, maxItemCrossSize);
      line->lineCrossSize = maxItemCrossSize;
      sumLinesCrossSize += maxItemCrossSize;
  
      //single line , set line height as container inner height
      if (flexLines.size() == 1 && isDefined(style.dim[axisDim[crossAxis]])) {
        //if following assert is true, means front-end's style is in unsuitable state ..
        //such as main axis is undefined but set flex-wrap as FlexWrap.
        //ASSERT(style.flexWrap == FlexNoWrap);
        float innerCrossSize = boundAxis(crossAxis, style.dim[axisDim[crossAxis]])
            - getPaddingAndBorder(crossAxis);
  
        line->lineCrossSize = innerCrossSize;
        sumLinesCrossSize = innerCrossSize;
      }
    }
  
    //9.Handle 'align-content: stretch' for lines
    if (isDefined(style.dim[axisDim[crossAxis]])
        && style.alignContent == FlexAlignStretch) {
  
      float innerCrossSize = boundAxis(crossAxis, style.dim[axisDim[crossAxis]])
          - getPaddingAndBorder(crossAxis);
      if (sumLinesCrossSize < innerCrossSize) {
        for (size_t i = 0; i < flexLines.size(); i++) {
          FlexLine * line = flexLines[i];
          line->lineCrossSize += (innerCrossSize - sumLinesCrossSize)
              / flexLines.size();
        }
      }
    }
  
    //11.Determine the used cross size of each flex item
    //Think about item align-self: stretch
    for (size_t i = 0; i < flexLines.size(); i++) {
      FlexLine * line = flexLines[i];
      for (size_t j = 0; j < line->items.size(); j++) {
        HPNodeRef item = line->items[j];
  
        //1): If a flex item has align-self: stretch, its computed cross size property is auto,
        //    and neither of its cross-axis margins are auto, the used outer cross size is the used
        //    cross size of its flex line, clamped according to the item's
        //    min and max cross size properties.
        //2):Otherwise,the used cross size is the item's hypothetical cross size.
        if (getNodeAlign(item) == FlexAlignStretch
            && item->style.isDimensionAuto(crossAxis)
            && !item->style.hasAutoMargin(crossAxis)) {
  
          item->result.dim[axisDim[crossAxis]] = item->boundAxis(
              crossAxis, line->lineCrossSize - item->getMargin(crossAxis));
          //If the flex item has align-self: stretch, redo layout for its contents,
          //treating this used size as its definite cross size
          //so that percentage-sized children can be resolved.
          float oldMainDim = item->style.getDim(mainAxis);
          float oldCrossDim = item->style.getDim(crossAxis);
          item->style.setDim(mainAxis, item->getLayoutDim(mainAxis));
          item->style.setDim(crossAxis, item->getLayoutDim(crossAxis));
          item->layoutImpl(availableSize.width, availableSize.height,
                           getLayoutDirection(), layoutAction, layoutContext);
          item->style.setDim(mainAxis, oldMainDim);
          item->style.setDim(crossAxis, oldCrossDim);
  
        } else {
          //Otherwise, the used cross size is the item's hypothetical cross size.
          //see the step7.
          //item's hypothetical cross size. has been set in result.dim[axisDim[crossAxis]].
        }
      }
    }
  
    //TODO::Why Determine  the flex container's used cross size in step 15.
    return sumLinesCrossSize;
  }
  
  //See  9.7 Resolving Flexible Lengths.
  void HPNode::determineItemsMainAxisSize(std::vector<FlexLine *> & flexLines,
                                          FlexLayoutAction layoutAction) {
    FlexDirection mainAxis = style.flexDirection;
    float mainAxisContentSize = result.dim[axisDim[mainAxis]]
        - getPaddingAndBorder(mainAxis);
    // 6. Resolve the flexible lengths of all the flex items to find their used main size (see section 9.7.)
    for (size_t i = 0; i < flexLines.size(); i++) {
      FlexLine * line = flexLines[i];
      line->SetContainerMainInnerSize(mainAxisContentSize);
      line->FreezeInflexibleItems(layoutAction);
      while (!line->ResolveFlexibleLengths()) {
        ASSERT(line->totalFlexGrow >= 0);
        ASSERT(line->totalFlexGrow >= 0);
      }
  
      if (layoutAction == LayoutActionLayout && line->remainingFreeSpace < 0) {
        result.hadOverflow = true;
      }
    }
  }
  
  //9.5 Main-Axis Alignment
  void HPNode::mainAxisAlignment(std::vector<FlexLine *> & flexLines) {
    //TODO:: RTL::
    // 12. Distribute any remaining free space. For each flex line:
    FlexDirection mainAxis = style.flexDirection;
    float mainAxisContentSize = getLayoutDim(mainAxis)
        - getPaddingAndBorder(mainAxis);
    for (size_t i = 0; i < flexLines.size(); i++) {
      FlexLine * line = flexLines[i];
      line->SetContainerMainInnerSize(mainAxisContentSize);
      line->alignItems();
    }
  }
  
  //9.6 Cross-Axis Alignment
  void HPNode::crossAxisAlignment(std::vector<FlexLine *> & flexLines) {
    FlexDirection crossAxis = resolveCrossAxis();
    float sumLinesCrossSize = 0;
    int linesCount = flexLines.size();
    for (int i = 0; i < linesCount; i++) {
      FlexLine * line = flexLines[i];
      sumLinesCrossSize += line->lineCrossSize;
      for (size_t j = 0; j < line->items.size(); j++) {
        HPNodeRef item = line->items[j];
        //13.Resolve cross-axis auto margins. If a flex item has auto cross-axis margins:
        float remainingFreeSpace = line->lineCrossSize
            - item->result.dim[axisDim[crossAxis]] - item->getMargin(crossAxis);
        if (remainingFreeSpace > 0) {
          //If its outer cross size (treating those auto margins as zero) is less than
          //the cross size of its flex line, distribute the difference
          //in those sizes equally to the auto margins.
          if (item->isAutoStartMargin(crossAxis)
              && item->isAutoEndMargin(crossAxis)) {
            item->setLayoutStartMargin(crossAxis, remainingFreeSpace / 2);
            item->setLayoutEndMargin(crossAxis, remainingFreeSpace / 2);
          } else if (item->isAutoStartMargin(crossAxis)) {
            item->setLayoutStartMargin(crossAxis, remainingFreeSpace);
          } else if (item->isAutoEndMargin(crossAxis)) {
            item->setLayoutEndMargin(crossAxis, remainingFreeSpace);
          } else {
            //For margin:: assign style value to result value at this place..
            item->setLayoutStartMargin(crossAxis,
                                       item->getStartMargin(crossAxis));
            item->setLayoutEndMargin(crossAxis, item->getEndMargin(crossAxis));
          }
        } else {
          //Otherwise, if the block-start or inline-start margin
          //(whichever is in the cross axis) is auto, set it to zero.
          //Set the opposite margin so that the outer cross size of the
          //item equals the cross size of its flex line.
          item->setLayoutStartMargin(crossAxis, item->getStartMargin(crossAxis));
          item->setLayoutEndMargin(crossAxis, item->getEndMargin(crossAxis));
        }
  
        //14.Align all flex items along the cross-axis per align-self,
        //if neither of the item's cross-axis margins are auto.
        //calculate item's offset in its line by style align-self
        remainingFreeSpace = line->lineCrossSize
            - item->result.dim[axisDim[crossAxis]]
            - item->getLayoutMargin(crossAxis);
        float offset = item->getLayoutStartMargin(crossAxis);
        switch (getNodeAlign(item)) {  //when align self is auto , it overwrite by align items
          case FlexAlignStart:
            break;
          case FlexAlignCenter:
            offset += remainingFreeSpace / 2;
            break;
          case FlexAlignEnd:
            offset += remainingFreeSpace;
            break;
            //TODO:: case baseline alignment
          default:
            break;
        }
        //include (axisStart[crossAxis] == CSSTop) and (axisStart[crossAxis] == CSSBottom)
        //For temporary store. use false parameter
        item->setLayoutStartPosition(crossAxis, offset, false);
      }
    }
  
    //15.Determine  the flex container's used cross size:
    //If the cross size property is a definite size, use that,
    //clamped by the min and max cross size properties of the flex container.
    //Otherwise, use the sum of the flex lines' cross sizes,
    //clamped by the min and max cross size properties of the flex container.
  
    float crossDimSize;
    if (isDefined(style.dim[axisDim[crossAxis]])) {
      crossDimSize = style.dim[axisDim[crossAxis]];
    } else {
      crossDimSize = (sumLinesCrossSize + getPaddingAndBorder(crossAxis));
    }
    result.dim[axisDim[crossAxis]] = boundAxis(crossAxis, crossDimSize);
  
    //when container's cross size determined align all flex lines by align-content
    //16.Align all flex lines per align-content
    float innerCrossSize = result.dim[axisDim[crossAxis]]
        - getPaddingAndBorder(crossAxis);
    float remainingFreeSpace = innerCrossSize - sumLinesCrossSize;
    float offset = getStartPaddingAndBorder(crossAxis);
    float space = 0;
    switch (style.alignContent) {
      case FlexAlignStart:
        break;
      case FlexAlignCenter:
        offset += remainingFreeSpace / 2;
        break;
      case FlexAlignEnd:
        offset += remainingFreeSpace;
        break;
      case FlexAlignSpaceBetween:
        space = remainingFreeSpace / (linesCount - 1);
        break;
      case FlexAlignSpaceAround:
        space = remainingFreeSpace / linesCount;
        offset += space / 2;
        break;
      default:
        break;
    }
  
    //flex-end::
    //The cross-end margin edge of the flex item is placed flush with the cross-end edge of the line.
    //crossAxisPostionStart calculated along the cross axis direction.
    float crossAxisPostionStart = offset;
    for (int i = 0; i < linesCount; i++) {
      FlexLine * line = flexLines[i];
      for (size_t j = 0; j < line->items.size(); j++) {
        HPNodeRef item = line->items[j];
        //include (axisStart[crossAxis] == CSSTop) and (axisStart[crossAxis] == CSSBottom)
        //getLayoutStartPosition set in step 14.
        item->setLayoutStartPosition(
            crossAxis,
            crossAxisPostionStart + item->getLayoutStartPosition(crossAxis));
        //layout start position has use relative ,so end position not use it ,use false parameter.
        item->setLayoutEndPosition(
            crossAxis,
            (getLayoutDim(crossAxis) - item->getLayoutStartPosition(crossAxis)
                - item->getLayoutDim(crossAxis)),
            false);
  
      }
  
      crossAxisPostionStart += line->lineCrossSize + space;
    }
  }
  
  //4.1. Absolutely-Positioned Flex Children
  //As it is out-of-flow, an absolutely-positioned child of a flex container
  //does not participate in flex layout.
  //The static position of an absolutely-positioned child of a flex container
  //is determined such that the child is positioned as if it were the sole flex item in the flex container,
  //assuming both the child and the flex container were fixed-size boxes of their used size.
  //For this purpose, auto margins are treated as zero.
  void HPNode::layoutFixedItems(HPSizeMode measureMode, void * layoutContext) {
    FlexDirection mainAxis = resolveMainAxis();
    FlexDirection crossAxis = resolveCrossAxis();
    std::vector<HPNodeRef>& items = children;
    for (size_t i = 0; i < items.size(); i++) {
      HPNodeRef item = items[i];
      //for display none item, reset its layout result.
      if (item->style.displayType == DisplayTypeNone) {
        item->resetLayoutRecursive();
        continue;
      }
      if (item->style.positionType != PositionTypeAbsolute) {
        continue;
      }
  
      float parentWidth = getLayoutDim(FLexDirectionRow)
          - getPaddingAndBorder(FLexDirectionRow);
      float parentHeight = getLayoutDim(FLexDirectionColumn)
          - getPaddingAndBorder(FLexDirectionColumn);
  
      float itemOldStyleDimMainAxis = item->style.getDim(mainAxis);
      float itemOldStyleDimCrossAxis = item->style.getDim(crossAxis);
  
      if (isUndefined(itemOldStyleDimMainAxis)
          && isDefined(item->style.getStartPosition(mainAxis))
          && isDefined(item->style.getEndPosition(mainAxis))) {
          item->style.setDim(
              mainAxis,
              (getLayoutDim(mainAxis) - style.getStartBorder(mainAxis)
                  - style.getEndBorder(mainAxis)
                  - item->style.getStartPosition(mainAxis)
                  - item->style.getEndPosition(mainAxis)
                  - item->getMargin(mainAxis)));
      }
  
      if (isUndefined(itemOldStyleDimCrossAxis)
          && isDefined(item->style.getStartPosition(crossAxis))
          && isDefined(item->style.getEndPosition(crossAxis))) {
          item->style.setDim(
              crossAxis,
              (getLayoutDim(crossAxis) - style.getStartBorder(crossAxis)
                  - style.getEndBorder(crossAxis)
                  - item->style.getStartPosition(crossAxis)
                  - item->style.getEndPosition(crossAxis)
                  - item->getMargin(crossAxis)));
      }
  
  
      item->layoutImpl(parentWidth, parentHeight, getLayoutDirection(),
                       LayoutActionLayout, layoutContext);
      //recover item's previous style value
      item->style.setDim(mainAxis, itemOldStyleDimMainAxis);
      item->style.setDim(crossAxis, itemOldStyleDimCrossAxis);
      //after layout, calculate fix item 's postion
      //1) for main axis
      calculateFixedItemPosition(item, mainAxis);
      //2)for cross axis
      calculateFixedItemPosition(item, crossAxis);
    }
  }
  
  //when item's layout complete, update fixed item's position on Specified axis
  //called in layoutFixedItems
  //should be called twice, one for main axis ,one for cross axis
  void HPNode::calculateFixedItemPosition(HPNodeRef item, FlexDirection axis) {
    if (isDefined(item->style.getStartPosition(axis))) {
      item->setLayoutStartPosition(
          axis,
          getStartBorder(axis) + item->getLayoutStartMargin(axis)
              + item->style.getStartPosition(axis));
      item->setLayoutEndPosition(
          axis,
          getLayoutDim(axis) - item->getLayoutStartPosition(axis)
              - item->getLayoutDim(axis));
  
    } else if (isDefined(item->style.getEndPosition(axis))) {
      item->setLayoutEndPosition(
          axis,
          getEndBorder(axis) + item->getLayoutEndMargin(axis)
              + item->style.getEndPosition(axis));
      item->setLayoutStartPosition(
          axis,
          getLayoutDim(axis) - item->getLayoutEndPosition(axis)
              - item->getLayoutDim(axis));
    } else {
      float remainingFreeSpace = getLayoutDim(axis) - getPaddingAndBorder(axis)
          - item->getLayoutDim(axis);
      float offset = getStartPaddingAndBorder(axis);
      FlexAlign alignMode =
          (axis == resolveMainAxis() ? style.justifyContent : getNodeAlign(item));
      switch (alignMode) {
        case FlexAlignStart:
          break;
        case FlexAlignCenter:
          offset += remainingFreeSpace / 2;
          break;
        case FlexAlignEnd:
          offset += remainingFreeSpace;
          break;
        default:
          break;
      }
  
      item->setLayoutStartPosition(
          axis,
          getStartPaddingAndBorder(axis) + item->getLayoutStartMargin(axis)
              + offset);
      item->setLayoutEndPosition(
          axis,
          getLayoutDim(axis) - item->getLayoutStartPosition(axis)
              - item->getLayoutDim(axis));
    }
  }
  
  
  //convert position and dimension values to integer value..
  //absLeft, absTop is mainly think about the influence of parent's Fraction offset
  //for example: if parent's Fraction offset is 0.3
  //and current child offset is 0.4
  //then the child's absolute offset  is 0.7.
  //if use roundf , roundf(0.7) == 1
  //so we need absLeft, absTop  parameter
  void HPNode::convertLayoutResult(float absLeft, float absTop) {
    if (!hasNewLayout()) {
      return;
    }
    const float left = result.position[CSSLeft];
    const float top = result.position[CSSTop];
    const float width = result.dim[DimWidth];
    const float height = result.dim[DimHeight];
  
    absLeft += left;
    absTop += top;
    bool isTextNode = style.nodeType == NodeTypeText;
    result.position[CSSLeft] = HPRoundValueToPixelGrid(left, false, isTextNode);
    result.position[CSSTop] = HPRoundValueToPixelGrid(top, false, isTextNode);
  
    const bool hasFractionalWidth = !FloatIsEqual(fmodf(width, 1.0), 0)
        && !FloatIsEqual(fmodf(width, 1.0), 1.0);
    const bool hasFractionalHeight = !FloatIsEqual(fmodf(height, 1.0), 0)
        && !FloatIsEqual(fmodf(height, 1.0), 1.0);
  
    const float absRight = absLeft + width;
    const float absBottom = absTop + height;
    result.dim[DimWidth] = HPRoundValueToPixelGrid(
        absRight, (isTextNode && hasFractionalWidth),
        (isTextNode && !hasFractionalWidth))
        - HPRoundValueToPixelGrid(absLeft, false, isTextNode);
  
    result.dim[DimHeight] = HPRoundValueToPixelGrid(
        absBottom, (isTextNode && hasFractionalHeight),
        (isTextNode && !hasFractionalHeight))
        - HPRoundValueToPixelGrid(absTop, false, isTextNode);
    std::vector<HPNodeRef>& items = children;
    for (size_t i = 0; i < items.size(); i++) {
      HPNodeRef item = items[i];
      item->convertLayoutResult(absLeft, absTop);
    }
  }
  
  