#include "set.h"
#include "baseadaptor.h"
#include "id.h"

#include <QSqlRecord>
#include <QStringList>
#include <QDebug>

SetData::SetData( const Database *db, const QList<Field> &fields, const Query &query, const QMap<QString,QVariant> &options ) :
        m_db(db), m_query(query), m_options(options)
{
    foreach( Field field, fields)
        m_fields[ field.scopedName() ] = field;
}


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

    foreach( Field f, indexsForQuery( fields ) )
        m_data->m_fields[ f.scopedName() ] = f;

    if( m_data->m_query.isValid() )
        select();
}

QList<Field> Set::indexsForQuery( const QList<Field> &fields ) const
{
    QList<Field> results;
    foreach( Field field, fields )
    {
        if( field.type() != (QVariant::Type)qMetaTypeId<Id>() )
        {
            Field id = field.table()->id();
            if( !results.contains( id ) )
                results << id;
        }
        else if( !results.contains( field ) )
                results << field;
    }
    return results;
}

bool Set::select()
{
    QString statement = m_data->m_db->db()->SELECT(m_data->m_fields.values(), m_data->m_query, m_data->m_options );
    qDebug() << statement;
    m_data->m_data = m_data->m_db->db()->connection().exec( statement );
    m_data->m_lastError = m_data->m_data.lastError();
    return m_data->m_data.first();
}

QMap<Field,QVariant> Set::values() const
{
   QMap<Field,QVariant> result;
   foreach( Field field, m_data->m_fields.values() )
   {
       result[field] = value(field);
   }
   return result;
}

QVariant Set::value( const Field &field ) const
{
    QSqlRecord record = m_data->m_data.record();
    foreach( Field f, indexsForQuery( QList<Field>() << field ) )
    {
        m_data->m_indexes[m_data->m_data.at()][f] = record.value( f.scopedName() );
    }

    return record.value(field.scopedName());
}

void Set::setValue( const Field &field, const QVariant &v )
{
    if ( value( field ) != v )
        m_data->m_updates[ m_data->m_data.at() ][field] = v;
}

void Set::setValues( const QMap<Field,QVariant> &vals )
{
    foreach( Field field, vals.keys() )
        setValue(field, vals[field] );
}

bool Set::commitCurrentRow( const QMap<Field,QVariant> &values )
{
    m_data->m_updates[ m_data->m_data.at() ] = values;
    return commitCurrentRow();
}

Query Set::commitQueryForCurrentRow( const QMap<Field,QVariant> &fields ) const
{
    Query q;
    foreach( Field field, fields.keys() )
    {
        if( q.isValid() )
            q = q && ( field == fields[field] );
        else
            q = field == fields[field];
    }
    return q;
}

bool Set::commitRow( int idx )
{
    QStringList args;
    foreach( Field field, m_data->m_updates[idx].keys() )
        if( !field.tableName().isEmpty() )
            args << field.tableName();

    args.removeDuplicates();
    if( args.count() > 1 || args.count() == 0 )
        return false;

    QString table = args.at(0);

    QMap<Field,QVariant> update = m_data->m_updates[ idx ];
    QString updateStatement = m_data->m_db->db()->UPDATE( table, update, commitQueryForCurrentRow( m_data->m_indexes[idx] ) );
    qDebug() << updateStatement;
    QSqlQuery result = m_data->m_db->db()->connection().exec( updateStatement );
    m_data->m_lastError = result.lastError();
    qDebug() << m_data->m_lastError;

    bool success = !m_data->m_lastError.isValid();
    if( success )
        m_data->m_updates.remove( idx );
    return success;
}

bool Set::commitSet()
{
    // start transaction...
    //m_data->m_db->begin();
    foreach( int row, m_data->m_updates.keys() )
    {
        if( !commitRow( row ) )
        {
            //m_data->m_db->rollback();
            // rollback transaction...
            return false;
        }
    }
    // commit transaction...
    //m_data->m_db->commit();
    return true;
}

bool Set::commitCurrentRow()
{
    return commitRow( m_data->m_data.at() );
}

int Set::nextRow()
{
    if( m_data->m_data.next() )
        return m_data->m_data.at();
    else
        return -1;
}

QSqlError Set::lastError() const
{
    return m_data->m_lastError;
}

