/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 * In particular this file is part of the iWear Database Access Library.
 *
 * The iWear Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation as in version 2 of the License.

 * 
 * The iWear Framework is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWEAR_DB_RECORDSET_H
#define __IWEAR_DB_RECORDSET_H


#include "iwear/utility.h"
#include "iwear/intrusive_ptr_base.h"
#include "boost/lexical_cast.hpp"
#include "boost/intrusive_ptr.hpp"

#include <string>
#include <stdexcept>
using namespace std;

namespace iwear {
    namespace db {
/**
 * This is a base class for all recordsets that different database incarnations
 * might use.
 * @todo Create iterators
 */

class record_set_implementation;
class record_set
{
private:
protected:
    boost::intrusive_ptr<const record_set_implementation> rimp;
    /* synthezied is ok for now
    record_set( const record_set& ) { }
    record_set& operator=(const record_set& ) { return *this; }
*/
public:
    record_set() : rimp(0) { }
    ~record_set();
    record_set( const record_set_implementation& ri ) : rimp(&ri) 
    {
    }

    record_set& operator=( const record_set_implementation& ri );

    class field
    {
    public:
	template<class T> void to( T& into ) const;
	template<class T> T as( const T& Default) const;
	template<class T> T as() const;

       /**
	* @return Size of the field in Bytes
	*/
	size_t size(void) const;
	/**
	 * @return true if the database value is null
	 */ 
	bool is_null() const;

	const char* name( void ) const;
	/**
	 */
	const char * c_str( void ) const;

	field( const record_set_implementation& rs, size_t rn, size_t cn )
	    : rset(rs), row_num( rn ), column( cn )
	{
	}

	field( const record_set_implementation& rs, size_t rn, const char* c );
	field( const record_set_implementation& rs, size_t rn, const std::string& c );
    protected:
	const record_set_implementation& rset;
	size_t row_num;
	size_t column;
    private:
    };

    class row
    {
    public:
	size_t size(void) const ;

	/**
	 * The checking and throwing variant of operator[].
	 */
	field at( size_t column ) const
	{
	    // TODO Check and throw ;)
	    return field(rset,row_num,column);
	}

	/**
	 * this cannot be size_t because it would be ambiguous with const char* then :/
	 */
	field operator[]( int column ) const
	{
	    return field(rset,row_num,column);
	}

	field operator[]( const char col[] ) const
	{
	    return field(rset,row_num,col);
	}

	field operator[]( const std::string& col ) const
	{
	    return field(rset,row_num,col);
	}

	row( const record_set_implementation& r, size_t rn );
    protected:
	const record_set_implementation& rset;
	size_t row_num;
    private:
	row& operator=(const row&);
    };

    friend class field;
    friend class row;
private:
    size_t colnum( size_t rown, const char* c) const;
    size_t colnum( size_t rown, const std::string& c ) const;
    bool is_null( size_t rown, size_t column ) const;
    size_t row_size( size_t rown ) const;
    size_t field_size( size_t rown, size_t coln ) const;
    const char* name( size_t r, size_t c ) const;
    const char* c_str( size_t r, size_t c ) const;
public:
    size_t size(void) const;
    size_t affected_rows( void ) const;
    row operator[]( size_t row ) const;
};

class record_set_implementation : virtual public intrusive_ptr_base
{
protected:
    friend class record_set;
    virtual size_t colnum( size_t rown, const char* c) const = 0;
    virtual size_t colnum( size_t rown, const std::string& c ) const = 0;
    virtual bool is_null( size_t rown, size_t column ) const = 0;
    virtual size_t row_size( size_t rown ) const = 0;
    virtual size_t field_size( size_t rown, size_t coln ) const = 0;
    virtual const char* name( size_t r, size_t c ) const = 0;
    virtual const char* c_str( size_t r, size_t c ) const = 0;
    virtual size_t size(void) const = 0;
    virtual size_t affected_rows(void) const = 0;
    virtual record_set::row operator[]( size_t row ) const = 0;
    virtual ~record_set_implementation() { }
};

inline
record_set::~record_set()
{
}

inline
record_set::row::row( const record_set_implementation& r, size_t rn ) 
	    : rset(r), row_num(rn) 
	{
	}
inline
	size_t record_set::row::size(void) const 
	{
	    return rset.row_size( row_num );
	}
inline
bool record_set::field::is_null() const
{
    return rset.is_null(row_num,column);
}

inline
record_set::field::field( const record_set_implementation& rs, size_t rn, const std::string& c )
: rset(rs), row_num( rn ), column( rset.colnum(rn,c) )
{
}

inline
record_set::field::field( const record_set_implementation& rs, size_t rn, const char* c )
: rset(rs), row_num( rn ), column( rset.colnum(rn,c) )
{
}

inline
const char * record_set::field::c_str( void ) const
{
    return rset.c_str(row_num,column);
}

inline
const char* record_set::field::name( void ) const
{
    return rset.name(row_num,column);
}

inline
size_t record_set::field::size(void) const 
{
    return rset.field_size(row_num,column);
}
inline
record_set& record_set::operator=( const record_set_implementation& ri )
{
    rimp = &ri;
    return *this;
}

inline 
size_t record_set::colnum( size_t rown, const char* c) const 
{ 
    return rimp->colnum( rown, c); 
}

inline
size_t record_set::affected_rows( void ) const
{
    return rimp->affected_rows();
}

inline 
size_t record_set::colnum( size_t rown, const std::string& c ) const 
{ 
    return rimp->colnum( rown, c); 
}

inline 
bool record_set::is_null( size_t rown, size_t column ) const 
{ 
    return rimp->is_null( rown, column ); 
}

inline 
size_t record_set::row_size( size_t rown ) const 
{ 
    return rimp->row_size(rown); 
}

inline 
size_t record_set::field_size( size_t rown, size_t coln ) const 
{ 
    return rimp->field_size( rown, coln ); 
}

inline 
const char* record_set::name( size_t r, size_t c ) const 
{ 
    return rimp->name(r,c); 
}

inline 
const char* record_set::c_str( size_t r, size_t c ) const 
{ 
    return rimp->c_str(r,c); 
}

inline 
size_t record_set::size(void) const 
{ 
    return rimp->size(); 
}

inline 
record_set::row record_set::operator[]( size_t row ) const 
{ 
    return rimp->operator[](row); 
}


//template<class T> T as( const T& Default) const;

template<class T> inline T record_set::field::as(const T& Default) const
{
    if ( ! is_null() )
    {
	return as<T>();
    }
    else
    {
	return Default;
    }
}

template<> inline timeval record_set::field::as() const
{
    return time_from_string(c_str());
}

template<> inline std::string record_set::field::as() const
{
    if ( c_str() ) return c_str();
    return string();
}

template<> inline const char * record_set::field::as() const
{
    return c_str();
}

template<> inline bool record_set::field::as() const
{
    return as<int>(0);
}

template<class T>
T record_set::field::as() const
{
    if( c_str() == NULL ) throw logic_error("Cannot convert NULL value");

    return boost::lexical_cast<T>(c_str());
}

} // namespace db
} // namespace iwear
#endif
