import { Node, Element, NodeInputs, ElementInputs } from "./data-model";
import { deformCpp } from "./deformCpp";

describe("deformCpp", () => {
  test("Bars from Logan's book example 3.9", () => {
    const nodes: Node[] = [
      [12, -3, -4],
      [0, 0, 0],
      [12, -3, -7],
      [14, 6, 0],
    ];
    const elements: Element[] = [
      [1, 0],
      [2, 0],
      [3, 0],
    ];
    const nodeInputs: NodeInputs = {
      supports: new Map(),
      loads: new Map(),
    };
    const elementInputs: ElementInputs = {
      elasticities: new Map(),
      areas: new Map(),
    };

    nodeInputs.supports?.set(1, [true, true, true, false, false, false]);
    nodeInputs.supports?.set(2, [true, true, true, false, false, false]);
    nodeInputs.supports?.set(3, [true, true, true, false, false, false]);
    nodeInputs.loads?.set(0, [20, 0, 0, 0, 0, 0]);

    elements.forEach((_, i) => {
      elementInputs.elasticities?.set(i, 210e6);
      elementInputs.areas?.set(i, 10e-4);
    });

    const deformOutputs = deformCpp(nodes, elements, nodeInputs, elementInputs);

    expect(deformOutputs).toEqual({
      deformations: new Map([
        [
          0,
          [
            0.001383724933236592, -0.000051566432467165236,
            0.000060150375939849595, 0, 0, 0,
          ],
        ],
        [1, [0, 0, 0, 0, 0, 0]],
        [2, [0, 0, 0, 0, 0, 0]],
        [3, [0, 0, 0, 0, 0, 0]],
      ]),
      reactions: new Map([
        [
          1,
          [-18.947368421052634, 4.736842105263158, 6.31578947368421, 0, 0, 0],
        ],
        [2, [0, 0, -4.210526315789472, 0, 0, 0]],
        [
          3,
          [
            -1.0526315789473686, -4.736842105263158, -2.105263157894737, 0, 0,
            0,
          ],
        ],
      ]),
    });
  });

  test("Frames from Logan's book example 5.8", () => {
    const nodes: Node[] = [
      [2.5, 0, 0],
      [0, 0, 0],
      [2.5, 0, -2.5],
      [2.5, -2.5, 0],
    ];
    const elements: Element[] = [
      [1, 0],
      [2, 0],
      [3, 0],
    ];
    const nodeInputs: NodeInputs = {
      supports: new Map(),
      loads: new Map(),
    };
    const elementInputs: ElementInputs = {
      elasticities: new Map(),
      shearModuli: new Map(),
      torsionalConstants: new Map(),
      areas: new Map(),
      momentsOfInertiaY: new Map(),
      momentsOfInertiaZ: new Map(),
    };

    nodeInputs.supports?.set(1, [true, true, true, true, true, true]);
    nodeInputs.supports?.set(2, [true, true, true, true, true, true]);
    nodeInputs.supports?.set(3, [true, true, true, true, true, true]);
    nodeInputs.loads?.set(0, [0, -200e3, 0, -100e3, 0, 0]);
    elements.forEach((_, i) => {
      elementInputs.elasticities?.set(i, 200e9);
      elementInputs.shearModuli?.set(i, 60e9);
      elementInputs.momentsOfInertiaZ?.set(i, 40e-6);
      elementInputs.momentsOfInertiaY?.set(i, 40e-6);
      elementInputs.torsionalConstants?.set(i, 20e-6);
      elementInputs.areas?.set(i, 6.25e-3);
    });

    const deformOutputs = deformCpp(nodes, elements, nodeInputs, elementInputs);

    // Expected values (kept from original test for toBeCloseTo comparison)
    const expectedDeformations = new Map([
      [
        0,
        [
          0.0000017466534414748468, -0.00033564417271263484,
          -0.000056507877693047675, -0.003752156183061716,
          0.00001715470855495142, -0.00009935435371409366,
        ],
      ],
      [1, [0, 0, 0, 0, 0, 0]],
      [2, [0, 0, 0, 0, 0, 0]],
      [3, [0, 0, 0, 0, 0, 0]],
    ]);

    const expectedReactions = new Map([
      [
        1,
        [
          -873.3267207374234, 1299.1563606221894, 215.43623884405807,
          1801.0349678696236, -324.19036593091715, 1941.8793826628366,
        ],
      ],
      [
        2,
        [
          121.01672295760545, 30878.757283060415, 28253.938846523837,
          -26591.54681802802, 96.37583632116227, 47.69008978276496,
        ],
      ],
      [
        3,
        [
          752.309997779818, 167822.08635631742, -28469.3750853679,
          -23579.819070912377, -8.23426010637668, -622.4535653396727,
        ],
      ],
    ]);

    // Compare sizes first
    expect(deformOutputs.deformations.size).toEqual(expectedDeformations.size);
    expect(deformOutputs.reactions.size).toEqual(expectedReactions.size);

    // Compare deformations using toBeCloseTo
    deformOutputs.deformations.forEach((actualDef, nodeIndex) => {
      const expectedDef = expectedDeformations.get(nodeIndex);
      expect(expectedDef).toBeDefined(); // Ensure the node exists in expected
      actualDef.forEach((val, i) => {
        expect(val).toBeCloseTo(expectedDef[i], 8); // Use 8 decimal places for tolerance
      });
    });

    // Compare reactions using toBeCloseTo
    deformOutputs.reactions.forEach((actualReact, nodeIndex) => {
      const expectedReact = expectedReactions.get(nodeIndex);
      expect(expectedReact).toBeDefined(); // Ensure the node exists in expected
      actualReact.forEach((val, i) => {
        expect(val).toBeCloseTo(expectedReact[i], 8); // Use 8 decimal places for tolerance
      });
    });
  });

  test("Plate", () => {
    const nodes: Node[] = [
      [0, 0, 0],
      [0, 5, 0],
      [5, 0, 0],
      [10, 5, 0],
      [10, 0, 0],
    ];
    const elements: Element[] = [
      [0, 1, 2],
      [2, 3, 4],
    ];

    const fixedSupport = [true, true, true, true, true, true] as any;
    const nodeInputs: NodeInputs = {
      supports: new Map([
        [0, fixedSupport],
        [1, fixedSupport],
        [3, fixedSupport],
        [4, fixedSupport],
      ]),
      loads: new Map([[2, [0, 0, -1, 0, 0, 0]]]),
    };

    const elementInputs: ElementInputs = {
      elasticities: new Map(elements.map((_, i) => [i, 10])),
      thicknesses: new Map(elements.map((_, i) => [i, 1])),
      poissonsRatios: new Map(elements.map((_, i) => [i, 0.3])),
    };

    const deformOutputs = deformCpp(nodes, elements, nodeInputs, elementInputs);

    expect(deformOutputs).toEqual({
      deformations: new Map([
        [0, [0, 0, 0, 0, 0, 0]],
        [1, [0, 0, 0, 0, 0, 0]],
        [
          2,
          [
            0, 0, -1.3467100041517628, 0.2006829256574201, -0.08312558954401499,
            0,
          ],
        ],
        [3, [0, 0, 0, 0, 0, 0]],
        [4, [0, 0, 0, 0, 0, 0]],
      ]),
      reactions: new Map([
        [
          0,
          [
            0, 0, 0.36780676281428193, 0.11886720202236686, 0.9739614221402426,
            0,
          ],
        ],
        [
          1,
          [
            0, 0, 0.13219323718571813, 0.14298603128138881, 0.5624946747141107,
            0,
          ],
        ],
        [
          3,
          [
            0, 0, 0.13219323718571813, -0.29663740653764714,
            -0.4988501912056905, 0,
          ],
        ],
        [
          4,
          [
            0, 0, 0.367806762814282, -0.6046429215987722, -0.7727465308201459,
            0,
          ],
        ],
      ]),
    });
  });
});
