/* =================================================================
 Copyright (C) 2014 BizStation Corp All rights reserved.

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 02111-1307, USA.
 ================================================================= */
#include <boost/test/included/unit_test.hpp>
#include <bzs/db/protocol/tdap/client/database.h>
#include <bzs/db/protocol/tdap/client/table.h>
#include <bzs/db/protocol/tdap/client/dbDef.h>
#include <bzs/db/protocol/tdap/mysql/characterset.h>
#include <bzs/db/protocol/tdap/tdapcapi.h>
#include <bzs/db/protocol/tdap/client/stringConverter.h>
#include <stdio.h>
#include <bzs/db/protocol/tdap/client/filter.h>

using namespace bzs::db::protocol::tdap::client;
using namespace bzs::db::protocol::tdap;
using namespace std;
#define ISOLATION_READ_COMMITTED

#define URL _T("tdap://localhost/testString?dbfile=test.bdf")
#define TABLE_NAME _T("comments")
#define TABLE_ID 1

#define FDN_ID _T("id")
#define FDN_USER _T("user_id")
#define FDN_BODY _T("body")
#define FDN_IMAGE _T("image")
#define FDI_ID (short)0
#define FDI_USER (short)1
#define FDI_BODY (short)2
#define FDI_IMAGE (short)3

boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]);

boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[])
{
    return 0;
}

class fixture
{
    mutable database* m_db;

public:
    fixture() : m_db(NULL)
    {
        m_db = database::create();
        if (!m_db)
            printf("Error database::create()\n");
    }

    ~fixture()
    {
        if (m_db)
            database::destroy(m_db);
    }

    ::database* db() const { return m_db; }
};

// ------------------------------------------------------------------------

void doInsertStringFileter(table* tb)
{
    tb->clearBuffer();
    tb->setFV(FDI_USER, 1);
    tb->setFV(FDI_BODY, _T("1\ntest\nテスト\n\nあいうえおあいうえおb"));
    tb->setFV(FDI_IMAGE, _T("1\ntest\nテスト\n\nあいうえおあいうえおi"));
    tb->insert();

    tb->clearBuffer();
    tb->setFV(FDI_USER, 1);
    tb->setFV(FDI_BODY, _T("2\ntest\nテスト\n\nあいうえおあいうえおb"));
    tb->setFV(FDI_IMAGE, _T("2\ntest\nテスト\n\nあいうえおあいうえおi"));
    tb->insert();

    tb->clearBuffer();
    tb->setFV(FDI_USER, 2);
    tb->setFV(FDI_BODY, _T("3\ntest\nテスト\n\nあいうえおあいうえおb"));
    tb->setFV(FDI_IMAGE, _T("3\ntest\nテスト\n\nあいうえおあいうえおi"));
    tb->insert();
}

void doTestSeek(table* tb)
{
    uint_td dummy = 0;
    // 1
    tb->clearBuffer();
    tb->setFV(FDI_ID, 1);
    tb->seek();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestSeek - stat 1");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 1, "doTestSeek - id 1");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 1, "doTestSeek - user_id 1");
    BOOST_CHECK_MESSAGE(
        _tstring(tb->getFVstr(FDI_BODY)) ==
            _tstring(_T("1\ntest\nテスト\n\nあいうえおあいうえおb")),
        "doTestSeek - body 1");
    // 2
    tb->seekNext();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestSeek - stat 2");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 2, "doTestSeek - id 2");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 1, "doTestSeek - user_id 2");
    BOOST_CHECK_MESSAGE(
        _tstring(tb->getFVstr(FDI_BODY)) ==
            _tstring(_T("2\ntest\nテスト\n\nあいうえおあいうえおb")),
        "doTestSeek - body 2");
    // 3
    tb->seekNext();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestSeek - stat 3");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 3, "doTestSeek - id 3");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 2, "doTestSeek - user_id 3");
    BOOST_CHECK_MESSAGE(
        _tstring(tb->getFVstr(FDI_BODY)) ==
            _tstring(_T("3\ntest\nテスト\n\nあいうえおあいうえおb")),
        "doTestSeek - body 3");
    // 2
    tb->seekPrev();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestSeek - stat 2");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 2, "doTestSeek - id 2");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 1, "doTestSeek - user_id 2");
    BOOST_CHECK_MESSAGE(
        _tstring(tb->getFVstr(FDI_BODY)) ==
            _tstring(_T("2\ntest\nテスト\n\nあいうえおあいうえおb")),
        "doTestSeek - body 2");
}

void doTestFind(table* tb)
{
    tb->setKeyNum(0);
    tb->clearBuffer();

    tb->setFilter(_T("id >= 1 and id < 3"), 1, 0);
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestFind - setFilter");

    tb->setFV(FDI_ID, 1);
    tb->find(table::findForword);
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestFind - find");
    BOOST_CHECK_MESSAGE(1 == tb->getFVint(FDI_ID), "doTestFind - getFVint 1");

    tb->findNext(true);
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestFind - findNext");
    BOOST_CHECK_MESSAGE(2 == tb->getFVint(FDI_ID), "doTestFind - getFVint 2");
    BOOST_CHECK_MESSAGE(
        _tstring(_T("2\ntest\nテスト\n\nあいうえおあいうえおb")) ==
            _tstring(tb->getFVstr(FDI_BODY)),
        "doTestFind - getFVstr 2");
    tb->findNext(true);
    BOOST_CHECK_MESSAGE(9 == tb->stat(), "doTestFind - findNext");
    // 2
    // tb->findPrev(true);
    // BOOST_CHECK_MESSAGE(tb->stat() == STATUS_PROGRAM_ERROR, "doTestFind -
    // findPrev");
}

void doTestUpdate(table* tb)
{
    // select 1
    tb->clearBuffer();
    tb->setFV(FDI_ID, 1);
    tb->seek();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - stat 1");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 1, "doTestUpdate - id 1");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 1,
                        "doTestUpdate - user_id 1");
    BOOST_CHECK_MESSAGE(
        _tstring(tb->getFVstr(FDI_BODY)) ==
            _tstring(_T("1\ntest\nテスト\n\nあいうえおあいうえおb")),
        "doTestUpdate - body 1");
    // update
    tb->setFV(FDI_USER, 11);
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - setFV stat 1");
    tb->setFV(FDI_BODY, "1\nテスト\ntest\n\nABCDEFG");
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - setFV stat 1");
    tb->update();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - update stat 1");
    // select 2
    tb->seekNext();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - stat 2");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 2, "doTestUpdate - id 2");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 1,
                        "doTestUpdate - user_id 2");
    BOOST_CHECK_MESSAGE(
        _tstring(tb->getFVstr(FDI_BODY)) ==
            _tstring(_T("2\ntest\nテスト\n\nあいうえおあいうえおb")),
        "doTestUpdate - body 2");
    // update
    tb->setFV(FDI_USER, 12);
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - setFV stat 2");
    tb->setFV(FDI_BODY, "2\nテスト\ntest\n\nABCDEFG");
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - setFV stat 2");
    tb->update();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - update stat 2");
    // check 1
    tb->seekPrev();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - stat 2 1");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 1, "doTestUpdate - id 2 1");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 11,
                        "doTestUpdate - user_id 2 1");
    BOOST_CHECK_MESSAGE(_tstring(tb->getFVstr(FDI_BODY)) ==
                            _tstring(_T("1\nテスト\ntest\n\nABCDEFG")),
                        "doTestUpdate - body 2 1");
    // check 2
    tb->seekNext();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestUpdate - stat 2 2");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 2, "doTestUpdate - id 2 2");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 12,
                        "doTestUpdate - user_id 2 2");
    BOOST_CHECK_MESSAGE(_tstring(tb->getFVstr(FDI_BODY)) ==
                            _tstring(_T("2\nテスト\ntest\n\nABCDEFG")),
                        "doTestUpdate - body 2 2");
}

void doTestDelete(table* tb)
{
    // delete 2
    tb->clearBuffer();
    tb->setFV(FDI_ID, 2);
    tb->seek();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestDelete - seek 2");
    tb->del();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestDelete - delete 2");
    // select 1
    tb->clearBuffer();
    tb->setFV(FDI_ID, 1);
    tb->seek();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestDelete - stat 1");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 1, "doTestDelete - id 1");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 11,
                        "doTestDelete - user_id 1");
    BOOST_CHECK_MESSAGE(_tstring(tb->getFVstr(FDI_BODY)) ==
                            _tstring(_T("1\nテスト\ntest\n\nABCDEFG")),
                        "doTestDelete - body 1");
    // next is 3
    tb->seekNext();
    BOOST_CHECK_MESSAGE(0 == tb->stat(), "doTestDelete - stat 3");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_ID) == 3, "doTestDelete - id 3");
    BOOST_CHECK_MESSAGE(tb->getFVint(FDI_USER) == 2,
                        "doTestDelete - user_id 3");
    BOOST_CHECK_MESSAGE(
        _tstring(tb->getFVstr(FDI_BODY)) ==
            _tstring(_T("3\ntest\nテスト\n\nあいうえおあいうえおb")),
        "doTestDelete - body 3");
    // eof
    tb->seekNext();
    BOOST_CHECK_MESSAGE(tb->stat() == STATUS_EOF, "doTestDelete - eof 3");
}

void createDatabase(database* db)
{
    db->create(URL);
    if (db->stat() == STATUS_TABLE_EXISTS_ERROR)
    {
        db->open(URL, 0, 0);
        BOOST_CHECK_MESSAGE(0 == db->stat(), "createNewDataBase 1");
        db->drop();
        BOOST_CHECK_MESSAGE(0 == db->stat(), "drop stat=" << db->stat());
        db->create(URL);
    }
    BOOST_CHECK_MESSAGE(0 == db->stat(), "createNewDataBase");
}

void createTable(database* db)
{
    // create table
    dbdef* def = db->dbDef();
    tabledef td;
    memset(&td, 0, sizeof(td));
    td.setTableName(TABLE_NAME);
    _TCHAR buf[267];
    _tcscpy_s(buf, 100, TABLE_NAME);
    _tcscat_s(buf, 100, _T(".dat"));
    td.setFileName(buf);
    td.id = TABLE_ID;
    td.primaryKeyNum = -1;
    td.parentKeyNum = -1;
    td.replicaKeyNum = -1;
    td.pageSize = 2048;
    td.charsetIndex = CHARSET_UTF8B4;

    def->insertTable(&td);
    BOOST_CHECK_MESSAGE(0 == def->stat(), "insertTable stat = " << def->stat());

    fielddef* fd = def->insertField(TABLE_ID, FDI_ID);
    fd->setName(FDN_ID);
    fd->type = ft_autoinc;
    fd->len = (ushort_td)4;
    def->updateTableDef(TABLE_ID);
    BOOST_CHECK_MESSAGE(0 == def->stat(),
                        "updateTableDef 1 stat = " << def->stat());

    fd = def->insertField(TABLE_ID, FDI_USER);
    fd->setName(FDN_USER);
    fd->type = ft_integer;
    fd->len = (ushort_td)4;
    def->updateTableDef(TABLE_ID);
    BOOST_CHECK_MESSAGE(0 == def->stat(),
                        "updateTableDef 1 stat = " << def->stat());

    fd = def->insertField(TABLE_ID, FDI_BODY);
    fd->setName(FDN_BODY);
    fd->type = ft_mytext;
    fd->len = 10;
    def->updateTableDef(TABLE_ID);
    BOOST_CHECK_MESSAGE(0 == def->stat(),
                        "updateTableDef 2 stat = " << def->stat());

    fd = def->insertField(TABLE_ID, FDI_IMAGE);
    fd->setName(FDN_IMAGE);
    fd->type = ft_myblob;
    fd->len = 10;
    def->updateTableDef(TABLE_ID);
    BOOST_CHECK_MESSAGE(0 == def->stat(), "updateTableDef 3");

    keydef* kd = def->insertKey(TABLE_ID, 0);
    kd->segments[0].fieldNum = 0;
    kd->segments[0].flags.bit8 = 1; // extended key type
    kd->segments[0].flags.bit1 = 1; // changeable
    kd->segmentCount = 1;
    def->updateTableDef(TABLE_ID);
    BOOST_CHECK_MESSAGE(0 == def->stat(), "updateTableDef 4");
}

void testStringFileter(database* db)
{
    createDatabase(db);

    db->open(URL, 0, 0);
    BOOST_CHECK_MESSAGE(0 == db->stat(),
                        "createNewDataBase 1 stat = " << db->stat());

    createTable(db);

    table* tb = db->openTable(TABLE_ID);
    BOOST_CHECK_MESSAGE(0 == db->stat(), "openTable");

    doInsertStringFileter(tb);
    doTestSeek(tb);
    doTestFind(tb);
    doTestUpdate(tb);
    doTestDelete(tb);

    tb->release();

    db->close();

    db->open(URL, 0, 0);
    BOOST_CHECK_MESSAGE(0 == db->stat(), "drop 1");
    db->drop();
    BOOST_CHECK_MESSAGE(0 == db->stat(), "drop stat=" << db->stat());
}

// ------------------------------------------------------------------------
BOOST_AUTO_TEST_SUITE(filter)

BOOST_FIXTURE_TEST_CASE(stringFileter, fixture)
{
    testStringFileter(db());
}

BOOST_AUTO_TEST_SUITE_END()
// ------------------------------------------------------------------------