// file      : odb/common.cxx
// copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC
// license   : GNU GPL v3; see accompanying LICENSE file

#include <odb/common.hxx>

using namespace std;

//
// object_members_base
//

void object_members_base::
traverse_simple (semantics::data_member&)
{
}

void object_members_base::
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
  if (!view_member (m)) // Not really "as if" pointed-to id member.
    traverse_member (m, utype (*id_member (c)));
}

void object_members_base::
traverse_composite (semantics::data_member*, semantics::class_& c)
{
  inherits (c);
  names (c);
}

void object_members_base::
traverse_composite_wrapper (semantics::data_member* m,
                            semantics::class_& c,
                            semantics::type*)
{
  traverse_composite (m, c);
}

void object_members_base::
traverse_container (semantics::data_member&, semantics::type&)
{
}

void object_members_base::
traverse_object (semantics::class_& c)
{
  inherits (c);
  names (c);
}

void object_members_base::
traverse_view (semantics::class_& c)
{
  // A view has no bases.
  //
  names (c);
}

void object_members_base::
traverse (semantics::class_& c)
{
  class_kind_type k (class_kind (c));

  if (k == class_other)
  {
    // Ignore transient bases.
    //
    return;
  }
  else if (k == class_composite)
  {
    if (member_scope_.empty ())
      member_scope_.push_back (class_inheritance_chain ());

    member_scope_.back ().push_back (&c);

    traverse_composite_wrapper (0, c, 0);

    member_scope_.back ().pop_back ();

    if (member_scope_.back ().empty ())
      member_scope_.pop_back ();

    return;
  }

  if (top_level_)
    top_level_ = false;
  else
  {
    // Unless requested otherwise, don't go into bases if we are a derived
    // type in a polymorphic hierarchy.
    //
    if (!traverse_poly_base_ && polymorphic (c))
      return;
  }

  if (context::top_object == 0)
    context::top_object = &c;

  semantics::class_* prev (context::cur_object);
  context::cur_object = &c;

  if (member_scope_.empty ())
    member_scope_.push_back (class_inheritance_chain ());

  member_scope_.back ().push_back (&c);

  if (build_table_prefix_)
  {
    // Don't reset the table prefix if we are traversing a base.
    //
    bool tb (false);

    if (table_prefix_.level == 0)
    {
      table_prefix_ = table_prefix (c);
      tb = true;
    }

    if (k == class_object)
      traverse_object (c);
    else
      traverse_view (c);

    if (tb)
      table_prefix_ = table_prefix ();
  }
  else
  {
    if (k == class_object)
      traverse_object (c);
    else
      traverse_view (c);
  }

  member_scope_.back ().pop_back ();

  if (member_scope_.back ().empty ())
    member_scope_.pop_back ();

  context::cur_object = prev;

  if (prev == 0)
    context::top_object = 0;
}

void object_members_base::
traverse_member (semantics::data_member& m, semantics::type& t)
{
  if (semantics::class_* comp = context::composite_wrapper (t))
  {
    member_scope_.push_back (class_inheritance_chain ());
    member_scope_.back ().push_back (comp);

    table_prefix old_table_prefix;
    string old_flat_prefix, old_member_prefix;

    if (build_flat_prefix_)
    {
      old_flat_prefix = flat_prefix_;
      flat_prefix_ += public_name (m);
      flat_prefix_ += '_';
    }

    if (build_member_prefix_)
    {
      old_member_prefix = member_prefix_;
      member_prefix_ += m.name ();
      member_prefix_ += '.';
    }

    if (build_table_prefix_)
    {
      old_table_prefix = table_prefix_;
      table_prefix_.append (m);
    }

    traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0));

    if (build_table_prefix_)
      table_prefix_ = old_table_prefix;

    if (build_flat_prefix_)
      flat_prefix_ = old_flat_prefix;

    if (build_member_prefix_)
      member_prefix_ = old_member_prefix;

    member_scope_.pop_back ();
  }
  else
    traverse_simple (m);
}

bool object_members_base::
section_test (data_member_path const& mp)
{
  // By default ignore members from the wrong section.
  //
  return section_ == 0 || *section_ == section (mp);
}

void object_members_base::member::
traverse (semantics::data_member& m)
{
  if (transient (m))
    return;

  om_.member_path_.push_back (&m);

  // Ignore members from the wrong section.
  //
  if (om_.section_test (om_.member_path_))
  {
    semantics::type& t (utype (m));

    if (semantics::type* c = context::container (m))
      om_.traverse_container (m, *c);
    else if (semantics::class_* c = object_pointer (t))
      om_.traverse_pointer (m, *c);
    else
      om_.traverse_member (m, t);
  }

  om_.member_path_.pop_back ();
}

//
// object_columns_base
//

void object_columns_base::
flush ()
{
}

bool object_columns_base::
traverse_column (semantics::data_member&, string const&, bool)
{
  return false;
}

void object_columns_base::
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
  if (!view_member (m)) // Not really "as if" pointed-to id column.
    traverse_member (m, utype (*id_member (c)));
}

void object_columns_base::
traverse_composite (semantics::data_member*, semantics::class_& c)
{
  inherits (c);
  names (c);
}

void object_columns_base::
traverse_object (semantics::class_& c)
{
  inherits (c);
  names (c);
}

void object_columns_base::
traverse_view (semantics::class_& c)
{
  // A view has no bases.
  //
  names (c);
}

void object_columns_base::
traverse_pre (semantics::nameable&)
{
}

void object_columns_base::
traverse_post (semantics::nameable&)
{
}

void object_columns_base::
traverse (semantics::data_member& m,
          semantics::type& t,
          std::string const& kp,
          std::string const& dn,
          semantics::class_* to)
{
  traverse_pre (m);

  semantics::class_* oto (context::top_object);

  if (to != 0)
    context::top_object = to;

  semantics::class_* c (object_pointer (t));
  semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c)));

  root_ = &m;
  root_id_ = (kp.empty () ? context::id (m) : kp == "id");
  root_op_ = (c != 0);
  root_null_ = context::null (m, kp);

  key_prefix_ = kp;
  default_name_ = dn;

  if (root_op_)
    traverse_pointer (m, *c);
  else
    traverse_member (m, *rt);

  key_prefix_.clear ();
  default_name_.clear ();

  if (!first_ && composite_wrapper (*rt))
    flush ();

  root_ = 0;
  context::top_object = oto;

  traverse_post (m);
}

void object_columns_base::
traverse (semantics::class_& c)
{
  class_kind_type k (class_kind (c));

  // Ignore transient bases.
  //
  if (k == class_other)
    return;

  bool f (top_level_);

  if (top_level_)
  {
    traverse_pre (c);
    top_level_ = false;
  }
  else
  {
    // Unless requested otherwise, don't go into bases if we are a derived
    // type in a polymorphic hierarchy.
    //
    if (!traverse_poly_base_ && polymorphic (c))
      return;
  }

  semantics::class_* prev (0);
  if (k == class_object || k == class_view)
  {
    if (context::top_object == 0)
      context::top_object = &c;

    prev = context::cur_object;
    context::cur_object = &c;
  }

  if (member_scope_.empty ())
    member_scope_.push_back (class_inheritance_chain ());

  member_scope_.back ().push_back (&c);

  if (k == class_object)
    traverse_object (c);
  else if (k == class_view)
    traverse_view (c);
  else if (k == class_composite)
    traverse_composite (0, c);

  member_scope_.back ().pop_back ();

  if (member_scope_.back ().empty ())
    member_scope_.pop_back ();

  if (k == class_object || k == class_view)
  {
    context::cur_object = prev;

    if (prev == 0)
      context::top_object = 0;
  }

  if (f)
  {
    if (!first_)
      flush ();

    traverse_post (c);
  }
}

void object_columns_base::
traverse_member (semantics::data_member& m, semantics::type& t)
{
  if (semantics::class_* comp = composite_wrapper (t))
  {
    member_scope_.push_back (class_inheritance_chain ());
    member_scope_.back ().push_back (comp);

    column_prefix old_prefix (column_prefix_);
    column_prefix_.append (m, key_prefix_, default_name_);

    // Save and clear the key prefix and default name.
    //
    string old_kp, old_dn;
    old_kp.swap (key_prefix_);
    old_dn.swap (default_name_);

    traverse_composite (&m, *comp);

    old_kp.swap (key_prefix_);
    old_dn.swap (default_name_);

    column_prefix_ = old_prefix;
    member_scope_.pop_back ();
  }
  else
  {
    string name (column_name (m, key_prefix_, default_name_, column_prefix_));

    if (traverse_column (m, name, first_))
    {
      if (first_)
        first_ = false;
    }
  }
}

bool object_columns_base::
section_test (data_member_path const& mp)
{
  // By default ignore members from the wrong section.
  //
  return section_ == 0 || *section_ == section (mp);
}

void object_columns_base::member::
traverse (semantics::data_member& m)
{
  if (transient (m))
    return;

  // Container gets its own table, so nothing to do here.
  //
  if (container (m))
    return;

  oc_.member_path_.push_back (&m);

  if (oc_.section_test (oc_.member_path_))
  {
    semantics::type& t (utype (m));

    if (semantics::class_* c = object_pointer (t))
      oc_.traverse_pointer (m, *c);
    else
      oc_.traverse_member (m, t);
  }

  oc_.member_path_.pop_back ();
}

//
// object_columns_list
//

void object_columns_list::
traverse_pointer (semantics::data_member& m, semantics::class_& c)
{
  // Ignore inverse object pointers.
  //
  if (!ignore_inverse_ || !inverse (m, key_prefix_))
    object_columns_base::traverse_pointer (m, c);
}

bool object_columns_list::
traverse_column (semantics::data_member& m, std::string const& name, bool)
{
  columns_.push_back (column (name, column_type (), m));
  return true;
}

//
// typedefs
//

void typedefs::
traverse (semantics::typedefs& t)
{
  if (check (t))
    traversal::typedefs::traverse (t);
}

bool typedefs::
check (semantics::typedefs& t)
{
  // This typedef must be for a class template instantiation.
  //
  using semantics::class_instantiation;
  class_instantiation* ci (dynamic_cast<class_instantiation*> (&t.type ()));

  if (ci == 0)
    return false;

  // It must be an object or composite value.
  //
  if (!object (*ci) && !composite (*ci))
    return false;

  // This typedef name should be the one that was used in the pragma.
  //
  using semantics::names;
  tree type (ci->get<tree> ("tree-node"));

  names* hint;
  if (ci->count ("tree-hint"))
    hint = ci->get<names*> ("tree-hint");
  else
  {
    hint = unit.find_hint (type);
    ci->set ("tree-hint", hint); // Cache it.
  }

  if (hint != &t)
    return false;

  // And the definition may have to be in the file we are compiling.
  //
  if (!included_)
  {
    if (!options.at_once () && class_file (*ci) != unit.file ())
      return false;
  }

  return true;
}
