/*
 * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
 */

#include "h2odbc.h"

Statement::Statement(Connection* c) {
    m_magic=MAGIC_STATEMENT;
    m_connection=c;
    m_appRow=&m_appRowDefault;
    m_impRow=&m_impRowDefault;
    m_appParam=&m_appParamDefault;
    m_impParam=&m_impParamDefault;
    m_columnCount = 0;
    m_updateCount = 0;
    m_resultSetId = -1;
    m_preparedId = 0;
    m_rowId = 0;
    m_hasResultSet = false;
    m_parameterCount = 0;    
    m_useBookmarks = false;
    m_error = 0;
}

Statement::~Statement() {
    if(m_magic==MAGIC_STATEMENT) {
        m_magic=0;
    } else {
        trace("~Statement %d",m_magic);
        return;
    }    
}

Statement* Statement::cast(void* pointer) {
    if(pointer==0) {
        return 0;
    }
    Statement* stat=(Statement*)pointer;
    if(stat->m_magic!=MAGIC_STATEMENT) {
        return 0;
    }
    return stat;
}

void Statement::processResultSet(Socket* s) {
    m_rowId = 0;
    m_updateCount = 0;
    m_state = S_EXECUTED;
    m_hasResultSet = true;
    m_resultSetId = s->readInt();
    m_columnCount = s->readInt();
    trace("  ResultSet id=%d cols=%d", m_resultSetId, m_columnCount);
    m_impRow->clearRecords();
    m_appRow->clearRecords();
    for(int i=0; i<m_columnCount; i++) {
        DescriptorRecord* rec = new DescriptorRecord(m_impRow);
        rec->readMeta(s);
        m_impRow->addRecord(rec);
        rec = new DescriptorRecord(m_appRow);
        m_appRow->addRecord(rec);        
    }
    trace("  imp=%d app=%d", m_impRow->getRecordCount(), m_appRow->getRecordCount());
}

void Statement::getMetaTables(char* catalog, char* schema, char* table, char* tabletypes) {
    Socket* s = m_connection->getSocket();
    s->writeByte('M');
    s->writeByte('T');
    s->writeString(catalog);
    s->writeString(schema);
    s->writeString(table);
    s->writeString(tabletypes);
    processResultSet(s);
}

void Statement::getMetaBestRowIdentifier(char* catalog, char* schema, char* table, int scope, bool nullable) {
    Socket* s = m_connection->getSocket();
    s->writeByte('M');
    s->writeByte('B');
    s->writeString(catalog);
    s->writeString(schema);
    s->writeString(table);
    s->writeInt(scope);
    s->writeBool(nullable);
    processResultSet(s);
}

void Statement::getMetaVersionColumns(char* catalog, char* schema, char* table) {
    Socket* s = m_connection->getSocket();
    s->writeByte('M');
    s->writeByte('V');
    s->writeString(catalog);
    s->writeString(schema);
    s->writeString(table);
    processResultSet(s);
}

void Statement::getMetaIndexInfo(char* catalog, char* schema, char* table, bool unique, bool approximate) {
    Socket* s = m_connection->getSocket();
    s->writeByte('M');
    s->writeByte('I');
    s->writeString(catalog);
    s->writeString(schema);
    s->writeString(table);
    s->writeBool(unique);
    s->writeBool(approximate);
    processResultSet(s);
}    

void Statement::getMetaColumns(char* catalog, char* schema, char* table, char* column) {
    Socket* s = m_connection->getSocket();
    s->writeByte('M');
    s->writeByte('C');
    s->writeString(catalog);
    s->writeString(schema);
    s->writeString(table);
    s->writeString(column);
    processResultSet(s);    
}  

void Statement::getMetaTypeInfoAll() {
    Socket* s = m_connection->getSocket();
    s->writeByte('M');
    s->writeByte('D');
    s->writeByte('A');
    processResultSet(s);     
}

void Statement::getMetaTypeInfo(int sqlType) {
    Socket* s = m_connection->getSocket();
    s->writeByte('M');
    s->writeByte('D');
    s->writeByte('T');
    s->writeInt(sqlType);
    processResultSet(s);     
}

const char* Statement::getSQL() {
    return m_sql.data();
}

bool Statement::prepare(char* sql) {
    Socket* s = m_connection->getSocket();
    m_state = S_PREPARED;
    s->writeByte('P');
    s->writeString(sql);
    m_sql = sql;
    int result = s->readByte();
    if(result=='E') {
        m_parameterCount = 0;
        m_state = S_CLOSED;
        setError(E_42000);
        return false;
    } else if(result=='O') {    
        m_preparedId = s->readInt();
        m_parameterCount = s->readInt();
        m_impParam->clearRecords();
        m_appParam->clearRecords();
        trace("   prepared %d params=%d", m_preparedId, m_parameterCount);
    }
    return true;
}

void Statement::addParameter() {
    m_impParam->addRecord(new DescriptorRecord(m_impParam));
    m_appParam->addRecord(new DescriptorRecord(m_appParam));
}

bool Statement::executePrepared() {
    Socket* s = m_connection->getSocket();
    s->writeByte('Q');
    s->writeInt(m_preparedId);
    trace(" executePrepared %d", m_preparedId);
    Descriptor* desc = getAppParamDesc();
    for(int i=0; i<m_parameterCount; i++) {
        s->writeByte('1');
        s->writeInt(i);
        DescriptorRecord* rec = desc->getRecord(i);
        rec->sendParameterValue(s);
    }
    // end of parameters
    s->writeByte('0');
    
    int result = s->readByte();
    // TODO: this is copied source code from execute!
    m_state = S_EXECUTED;
    m_rowId = 0;
    m_resultSetId = 0;
    m_columnCount = 0;
    m_updateCount = 0;
    if(result=='E') {
        m_state = S_CLOSED;
        setError(E_42000);
        return false;
    } else if(result=='R') {
        processResultSet(s);
    } else if(result=='U') {
        m_state = S_EXECUTED;
        m_hasResultSet = false;
        m_updateCount = s->readInt();
    }
    return true;
}

bool Statement::execute(char* sql) {
    Socket* s = m_connection->getSocket();
    s->writeByte('E');
    s->writeString(sql);
    m_sql = sql;
    int result = s->readByte();
    m_state = S_EXECUTED;
    m_rowId = 0;
    m_resultSetId = 0;
    m_columnCount = 0;
    m_updateCount = 0;
    m_parameterCount = 0;
    if(result=='E') {
        m_state = S_CLOSED;
        // TODO set correct error
        setError(E_42000);
        return false;
    } else if(result=='R') {
        processResultSet(s);
    } else if(result=='U') {
        m_state = S_EXECUTED;
        m_hasResultSet = false;
        m_updateCount = s->readInt();
    } else if(result=='O') {
        m_preparedId = s->readInt();
        m_parameterCount = s->readInt();
        trace("   executeDirect prepared %d params=%d", m_preparedId, m_parameterCount);
        return executePrepared();
    }
    return true;
}

bool Statement::next() {
    if(!m_hasResultSet) {
        return false;
    }
    m_rowId++;
    Socket* s = m_connection->getSocket();
    s->writeByte('G');
    s->writeInt(m_resultSetId);
    int result = s->readByte();
    trace("  next %c", result);
    if(result=='E') {
        m_state = S_CLOSED;
        return false;
    } else if(result == '1') {
        for(int i=0; i<m_columnCount; i++) {
            m_impRow->readData(i, s);
            m_impRow->getRecord(i)->copyData(m_appRow->getRecord(i));
        }
        trace("  setStatus");
        m_impRow->setStatus(SQL_ROW_SUCCESS);
        trace("  setRowsProcessed");
        m_impRow->setRowsProcessed(1);
        return true;
    } else {
        m_impRow->setStatus(SQL_ROW_NOROW);
        m_impRow->setRowsProcessed(0);
        return false;
    }
    return m_resultSetId == 0;
} 

void Statement::closeCursor() {
    if(m_resultSetId >= 0) {
        Socket* s = m_connection->getSocket();
        s->writeByte('F');
        s->writeInt(m_resultSetId);
        m_resultSetId = -1;
    }
    m_state=S_CLOSED;
}

Descriptor* Statement::getAppRowDesc() {
    return m_appRow;
}

Descriptor* Statement::getImpRowDesc() {
    return m_impRow;
}

Descriptor* Statement::getAppParamDesc() {
    return m_appParam;
}

Descriptor* Statement::getImpParamDesc() {
    return m_impParam;
}