// file      : odb/relational/inline.hxx
// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
// license   : GNU GPL v3; see accompanying LICENSE file

#ifndef ODB_RELATIONAL_INLINE_HXX
#define ODB_RELATIONAL_INLINE_HXX

#include <odb/relational/context.hxx>
#include <odb/relational/common.hxx>

namespace relational
{
  namespace inline_
  {
    //
    //
    struct callback_calls: traversal::class_, virtual context
    {
      typedef callback_calls base;

      callback_calls ()
      {
        *this >> inherits_ >> *this;
      }

      callback_calls (callback_calls const&)
          : root_context (), //@@ -Wextra
            context ()
      {
        *this >> inherits_ >> *this;
      }

      virtual void
      traverse (type& c, bool constant)
      {
        const_ = constant;
        traverse (c);
      }

      virtual void
      traverse (type& c)
      {
        bool obj (object (c));

        // Ignore transient bases.
        //
        if (!(obj || view (c)))
          return;

        if (c.count ("callback"))
        {
          string name (c.get<string> ("callback"));

          // In case of the const instance, we only generate the call if
          // there is a const callback.
          //
          string const& type (class_fq_name (c));

          if (const_)
          {
            if (c.count ("callback-const"))
              os << "static_cast< const " << type << "& > (x)." <<
                name << " (e, db);";
          }
          else
            os << "static_cast< " << type << "& > (x)." << name << " (e, db);";
        }
        else if (obj)
          inherits (c);
      }

    protected:
      bool const_;
      traversal::inherits inherits_;
    };

    //
    //
    struct class_: traversal::class_, virtual context
    {
      typedef class_ base;

      virtual void
      traverse (type& c)
      {
        if (class_file (c) != unit.file ())
          return;

        if (object (c))
          traverse_object (c);
        if (view (c))
          traverse_view (c);
        else if (composite (c))
          traverse_composite (c);
      }

      virtual void
      object_extra (type&)
      {
      }

      virtual void
      traverse_object (type& c)
      {
        bool abstract (context::abstract (c));
        string const& type (class_fq_name (c));
        string traits ("access::object_traits< " + type + " >");

        semantics::data_member* id (id_member (c));
        bool base_id (id ? &id->scope () != &c : false); // Comes from base.

        semantics::data_member* optimistic (context::optimistic (c));

        // Base class the contains the object id and version for optimistic
        // concurrency.
        //
        semantics::class_* base (
          id != 0 && base_id
          ? dynamic_cast<semantics::class_*> (&id->scope ())
          : 0);

        os << "// " << class_name (c) << endl
           << "//" << endl
           << endl;

        object_extra (c);

        // id (object_type)
        //
        if (id != 0 || !abstract)
        {
          os << "inline" << endl
             << traits << "::id_type" << endl
             << traits << "::" << endl
             << "id (const object_type&" << (id != 0 ? " obj" : "") << ")"
             << "{";

          if (id != 0)
          {
            if (base_id)
              os << "return object_traits< " << class_fq_name (*base) <<
                " >::id (obj);";
            else
              os << "return obj." << id->name () << ";";
          }

          os << "}";
        }

        if (id != 0)
        {
          // id (image_type)
          //
          if (options.generate_query () && base_id)
          {
            os << "inline" << endl
               << traits << "::id_type" << endl
               << traits << "::" << endl
               << "id (const image_type& i)"
               << "{"
               << "return object_traits< " << class_fq_name (*base) <<
              " >::id (i);"
               << "}";
          }

          // version (image_type)
          //
          if (optimistic != 0 && base_id)
          {
            os << "inline" << endl
               << traits << "::version_type" << endl
               << traits << "::" << endl
               << "version (const image_type& i)"
               << "{"
               << "return object_traits< " << class_fq_name (*base) <<
              " >::version (i);"
               << "}";
          }

          // bind (id_image_type)
          //
          if (base_id)
          {
            os << "inline" << endl
               << "void " << traits << "::" << endl
               << "bind (" << bind_vector << " b, id_image_type& i)"
               << "{"
               << "object_traits< " << class_fq_name (*base) <<
              " >::bind (b, i);"
               << "}";
          }

          if (base_id)
          {
            os << "inline" << endl
               << "void " << traits << "::" << endl
               << "init (id_image_type& i, const id_type& id" <<
              (optimistic != 0 ? ", const version_type* v" : "") << ")"
               << "{"
               << "object_traits< " << class_fq_name (*base) <<
              " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");"
               << "}";
          }
        }

        //
        // The rest only applies to concrete objects.
        //
        if (abstract)
          return;

        // erase (object_type)
        //
        if (id != 0 && optimistic == 0)
        {
          os << "inline" << endl
             << "void " << traits << "::" << endl
             << "erase (database& db, const object_type& obj)"
             << "{"
             << "erase (db, id (obj));"
             << "}";
        }

        // callback ()
        //
        os << "inline" << endl
           << "void " << traits << "::" << endl
           << "callback (database& db, object_type& x, callback_event e)"
           <<  endl
           << "{"
           << "ODB_POTENTIALLY_UNUSED (db);"
           << "ODB_POTENTIALLY_UNUSED (x);"
           << "ODB_POTENTIALLY_UNUSED (e);"
           << endl;
        callback_calls_->traverse (c, false);
        os << "}";

        os << "inline" << endl
           << "void " << traits << "::" << endl
           << "callback (database& db, const object_type& x, callback_event e)"
           << "{"
           << "ODB_POTENTIALLY_UNUSED (db);"
           << "ODB_POTENTIALLY_UNUSED (x);"
           << "ODB_POTENTIALLY_UNUSED (e);"
           << endl;
        callback_calls_->traverse (c, true);
        os << "}";

        // query_type
        //
        if (options.generate_query ())
        {
          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type ()"
             << "{"
             << "}";

          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type (bool v)" << endl
             << "  : query_base_type (v)"
             << "{"
             << "}";

          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type (const char* q)" << endl
             << "  : query_base_type (q)"
             << "{"
             << "}";

          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type (const std::string& q)" << endl
             << "  : query_base_type (q)"
             << "{"
             << "}";

          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type (const query_base_type& q)" << endl
             << "  : query_base_type (q)"
             << "{"
             << "}";
        }

        // load_()
        //
        if (id != 0 && !has_a (c, test_container))
        {
          os << "inline" << endl
             << "void " << traits << "::" << endl
             << "load_ (" << db << "::object_statements< object_type >&, " <<
            "object_type&)"
             << "{"
             << "}";
        }
      }

      virtual void
      view_extra (type&)
      {
      }

      virtual void
      traverse_view (type& c)
      {
        string const& type (class_fq_name (c));
        string traits ("access::view_traits< " + type + " >");

        os << "// " << class_name (c) << endl
           << "//" << endl
           << endl;

        view_extra (c);

        // query_type
        //
        size_t obj_count (c.get<size_t> ("object-count"));

        if (obj_count != 0)
        {
          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type ()"
             << "{"
             << "}";

          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type (bool v)" << endl
             << "  : query_base_type (v)"
             << "{"
             << "}";

          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type (const char* q)" << endl
             << "  : query_base_type (q)"
             << "{"
             << "}";

          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type (const std::string& q)" << endl
             << "  : query_base_type (q)"
             << "{"
             << "}";

          os << "inline" << endl
             << traits << "::query_type::" << endl
             << "query_type (const query_base_type& q)" << endl
             << "  : query_base_type (q)"
             << "{"
             << "}";
        }

        // callback ()
        //
        os << "inline" << endl
           << "void " << traits << "::" << endl
           << "callback (database& db, view_type& x, callback_event e)"
           <<  endl
           << "{"
           << "ODB_POTENTIALLY_UNUSED (db);"
           << "ODB_POTENTIALLY_UNUSED (x);"
           << "ODB_POTENTIALLY_UNUSED (e);"
           << endl;
        callback_calls_->traverse (c, false);
        os << "}";
      }

      virtual void
      traverse_composite (type&)
      {
      }

    private:
      instance<callback_calls> callback_calls_;
    };
  }
}

#endif // ODB_RELATIONAL_INLINE_HXX
