// file      : inverse/employee.hxx
// copyright : not copyrighted - public domain

#ifndef EMPLOYEE_HXX
#define EMPLOYEE_HXX

#include <vector>
#include <string>

#include <odb/core.hxx>

// Include TR1 <memory> header in a compiler-specific fashion. Fall back
// on the Boost implementation if the compiler does not support TR1.
//
#include <odb/tr1/memory.hxx>

#include <odb/tr1/lazy-ptr.hxx>

using std::tr1::shared_ptr;

using odb::tr1::lazy_shared_ptr;
using odb::tr1::lazy_weak_ptr;

// The "pointer architecture" in this object model is as follows: All
// object pointers are lazy. The employee class holds shared pointers
// to employer, position, and projects. All other objects hold weak
// pointers back to the employee object. The weak sides are also the
// ones that are made inverse.
//
// The following bidirectional relationships are used:
//
// many-to-one  : employee <--> employer
// one-to-one   : employee <--> position
// many-to-many : employee <--> project
//

// Forward declarations.
//
class employer;
class position;
class project;
class employee;

typedef std::vector<lazy_shared_ptr<project> > projects;
typedef std::vector<lazy_weak_ptr<employee> > employees;

#pragma db object
class employer
{
public:
  employer (const std::string& name)
      : name_ (name)
  {
  }

  const std::string&
  name () const
  {
    return name_;
  }

  // Employees of this employer.
  //
  typedef ::employees employees_type;

  const employees_type&
  employees () const
  {
    return employees_;
  }

  employees_type&
  employees ()
  {
    return employees_;
  }

private:
  friend class odb::access;

  employer () {}

  #pragma db id
  std::string name_;

  #pragma db value_not_null inverse(employer_)
  employees_type employees_;
};

#pragma db object
class position
{
public:
  position (const std::string& title)
      : title_ (title)
  {
  }

  const std::string&
  title () const
  {
    return title_;
  }

  // Employee that fills this position. NULL if the position is vacant.
  //
  typedef ::employee employee_type;

  const lazy_weak_ptr<employee_type>&
  employee () const
  {
    return employee_;
  }

  void
  employee (lazy_weak_ptr<employee_type> employee)
  {
    employee_ = employee;
  }

private:
  friend class odb::access;

  position () {}

  #pragma db id auto
  unsigned long id_;

  std::string title_;

  #pragma db inverse(position_)
  lazy_weak_ptr<employee_type> employee_;
};

#pragma db object
class project
{
public:
  project (const std::string& name)
      : name_ (name)
  {
  }

  const std::string&
  name () const
  {
    return name_;
  }

  // Employees working on this project.
  //
  typedef ::employees employees_type;

  const employees_type&
  employees () const
  {
    return employees_;
  }

  employees_type&
  employees ()
  {
    return employees_;
  }

private:
  friend class odb::access;

  project () {}

  #pragma db id
  std::string name_;

  #pragma db value_not_null inverse(projects_)
  employees_type employees_;
};

#pragma db object
class employee
{
public:
  typedef ::employer employer_type;
  typedef ::position position_type;

  employee (const std::string& first,
            const std::string& last,
            lazy_shared_ptr<employer_type> employer,
            lazy_shared_ptr<position_type> position)
      : first_ (first), last_ (last),
        employer_ (employer),
        position_ (position)
  {
  }

  // Name.
  //
  const std::string&
  first () const
  {
    return first_;
  }

  const std::string&
  last () const
  {
    return last_;
  }

  // Employer.
  //
  const lazy_shared_ptr<employer_type>&
  employer () const
  {
    return employer_;
  }

  void
  employer (lazy_shared_ptr<employer_type> employer)
  {
    employer_ = employer;
  }

  // Position.
  //
  const lazy_shared_ptr<position_type>&
  position () const
  {
    return position_;
  }

  void
  position (lazy_shared_ptr<position_type> position)
  {
    position_ = position;
  }

  // Projects.
  //
  typedef ::projects projects_type;

  const projects_type&
  projects () const
  {
    return projects_;
  }

  projects_type&
  projects ()
  {
    return projects_;
  }

private:
  friend class odb::access;

  employee () {}

  #pragma db id auto
  unsigned long id_;

  std::string first_;
  std::string last_;

  #pragma db not_null
  lazy_shared_ptr<employer_type> employer_;

  #pragma db not_null
  lazy_shared_ptr<position_type> position_;

  #pragma db value_not_null unordered
  projects_type projects_;
};

#endif // EMPLOYEE_HXX