// @note(judah): these are put in a separate module to fix some weirdness with #module_parameters
// when used in the test runner.

#import "jc";

#run,stallable {
   // arrays
   Test("slice", t => {
      a1 := int.[ 1, 2, 3, 4, 5 ];
      a2 := ArraySlice(a1, 2);
      Expect(a2.count == 3);
      Expect(ArrayEquals(a2, int.[ 3, 4, 5 ]));

      b1 := int.[ 1, 2, 3, 4, 5 ];
      b2 := ArraySlice(b1, 2, 0);
      Expect(b2.count == 0);
      Expect(b2.data  == b1.data + 2);

      c1 := int.[ 1, 2, 3, 4, 5 ];
      c2 := ArraySlice(c1, 3, 1);
      Expect(c2.count == 1);
      Expect(ArrayEquals(c2, int.[ 4 ]));

      d1 := int.[ 1, 2, 3 ];
      d2 := ArraySlice(d1, 2);
      Expect(d2.count == 1);
      Expect(ArrayEquals(d2, int.[ 3 ]));
   });

   Test("find", t => {
      a := int.[ 1, 2, 3, 4, 5 ];

      ok, res := ArrayFind(a, 3);
      Expect(ok && res.index == 2);
      Expect(res.value == 3);

      ok, res = ArrayFind(a, -1);
      Expect(!ok && res.index == -1);

      b := int.[ 1, 2, 2, 3, 4, 5, 2 ];

      ok, res = ArrayFind(b, 2);
      Expect(ok && res.index == 1);

      ok, res = ArrayFind(b, 2, .FromEnd);
      Expect(ok && res.index == 6);

      c := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];

      ok, res = ArrayFind(c, 0, .Last);
      Expect(ok && res.index == 8);

      ok, res = ArrayFind(c, 0, .FromEnd | .Last);
      Expect(ok && res.index == 0);
   });

   Test("contains", t => {
      a := int.[ 1, 2, 3, 4, 5 ];
      Expect(ArrayContains(a, 3));
      Expect(!ArrayContains(a, -1));
   });

   Test("trim", t => {
      a1 := int.[ 0, 0, 0, 1, 2, 3 ];
      a2 := ArrayTrim(a1, .[ 0 ]);
      Expect(ArrayEquals(a1, .[ 0, 0, 0, 1, 2, 3 ]));
      Expect(ArrayEquals(a2, .[ 1, 2, 3 ]));

      b1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
      b2 := ArrayTrim(b1, .[ 0 ], .FromEnd);
      Expect(ArrayEquals(b1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
      Expect(ArrayEquals(b2, .[ 0, 0, 0, 1, 2, 3 ]));

      c1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
      c2 := ArrayTrim(c1, .[ 0 ], .FromStart | .FromEnd);
      Expect(ArrayEquals(c1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
      Expect(ArrayEquals(c2, .[ 1, 2, 3 ]));

      d1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
      d2 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .MatchInFull);
      d3 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromEnd   | .MatchInFull);
      d4 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .FromEnd | .MatchInFull);
      Expect(ArrayEquals(d1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
      Expect(ArrayEquals(d2, .[ 1, 2, 3, 0, 0, 0 ]));
      Expect(ArrayEquals(d3, .[ 0, 0, 0, 1, 2, 3 ]));
      Expect(ArrayEquals(d4, .[ 1, 2, 3 ]));
   });

   // type_info
   Test("min_of/max_of:enums", t => {
      U8Enum :: enum u8 { lo :: -1; hi :: +1; }
      S8Enum :: enum s8 { lo :: -2; hi :: -1; }
      {
         Expect(min_of(U8Enum) == U8Enum.lo);
         Expect(min_of(S8Enum) == S8Enum.lo);
         Expect(max_of(U8Enum) == U8Enum.hi);
         Expect(max_of(S8Enum) == S8Enum.hi);
      }

      U16Enum :: enum u16 { lo :: -1; hi :: +1; }
      S16Enum :: enum s16 { lo :: -2; hi :: -1; }
      {
         Expect(min_of(U16Enum) == U16Enum.lo);
         Expect(min_of(S16Enum) == S16Enum.lo);
         Expect(max_of(U16Enum) == U16Enum.hi);
         Expect(max_of(S16Enum) == S16Enum.hi);
      }

      U32Enum :: enum u32 { lo :: -1; hi :: +1; }
      S32Enum :: enum s32 { lo :: -2; hi :: -1; }
      {
         Expect(min_of(U32Enum) == U32Enum.lo);
         Expect(min_of(S32Enum) == S32Enum.lo);
         Expect(max_of(U32Enum) == U32Enum.hi);
         Expect(max_of(S32Enum) == S32Enum.hi);
      }

      U64Enum :: enum u64 { lo :: -1; hi :: +1; }
      S64Enum :: enum s64 { lo :: -2; hi :: -1; }
      {
         Expect(min_of(U64Enum) == U64Enum.lo);
         Expect(min_of(S64Enum) == S64Enum.lo);
         Expect(max_of(U64Enum) == U64Enum.hi);
         Expect(max_of(S64Enum) == S64Enum.hi);
      }

      // @note(judah): just making sure this compiles
      lo, hi := range_of(U64Enum);
      Expect(lo == U64Enum.lo);
      Expect(hi == U64Enum.hi);
   });

   // map
   Test("map:basic operations", t => {
      ITERATIONS :: 64;

      values: Map(int, int);
      for 0..ITERATIONS {
         MapSet(*values, it, it * it);
      }

      for 0..ITERATIONS {
         v, ok := MapGet(*values, it);
         Expect(v == it * it);
      }

      for 0..ITERATIONS if it % 2 == 0 {
         ok := MapRemove(*values, it);
         Expect(ok);
      }

      for 0..ITERATIONS if it % 2 == 0 {
         _, ok := MapGet(*values, it);
         Expect(!ok);
      }
   });

   Test("map:free slots", t => {
      values: Map(int, int);

      MapSet(*values, 1, 100);
      MapSet(*values, 2, 200);
      MapSet(*values, 3, 300);
      Expect(values.count == 3);
      Expect(values.slots.allocated == values.AllocatedItemsAtStart);

      // deleting something that doesn't exist should do nothing
      ok := MapRemove(*values, 0);
      Expect(!ok);
      Expect(values.count == 3);

      MapRemove(*values, 2);
      Expect(values.count == 2);
   });

   Test("map:iteration", t => {
      values: Map(int, int);

      for 0..10 MapSet(*values, it, it * it);
      Expect(values.count == 11);

      for   v, k: values Expect(v == k * k);
      for < v, k: values Expect(v == k * k);
   });
}
