// file      : common/readonly/driver.cxx
// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
// license   : GNU GPL v2; see accompanying LICENSE file

// Test readonly members/objects. Also test that const members are
// treated as readonly. For other const member tests, see the const-
// member test.
//

#include <memory>   // std::auto_ptr
#include <cassert>
#include <iostream>

#include <odb/database.hxx>
#include <odb/transaction.hxx>

#include <common/config.hxx> // DATABASE_*
#include <common/common.hxx>

#include "test.hxx"
#include "test-odb.hxx"

using namespace std;
using namespace odb::core;

int
main (int argc, char* argv[])
{
  try
  {
    auto_ptr<database> db (create_database (argc, argv));

    // Simple.
    //
    {
      simple o (1, 1);

      {
        transaction t (db->begin ());
        db->persist (o);
        t.commit ();
      }

      o.ro++;
      const_cast<unsigned long&> (o.co)++;
      o.rw++;

      {
        transaction t (db->begin ());
        db->update (o);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        db->load<simple> (1, o);
        t.commit ();
      }

      assert (o.ro == 1 && o.co == 1 && o.rw == 2);
    }

    // Pointer.
    //
    {
      pointer p (1, new pointer (2));
      auto_ptr<pointer> p1 (new pointer (3));

      {
        transaction t (db->begin ());
        db->persist (p);
        db->persist (p.ro);
        db->persist (*p1);
        t.commit ();
      }

      delete p.ro;
      p.ro = p1.release ();
      const_cast<pointer*&> (p.co) = p.ro;
      p.rw = p.ro;

      {
        transaction t (db->begin ());
        db->update (p);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        auto_ptr<pointer> p (db->load<pointer> (1));
        t.commit ();

        assert (p->ro->id == 2 && p->co->id == 2 && p->rw->id == 3);
      }
    }

    // Composite.
    //
    {
      composite o (1, 1);

      {
        transaction t (db->begin ());
        db->persist (o);
        t.commit ();
      }

      o.ro.v++;
      o.ro.ro++;
      const_cast<unsigned long&> (o.ro.co)++;
      o.ro.rw++;

      value& co (const_cast<value&> (o.co));
      co.v++;
      co.ro++;
      const_cast<unsigned long&> (co.co)++;
      co.rw++;

      o.rw.v++;
      o.rw.ro++;
      const_cast<unsigned long&> (o.rw.co)++;
      o.rw.rw++;

      o.v.v++;

      {
        transaction t (db->begin ());
        db->update (o);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        db->load<composite> (1, o);
        t.commit ();
      }

      assert (o.ro.v  == 1 &&
              o.ro.ro == 1 &&
              o.ro.co == 1 &&
              o.ro.rw == 1 &&

              o.co.v  == 1 &&
              o.co.ro == 1 &&
              o.co.co == 1 &&
              o.co.rw == 1 &&

              o.rw.v  == 1 &&
              o.rw.ro == 1 &&
              o.rw.co == 1 &&
              o.rw.rw == 2 &&

              o.v.v   == 1);
    }

    // Container.
    //
    {
      typedef vector<unsigned long> ulongs;

      container o (1);

      o.ro.push_back (1);
      o.ro.push_back (2);

      ulongs& co (const_cast<ulongs&> (o.co));
      co.push_back (1);
      co.push_back (2);

      o.rw.push_back (1);
      o.rw.push_back (2);

      {
        transaction t (db->begin ());
        db->persist (o);
        t.commit ();
      }

      o.ro[0]++;
      o.ro.pop_back ();

      co[0]++;
      co.pop_back ();

      o.rw[0]++;
      o.rw.pop_back ();

      {
        transaction t (db->begin ());
        db->update (o);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        db->load<container> (1, o);
        t.commit ();
      }

      assert (o.ro.size () == 2 && o.ro[0] == 1 && o.ro[1] == 2 &&
              o.co.size () == 2 && o.co[0] == 1 && o.co[1] == 2 &&
              o.rw.size () == 1 && o.rw[0] == 2);
    }

    // Readonly object.
    //
    {
#ifndef DATABASE_COMMON
      typedef odb::object_traits_impl<simple_object, odb::id_common> so_traits;
      typedef odb::object_traits_impl<ro_object, odb::id_common> ro_traits;
      typedef odb::object_traits_impl<rw_object, odb::id_common> rw_traits;

      assert (so_traits::column_count ==
              so_traits::id_column_count + so_traits::readonly_column_count);

      assert (ro_traits::column_count ==
              ro_traits::id_column_count + ro_traits::readonly_column_count);

      assert (rw_traits::column_count !=
              rw_traits::id_column_count + rw_traits::readonly_column_count);
#endif

      simple_object so (1, 1);
      ro_object ro_o (1, 1);
      rw_object rw_o (1, 1);

      ro_o.cr.push_back (1);
      ro_o.cr.push_back (2);

      rw_o.cr.push_back (1);
      rw_o.cr.push_back (2);

      {
        transaction t (db->begin ());
        db->persist (so);
        db->persist (ro_o);
        db->persist (rw_o);
        t.commit ();
      }

      rw_o.sv++;
      rw_o.rw_sv++;

      {
        transaction t (db->begin ());
        //db->update (so);   // Compile error.
        //db->update (ro_o); // Compile error.
        db->update (rw_o);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        db->load (1, so);
        db->load (1, ro_o);
        db->load (1, rw_o);
        t.commit ();
      }

      assert (rw_o.sv == 1 && rw_o.rw_sv == 2);
    }

    // Readonly object.
    //
    {
      wrapper o (1, 1);

      {
        transaction t (db->begin ());
        db->persist (o);
        t.commit ();
      }

      *o.pl = 2;
      *o.cpl = 2;
      o.pcl.reset (new unsigned long (2));
      const_cast<unsigned long&> (*o.cpcl) = 2;

      {
        transaction t (db->begin ());
        db->update (o);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        db->load<wrapper> (1, o);
        t.commit ();
      }

      assert (*o.pl == 2 && *o.cpl == 2 && *o.pcl == 2 && *o.cpcl == 1);
    }

    // Readonly object with auto id.
    //
    {
      ro_auto o1 (1);
      ro_auto o2 (2);

      {
        transaction t (db->begin ());
        db->persist (o1);
        db->persist (o2);
        t.commit ();
      }

      {
        transaction t (db->begin ());
        auto_ptr<ro_auto> p1 (db->load<ro_auto> (o1.id));
        auto_ptr<ro_auto> p2 (db->load<ro_auto> (o2.id));
        t.commit ();

        assert (p1->num == o1.num);
        assert (p2->num == o2.num);
      }
    }
  }
  catch (const odb::exception& e)
  {
    cerr << e.what () << endl;
    return 1;
  }
}