#include <eter/py_tinker.h>

#include <mysql/mysql.h>

using namespace std;
using namespace py_tinker;

struct MySQL_SResult
{
    MySQL_SResult()
    {}
    MySQL_SResult(MYSQL_RES* sqlRes) : m_sqlRes(sqlRes), m_sqlRow(0)
    {
    }
    ~MySQL_SResult()
    {
        mysql_free_result(m_sqlRes);
    }
    PyObject* GetRow()
    {
        unsigned int nNumFields = GetNumFields();

        PyObject* poNewTuple = PyTuple_New(nNumFields);

        for (unsigned int iCol = 0; iCol != nNumFields; ++iCol)
            PyTuple_SetItem(poNewTuple, iCol, PyObject_From(GetRowCol(iCol)));

        Py_INCREF(poNewTuple);
        return poNewTuple;
    }
    bool FetchField()
    {
        m_sqlField = mysql_fetch_field(m_sqlRes);
        if (!m_sqlField)
            return false;

        return true;
    }
    bool FetchRow()
    {
        m_sqlRow = mysql_fetch_row(m_sqlRes);
        if (!m_sqlRow)
            return false;

        return true;
    }
    unsigned int GetNumRows()
    {
        return mysql_num_rows(m_sqlRes);
    }
    unsigned int GetNumFields()
    {
        return mysql_num_fields(m_sqlRes);
    }
    const char* GetFieldName()
    {
        if (!m_sqlField)
            return "";
        return m_sqlField->name;
    }
    const char* GetRowCol(int pos)
    {
        if (!m_sqlRow)
            return "";

        if (!m_sqlRow[pos])
            return "";

        return m_sqlRow[pos];
    }

    MYSQL_RES*  m_sqlRes;
    MYSQL_ROW   m_sqlRow;
    MYSQL_FIELD* m_sqlField;
};

struct MySQL_SClient
{

    MySQL_SClient()
    {
        m_isOnline = false;
    }
    ~MySQL_SClient()
    {
        Close();
    }
    void Close()
    {
        if (!m_isOnline)
            return;

        mysql_close(&m_sqlConn);
    }
    bool Connect(const char* szAddr, const char* szUserName, const char* szUserPass, const char* szDBName)
    {
        if (m_isOnline)
            return false;

        strncpy(m_szAddr, szAddr, ADDR_LEN);
        strncpy(m_szUserName, szUserName, USER_NAME_LEN);
        strncpy(m_szUserPass, szUserPass, USER_PASS_LEN);
        strncpy(m_szDBName, szDBName, DB_NAME_LEN);

        mysql_init(&m_sqlConn);
        if (!mysql_real_connect(&m_sqlConn, szAddr, szUserName, szUserPass, szDBName, 0, NULL, 0))
        {
            throw PyTinkerException(PyExc_RuntimeError, GetErrorString());
            return false;
        }

        m_isOnline = true;
        return true;
    }
    bool Query(const char* c_szQuery)
    {
        if (mysql_query(&m_sqlConn, c_szQuery) != 0)
        {
            throw PyTinkerException(PyExc_RuntimeError, GetErrorString());
            return false;
        }
        return true;
    }
    MySQL_SResult* GetResult()
    {
        return new MySQL_SResult(mysql_store_result(&m_sqlConn));
    }
    const char* GetErrorString()
    {
        return mysql_error(&m_sqlConn);
    }

    enum
    {
        ADDR_LEN        = 64,
        USER_NAME_LEN   = 16,
        USER_PASS_LEN   = 16,
        DB_NAME_LEN     = 32,
    };

    MYSQL       m_sqlConn;

    char        m_szAddr[ADDR_LEN];
    char        m_szUserName[USER_NAME_LEN];
    char        m_szUserPass[USER_PASS_LEN];
    char        m_szDBName[DB_NAME_LEN];

    bool        m_isOnline;

};

class_<MySQL_SResult> build_result()
{
    return class_<MySQL_SResult>("Result")
        .deft("GetNumRows",     &MySQL_SResult::GetNumRows)
        .deft("GetNumFields",   &MySQL_SResult::GetNumFields)
        .deft("GetRowCol",      &MySQL_SResult::GetRowCol)
        .deft("GetRow",         &MySQL_SResult::GetRow)
        .deft("GetFieldName",   &MySQL_SResult::GetFieldName)
        .deft("FetchRow",       &MySQL_SResult::FetchRow)
        .deft("FetchField",     &MySQL_SResult::FetchField)
        ;
}

impl_arg_class_(MySQL_SResult)

class_<MySQL_SClient> build_client()
{
    return class_<MySQL_SClient>("Client")
        .deft("Connect",        &MySQL_SClient::Connect)
        .defv("Close",          &MySQL_SClient::Close)
        .deft("Query",          &MySQL_SClient::Query)
        .deft("GetResult",      &MySQL_SClient::GetResult)
        .deft("GetErrorString", &MySQL_SClient::GetErrorString)
        ;
}

PyMODINIT_FUNC
initmysql()
{
    module_("mysql")
        .def(build_client())
        .def(build_result())
        ;
}

