// file      : odb/relational/schema.cxx
// author    : Boris Kolpackov <boris@codesynthesis.com>
// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
// license   : GNU GPL v3; see accompanying LICENSE file

#include <odb/gcc.hxx>

#include <cassert>
#include <limits>
#include <sstream>

#include <odb/emitter.hxx>

#include <odb/relational/schema.hxx>
#include <odb/relational/generate.hxx>

using namespace std;

namespace relational
{
  namespace schema
  {
    // object_columns
    //
    void object_columns::
    default_ (semantics::data_member& m)
    {
      default_value* dv (0);

      semantics::type& t (m.type ());

      if (m.count ("default"))
        dv = &m.get<default_value> ("default");
      else if (t.count ("default"))
        dv = &t.get<default_value> ("default");
      else
        return; // No default value for this column.

      switch (dv->kind)
      {
      case default_value::reset:
        {
          // No default value.
          break;
        }
      case default_value::null:
        {
          default_null (m);
          break;
        }
      case default_value::boolean:
        {
          default_bool (m, dv->value == "true");
          break;
        }
      case default_value::number:
        {
          tree n (dv->node);

          switch (TREE_CODE (n))
          {
          case INTEGER_CST:
            {
              HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n));
              HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n));

              unsigned long long l (hwl);
              unsigned long long h (hwh);
              unsigned short width (HOST_BITS_PER_WIDE_INT);

              unsigned long long v ((h << width) + l);

              default_integer (m, v, dv->value == "-");
              break;
            }
          case REAL_CST:
            {
              double v;

              REAL_VALUE_TYPE d (TREE_REAL_CST (n));

              if (REAL_VALUE_ISINF (d))
                v = numeric_limits<double>::infinity ();
              else if (REAL_VALUE_ISNAN (d))
                v = numeric_limits<double>::quiet_NaN ();
              else
              {
                char tmp[256];
                real_to_decimal (tmp, &d, sizeof (tmp), 0, true);
                istringstream is (tmp);
                is >> v;
              }

              if (dv->value == "-")
                v = -v;

              default_float (m, v);
              break;
            }
          default:
            assert (false);
          }
          break;
        }
      case default_value::string:
        {
          default_string (m, dv->value);
          break;
        }
      case default_value::enumerator:
        {
          default_enum (m, dv->node, dv->value);
          break;
        }
      }
    }

    struct schema_emitter: emitter, context
    {
      virtual void
      pre ()
      {
        first_ = true;
      }

      virtual void
      line (const std::string& l)
      {
        if (first_)
          first_ = false;
        else
          os << endl;

        os << l;
      }

      virtual void
      post ()
      {
        if (!first_) // Ignore empty statements.
          os << ';' << endl
             << endl;
      }

    private:
      bool first_;
    };

    static char const file_header[] =
    "/* This file was generated by ODB, object-relational mapping (ORM)\n"
    " * compiler for C++.\n"
    " */\n\n";

    void
    generate ()
    {
      context ctx;
      ostream& os (ctx.os);

      os << file_header;

      schema_emitter emitter;

      // Drop.
      //
      {
        traversal::unit unit;
        traversal::defines unit_defines;
        traversal::namespace_ ns;
        instance<class_drop> c (emitter);

        unit >> unit_defines >> ns;
        unit_defines >> c;

        traversal::defines ns_defines;

        ns >> ns_defines >> ns;
        ns_defines >> c;

        // Pass 1 and 2.
        //
        for (unsigned short pass (1); pass < 3; ++pass)
        {
          c->pass (pass);
          unit.dispatch (ctx.unit);
        }
      }

      os << endl;

      // Create.
      //
      {
        traversal::unit unit;
        traversal::defines unit_defines;
        traversal::namespace_ ns;
        instance<class_create> c (emitter);

        unit >> unit_defines >> ns;
        unit_defines >> c;

        traversal::defines ns_defines;

        ns >> ns_defines >> ns;
        ns_defines >> c;

        // Pass 1 and 2.
        //
        for (unsigned short pass (1); pass < 3; ++pass)
        {
          c->pass (pass);
          unit.dispatch (ctx.unit);
        }
      }
    }
  }
}
