// file      : tests/small-list/driver.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <cassert>

#ifndef __cpp_lib_modules_ts
#include <string>
#include <iostream>
#endif

// Other includes.

#ifdef __cpp_modules_ts
#ifdef __cpp_lib_modules_ts
import std.core;
import std.io;
#endif
import butl.small_list;
#else
#include <libbutl/small-list.mxx>
#endif

using namespace std;
using namespace butl;

// Return true if the data is stored entirely inside l.
//
template <typename T, size_t N>
inline bool
small (const small_list<T, N>& l)
{
  // In VC it is always "large" (see note in small_list) so we omit this
  // test.
  //
#ifndef _MSC_VER
  for (const T& x: l)
  {
    const void* p (&x);

    if (p < &l || p >= (&l + 1))
      return false;
  }
#endif

  return true;
}

template <typename T, size_t N>
inline bool
large (const small_list<T, N>& l)
{
#ifndef _MSC_VER
  return !small (l);
#else
  return true;
#endif
}

int
main ()
{
  using list = small_list<string, 1>;

  {
    list l;

    l.push_back ("abc");
    assert (l.front () == "abc" && small (l));

    l.push_back ("ABC");
    assert (l.front () == "abc" && l.back () == "ABC" && large (l));

    l.pop_back ();
    assert (l.front () == "abc" && small (l));

    l.push_back ("ABC");
    l.pop_front ();
    assert (l.front () == "ABC" && large (l));

    l.push_back ("abc");
    l.pop_front ();
    assert (l.front () == "abc" && small (l));

    l.clear ();
    l.push_back ("abc");
    assert (l.front () == "abc" && small (l));
  }

  // Copy constructor.
  //
  {
    list s1 ({"abc"}), s2 (s1);
    assert (s1 == s2 && small (s2));

    list l1 ({"abc", "ABC"}), l2 (l1);
    assert (l1 == l2 && large (l2));
  }

  // Move constructor.
  //
  {
    struct mstring: string // Move-only string.
    {
      mstring () = default;
      explicit mstring (const char* s): string (s) {}

      mstring (mstring&&) = default;
      mstring& operator= (mstring&&) = default;

      mstring (const mstring&) = delete;
      mstring& operator= (const mstring&) = delete;
    };

    using list = small_list<mstring, 1>;

    {
      list s1;
      s1.emplace_back ("abc");
      list s2 (move (s1));
      assert (s2.front () == "abc" && small (s2));
    }

    {
      list l1;
      l1.emplace_back ("abc");
      l1.emplace_back ("ABC");
      list l2 (move (l1));
      assert (l2.front () == "abc" && l2.back () == "ABC" && large (l2));
    }
  }

  // Other constructors.
  //

  {
    const char* sa[] = {"abc"};
    const char* la[] = {"abc", "ABC"};

    list s (sa, sa + 1);
    assert (s.front () == "abc" && small (s));

    list l (la, la + 2);
    assert (l.front () == "abc" && l.back () == "ABC" && large (l));
  }

  {
    list s (1, "abc");
    assert (s.front () == "abc" && small (s));

    list l (3, "abc");
    assert (l.front () == "abc" && l.back () == "abc" && large (l));
  }

  {
    list s (1);
    assert (s.front () == "" && small (s));

    list l (3);
    assert (l.front () == "" && l.back () == "" && large (l));
  }
}