#ifndef SET_H
#define SET_H

#include "query.h"
#include "field.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QList>
#include <QMap>
#include <QExplicitlySharedDataPointer>
#include <QSharedData>

class Database;

class SetData : public QSharedData
{
public:
    SetData( const Database *db, const QList<Field> &fields, const Query &query, const QMap<QString,QVariant> &options );

    const Database *m_db;
    Query m_query;
    QMap<QString,Field> m_fields;
    QSqlQuery m_data;
    QMap<int,QMap<Field,QVariant> > m_cache;
    QMap<int,QMap<Field,QVariant> > m_updates;
    QMap<int,QMap<Field,QVariant> > m_indexes;

    QSqlError m_lastError;
    QMap<QString,QVariant> m_options;
};

class Set
{
public:
    Set( const Database *db, const QList<Field> &fields, const Query &query, const QMap<QString,QVariant> &options = QMap<QString,QVariant>() );

    /**
      extract a value from the internal query result's active row.
      */
    QVariant value( const Field &field ) const;

    /**
      set a value in the Set cache, this value is not written until commitSet or commitRow is called.
      */
    void setValue( const Field &field, const QVariant &value );
    void setValue( const Field &field, const Field &source, const Query &where );

    /**
      Returns an entire row of data from the active row
      */
    QMap<Field,QVariant> values() const;

    /**
      Sets the active row of the cache to the values.  These values are not written to the database until commitSet or commitRow are called.
      */
    void setValues( const QMap<Field,QVariant> &vals );

    QStringList columns() const;
    int columnCount() const;
    QMap<QString,Field> fields() const;
    Query query() const;

    bool isForwardOnly() const;
    bool hasSize() const;
    int rows() const;
    bool setCurrentRow( int row );
    int currentRow() const;
    int nextRow();
    int previousRow();
    int firstRow();
    int lastRow();

    Query selectQueryForRow() const;

    /**
      Updates a specific in the specific tables of the Set.
      */
    bool commitCurrentRow( const Field &field, const Field &source, const Query &where );
    bool commitCurrentRow( const Field &field, const QVariant &value );
    bool commitCurrentRow( int field, const QVariant &value );

    /**
      commits the values to the database.  this will try to update all of the fields in all of the tables involved via a transation. This will fail though if constraints are not met throughout the entire transaction.
    */
    bool commitCurrentRow( const QMap<Field,QVariant> &values );

    /**
      commits the current rows cache to the database.  this will try to update all of the fields in all of the tables involved via a transation. This will fail though if constraints are not met throughout the entire transaction.
    */
    bool commitCurrentRow();

    /**
      updates an entire column in the database so that the contents of the selected field's column are set to the result of the source column of the other Set's query.
      */
    bool commitColumn( const Field &field, const QString &source, const Set &where );
    bool commitColumn( const QString &field, const QString &source, const Set &where );
    bool commitColumn( int col, const QString &source, const Set &where );
    bool commitColumn( const Field &field, const Field &source, const Query &where );
    bool commitColumn( const QString &field, const Field &source, const Query &where );
    bool commitColumn( int col, const Field &source, const Query &where );

    /**
      updates an entire column in the database so that the contents of the selected field's column are set to the value.  this value can be a scalar value or an expression.
      */
    bool commitColumn( const Field &field, const QVariant &value );
    bool commitColumn( const QString &field, const QVariant &value );
    bool commitColumn( int col, const QVariant &value );

    /**
      Commit entire cache to the database via a transation of multiple commands. This will fail if constraints are not met the entire time of the transaction.
      */
    bool commitSet();

    /**
      Check if the internal query result is valid and the data in the Set has valid data.
      */
    bool isValid();

    /**
      Clear cache and reexecute the query
      */
    bool select();

    /**
      Clear the cache, but do not reexecute the query
      */
    bool reset();

    QSqlError lastError() const;

private:
    QList<Field> indexsForQuery( const QList<Field> &fields ) const;
    Query commitQueryForCurrentRow( const QMap<Field,QVariant> &fields ) const;
    bool commitRow( int idx );

    Query commitQueryForTable( const QString &table ) const;
    QList<Field> commitFieldsForTable( const QString &table );
    QMap<QString,QVariant> commitValuesForTable( const QString &table );

    QExplicitlySharedDataPointer<SetData> m_data;
};


#endif // SET_H

