#include "field.h"
#include "baseadaptor.h"
#include "table.h"
#include "fieldproperties.h"
#include <QSqlDatabase>
#include <QSqlDriver>

Field::Field(const BaseAdaptor * const db,
    const QString &fieldName, QVariant::Type type ) :
    Expression( db )
{
    m_properties = new FieldProperties( fieldName, type);
    m_nativeTypeId = qMetaTypeId<Field>();
}

void Field::setTable( const Table *table )
{
    m_properties->m_parent = table;
}

const Table *Field::table() const
{
    return m_properties->m_parent;
}

QString Field::fullyQualifiedName() const
{
    return QString(QLatin1String("%1.%2"))
            .arg( tableName() )
            .arg( name() );
}

QString Field::scopedName() const
{
   return QString(QLatin1String("%1/%2"))
        .arg( tableName() )
        .arg( name() );
}

QString Field::tableName() const
{
    if( m_properties->m_parent )
        return m_properties->m_parent->name();
    else
        return QString();
}

QString Field::name() const
{
    return m_properties->m_fieldName;
}

QVariant::Type Field::type() const
{
    return m_properties->m_type;
}

QString Field::toString() const
{
    return m_db->quoteFieldName( *this );
}

Expression Field::lower() const
{
    QVariantList args;
    args << toVariant();

    return Expression(m_db, &BaseAdaptor::LOWER, args );
}
Expression Field::upper() const
{
    QVariantList args;
    args << toVariant();

    return Expression(m_db, &BaseAdaptor::UPPER, args );
}
Expression Field::year() const
{
    QVariantList args;
    args << toVariant();
    args << "year";
    return Expression(m_db, &BaseAdaptor::EXTRACT, args );
}
Expression Field::month() const
{
    QVariantList args;
    args << toVariant();
    args << "month";
    return Expression(m_db, &BaseAdaptor::EXTRACT, args );
}
Expression Field::day() const
{
    QVariantList args;
    args << toVariant();
    args << "day";
    return Expression(m_db, &BaseAdaptor::EXTRACT, args );

}
Expression Field::hour() const
{
    QVariantList args;
    args << toVariant();
    args << "hour";
    return Expression(m_db, &BaseAdaptor::EXTRACT, args );

}
Expression Field::minutes() const
{
    QVariantList args;
    args << toVariant();
    args << "minutes";
    return Expression(m_db, &BaseAdaptor::EXTRACT, args );

}
Expression Field::seconds() const
{
    QVariantList args;
    args << toVariant();
    args << "seconds";
    return Expression(m_db, &BaseAdaptor::EXTRACT, args );

}
Expression Field::count() const
{
    QVariantList args;
    args << toVariant();
    args << "count";
    return Expression(m_db, &BaseAdaptor::AGGREGATE, args );
}
Expression Field::sum() const
{
    QVariantList args;
    args << toVariant();
    args << "sum";
    return Expression(m_db, &BaseAdaptor::AGGREGATE, args );

}
Expression Field::max() const
{
    QVariantList args;
    args << toVariant();
    args << "max";
    return Expression(m_db, &BaseAdaptor::AGGREGATE, args );

}
Expression Field::min() const
{
    QVariantList args;
    args << toVariant();
    args << "min";
    return Expression(m_db, &BaseAdaptor::AGGREGATE, args );

}
Expression Field::len() const
{
    QVariantList args;
    args << toVariant();
    args << "length";
    return Expression(m_db, &BaseAdaptor::AGGREGATE, args );
}

QVariant Field::property( const QString &propname ) const
{
    return m_properties->m_props[propname];
}

void Field::setProperty( const QString &propname, const QVariant &value )
{
    m_properties->m_props[propname] = value;
}

void Field::removeProperty( const QString &propname )
{
    m_properties->m_props.remove(propname);
}

void Field::setCheck( const Query &query )
{
    m_properties->m_props["check"] = query;
}

Query Field::check() const
{
    return m_properties->m_props["check"].value<Query>();
}

void Field::setUnique( bool unique )
{
    m_properties->m_props["unique"] = unique;
}

void Field::setNotNull( bool notnull )
{
    m_properties->m_props["notnull"] = notnull;
}

bool Field::isPrimaryKey() const
{
    return m_properties->m_props["primarykey"].toBool();
}

bool Field::isUnique() const
{
    return m_properties->m_props["unique"].toBool();
}

QString Field::create( ) const
{
    QMap<QString,QString> args;
    foreach( QString key, m_properties->m_props.keys() )
    {
        if( m_properties->m_props[key].canConvert(QVariant::String) )
            args[key] = m_properties->m_props[key].toString();
        else
            args[key] = m_db->expand( m_properties->m_props[key] );
    }

    QString col = formattedName();
    QString value = m_db->mapFieldType( m_properties->m_type );
    if ( value == QLatin1String("REFERENCE") )
        value = m_db->mapFieldType( property("target").value<Field>().type() );
    if ( args.contains(QLatin1String("default")))
        value += m_db->DEFAULT( args[QLatin1String("default")]);
    if ( args.contains(QLatin1String("notnull")))
        value += m_db->NOT_NULL( args[QLatin1String("notnull")] == QLatin1String("true") );
    if ( args.contains(QLatin1String("autoincrement")) &&  args[QLatin1String("autoincrement")] == QLatin1String("true"))
        value += m_db->PRIMARY_KEY( );
    return QString(QLatin1String("%1 %2"))
            .arg(col)
            .arg( m_db->formatDatabaseString( value, args ));
}

QString Field::formattedName() const
{
    return m_db->quoteFieldName( m_properties->m_fieldName );
}

QString Field::formattedTableName() const
{
    return m_db->quoteFieldName( tableName() );
}

bool operator==( const Field &left, const Field &right)
{
    if( left.m_nativeTypeId != right.m_nativeTypeId )
        return false;
    if( left.m_db != right.m_db )
        return false;
    if( left.m_properties->m_fieldName != right.m_properties->m_fieldName )
        return false;
    if( left.m_properties->m_type != right.m_properties->m_type )
        return false;
    if( left.m_properties->m_parent == 0 &&
        right.m_properties->m_parent == 0 )
        return true;
    if( left.m_properties->m_parent->name() != right.m_properties->m_parent->name() )
        return false;
    return true;
}

bool operator<( const Field &left, const Field &right )
{
    if( left.m_properties->m_fieldName < right.m_properties->m_fieldName )
        return true;
    else
        return false;
}

