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

#include <string>

#include <Wt/WDate>
#include <Wt/WDateTime>
#include <Wt/WTime>
#include <Wt/WString>

#include <Wt/Dbo/SqlTraits>
#include <Wt/Dbo/SqlStatement>

#include <boost/lexical_cast.hpp>

namespace Wt {
  namespace Dbo {

template<>
struct sql_value_traits<WDate, void>
{
  static const bool specialized = true;

  static const char *format;

  static const char *type(SqlConnection *conn, int size);
  static void bind(const WDate& v, SqlStatement *statement, int column, int size);
  static bool read(WDate& v, SqlStatement *statement, int column, int size);
};

template<>
struct sql_value_traits<WDateTime, void>
{
  static const bool specialized = true;

  static const char *type(SqlConnection *conn, int size);
  static void bind(const WDateTime& v, SqlStatement *statement, int column,
		   int size);
  static bool read(WDateTime& v, SqlStatement *statement, int column, int size);
};

template<>
struct sql_value_traits<WTime, void>
{
  static const bool specialized = true;

  static const char *format;

  static const char *type(SqlConnection *conn, int size);
  static void bind(const WTime& v, SqlStatement *statement, int column,
		   int size);
  static bool read(WTime& v, SqlStatement *statement, int column, int size);
};

template<>
struct sql_value_traits<WString, void>
{
  static const bool specialized = true;

  static std::string type(SqlConnection *conn, int size);
  static void bind(const WString& v, SqlStatement *statement, int column,
		   int size);
  static bool read(WString& v, SqlStatement *statement, int column, int size);
};

    /*
     * WDate
     */

inline const char *sql_value_traits<WDate, void>::type(SqlConnection *conn,
						       int size)
{
  return conn->dateTimeType(SqlDate);
}

inline void sql_value_traits<WDate, void>
::bind(const WDate& v, SqlStatement *statement, int column, int size)
{
  if (v.isNull())
    statement->bindNull(column);
  else
    statement->bind(column, Wt::WDateTime(v).toPosixTime(), SqlDate);
}

inline bool sql_value_traits<WDate, void>
::read(WDate& v, SqlStatement *statement, int column, int size)
{
  boost::posix_time::ptime t;

  if (statement->getResult(column, &t, SqlDate)) {
    boost::gregorian::date d = t.date();
    v.setDate(d.year(), d.month(), d.day());
    return true;
  } else {
    v = WDate();
    return false;
  }
}

    /*
     * WTime
     */

inline const char *sql_value_traits<WTime, void>::type(SqlConnection *conn,
						       int size)
{
  return conn->dateTimeType(SqlTime);
}

inline void sql_value_traits<WTime, void>
::bind(const WTime& v, SqlStatement *statement, int column, int size)
{
  if (v.isNull())
    statement->bindNull(column);
  else {
    boost::posix_time::time_duration::fractional_seconds_type ticks_per_msec =
      boost::posix_time::time_duration::ticks_per_second() / 1000;

    statement->bind
      (column,
       boost::posix_time::time_duration(v.hour(), v.minute(), v.second(),
					v.msec() * ticks_per_msec));
  }
}

inline bool sql_value_traits<WTime, void>
::read(WTime& v, SqlStatement *statement, int column, int size)
{
  boost::posix_time::time_duration t;
 
  if (statement->getResult(column, &t)) {
    int h = t.hours();
    int m = t.minutes();
    int s = t.seconds();
    int ms = static_cast<int>(t.total_milliseconds() % 1000);

    if (!v.setHMS (h, m, s, ms)) {
      std::cerr << "Wt::Dbo: WTime can only hold durations < 24h"
		<< std::endl;
      return true;
    } else
      return false;

  } else {
    v = WTime();
    return false;
  }
}

    /*
     * WDateTime
     */

inline const char *sql_value_traits<WDateTime, void>::type(SqlConnection *conn,
							   int size)
{
  return conn->dateTimeType(SqlDateTime);
}

inline void sql_value_traits<WDateTime, void>
::bind(const WDateTime& v, SqlStatement *statement, int column, int size)
{
  if (v.isNull())
    statement->bindNull(column);
  else
    statement->bind(column, v.toPosixTime(), SqlDateTime);
}

inline bool sql_value_traits<WDateTime, void>
::read(WDateTime& v, SqlStatement *statement, int column, int size)
{
  boost::posix_time::ptime t;

  if (statement->getResult(column, &t, SqlDateTime)) {
    v = WDateTime::fromPosixTime(t);
    return true;
  } else {
    v = WDateTime();
    return false;
  }
}

    /*
     * WString
     */

inline std::string sql_value_traits<WString, void>::type(SqlConnection *conn,
							 int size)
{
    return conn->textType(size) + " not null";
}

inline void sql_value_traits<WString, void>
::bind(const WString& v, SqlStatement *statement, int column, int size)
{
  statement->bind(column, v.toUTF8());
}

inline bool sql_value_traits<WString, void>
::read(WString& v, SqlStatement *statement, int column, int size)
{
  std::string d;
  if (statement->getResult(column, &d, size)) {
    v = WString::fromUTF8(d);
    return true;
  } else {
    v = WString::Empty;
    return false;
  }
}

  }
}

#endif // WT_DBO_WT_TYPE_TRAITS_H_
