/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Yoga from 'yoga-layout';

test('dirtied', () => {
  const root = Yoga.Node.create();
  root.setAlignItems(Yoga.ALIGN_FLEX_START);
  root.setWidth(100);
  root.setHeight(100);

  root.calculateLayout(undefined, undefined, Yoga.DIRECTION_LTR);

  let dirtied = 0;
  root.setDirtiedFunc(() => {
    dirtied++;
  });

  // only nodes with a measure function can be marked dirty
  root.setMeasureFunc(() => ({width: 0, height: 0}));

  expect(dirtied).toBe(0);

  // dirtied func MUST be called in case of explicit dirtying.
  root.markDirty();
  expect(dirtied).toBe(1);

  // dirtied func MUST be called ONCE.
  root.markDirty();
  expect(dirtied).toBe(1);

  root.freeRecursive();
});

test('dirtied_propagation', () => {
  const root = Yoga.Node.create();
  root.setAlignItems(Yoga.ALIGN_FLEX_START);
  root.setWidth(100);
  root.setHeight(100);

  const root_child0 = Yoga.Node.create();
  root_child0.setAlignItems(Yoga.ALIGN_FLEX_START);
  root_child0.setWidth(50);
  root_child0.setHeight(20);
  root_child0.setMeasureFunc(() => ({width: 0, height: 0}));
  root.insertChild(root_child0, 0);

  const root_child1 = Yoga.Node.create();
  root_child1.setAlignItems(Yoga.ALIGN_FLEX_START);
  root_child1.setWidth(50);
  root_child1.setHeight(20);
  root.insertChild(root_child1, 0);

  root.calculateLayout(undefined, undefined, Yoga.DIRECTION_LTR);

  let dirtied = 0;
  root.setDirtiedFunc(() => {
    dirtied++;
  });

  expect(dirtied).toBe(0);

  // dirtied func MUST be called for the first time.
  root_child0.markDirty();
  expect(dirtied).toBe(1);

  // dirtied func must NOT be called for the second time.
  root_child0.markDirty();
  expect(dirtied).toBe(1);

  root.freeRecursive();
});

test('dirtied_hierarchy', () => {
  const root = Yoga.Node.create();
  root.setAlignItems(Yoga.ALIGN_FLEX_START);
  root.setWidth(100);
  root.setHeight(100);

  const root_child0 = Yoga.Node.create();
  root_child0.setAlignItems(Yoga.ALIGN_FLEX_START);
  root_child0.setWidth(50);
  root_child0.setHeight(20);
  root_child0.setMeasureFunc(() => ({width: 0, height: 0}));
  root.insertChild(root_child0, 0);

  const root_child1 = Yoga.Node.create();
  root_child1.setAlignItems(Yoga.ALIGN_FLEX_START);
  root_child1.setWidth(50);
  root_child1.setHeight(20);
  root_child0.setMeasureFunc(() => ({width: 0, height: 0}));
  root.insertChild(root_child1, 0);

  root.calculateLayout(undefined, undefined, Yoga.DIRECTION_LTR);

  let dirtied = 0;
  root_child0.setDirtiedFunc(() => {
    dirtied++;
  });

  expect(dirtied).toBe(0);

  // dirtied func must NOT be called for descendants.
  // NOTE: nodes without a measure function cannot be marked dirty manually,
  // but nodes with a measure function can not have children.
  // Update the width to dirty the node instead.
  root.setWidth(110);
  expect(dirtied).toBe(0);

  // dirtied func MUST be called in case of explicit dirtying.
  root_child0.markDirty();
  expect(dirtied).toBe(1);

  root.freeRecursive();
});

test('dirtied_reset', () => {
  const root = Yoga.Node.create();
  root.setAlignItems(Yoga.ALIGN_FLEX_START);
  root.setWidth(100);
  root.setHeight(100);
  root.setMeasureFunc(() => ({width: 0, height: 0}));

  root.calculateLayout(undefined, undefined, Yoga.DIRECTION_LTR);

  let dirtied = 0;
  root.setDirtiedFunc(() => {
    dirtied++;
  });

  expect(dirtied).toBe(0);

  // dirtied func MUST be called in case of explicit dirtying.
  root.markDirty();
  expect(dirtied).toBe(1);

  // recalculate so the root is no longer dirty
  root.calculateLayout(undefined, undefined, Yoga.DIRECTION_LTR);

  root.reset();
  root.setAlignItems(Yoga.ALIGN_FLEX_START);
  root.setWidth(100);
  root.setHeight(100);
  root.setMeasureFunc(() => ({width: 0, height: 0}));

  root.markDirty();

  // dirtied func must NOT be called after reset.
  root.markDirty();
  expect(dirtied).toBe(1);

  root.freeRecursive();
});
