// Copyright: Sebastien Alaiwan 2018

// Boost Software License - Version 1.0 - August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// tests that the minimalistic D runtime/helpers actually works
pragma(LDC_no_moduleinfo);
import standard;

extern(C)
int main()
{
  runTest!("struct: ctor/dtor", testStructCtorAndDtor);

  runTest!("class: empty", testEmptyClass);
  runTest!("class: ctor/dtor", testClassCtorAndDtor);
  runTest!("class: derived", testDerivedClass);

  runTest!("floating point: basic", testFloatingPoint);
  runTest!("arrays: copy", testArrayCopy);
  runTest!("arrays: dynamic", testArrayDynamic);

  runTest!("delegates", testDelegate);

  return 0;
}

///////////////////////////////////////////////////////////////////////////////

float g_f; // global nan-initialized float compile-test

void testFloatingPoint()
{
  check(g_f != g_f);

  float f;
  check(f != f);
}

void testEmptyClass()
{
  static class E
  {
  }

  auto o = new E;
  check(o !is null);
  deleteObject(o);
}

void testClassCtorAndDtor()
{
  static class C
  {
    static bool called;
    static bool constructed;
    static bool destroyed;

    this() nothrow
    {
      constructed = true;
    }

    ~this() nothrow
    {
      destroyed = true;
    }

    void f() nothrow
    {
      called = true;
    }
  }

  auto c = new C;
  check(C.constructed);
  c.f();
  check(C.called);

  deleteObject(c);
  check(C.destroyed);
}

void testDerivedClass()
{
  static class Base
  {
    void f()
    {
    }
  }

  // construction/destruction
  static class D : Base
  {
    static bool derivedObjectConstructed;
    static bool called;

    this()
    {
      derivedObjectConstructed = true;
    }

    override void f()
    {
      called = true;
    }
  }

  // polymorphism
  {
    Base c = new D;
    check(D.derivedObjectConstructed);

    c.f();
    check(D.called);

    deleteObject(c);
  }
}

void testStructCtorAndDtor() nothrow
{
  static struct MyStruct
  {
    int initialized = 7654;
    bool ctorCalled;
    int ctorArg;
    this(int arg_) nothrow
    {
      ctorCalled = true;
      ctorArg = arg_;
    }

    static bool dtorCalled;

    ~this() nothrow
    {
      dtorCalled = true;
    }
  }

  // statically allocated struct
  {
    {
      auto s = MyStruct(123);
      check(s.initialized == 7654);
      check(s.ctorCalled);
      check(s.ctorArg == 123);
      check(!s.dtorCalled);
    }
    check(MyStruct.dtorCalled);
  }

  // dynamically allocated struct
  MyStruct.dtorCalled = false;
  {
    auto s = new MyStruct(123);
    assertEquals(7654, s.initialized);
    check(s.ctorCalled);
    assertEquals(123, s.ctorArg);
    check(!s.dtorCalled);

    deleteStruct(s);
  }
  check(MyStruct.dtorCalled);
}

void testArrayCopy()
{
  int[10] tab;
  tab[] = 4;
  assertEquals(4, tab[0]);
  assertEquals(4, tab[9]);

  int[10] tab2;
  tab2 = tab;
  assertEquals(4, tab2[0]);
  assertEquals(4, tab2[9]);
}

void testArrayDynamic()
{
  int[] tab = newArray!int(256);
  assertEquals(256, cast(int)tab.length);
  assert(tab.ptr);
  assertEquals(0, tab[0]);
  tab[1] = 1234;

  deleteArray(tab);
}

void testDelegate()
{
  static class C
  {
    int a;
    void f()
    {
      ++a;
    }
  }

  static void callMe(void delegate() func)
  {
    for(int i=0;i < 123;++i)
      func();
  }

  auto c = new C;
  callMe(&c.f);
  assertEquals(123, c.a);
}

///////////////////////////////////////////////////////////////////////////////
void runTest(string name, alias f)()
{
  printf("Test: %s\n", name.ptr);
  f();
}

void assertEquals(int expected, int actual, string file=__FILE__, int line=__LINE__) nothrow
{
  if(expected == actual)
    return;

  printf("At %s(%d): expected %d, got %d\n", file.ptr, line, expected, actual);
  exit(1);
}

void check(bool condition, string file=__FILE__, int line=__LINE__) nothrow
{
  if(condition)
    return;

  printf("At %s(%d): assertion failure\n", file.ptr, line);
  exit(1);
}

