// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WT_DBO_DBACTION_H_
#define WT_DBO_DBACTION_H_

#include <set>
#include <string>
#include <sstream>
#include <vector>

#include <Wt/Dbo/ptr>
#include <Wt/Dbo/collection>

#include <Wt/Dbo/Field>

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_base_of.hpp>

namespace Wt {
  namespace Dbo {

template <class C, class Enable = void>
struct persist
{
  template <class A> static void apply(C& obj, A& action);
};

class SqlStatement;

class WTDBO_API PrepareStatements
{
public:
  PrepareStatements(Session& session, const char *tableName);

  template<class C> void visitSelf(C& obj);
  template<class C> void visitCollections(C& obj);

  template<typename V> void act(const FieldRef<V>& field);
  template<class C> void actCollection(const CollectionRef<C>& field);
  template<class C> void descend(ptr<C>& obj);

  std::vector<std::string> getSelfSql() const;
  void addCollectionsSql(std::vector<std::string>& statements) const;
  int columnCount() const;

  bool isWriting() const;
  bool isReading() const;
  bool isSchema() const;

private:
  struct WTDBO_API SetInfo {
    const char *tableName;
    std::string joinName;
    std::string joinSelfId, joinOtherId;
    RelationType type;

    SetInfo(const char *aTableName, RelationType type,
	    const std::string& aJoinName,
	    const std::string& aJoinSelfId,
	    const std::string& aJoinOtherId);
  };

  Session& session_;
  const char* tableName_;

  enum { Self, Collections } pass_;

  std::vector<std::string> fields_;
  std::vector<SetInfo> sets_;
};

class WTDBO_API CreateSchema
{
public:
  CreateSchema(Session& session, const char *tableName,
	       std::set<std::string>& tablesCreated);

  template<class C> void visit(C& obj);

  template<typename V> void act(const FieldRef<V>& field);
  template<class C> void actCollection(const CollectionRef<C>& field);
  template<class C> void descend(ptr<C>& obj);

  bool isWriting() const;
  bool isReading() const;
  bool isSchema() const;

private:
  Session& session_;
  const char *tableName_;
  std::set<std::string>& tablesCreated_;

  enum { Self, Sets } pass_;
  bool needSetsPass_;
  std::stringstream sql_;

  void exec();
  void createJoinTable(const std::string& joinName,
		       const char *table1, const char *table2,
		       const std::string& joinId1,
		       const std::string& joinId2);
};

class WTDBO_API DropSchema
{
public:
  DropSchema(Session& session, const char *tableName,
	     std::set<std::string>& tablesCreated);

  template<class C> void visit(C& obj);

  template<typename V> void act(const FieldRef<V>& field);
  template<class C> void actCollection(const CollectionRef<C>& field);
  template<class C> void descend(ptr<C>& obj);

  bool isWriting() const;
  bool isReading() const;
  bool isSchema() const;

private:
  Session& session_;
  const char *tableName_;
  std::set<std::string>& tablesDropped_;

  void drop(const std::string& tableName);
};

class WTDBO_API LoadDbAction
{
public:
  LoadDbAction(MetaDboBase& dbo, SqlStatement *statement, int& column);

  template<class C> void visit(C& obj);

  template<typename V> void act(const FieldRef<V>& field);
  template<class C> void actCollection(const CollectionRef<C>& field);
  template<class C> void descend(ptr<C>& obj);

  bool isWriting() const;
  bool isReading() const;
  bool isSchema() const;

  void setTableName(const char *tableName);

  Session *session() { return dbo_.session(); }

  int setStatementIdx() const { return setStatementIdx_; }
  
private:
  MetaDboBase& dbo_;
  const char *tableName_;
  SqlStatement *statement_;
  int& column_, setStatementIdx_;

  void start();
  void exec();
  void done();
};

class WTDBO_API SaveDbAction
{
public:
  SaveDbAction(MetaDboBase& dbo);

  template<class C> void visit(C& obj);

  template<typename V> void act(const FieldRef<V>& field);
  template<class C> void actCollection(const CollectionRef<C>& field);
  template<class C> void descend(ptr<C>& obj);

  bool isWriting() const;
  bool isReading() const;
  bool isSchema() const;

private:
  int dummy_;
  LoadDbAction loadSets_;
  MetaDboBase& dbo_;
  SqlStatement *statement_;
  bool isInsert_;
  int column_;
  const char *tableName_;

  enum { Dependencies, Self, Sets } pass_;
  bool needSetsPass_;

  void startDependencyPass();
  void startSelfPass();
  void exec();

  void startSetsPass();
};

class WTDBO_API TransactionDoneAction
{
public:
  TransactionDoneAction(Session *session, MetaDboBase& dbo, bool success);

  template<class C> void visit(C& obj);

  template<typename V> void act(const FieldRef<V>& field);
  template<class C> void actCollection(const CollectionRef<C>& field);
  template<class C> void descend(ptr<C>& obj);

  bool isWriting() const;
  bool isReading() const;
  bool isSchema() const;

private:
  Session *session_;
  bool success_;
  int dummy_;
  LoadDbAction undoLoadSets_;
};

class WTDBO_API SessionAddAction
{
public:
  SessionAddAction(Session *session);

  template<class C> void visit(C& obj);

  template<typename V> void act(const FieldRef<V>& field);
  template<class C> void actCollection(const CollectionRef<C>& field);
  template<class C> void descend(ptr<C>& obj);

  bool isWriting() const;
  bool isReading() const;
  bool isSchema() const;

private:
  Session *session_;
};

class WTDBO_API GetManyToManyJoinIdAction
{
public:
  GetManyToManyJoinIdAction(const std::string& joinName,
			    const std::string& joinId);

  template<class C> void visit(C& obj);

  template<typename V> void act(const FieldRef<V>& field);
  template<class C> void actCollection(const CollectionRef<C>& field);
  template<class C> void descend(ptr<C>& obj);

  bool isWriting() const;
  bool isReading() const;
  bool isSchema() const;

  bool found() const { return found_; }
  std::string result() const { return result_; }

private:
  std::string joinName_;
  std::string result_;
  bool found_;
};

  }
}

#endif // WT_DBO_DBACTION_H_
