/**
 * Copyright (c) 2014-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// @Generated by gentest/gentest.rb from gentest/fixtures/YGPercentageTest.html

#include <gtest/gtest.h>
#include <yoga/Yoga.h>

TEST(YogaTest, cloning_shared_root) {
  const YGConfigRef config = YGConfigNew();

  const YGNodeRef root = YGNodeNewWithConfig(config);
  YGNodeStyleSetWidth(root, 100);
  YGNodeStyleSetHeight(root, 100);

  const YGNodeRef root_child0 = YGNodeNewWithConfig(config);
  YGNodeStyleSetFlexGrow(root_child0, 1);
  YGNodeStyleSetFlexBasis(root_child0, 50);
  YGNodeInsertChild(root, root_child0, 0);

  const YGNodeRef root_child1 = YGNodeNewWithConfig(config);
  YGNodeStyleSetFlexGrow(root_child1, 1);
  YGNodeInsertChild(root, root_child1, 1);
  YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);

  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
  ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root));
  ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root));

  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
  ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0));
  ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0));

  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1));
  ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1));
  ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1));
  ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1));

  const YGNodeRef root2 = YGNodeClone(root);
  YGNodeStyleSetWidth(root2, 100);

  ASSERT_EQ(2, YGNodeGetChildCount(root2));
  // The children should have referential equality at this point.
  ASSERT_EQ(root_child0, YGNodeGetChild(root2, 0));
  ASSERT_EQ(root_child1, YGNodeGetChild(root2, 1));

  YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR);

  ASSERT_EQ(2, YGNodeGetChildCount(root2));
  // Relayout with no changed input should result in referential equality.
  ASSERT_EQ(root_child0, YGNodeGetChild(root2, 0));
  ASSERT_EQ(root_child1, YGNodeGetChild(root2, 1));

  YGNodeStyleSetWidth(root2, 150);
  YGNodeStyleSetHeight(root2, 200);
  YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR);

  ASSERT_EQ(2, YGNodeGetChildCount(root2));
  // Relayout with changed input should result in cloned children.
  const YGNodeRef root2_child0 = YGNodeGetChild(root2, 0);
  const YGNodeRef root2_child1 = YGNodeGetChild(root2, 1);
  ASSERT_NE(root_child0, root2_child0);
  ASSERT_NE(root_child1, root2_child1);

  // Everything in the root should remain unchanged.
  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
  ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root));
  ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root));

  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
  ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0));
  ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0));

  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1));
  ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1));
  ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1));
  ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1));

  // The new root now has new layout.
  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2));
  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root2));
  ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2));
  ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root2));

  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2_child0));
  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root2_child0));
  ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2_child0));
  ASSERT_FLOAT_EQ(125, YGNodeLayoutGetHeight(root2_child0));

  ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2_child1));
  ASSERT_FLOAT_EQ(125, YGNodeLayoutGetTop(root2_child1));
  ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2_child1));
  ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root2_child1));

  YGNodeFreeRecursive(root2);

  YGNodeFreeRecursive(root);

  YGConfigFree(config);
}

TEST(YogaTest, mutating_children_of_a_clone_clones) {
  const YGConfigRef config = YGConfigNew();

  const YGNodeRef root = YGNodeNewWithConfig(config);
  ASSERT_EQ(0, YGNodeGetChildCount(root));

  const YGNodeRef root2 = YGNodeClone(root);
  ASSERT_EQ(0, YGNodeGetChildCount(root2));

  const YGNodeRef root2_child0 = YGNodeNewWithConfig(config);
  YGNodeInsertChild(root2, root2_child0, 0);

  ASSERT_EQ(0, YGNodeGetChildCount(root));
  ASSERT_EQ(1, YGNodeGetChildCount(root2));

  const YGNodeRef root3 = YGNodeClone(root2);
  ASSERT_EQ(1, YGNodeGetChildCount(root2));
  ASSERT_EQ(1, YGNodeGetChildCount(root3));
  ASSERT_EQ(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0));

  const YGNodeRef root3_child1 = YGNodeNewWithConfig(config);
  YGNodeInsertChild(root3, root3_child1, 1);
  ASSERT_EQ(1, YGNodeGetChildCount(root2));
  ASSERT_EQ(2, YGNodeGetChildCount(root3));
  ASSERT_EQ(root3_child1, YGNodeGetChild(root3, 1));
  ASSERT_NE(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0));

  const YGNodeRef root4 = YGNodeClone(root3);
  ASSERT_EQ(root3_child1, YGNodeGetChild(root4, 1));

  YGNodeRemoveChild(root4, root3_child1);
  ASSERT_EQ(2, YGNodeGetChildCount(root3));
  ASSERT_EQ(1, YGNodeGetChildCount(root4));
  ASSERT_NE(YGNodeGetChild(root3, 0), YGNodeGetChild(root4, 0));

  YGNodeFreeRecursive(root4);
  YGNodeFreeRecursive(root3);
  YGNodeFreeRecursive(root2);
  YGNodeFreeRecursive(root);

  YGConfigFree(config);
}

TEST(YogaTest, cloning_two_levels) {
  const YGConfigRef config = YGConfigNew();

  const YGNodeRef root = YGNodeNewWithConfig(config);
  YGNodeStyleSetWidth(root, 100);
  YGNodeStyleSetHeight(root, 100);

  const YGNodeRef root_child0 = YGNodeNewWithConfig(config);
  YGNodeStyleSetFlexGrow(root_child0, 1);
  YGNodeStyleSetFlexBasis(root_child0, 15);
  YGNodeInsertChild(root, root_child0, 0);

  const YGNodeRef root_child1 = YGNodeNewWithConfig(config);
  YGNodeStyleSetFlexGrow(root_child1, 1);
  YGNodeInsertChild(root, root_child1, 1);

  const YGNodeRef root_child1_0 = YGNodeNewWithConfig(config);
  YGNodeStyleSetFlexBasis(root_child1_0, 10);
  YGNodeStyleSetFlexGrow(root_child1_0, 1);
  YGNodeInsertChild(root_child1, root_child1_0, 0);

  const YGNodeRef root_child1_1 = YGNodeNewWithConfig(config);
  YGNodeStyleSetFlexBasis(root_child1_1, 25);
  YGNodeInsertChild(root_child1, root_child1_1, 1);

  YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);

  ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0));
  ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1));
  ASSERT_FLOAT_EQ(35, YGNodeLayoutGetHeight(root_child1_0));
  ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1_1));

  const YGNodeRef root2_child0 = YGNodeClone(root_child0);
  const YGNodeRef root2_child1 = YGNodeClone(root_child1);
  const YGNodeRef root2 = YGNodeClone(root);

  YGNodeStyleSetFlexGrow(root2_child0, 0);
  YGNodeStyleSetFlexBasis(root2_child0, 40);

  YGNodeRemoveAllChildren(root2);
  YGNodeInsertChild(root2, root2_child0, 0);
  YGNodeInsertChild(root2, root2_child1, 1);
  ASSERT_EQ(2, YGNodeGetChildCount(root2));

  YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR);

  // Original root is unchanged
  ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0));
  ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1));
  ASSERT_FLOAT_EQ(35, YGNodeLayoutGetHeight(root_child1_0));
  ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1_1));

  // New root has new layout at the top
  ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root2_child0));
  ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root2_child1));

  // The deeper children are untouched.
  ASSERT_EQ(YGNodeGetChild(root2_child1, 0), root_child1_0);
  ASSERT_EQ(YGNodeGetChild(root2_child1, 1), root_child1_1);

  YGNodeFreeRecursive(root2);
  YGNodeFreeRecursive(root);

  YGConfigFree(config);
}

TEST(YogaTest, cloning_and_freeing) {
  const int32_t initialInstanceCount = YGNodeGetInstanceCount();

  const YGConfigRef config = YGConfigNew();

  const YGNodeRef root = YGNodeNewWithConfig(config);
  YGNodeStyleSetWidth(root, 100);
  YGNodeStyleSetHeight(root, 100);
  const YGNodeRef root_child0 = YGNodeNewWithConfig(config);
  YGNodeInsertChild(root, root_child0, 0);
  const YGNodeRef root_child1 = YGNodeNewWithConfig(config);
  YGNodeInsertChild(root, root_child1, 1);

  YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);

  const YGNodeRef root2 = YGNodeClone(root);

  // Freeing the original root should be safe as long as we don't free its
  // children.
  YGNodeFree(root);

  YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR);

  YGNodeFreeRecursive(root2);

  YGNodeFree(root_child0);
  YGNodeFree(root_child1);

  YGConfigFree(config);

  ASSERT_EQ(initialInstanceCount, YGNodeGetInstanceCount());
}
