// file      : libbuild2/function.test.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <iostream>

#include <libbuild2/types.hxx>
#include <libbuild2/utility.hxx>

#include <libbuild2/scope.hxx>
#include <libbuild2/parser.hxx>
#include <libbuild2/context.hxx>
#include <libbuild2/scheduler.hxx>
#include <libbuild2/function.hxx>
#include <libbuild2/variable.hxx>
#include <libbuild2/file-cache.hxx>
#include <libbuild2/diagnostics.hxx>

#undef NDEBUG
#include <cassert>

using namespace std;

namespace build2
{
  static const optional<const value_type*> arg_bool[1] =
  {
    &value_traits<bool>::value_type
  };

  static dir_path
  scoped (const scope*, dir_path d)
  {
    return d;
  }

  static void
  scoped_void (const scope*, dir_path)
  {
  }

  int
  main (int, char* argv[])
  {
    // Fake build system driver, default verbosity.
    //
    init_diag (1);
    init (nullptr, argv[0], true);

    // Serial execution.
    //
    scheduler sched (1);
    global_mutexes mutexes (1);
    file_cache fcache (true);
    context ctx (sched, mutexes, fcache);

    auto& functions (ctx.functions);

    function_family f (functions, "dummy");

    f["fail"]     += []()        {fail << "failed" << endf;};
    f["fail_arg"] += [](names a) {return convert<uint64_t> (move (a[0]));};

    f["nullable"] += [](names* a)          {return a == nullptr;};
    f["optional"] += [](optional<names> a) {return !a;};

    f["dummy0"] += []()         {return "abc";};
    f["dummy1"] += [](string s) {return s;};
    f["dummy2"] += [](uint64_t x, uint64_t y) {return x + y;};

    f["ambig"] += [](names a, optional<string>)   {return a;};
    f["ambig"] += [](names a, optional<uint64_t>) {return a;};

    f["reverse"] += [](names a) {return a;};

    f["scoped"]      += [](const scope*, names a) {return a;};
    f["scoped_void"] += [](const scope*, names) {};
    f["scoped"]      += &scoped;
    f["scoped_void"] += &scoped_void;

    f[".qual"] += []() {return "abc";};

    f[".length"] += &path::size; // Member function.
    f[".type"]   += &name::type; // Data member.

    f[".abs"] += [](dir_path d) {return d.absolute ();};

    // Variadic function with first required argument of type bool. Returns
    // number of arguments passed.
    //
    functions.insert ("variadic", true).insert (
      function_overload (
        nullptr,
        1,
        function_overload::arg_variadic,
        function_overload::types (arg_bool, 1),
        [] (const scope*, vector_view<value> args, const function_overload&)
        {
          return value (static_cast<uint64_t> (args.size ()));
        }));

    // Dump arguments.
    //
    functions.insert ("dump", true).insert (
      function_overload (
        nullptr,
        0,
        function_overload::arg_variadic,
        function_overload::types (),
        [] (const scope*, vector_view<value> args, const function_overload&)
        {
          for (value& a: args)
          {
            if (a.null)
              cout << "[null]";
            else if (!a.empty ())
            {
              names storage;
              cout << reverse (a, storage, true /* reduce */);
            }
            cout << endl;
          }
          return value (nullptr);
        }));

    try
    {
      // Use temp scope for the private variable pool.
      //
      temp_scope s (ctx.global_scope.rw (), scope::role::temp_generic);

      parser p (ctx);
      p.parse_buildfile (cin, path_name ("buildfile"), &s, s);
    }
    catch (const failed&)
    {
      return 1;
    }

    return 0;
  }
}

int
main (int argc, char* argv[])
{
  return build2::main (argc, argv);
}
