<commit_before>#include "game_state.hpp"

#include "start_game_state.hpp"
#include "imove.hpp"
#include "walk_move.hpp"
#include "wall_move.hpp"
#include "logger.hpp"
#include "exception.hpp"

static boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;

namespace Quoridor {

static std::vector<std::string> colors = {"red", "green", "blue", "yellow"};
std::string GameState::name_("Game State");

GameState::GameState(std::shared_ptr<StateManager> stm,
        const std::vector<std::string> &player_types) : stm_(stm), anim_(),
    game_(new Game(9)), pf_(), players_(), pawn_list_(), cur_pawn_(),
    drag_list_(), pawn_wins_(), wall_wins_(), pawn_path_(),
    added_wall_(Wall::kInvalid, 0, 0, 0), wall_idx_(0),
    status_(kWaitingForMove),
    pos_utils_(9, 52, 50)
{
    lg.add_attribute("Tag", blattrs::constant<std::string>("game state"));

    init_gui_();
    subscribe_for_events_();

    if ((player_types.size() != 2) && (player_types.size() != 4)) {
        throw Exception("Invalid number of players");
    }

    for (size_t i = 0; i < player_types.size(); ++i) {
        std::shared_ptr<Pawn> pawn(new Pawn(colors[i]));
        pawn_list_.push_back(pawn);
    }

    try {
        game_->set_pawns(pawn_list_);
    }
    catch (Exception &e) {
        BOOST_LOG_ERROR(lg) << "failed to create game: " << e.what();
        throw;
    }

    int i = 0;
    for (auto player_type : player_types) {
        BOOST_LOG_INFO(lg) << "adding player " << player_type;
        players_[pawn_list_[i]] = pf_.make_player(player_type, game_, pawn_list_[i]);
        ++i;
    }

    set_pawns_();
    init_walls_();
    switch_cur_pawn_();

    if (drag_list_.count(cur_pawn_)) {
        drag_list_[cur_pawn_]->enable_drag();
    }
}

GameState::~GameState()
{
}

void GameState::update()
{
    switch (status_) {
    case kPreparingMove:
        pre_process_move_();
        status_ = kWaitingForMove;
        break;
    case kWaitingForMove:
        make_move_();
        break;
    case kPerformedMove:
        post_process_move_();
        if (is_finished_()) {
            BOOST_LOG_INFO(lg) << cur_pawn_->color() << " win";
            status_ = kFinished;
        }
        else {
            switch_cur_pawn_();
            status_ = kPreparingMove;
        }
        break;
    case kNeedPawnRedraw:
        redraw_pawn_();
        status_ = kWaitingForAnimationEnd;
        break;
    case kNeedDrawWall:
        draw_wall_();
        status_ = kPerformedMove;
        break;
    case kWaitingForAnimationEnd:
        break;
    case kFinished:
        break;
    default:
        break;
    }
}

std::shared_ptr<CEGUI::Window> GameState::window() const
{
    return win_;
}

const std::string &GameState::name() const
{
    return name_;
}

void GameState::init_gui_()
{
    CEGUI::ImageManager::getSingleton().loadImageset("pawn.imageset");
    CEGUI::ImageManager::getSingleton().loadImageset("board.imageset");
    CEGUI::ImageManager::getSingleton().loadImageset("wall.imageset");
    CEGUI::ImageManager::getSingleton().loadImageset("wall_repr.imageset");

    win_ = std::shared_ptr<CEGUI::Window>(
            CEGUI::WindowManager::getSingleton().
                    loadLayoutFromFile("game.layout"),
            [=](CEGUI::Window *w) {
                CEGUI::WindowManager::getSingleton().destroyWindow(w);
            }
    );

    anim_ = std::shared_ptr<CEGUI::Animation>(
            CEGUI::AnimationManager::getSingleton().
                    createAnimation("movePawn"),
            [=](CEGUI::Animation *anim) {
                CEGUI::AnimationManager::getSingleton().destroyAnimation(anim);
            }
    );
    anim_->setDuration(0.5);
    anim_->setReplayMode(CEGUI::Animation::RM_Once);
}

void GameState::set_pawns_()
{
    auto board_win = static_cast<CEGUI::DefaultWindow*>(win_->getChild("boardWindow"));

    board_win->subscribeEvent(
            CEGUI_Ext::DraggableWindow::EventDraggableWindowDropped,
            CEGUI::Event::Subscriber(&GameState::handle_window_dropped_, this));

    CEGUI::Window *drag_win;
    for (auto pawn_data : game_->pawn_data_list()) {
        if (players_[pawn_data.pawn]->is_interactive()) {
            drag_win = new CEGUI_Ext::DraggableWindow("DefaultWindow", pawn_data.pawn->color());
            drag_list_[pawn_data.pawn] = static_cast<CEGUI_Ext::DraggableWindow*>(drag_win);
            drag_list_[pawn_data.pawn]->disable_drag();
            pawn_wins_[drag_win] = pawn_data.pawn;
        }
        else {
            drag_win = CEGUI::WindowManager::getSingleton().
                createWindow("DefaultWindow", pawn_data.pawn->color());
            drag_win->subscribeEvent(
                    CEGUI::AnimationInstance::EventAnimationEnded,
                    CEGUI::Event::Subscriber(&GameState::handle_end_anim_, this));
        }

        drag_win->setSize(CEGUI::USize({0.1, 0}, {0.1, 0}));
        CEGUI::UVector2 pos = pos_utils_.node_to_pos(pawn_data.node);
        drag_win->setPosition(pos);

        auto pawn_win = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("pawn_anim.layout");
        drag_win->addChild(pawn_win);
        board_win->addChild(drag_win);
    }
}

void GameState::init_walls_()
{
    auto ws1 = static_cast<CEGUI::DefaultWindow*>(win_->getChild("boardWindow"));
    for (int i = 0; i < 10; ++i) {
        auto w1 = new CEGUI_Ext::DraggableWindow("DefaultWindow", "wall_1_" + std::to_string(i));
        w1->setPosition(CEGUI::UVector2({{0.0f, 25.0f * i + 4.0f}, {0.0f, 4.0f}}));
        w1->setSize(CEGUI::USize({{0.0, 21.0}, {0.0, 42.0}}));

        auto wall_img = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("wall_repr.layout");
        w1->addChild(wall_img);
        ws1->addChild(w1);

        wall_wins_[w1] = 1;
    }
}

void GameState::redraw_pawn_()
{
    Node old_node = pawn_path_[0];
    Node new_node = pawn_path_[1];

    pawn_path_.clear();

    CEGUI::UVector2 old_pos = pos_utils_.node_to_pos(old_node);
    CEGUI::UVector2 new_pos = pos_utils_.node_to_pos(new_node);
    std::string old_pos_str = "{{" + std::to_string(old_pos.d_x.d_scale) + ", "
        + std::to_string(old_pos.d_x.d_offset) + "}, {"
        + std::to_string(old_pos.d_y.d_scale) + ", "
        + std::to_string(old_pos.d_y.d_offset)+ "}}";
    std::string new_pos_str = "{{" + std::to_string(new_pos.d_x.d_scale) + ", "
        + std::to_string(new_pos.d_x.d_offset) + "}, {"
        + std::to_string(new_pos.d_y.d_scale) + ", "
        + std::to_string(new_pos.d_y.d_offset)+ "}}";

    CEGUI::Affector *affector = anim_->createAffector("Position", "UVector2");
    affector->setApplicationMethod(CEGUI::Affector::AM_Absolute);
    affector->createKeyFrame(0.0, old_pos_str);
    affector->createKeyFrame(0.5, new_pos_str);

    auto pawn_win = win_->getChild("boardWindow/" + cur_pawn_->color());
    CEGUI::AnimationInstance *instance = CEGUI::AnimationManager::
            getSingleton().instantiateAnimation(anim_.get());
    instance->setTargetWindow(pawn_win);
    instance->start();
}

void GameState::draw_wall_()
{
    auto board_win = static_cast<CEGUI::DefaultWindow*>(win_->getChild("boardWindow"));
    Node node;
    CEGUI::UVector2 pos;

    if (added_wall_.orientation() == Wall::kHorizontal) {
        for (int i = 0; i < added_wall_.cnt(); ++i) {
            node.set_row(added_wall_.row() - 1);
            node.set_col(added_wall_.col() + i);
            pos = pos_utils_.node_to_pos(node);
            pos.d_y.d_offset = -2.0;
            auto wall_win = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("horizontal_wall.layout");
            wall_win->setPosition(pos);
            wall_win->setName("wallWindow" + std::to_string(wall_idx_));
            ++wall_idx_;
            board_win->addChild(wall_win);
            BOOST_LOG_INFO(lg) << "added horizontal wall at "
                << added_wall_.row() << ":" << added_wall_.col() + i;
        }
    }
    else if (added_wall_.orientation() == Wall::kVertical) {
        for (int i = 0; i < added_wall_.cnt(); ++i) {
            node.set_row(added_wall_.row() + i);
            node.set_col(added_wall_.col());
            pos = pos_utils_.node_to_pos(node);
            pos.d_x.d_offset = -2.0;
            auto wall_win = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("vertical_wall.layout");
            wall_win->setPosition(pos);
            wall_win->setName("wallWindow" + std::to_string(wall_idx_));
            ++wall_idx_;
            board_win->addChild(wall_win);
            BOOST_LOG_INFO(lg) << "added vertiacl wall at "
                << added_wall_.row() << ":" << added_wall_.col() + i;
        }
    }
}

void GameState::pre_process_move_()
{
    if (drag_list_.count(cur_pawn_)) {
        drag_list_.at(cur_pawn_)->enable_drag();
    }
}

void GameState::post_process_move_()
{
    if (drag_list_.count(cur_pawn_)) {
        drag_list_.at(cur_pawn_)->disable_drag();
    }
}

void GameState::switch_cur_pawn_()
{
    game_->switch_pawn();
    cur_pawn_ = game_->cur_pawn_data().pawn;
}

void GameState::make_move_()
{
    IMove *move = NULL;

    // bot's turn
    if (!players_[cur_pawn_]->is_interactive()) {
        move = players_[cur_pawn_]->get_move();
    }
    // human's turn, handle it in one of event handlers
    else {
        return;
    }

    if (move != NULL) {
        Node cur_node = game_->cur_pawn_data().node;
        int rc;

        if (WalkMove *walk_move = dynamic_cast<WalkMove*>(move)) {
            BOOST_LOG_DEBUG(lg) << cur_pawn_->color()
                << " move: " << cur_node.row() << ":" << cur_node.col()
                << " -> " << walk_move->node().row() << ":"
                << walk_move->node().col();

            rc = game_->move_pawn(walk_move->node());
            if (rc == 0) {
                pawn_path_.push_back(cur_node);
                pawn_path_.push_back(game_->cur_pawn_data().node);
                status_ = kNeedPawnRedraw;
            }
        }
        else if (WallMove *wall_move = dynamic_cast<WallMove*>(move)) {
            rc = game_->add_wall(wall_move->wall());
            if (rc == 0) {
                added_wall_ = wall_move->wall();
                status_ = kNeedDrawWall;
            }
        }
    }
}

bool GameState::is_finished_() const
{
    return game_->is_finished();
}

void GameState::subscribe_for_events_()
{
    win_->getChild("back")->subscribeEvent(
            CEGUI::Window::EventMouseClick,
            CEGUI::Event::Subscriber(
                    &GameState::handle_back_, this
            )
    );
}

bool GameState::handle_back_(const CEGUI::EventArgs &/* e */)
{
    BOOST_LOG_DEBUG(lg) << "returning to start game menu";
    stm_->change_state(std::shared_ptr<IState>(new StartGameState(stm_)));
    return true;
}

bool GameState::handle_end_anim_(const CEGUI::EventArgs &/* e */)
{
    status_ = kPerformedMove;
    return true;
}

bool GameState::handle_window_dropped_(const CEGUI::EventArgs &e)
{
    auto de = static_cast<const CEGUI_Ext::DragEvent&>(e);
    if (pawn_wins_.count(de.window())) {
        BOOST_LOG_DEBUG(lg) << "pawn was dropped!";
        return handle_pawn_dropped_(de);
    }
    else if (wall_wins_.count(de.window())) {
        BOOST_LOG_DEBUG(lg) << "wall was dropped!";
        return handle_wall_dropped_(de);
    }
    else {
        BOOST_LOG_DEBUG(lg) << "unknown object was dropped!";
        return false;
    }
}

bool GameState::handle_pawn_dropped_(const CEGUI_Ext::DragEvent &de)
{
    CEGUI::Vector2f rel_pos = CEGUI::CoordConverter::asRelative(
            de.window()->getPosition() + de.pos(),
            {568, 568}  // @fixme get parent size
    );
    Node node = normalize_pawn_pos_(rel_pos);

    BOOST_LOG_INFO(lg) << de.window() << " position "
        << node.row() << ":" << node.col();

    CEGUI::UVector2 pos;
    int rc = game_->move_pawn(node);
    if (rc == 0) {
        pos = pos_utils_.node_to_pos(node);
        status_ = kPerformedMove;
    }
    else {
        Node cur_node = game_->cur_pawn_data().node;
        pos = pos_utils_.node_to_pos(cur_node);
    }

    de.window()->setPosition(pos);

    return true;
}

bool GameState::handle_wall_dropped_(const CEGUI_Ext::DragEvent &de)
{
    CEGUI::Vector2f rel_pos = CEGUI::CoordConverter::asRelative(
            de.window()->getPosition() + de.pos(),
            {568, 568}  // @fixme get parent size
    );
    Wall wall = normalize_wall_pos_(rel_pos);
    BOOST_LOG_DEBUG(lg) << "adding wall: "
        << wall.orientation() << ", " << wall.row() << ", "
        << wall.col();
    int rc = game_->add_wall(wall);
    if (rc == 0) {
        added_wall_ = wall;
        status_ = kNeedDrawWall;
        de.window()->setVisible(false);
    }

    return true;
}

Node GameState::normalize_pawn_pos_(const CEGUI::Vector2f &rel_pos)
{
    CEGUI::UVector2 pos({rel_pos.d_x, 0.0}, {rel_pos.d_y, 0.0});
    return pos_utils_.pos_to_node(pos);
}

Wall GameState::normalize_wall_pos_(const CEGUI::Vector2f &rel_pos)
{
    CEGUI::UVector2 pos({rel_pos.d_x, 0.0}, {rel_pos.d_y, 0.0});
    Wall wall = pos_utils_.pos_to_wall(pos, 2);
    return wall;
}

}  /* namespace Quoridor */
<commit_msg>Fix logging messages<commit_after>#include "game_state.hpp"

#include "start_game_state.hpp"
#include "imove.hpp"
#include "walk_move.hpp"
#include "wall_move.hpp"
#include "logger.hpp"
#include "exception.hpp"

static boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;

namespace Quoridor {

static std::vector<std::string> colors = {"red", "green", "blue", "yellow"};
std::string GameState::name_("Game State");

GameState::GameState(std::shared_ptr<StateManager> stm,
        const std::vector<std::string> &player_types) : stm_(stm), anim_(),
    game_(new Game(9)), pf_(), players_(), pawn_list_(), cur_pawn_(),
    drag_list_(), pawn_wins_(), wall_wins_(), pawn_path_(),
    added_wall_(Wall::kInvalid, 0, 0, 0), wall_idx_(0),
    status_(kWaitingForMove),
    pos_utils_(9, 52, 50)
{
    lg.add_attribute("Tag", blattrs::constant<std::string>("game state"));

    init_gui_();
    subscribe_for_events_();

    if ((player_types.size() != 2) && (player_types.size() != 4)) {
        throw Exception("Invalid number of players");
    }

    for (size_t i = 0; i < player_types.size(); ++i) {
        std::shared_ptr<Pawn> pawn(new Pawn(colors[i]));
        pawn_list_.push_back(pawn);
    }

    try {
        game_->set_pawns(pawn_list_);
    }
    catch (Exception &e) {
        BOOST_LOG_ERROR(lg) << "failed to create game: " << e.what();
        throw;
    }

    int i = 0;
    for (auto player_type : player_types) {
        BOOST_LOG_INFO(lg) << "adding player " << player_type;
        players_[pawn_list_[i]] = pf_.make_player(player_type, game_, pawn_list_[i]);
        ++i;
    }

    set_pawns_();
    init_walls_();
    switch_cur_pawn_();

    if (drag_list_.count(cur_pawn_)) {
        drag_list_[cur_pawn_]->enable_drag();
    }
}

GameState::~GameState()
{
}

void GameState::update()
{
    switch (status_) {
    case kPreparingMove:
        pre_process_move_();
        status_ = kWaitingForMove;
        break;
    case kWaitingForMove:
        make_move_();
        break;
    case kPerformedMove:
        post_process_move_();
        if (is_finished_()) {
            BOOST_LOG_INFO(lg) << cur_pawn_->color() << " win";
            status_ = kFinished;
        }
        else {
            switch_cur_pawn_();
            status_ = kPreparingMove;
        }
        break;
    case kNeedPawnRedraw:
        redraw_pawn_();
        status_ = kWaitingForAnimationEnd;
        break;
    case kNeedDrawWall:
        draw_wall_();
        status_ = kPerformedMove;
        break;
    case kWaitingForAnimationEnd:
        break;
    case kFinished:
        break;
    default:
        break;
    }
}

std::shared_ptr<CEGUI::Window> GameState::window() const
{
    return win_;
}

const std::string &GameState::name() const
{
    return name_;
}

void GameState::init_gui_()
{
    CEGUI::ImageManager::getSingleton().loadImageset("pawn.imageset");
    CEGUI::ImageManager::getSingleton().loadImageset("board.imageset");
    CEGUI::ImageManager::getSingleton().loadImageset("wall.imageset");
    CEGUI::ImageManager::getSingleton().loadImageset("wall_repr.imageset");

    win_ = std::shared_ptr<CEGUI::Window>(
            CEGUI::WindowManager::getSingleton().
                    loadLayoutFromFile("game.layout"),
            [=](CEGUI::Window *w) {
                CEGUI::WindowManager::getSingleton().destroyWindow(w);
            }
    );

    anim_ = std::shared_ptr<CEGUI::Animation>(
            CEGUI::AnimationManager::getSingleton().
                    createAnimation("movePawn"),
            [=](CEGUI::Animation *anim) {
                CEGUI::AnimationManager::getSingleton().destroyAnimation(anim);
            }
    );
    anim_->setDuration(0.5);
    anim_->setReplayMode(CEGUI::Animation::RM_Once);
}

void GameState::set_pawns_()
{
    auto board_win = static_cast<CEGUI::DefaultWindow*>(win_->getChild("boardWindow"));

    board_win->subscribeEvent(
            CEGUI_Ext::DraggableWindow::EventDraggableWindowDropped,
            CEGUI::Event::Subscriber(&GameState::handle_window_dropped_, this));

    CEGUI::Window *drag_win;
    for (auto pawn_data : game_->pawn_data_list()) {
        if (players_[pawn_data.pawn]->is_interactive()) {
            drag_win = new CEGUI_Ext::DraggableWindow("DefaultWindow", pawn_data.pawn->color());
            drag_list_[pawn_data.pawn] = static_cast<CEGUI_Ext::DraggableWindow*>(drag_win);
            drag_list_[pawn_data.pawn]->disable_drag();
            pawn_wins_[drag_win] = pawn_data.pawn;
        }
        else {
            drag_win = CEGUI::WindowManager::getSingleton().
                createWindow("DefaultWindow", pawn_data.pawn->color());
            drag_win->subscribeEvent(
                    CEGUI::AnimationInstance::EventAnimationEnded,
                    CEGUI::Event::Subscriber(&GameState::handle_end_anim_, this));
        }

        drag_win->setSize(CEGUI::USize({0.1, 0}, {0.1, 0}));
        CEGUI::UVector2 pos = pos_utils_.node_to_pos(pawn_data.node);
        drag_win->setPosition(pos);

        auto pawn_win = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("pawn_anim.layout");
        drag_win->addChild(pawn_win);
        board_win->addChild(drag_win);
    }
}

void GameState::init_walls_()
{
    auto ws1 = static_cast<CEGUI::DefaultWindow*>(win_->getChild("boardWindow"));
    for (int i = 0; i < 10; ++i) {
        auto w1 = new CEGUI_Ext::DraggableWindow("DefaultWindow", "wall_1_" + std::to_string(i));
        w1->setPosition(CEGUI::UVector2({{0.0f, 25.0f * i + 4.0f}, {0.0f, 4.0f}}));
        w1->setSize(CEGUI::USize({{0.0, 21.0}, {0.0, 42.0}}));

        auto wall_img = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("wall_repr.layout");
        w1->addChild(wall_img);
        ws1->addChild(w1);

        wall_wins_[w1] = 1;
    }
}

void GameState::redraw_pawn_()
{
    Node old_node = pawn_path_[0];
    Node new_node = pawn_path_[1];

    pawn_path_.clear();

    CEGUI::UVector2 old_pos = pos_utils_.node_to_pos(old_node);
    CEGUI::UVector2 new_pos = pos_utils_.node_to_pos(new_node);
    std::string old_pos_str = "{{" + std::to_string(old_pos.d_x.d_scale) + ", "
        + std::to_string(old_pos.d_x.d_offset) + "}, {"
        + std::to_string(old_pos.d_y.d_scale) + ", "
        + std::to_string(old_pos.d_y.d_offset)+ "}}";
    std::string new_pos_str = "{{" + std::to_string(new_pos.d_x.d_scale) + ", "
        + std::to_string(new_pos.d_x.d_offset) + "}, {"
        + std::to_string(new_pos.d_y.d_scale) + ", "
        + std::to_string(new_pos.d_y.d_offset)+ "}}";

    CEGUI::Affector *affector = anim_->createAffector("Position", "UVector2");
    affector->setApplicationMethod(CEGUI::Affector::AM_Absolute);
    affector->createKeyFrame(0.0, old_pos_str);
    affector->createKeyFrame(0.5, new_pos_str);

    auto pawn_win = win_->getChild("boardWindow/" + cur_pawn_->color());
    CEGUI::AnimationInstance *instance = CEGUI::AnimationManager::
            getSingleton().instantiateAnimation(anim_.get());
    instance->setTargetWindow(pawn_win);
    instance->start();
}

void GameState::draw_wall_()
{
    auto board_win = static_cast<CEGUI::DefaultWindow*>(win_->getChild("boardWindow"));
    Node node;
    CEGUI::UVector2 pos;

    BOOST_LOG_INFO(lg) << "adding " << added_wall_;
    if (added_wall_.orientation() == Wall::kHorizontal) {
        for (int i = 0; i < added_wall_.cnt(); ++i) {
            node.set_row(added_wall_.row() - 1);
            node.set_col(added_wall_.col() + i);
            pos = pos_utils_.node_to_pos(node);
            pos.d_y.d_offset = -2.0;
            auto wall_win = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("horizontal_wall.layout");
            wall_win->setPosition(pos);
            wall_win->setName("wallWindow" + std::to_string(wall_idx_));
            ++wall_idx_;
            board_win->addChild(wall_win);
        }
    }
    else if (added_wall_.orientation() == Wall::kVertical) {
        for (int i = 0; i < added_wall_.cnt(); ++i) {
            node.set_row(added_wall_.row() + i);
            node.set_col(added_wall_.col());
            pos = pos_utils_.node_to_pos(node);
            pos.d_x.d_offset = -2.0;
            auto wall_win = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("vertical_wall.layout");
            wall_win->setPosition(pos);
            wall_win->setName("wallWindow" + std::to_string(wall_idx_));
            ++wall_idx_;
            board_win->addChild(wall_win);
        }
    }
}

void GameState::pre_process_move_()
{
    if (drag_list_.count(cur_pawn_)) {
        drag_list_.at(cur_pawn_)->enable_drag();
    }
}

void GameState::post_process_move_()
{
    if (drag_list_.count(cur_pawn_)) {
        drag_list_.at(cur_pawn_)->disable_drag();
    }
}

void GameState::switch_cur_pawn_()
{
    game_->switch_pawn();
    cur_pawn_ = game_->cur_pawn_data().pawn;
}

void GameState::make_move_()
{
    IMove *move = NULL;

    // bot's turn
    if (!players_[cur_pawn_]->is_interactive()) {
        move = players_[cur_pawn_]->get_move();
    }
    // human's turn, handle it in one of event handlers
    else {
        return;
    }

    if (move != NULL) {
        Node cur_node = game_->cur_pawn_data().node;
        int rc;

        if (WalkMove *walk_move = dynamic_cast<WalkMove*>(move)) {
            BOOST_LOG_DEBUG(lg) << cur_pawn_->color()
                << " move: " << cur_node.row() << ":" << cur_node.col()
                << " -> " << walk_move->node().row() << ":"
                << walk_move->node().col();

            rc = game_->move_pawn(walk_move->node());
            if (rc == 0) {
                pawn_path_.push_back(cur_node);
                pawn_path_.push_back(game_->cur_pawn_data().node);
                status_ = kNeedPawnRedraw;
            }
        }
        else if (WallMove *wall_move = dynamic_cast<WallMove*>(move)) {
            rc = game_->add_wall(wall_move->wall());
            if (rc == 0) {
                added_wall_ = wall_move->wall();
                status_ = kNeedDrawWall;
            }
        }
    }
}

bool GameState::is_finished_() const
{
    return game_->is_finished();
}

void GameState::subscribe_for_events_()
{
    win_->getChild("back")->subscribeEvent(
            CEGUI::Window::EventMouseClick,
            CEGUI::Event::Subscriber(
                    &GameState::handle_back_, this
            )
    );
}

bool GameState::handle_back_(const CEGUI::EventArgs &/* e */)
{
    BOOST_LOG_DEBUG(lg) << "returning to start game menu";
    stm_->change_state(std::shared_ptr<IState>(new StartGameState(stm_)));
    return true;
}

bool GameState::handle_end_anim_(const CEGUI::EventArgs &/* e */)
{
    status_ = kPerformedMove;
    return true;
}

bool GameState::handle_window_dropped_(const CEGUI::EventArgs &e)
{
    auto de = static_cast<const CEGUI_Ext::DragEvent&>(e);
    if (pawn_wins_.count(de.window())) {
        BOOST_LOG_DEBUG(lg) << "pawn was dropped!";
        return handle_pawn_dropped_(de);
    }
    else if (wall_wins_.count(de.window())) {
        BOOST_LOG_DEBUG(lg) << "wall was dropped!";
        return handle_wall_dropped_(de);
    }
    else {
        BOOST_LOG_DEBUG(lg) << "unknown object was dropped!";
        return false;
    }
}

bool GameState::handle_pawn_dropped_(const CEGUI_Ext::DragEvent &de)
{
    CEGUI::Vector2f rel_pos = CEGUI::CoordConverter::asRelative(
            de.window()->getPosition() + de.pos(),
            {568, 568}  // @fixme get parent size
    );
    Node node = normalize_pawn_pos_(rel_pos);

    BOOST_LOG_INFO(lg) << de.window() << " position "
        << node.row() << ":" << node.col();

    CEGUI::UVector2 pos;
    int rc = game_->move_pawn(node);
    if (rc == 0) {
        pos = pos_utils_.node_to_pos(node);
        status_ = kPerformedMove;
    }
    else {
        Node cur_node = game_->cur_pawn_data().node;
        pos = pos_utils_.node_to_pos(cur_node);
    }

    de.window()->setPosition(pos);

    return true;
}

bool GameState::handle_wall_dropped_(const CEGUI_Ext::DragEvent &de)
{
    CEGUI::Vector2f rel_pos = CEGUI::CoordConverter::asRelative(
            de.window()->getPosition() + de.pos(),
            {568, 568}  // @fixme get parent size
    );
    Wall wall = normalize_wall_pos_(rel_pos);
    BOOST_LOG_DEBUG(lg) << "adding wall: "
        << wall.orientation() << ", " << wall.row() << ", "
        << wall.col();
    int rc = game_->add_wall(wall);
    if (rc == 0) {
        added_wall_ = wall;
        status_ = kNeedDrawWall;
        de.window()->setVisible(false);
    }

    return true;
}

Node GameState::normalize_pawn_pos_(const CEGUI::Vector2f &rel_pos)
{
    CEGUI::UVector2 pos({rel_pos.d_x, 0.0}, {rel_pos.d_y, 0.0});
    return pos_utils_.pos_to_node(pos);
}

Wall GameState::normalize_wall_pos_(const CEGUI::Vector2f &rel_pos)
{
    CEGUI::UVector2 pos({rel_pos.d_x, 0.0}, {rel_pos.d_y, 0.0});
    Wall wall = pos_utils_.pos_to_wall(pos, 2);
    return wall;
}

}  /* namespace Quoridor */
<|endoftext|>


<commit_before>#pragma once

#include <regex>

#include "acmacs-base/stream.hh"

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

namespace passage
{
    namespace _internal
    {
        constexpr const char* re_nimr_isolate = R"#(( (ISOLATE|CLONE) [0-9\-]+)*)#"; // NIMR isolate and/or clone, NIMR H1pdm has CLONE 38-32
        constexpr const char* re_niid_plus_number = R"#(( *\+[1-9])?)#"; // NIID has +1 at the end of passage
        constexpr const char* re_passage_date = R"#(( \([12][0129][0-9][0-9]-[01][0-9]-[0-3][0-9]\))?$)#"; // passage date
    }

    inline bool is_egg(std::string aPassage)
    {
#include "acmacs-base/global-constructors-push.hh"
        using namespace _internal;
        static std::regex egg_passage{std::string(R"#(E(\?|[0-9][0-9]?))#") + re_nimr_isolate +  re_niid_plus_number + re_passage_date};
#include "acmacs-base/diagnostics-pop.hh"
        return std::regex_search(aPassage, egg_passage);

    } // is_egg

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

    inline bool is_cell(std::string aPassage)
    {
#include "acmacs-base/global-constructors-push.hh"
        using namespace _internal;
        static std::regex cell_passage{std::string(R"#((MDCK|SIAT|MK|CKC|CEK|CACO|LLC|LLK|PRMK|MEK|C)(\?|[0-9][0-9]?))#") + re_nimr_isolate +  re_niid_plus_number + re_passage_date};
#include "acmacs-base/diagnostics-pop.hh"
        return std::regex_search(aPassage, cell_passage);

    } // is_cell

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

    inline std::string without_date(std::string aPassage)
    {
        if (aPassage.size() > 13 && aPassage[aPassage.size() - 1] == ')' && aPassage[aPassage.size() - 12] == '(' && aPassage[aPassage.size() - 13] == ' ' && aPassage[aPassage.size() - 4] == '-' && aPassage[aPassage.size() - 7] == '-')
            return std::string(aPassage, 0, aPassage.size() - 13);
        else
            return aPassage;
    }

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

    enum class CellOrEgg {Unknown, Cell, Egg, CellAndEgg};

    inline CellOrEgg cell_or_egg(std::string aPassage)
    {
        return is_egg(aPassage) ? CellOrEgg::Egg : (is_cell(aPassage) ? CellOrEgg::Cell : CellOrEgg::CellAndEgg); // OR is CellAndEgg
    }

    inline CellOrEgg cell_or_egg(const std::vector<std::string>& variants)
    {
        CellOrEgg r = CellOrEgg::Unknown;
        for (const auto& passage: variants) {
            CellOrEgg p = cell_or_egg(passage);
            if (r == CellOrEgg::Unknown)
                r = p;
            else if (p != r) {
                r = CellOrEgg::CellAndEgg;
                break;
            }
        }
        return r == CellOrEgg::Unknown ? CellOrEgg::CellAndEgg : r; // unknown (or no passage) gives CellOrEgg::CellAndEgg to match anything
    }

    inline bool match_cell_egg(CellOrEgg a, CellOrEgg b)
    {
        return a == b || a == CellOrEgg::CellAndEgg || b == CellOrEgg::CellAndEgg;

    }

} // namespace passage

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>support for QMC passage (CDC)<commit_after>#pragma once

#include <regex>

#include "acmacs-base/stream.hh"

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

namespace passage
{
    namespace _internal
    {
        constexpr const char* re_nimr_isolate = R"#(( (ISOLATE|CLONE) [0-9\-]+)*)#"; // NIMR isolate and/or clone, NIMR H1pdm has CLONE 38-32
        constexpr const char* re_niid_plus_number = R"#(( *\+[1-9])?)#"; // NIID has +1 at the end of passage
        constexpr const char* re_passage_date = R"#(( \([12][0129][0-9][0-9]-[01][0-9]-[0-3][0-9]\))?$)#"; // passage date
    }

    inline bool is_egg(std::string aPassage)
    {
#include "acmacs-base/global-constructors-push.hh"
        using namespace _internal;
        static std::regex egg_passage{std::string(R"#(E(\?|[0-9][0-9]?))#") + re_nimr_isolate +  re_niid_plus_number + re_passage_date};
#include "acmacs-base/diagnostics-pop.hh"
        return std::regex_search(aPassage, egg_passage);

    } // is_egg

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

    inline bool is_cell(std::string aPassage)
    {
#include "acmacs-base/global-constructors-push.hh"
        using namespace _internal;
        static std::regex cell_passage{std::string(R"#((MDCK|SIAT|QMC|MK|CKC|CEK|CACO|LLC|LLK|PRMK|MEK|C)(\?|[0-9][0-9]?))#") + re_nimr_isolate +  re_niid_plus_number + re_passage_date};
#include "acmacs-base/diagnostics-pop.hh"
        return std::regex_search(aPassage, cell_passage);

    } // is_cell

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

    inline std::string without_date(std::string aPassage)
    {
        if (aPassage.size() > 13 && aPassage[aPassage.size() - 1] == ')' && aPassage[aPassage.size() - 12] == '(' && aPassage[aPassage.size() - 13] == ' ' && aPassage[aPassage.size() - 4] == '-' && aPassage[aPassage.size() - 7] == '-')
            return std::string(aPassage, 0, aPassage.size() - 13);
        else
            return aPassage;
    }

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

    enum class CellOrEgg {Unknown, Cell, Egg, CellAndEgg};

    inline CellOrEgg cell_or_egg(std::string aPassage)
    {
        return is_egg(aPassage) ? CellOrEgg::Egg : (is_cell(aPassage) ? CellOrEgg::Cell : CellOrEgg::CellAndEgg); // OR is CellAndEgg
    }

    inline CellOrEgg cell_or_egg(const std::vector<std::string>& variants)
    {
        CellOrEgg r = CellOrEgg::Unknown;
        for (const auto& passage: variants) {
            CellOrEgg p = cell_or_egg(passage);
            if (r == CellOrEgg::Unknown)
                r = p;
            else if (p != r) {
                r = CellOrEgg::CellAndEgg;
                break;
            }
        }
        return r == CellOrEgg::Unknown ? CellOrEgg::CellAndEgg : r; // unknown (or no passage) gives CellOrEgg::CellAndEgg to match anything
    }

    inline bool match_cell_egg(CellOrEgg a, CellOrEgg b)
    {
        return a == b || a == CellOrEgg::CellAndEgg || b == CellOrEgg::CellAndEgg;

    }

} // namespace passage

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>#include <algorithm>
#include <cstring>
#include <ctime>

#include "frame/test_macros.h"
#include "time/time_utility.h"
#include <time/jiffies_timer.h>

CASE_TEST(time_test, zone_offset) {
    util::time::time_utility::update();
    time_t offset = util::time::time_utility::get_sys_zone_offset() - 5 * util::time::time_utility::HOUR_SECONDS;
    util::time::time_utility::set_zone_offset(offset);
    CASE_EXPECT_EQ(offset, util::time::time_utility::get_zone_offset());
    CASE_EXPECT_NE(offset, util::time::time_utility::get_sys_zone_offset());

    // 恢复时区设置
    util::time::time_utility::set_zone_offset(util::time::time_utility::get_sys_zone_offset());
}

CASE_TEST(time_test, today_offset) {
    using std::abs;
    struct tm tobj;
    time_t tnow, loffset, cnow;
    util::time::time_utility::update();

    tnow = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&tnow, &tobj);
    loffset = tobj.tm_hour * util::time::time_utility::HOUR_SECONDS + tobj.tm_min * util::time::time_utility::MINITE_SECONDS + tobj.tm_sec;
    cnow = util::time::time_utility::get_today_offset(loffset);

    // 只有闰秒误差，肯定在5秒以内
    // 容忍夏时令误差，所以要加一小时
    CASE_EXPECT_LE(abs(cnow - tnow), 3605);
}

CASE_TEST(time_test, is_same_day) {
    struct tm tobj;
    time_t lt, rt;
    util::time::time_utility::update();
    lt = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&lt, &tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = 0;
    tobj.tm_min = 0;
    tobj.tm_sec = 5;
    lt = mktime(&tobj);
    rt = lt + 5;
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_day(lt, rt));

    tobj.tm_hour = 23;
    tobj.tm_min = 59;
    tobj.tm_sec = 55;
    rt = mktime(&tobj);
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_day(lt, rt));

    lt = rt - 5;
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_day(lt, rt));
    lt = rt + 10;
    CASE_EXPECT_FALSE(util::time::time_utility::is_same_day(lt, rt));
}

CASE_TEST(time_test, is_same_day_with_offset) {
    struct tm tobj;
    time_t lt, rt;
    int zero_hore = 5;
    time_t day_offset = zero_hore * util::time::time_utility::HOUR_SECONDS;

    util::time::time_utility::update();
    lt = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&lt, &tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = zero_hore;
    tobj.tm_min = 0;
    tobj.tm_sec = 5;
    lt = mktime(&tobj);
    rt = lt + 5;
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_day(lt, rt, day_offset));

    tobj.tm_hour = zero_hore - 1;
    tobj.tm_min = 59;
    tobj.tm_sec = 55;
    rt = mktime(&tobj);
    CASE_EXPECT_FALSE(util::time::time_utility::is_same_day(lt, rt, day_offset));
}

CASE_TEST(time_test, is_same_week) {
    struct tm tobj;
    time_t lt, rt, tnow;

    util::time::time_utility::update();
    tnow = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&tnow, &tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = 0;
    tobj.tm_min = 0;
    tobj.tm_sec = 5;
    lt = mktime(&tobj);
    lt -= util::time::time_utility::DAY_SECONDS * tobj.tm_wday;
    rt = lt + util::time::time_utility::WEEK_SECONDS;
    CASE_EXPECT_FALSE(util::time::time_utility::is_same_week(lt, rt));

    rt -= 10;
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_week(lt, rt));
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_week(lt, tnow));
}

CASE_TEST(time_test, get_week_day) {
    struct tm tobj;
    time_t lt, rt, tnow;

    util::time::time_utility::update();
    tnow = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&tnow, &tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = 0;
    tobj.tm_min = 0;
    tobj.tm_sec = 5;
    lt = mktime(&tobj);

    tobj.tm_hour = 23;
    tobj.tm_min = 59;
    tobj.tm_sec = 55;
    rt = mktime(&tobj);

    CASE_MSG_INFO() << "lt=" << lt << ",tnow=" << tnow << ",rt=" << rt << std::endl;
    CASE_EXPECT_EQ(util::time::time_utility::get_week_day(lt), util::time::time_utility::get_week_day(tnow));
    CASE_EXPECT_EQ(util::time::time_utility::get_week_day(lt), util::time::time_utility::get_week_day(rt));
}

CASE_TEST(time_test, is_same_month) {
    // nothing todo use libc now
}

typedef util::time::jiffies_timer<6, 3, 4> short_timer_t;
struct jiffies_timer_fn {
    void *check_priv_data;
    jiffies_timer_fn(void *pd) : check_priv_data(pd) {}

    void operator()(time_t, const short_timer_t::timer_t &timer) {
        if (NULL != check_priv_data) {
            CASE_EXPECT_EQ(short_timer_t::get_timer_private_data(timer), check_priv_data);
        } else if (NULL != short_timer_t::get_timer_private_data(timer)) {
            ++(*reinterpret_cast<int *>(short_timer_t::get_timer_private_data(timer)));
        }

        CASE_MSG_INFO() << "jiffies_timer " << short_timer_t::get_timer_sequence(timer) << " actived" << std::endl;
    }
};

CASE_TEST(time_test, jiffies_timer_basic) {
    short_timer_t short_timer;
    int count = 0;
    time_t max_tick = short_timer.get_max_tick_distance() + 1;

    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_NOT_INITED, short_timer.add_timer(123, jiffies_timer_fn(NULL), NULL));
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_NOT_INITED, short_timer.tick(456));

    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.init(max_tick));
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_ALREADY_INITED, short_timer.init(max_tick));

    CASE_EXPECT_EQ(32767, short_timer.get_max_tick_distance());
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_TIMEOUT_EXTENDED,
                   short_timer.add_timer(short_timer.get_max_tick_distance() + 1, jiffies_timer_fn(NULL), NULL));

    // 理论上会被放在（数组下标: 2^6*3=192）
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS,
                   short_timer.add_timer(short_timer.get_max_tick_distance(), jiffies_timer_fn(&short_timer), &short_timer));
    // 理论上会被放在（数组下标: 2^6*4-1=255）
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS,
                   short_timer.add_timer(short_timer.get_max_tick_distance() - short_timer_t::LVL_GRAN(3), jiffies_timer_fn(&short_timer),
                                         &short_timer));

    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(-123, jiffies_timer_fn(NULL), &count));
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(30, jiffies_timer_fn(NULL), &count));
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(40, jiffies_timer_fn(NULL), &count));
    CASE_EXPECT_EQ(5, static_cast<int>(short_timer.size()));

    CASE_EXPECT_EQ(0, count);
    short_timer.tick(max_tick);
    CASE_EXPECT_EQ(0, count);
    short_timer.tick(max_tick + 1);
    CASE_EXPECT_EQ(1, count);
    CASE_EXPECT_EQ(4, static_cast<int>(short_timer.size()));

    // +30的触发点是+31。因为添加定时器的时候会认为当前时间是+0.XXX，并且由于触发只会延后不会提前
    short_timer.tick(max_tick + 30);
    CASE_EXPECT_EQ(1, count);
    short_timer.tick(max_tick + 31);
    CASE_EXPECT_EQ(2, count);

    // 跨tick
    short_timer.tick(max_tick + 64);
    CASE_EXPECT_EQ(3, count);
    CASE_EXPECT_EQ(2, static_cast<int>(short_timer.size()));

    // 非第一层、非第一个定时器组.（512+64*5-1 = 831）
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(831, jiffies_timer_fn(NULL), &count));
    short_timer.tick(max_tick + 64 + 831);
    CASE_EXPECT_EQ(3, count);
    // 768-831 share tick
    short_timer.tick(max_tick + 64 + 832);
    CASE_EXPECT_EQ(4, count);

    // 32767
    short_timer.tick(32767 + max_tick);

    // 这个应该会放在数组下标为0的位置
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(0, jiffies_timer_fn(NULL), &count));

    // 环状数组的复用
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(1000, jiffies_timer_fn(NULL), &count));

    // 全部执行掉
    short_timer.tick(32767 + max_tick + 2000);
    CASE_EXPECT_EQ(6, count);
    CASE_EXPECT_EQ(0, static_cast<int>(short_timer.size()));
}

CASE_TEST(time_test, jiffies_timer_slot) {
    size_t timer_list_count[short_timer_t::WHEEL_SIZE] = {0};
    time_t blank_area[short_timer_t::WHEEL_SIZE / short_timer_t::LVL_SIZE] = {0};
    time_t max_tick = short_timer_t::get_max_tick_distance();
    for (time_t i = 0; i <= max_tick; ++i) {
        size_t idx = short_timer_t::calc_wheel_index(i, 0);
        CASE_EXPECT_LT(idx, static_cast<size_t>(short_timer_t::WHEEL_SIZE));
        ++timer_list_count[idx];
    }

    // 每个idx的计数器测试
    for (size_t i = 0; i < short_timer_t::WHEEL_SIZE; ++i) {
        size_t timer_count = timer_list_count[i];
        // 除了第一个定时器区间外，每个定时器区间都有一段空白区域
        if (0 == timer_count) {
            ++blank_area[i / short_timer_t::LVL_SIZE];
        } else {
            CASE_EXPECT_EQ(timer_count, short_timer_t::LVL_GRAN(i / short_timer_t::LVL_SIZE));
        }
    }

    // 定时器空白区间的个数应该等于重合区域
    for (size_t i = 1; i < short_timer_t::WHEEL_SIZE / short_timer_t::LVL_SIZE; ++i) {
        CASE_EXPECT_EQ(blank_area[i], short_timer_t::LVL_START(i) / short_timer_t::LVL_GRAN(i));
    }
}
<commit_msg>夏时令误差-2<commit_after>#include <algorithm>
#include <cstring>
#include <ctime>

#include "frame/test_macros.h"
#include "time/time_utility.h"
#include <time/jiffies_timer.h>

CASE_TEST(time_test, zone_offset) {
    util::time::time_utility::update();
    time_t offset = util::time::time_utility::get_sys_zone_offset() - 5 * util::time::time_utility::HOUR_SECONDS;
    util::time::time_utility::set_zone_offset(offset);
    CASE_EXPECT_EQ(offset, util::time::time_utility::get_zone_offset());
    CASE_EXPECT_NE(offset, util::time::time_utility::get_sys_zone_offset());

    // 恢复时区设置
    util::time::time_utility::set_zone_offset(util::time::time_utility::get_sys_zone_offset());
}

CASE_TEST(time_test, today_offset) {
    using std::abs;
    struct tm tobj;
    time_t tnow, loffset, cnow;
    util::time::time_utility::update();

    tnow = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&tnow, &tobj);
    loffset = tobj.tm_hour * util::time::time_utility::HOUR_SECONDS + tobj.tm_min * util::time::time_utility::MINITE_SECONDS + tobj.tm_sec;
    cnow = util::time::time_utility::get_today_offset(loffset);

    // 只有闰秒误差，肯定在5秒以内
    // 容忍夏时令误差，所以要加一小时
    CASE_EXPECT_LE(abs(cnow - tnow), 3605);
}

CASE_TEST(time_test, is_same_day) {
    struct tm tobj;
    time_t lt, rt;
    util::time::time_utility::update();
    lt = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&lt, &tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = 0;
    tobj.tm_min = 0;
    tobj.tm_sec = 5;
    lt = mktime(&tobj);
    rt = lt + 5;
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_day(lt, rt));

    tobj.tm_isdst = 0;
    tobj.tm_hour = 23;
    tobj.tm_min = 59;
    tobj.tm_sec = 55;
    rt = mktime(&tobj);
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_day(lt, rt));

    lt = rt - 5;
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_day(lt, rt));
    // 容忍夏时令误差
    lt = rt + 3610;
    CASE_EXPECT_FALSE(util::time::time_utility::is_same_day(lt, rt));
}

CASE_TEST(time_test, is_same_day_with_offset) {
    struct tm tobj;
    time_t lt, rt;
    int zero_hore = 5;
    time_t day_offset = zero_hore * util::time::time_utility::HOUR_SECONDS;

    util::time::time_utility::update();
    lt = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&lt, &tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = zero_hore;
    tobj.tm_min = 0;
    tobj.tm_sec = 5;
    lt = mktime(&tobj);
    rt = lt + 5;
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_day(lt, rt, day_offset));

    tobj.tm_isdst = 0;
    tobj.tm_hour = zero_hore - 1;
    tobj.tm_min = 59;
    tobj.tm_sec = 55;
    rt = mktime(&tobj);
    CASE_EXPECT_FALSE(util::time::time_utility::is_same_day(lt, rt, day_offset));
}

CASE_TEST(time_test, is_same_week) {
    struct tm tobj;
    time_t lt, rt, tnow;

    util::time::time_utility::update();
    tnow = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&tnow, &tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = 0;
    tobj.tm_min = 0;
    tobj.tm_sec = 5;
    lt = mktime(&tobj);
    lt -= util::time::time_utility::DAY_SECONDS * tobj.tm_wday;
    rt = lt + util::time::time_utility::WEEK_SECONDS;
    CASE_EXPECT_FALSE(util::time::time_utility::is_same_week(lt, rt));

    rt -= 10;
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_week(lt, rt));
    CASE_EXPECT_TRUE(util::time::time_utility::is_same_week(lt, tnow));
}

CASE_TEST(time_test, get_week_day) {
    struct tm tobj;
    time_t lt, rt, tnow;

    util::time::time_utility::update();
    tnow = util::time::time_utility::get_now();
    UTIL_STRFUNC_LOCALTIME_S(&tnow, &tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = 0;
    tobj.tm_min = 0;
    tobj.tm_sec = 5;
    lt = mktime(&tobj);

    tobj.tm_isdst = 0;
    tobj.tm_hour = 23;
    tobj.tm_min = 59;
    tobj.tm_sec = 55;
    rt = mktime(&tobj);

    CASE_MSG_INFO() << "lt=" << lt << ",tnow=" << tnow << ",rt=" << rt << std::endl;
    CASE_EXPECT_EQ(util::time::time_utility::get_week_day(lt), util::time::time_utility::get_week_day(tnow));
    CASE_EXPECT_EQ(util::time::time_utility::get_week_day(lt), util::time::time_utility::get_week_day(rt));
}

CASE_TEST(time_test, is_same_month) {
    // nothing todo use libc now
}

typedef util::time::jiffies_timer<6, 3, 4> short_timer_t;
struct jiffies_timer_fn {
    void *check_priv_data;
    jiffies_timer_fn(void *pd) : check_priv_data(pd) {}

    void operator()(time_t, const short_timer_t::timer_t &timer) {
        if (NULL != check_priv_data) {
            CASE_EXPECT_EQ(short_timer_t::get_timer_private_data(timer), check_priv_data);
        } else if (NULL != short_timer_t::get_timer_private_data(timer)) {
            ++(*reinterpret_cast<int *>(short_timer_t::get_timer_private_data(timer)));
        }

        CASE_MSG_INFO() << "jiffies_timer " << short_timer_t::get_timer_sequence(timer) << " actived" << std::endl;
    }
};

CASE_TEST(time_test, jiffies_timer_basic) {
    short_timer_t short_timer;
    int count = 0;
    time_t max_tick = short_timer.get_max_tick_distance() + 1;

    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_NOT_INITED, short_timer.add_timer(123, jiffies_timer_fn(NULL), NULL));
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_NOT_INITED, short_timer.tick(456));

    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.init(max_tick));
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_ALREADY_INITED, short_timer.init(max_tick));

    CASE_EXPECT_EQ(32767, short_timer.get_max_tick_distance());
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_TIMEOUT_EXTENDED,
                   short_timer.add_timer(short_timer.get_max_tick_distance() + 1, jiffies_timer_fn(NULL), NULL));

    // 理论上会被放在（数组下标: 2^6*3=192）
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS,
                   short_timer.add_timer(short_timer.get_max_tick_distance(), jiffies_timer_fn(&short_timer), &short_timer));
    // 理论上会被放在（数组下标: 2^6*4-1=255）
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS,
                   short_timer.add_timer(short_timer.get_max_tick_distance() - short_timer_t::LVL_GRAN(3), jiffies_timer_fn(&short_timer),
                                         &short_timer));

    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(-123, jiffies_timer_fn(NULL), &count));
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(30, jiffies_timer_fn(NULL), &count));
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(40, jiffies_timer_fn(NULL), &count));
    CASE_EXPECT_EQ(5, static_cast<int>(short_timer.size()));

    CASE_EXPECT_EQ(0, count);
    short_timer.tick(max_tick);
    CASE_EXPECT_EQ(0, count);
    short_timer.tick(max_tick + 1);
    CASE_EXPECT_EQ(1, count);
    CASE_EXPECT_EQ(4, static_cast<int>(short_timer.size()));

    // +30的触发点是+31。因为添加定时器的时候会认为当前时间是+0.XXX，并且由于触发只会延后不会提前
    short_timer.tick(max_tick + 30);
    CASE_EXPECT_EQ(1, count);
    short_timer.tick(max_tick + 31);
    CASE_EXPECT_EQ(2, count);

    // 跨tick
    short_timer.tick(max_tick + 64);
    CASE_EXPECT_EQ(3, count);
    CASE_EXPECT_EQ(2, static_cast<int>(short_timer.size()));

    // 非第一层、非第一个定时器组.（512+64*5-1 = 831）
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(831, jiffies_timer_fn(NULL), &count));
    short_timer.tick(max_tick + 64 + 831);
    CASE_EXPECT_EQ(3, count);
    // 768-831 share tick
    short_timer.tick(max_tick + 64 + 832);
    CASE_EXPECT_EQ(4, count);

    // 32767
    short_timer.tick(32767 + max_tick);

    // 这个应该会放在数组下标为0的位置
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(0, jiffies_timer_fn(NULL), &count));

    // 环状数组的复用
    CASE_EXPECT_EQ(short_timer_t::error_type_t::EN_JTET_SUCCESS, short_timer.add_timer(1000, jiffies_timer_fn(NULL), &count));

    // 全部执行掉
    short_timer.tick(32767 + max_tick + 2000);
    CASE_EXPECT_EQ(6, count);
    CASE_EXPECT_EQ(0, static_cast<int>(short_timer.size()));
}

CASE_TEST(time_test, jiffies_timer_slot) {
    size_t timer_list_count[short_timer_t::WHEEL_SIZE] = {0};
    time_t blank_area[short_timer_t::WHEEL_SIZE / short_timer_t::LVL_SIZE] = {0};
    time_t max_tick = short_timer_t::get_max_tick_distance();
    for (time_t i = 0; i <= max_tick; ++i) {
        size_t idx = short_timer_t::calc_wheel_index(i, 0);
        CASE_EXPECT_LT(idx, static_cast<size_t>(short_timer_t::WHEEL_SIZE));
        ++timer_list_count[idx];
    }

    // 每个idx的计数器测试
    for (size_t i = 0; i < short_timer_t::WHEEL_SIZE; ++i) {
        size_t timer_count = timer_list_count[i];
        // 除了第一个定时器区间外，每个定时器区间都有一段空白区域
        if (0 == timer_count) {
            ++blank_area[i / short_timer_t::LVL_SIZE];
        } else {
            CASE_EXPECT_EQ(timer_count, short_timer_t::LVL_GRAN(i / short_timer_t::LVL_SIZE));
        }
    }

    // 定时器空白区间的个数应该等于重合区域
    for (size_t i = 1; i < short_timer_t::WHEEL_SIZE / short_timer_t::LVL_SIZE; ++i) {
        CASE_EXPECT_EQ(blank_area[i], short_timer_t::LVL_START(i) / short_timer_t::LVL_GRAN(i));
    }
}
<|endoftext|>


<commit_before>#ifndef RANG_DOT_HPP
#define RANG_DOT_HPP

#if defined(__unix__) || defined(__unix) || defined(__linux__)
#define OS_LINUX
#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64)
#define OS_WIN
#elif defined(__APPLE__) || defined(__MACH__)
#define OS_MAC
#else
#error Unknown Platform
#endif

#if defined(OS_LINUX) || defined(OS_MAC)
#include <unistd.h>
#elif defined(OS_WIN)
#include <windows.h>
#include <io.h>
#include <VersionHelpers.h>
#endif

#include <algorithm>
#include <cstdlib>
#include <ios>
#include <iostream>
#include <iterator>
#include <type_traits>

namespace rang {

inline std::streambuf const*& RANG_coutbuf() {
	static std::streambuf const* pOutbuff = std::cout.rdbuf();
	return pOutbuff;
}

inline std::streambuf const*& RANG_cerrbuf() {
	static std::streambuf const* pErrbuff = std::cerr.rdbuf();
	return pErrbuff;
}

inline std::streambuf const*& RANG_clogbuf() {
	static std::streambuf const* pLogbuff = std::clog.rdbuf();
	return pLogbuff;
}

inline int getIword()
{
	static int i = std::ios_base::xalloc();
	return i;
}

void init()
{
	RANG_coutbuf();
	RANG_cerrbuf();
	RANG_clogbuf();
}

enum class style {
	reset     = 0,
	bold      = 1,
	dim       = 2,
	italic    = 3,
	underline = 4,
	blink     = 5,
	reversed  = 6,
	conceal   = 7,
	crossed   = 8
};

enum class fg {
	black   = 30,
	red     = 31,
	green   = 32,
	yellow  = 33,
	blue    = 34,
	magenta = 35,
	cyan    = 36,
	gray    = 37
};

enum class bg {
	black   = 40,
	red     = 41,
	green   = 42,
	yellow  = 43,
	blue    = 44,
	magenta = 45,
	cyan    = 46,
	gray    = 47
};

enum class fgB {
	black   = 90,
	red     = 91,
	green   = 92,
	yellow  = 93,
	blue    = 94,
	magenta = 95,
	cyan    = 96,
	gray    = 97
};

enum class bgB {
	black   = 100,
	red     = 101,
	green   = 102,
	yellow  = 103,
	blue    = 104,
	magenta = 105,
	cyan    = 106,
	gray    = 107
};

enum class control {
	autoColor = 0,
	forceColor = 1
};

inline bool supportsColor()
{

#if defined(OS_LINUX) || defined(OS_MAC)
	const std::string Terms[] =
		{
			"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
			"linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
		};

	const char *env_p = std::getenv("TERM");
	if (env_p == nullptr) {
		return false;
	}

	std::string env_string(env_p);
	static const bool result = std::any_of(std::begin(Terms), std::end(Terms),
		[&](std::string term) {
			return env_string.find(term) != std::string::npos;
		});

#elif defined(OS_WIN)
	static const bool result = true;
#endif

	return result;
}

inline bool isTerminal(const std::streambuf *osbuf)
{
	if (osbuf == RANG_coutbuf()) {
#if defined(OS_LINUX) || defined(OS_MAC)
		return isatty(fileno(stdout)) ? true : false;
#elif defined(OS_WIN)
		return _isatty(_fileno(stdout)) ? true : false;
#endif
	}

	if (osbuf == RANG_cerrbuf() || osbuf == RANG_clogbuf()) {
#if defined(OS_LINUX) || defined(OS_MAC)
		return isatty(fileno(stderr)) ? true : false;
#elif defined(OS_WIN)
		return _isatty(_fileno(stderr)) ? true : false;
#endif
	}
	return false;
}

template <typename T>
using enableStd = typename std::enable_if
	<
		std::is_same<T, rang::style>::value ||
		std::is_same<T, rang::fg>::value ||
		std::is_same<T, rang::bg>::value ||
		std::is_same<T, rang::fgB>::value ||
		std::is_same<T, rang::bgB>::value,
		std::ostream&
	>::type;

#ifdef OS_WIN
HANDLE getVersionDependentHandle()
{
	if (IsWindowsVersionOrGreater(10, 0, 0))
		return nullptr;
	return GetStdHandle(STD_OUTPUT_HANDLE);
}

inline HANDLE getConsoleHandle()
{
	static HANDLE h = getVersionDependentHandle();
	return h;
}

inline WORD reverseRGB(WORD rgb)
{
	static const WORD rev[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
	return rev[rgb];
}

inline void setWinAttribute(rang::bg col, WORD& state)
{
	state &= 0xFF0F;
	state |= reverseRGB(static_cast<WORD>(col) - 40) << 4;
}

inline void setWinAttribute(rang::fg col, WORD& state)
{
	state &= 0xFFF0;
	state |= reverseRGB(static_cast<WORD>(col) - 30);
}

inline void setWinAttribute(rang::bgB col, WORD& state)
{
	state &= 0xFF0F;
	state |= (0x8 | reverseRGB(static_cast<WORD>(col) - 100)) << 4;
}

inline void setWinAttribute(rang::fgB col, WORD& state)
{
	state &= 0xFFF0;
	state |= (0x8 | reverseRGB(static_cast<WORD>(col) - 90));
}

inline void setWinAttribute(rang::style style, WORD& state)
{
	if (style == rang::style::reset)
	{
		state = (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
	}
}

inline WORD& current_state()
{
	static WORD state = (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
	return state;
}

template <typename T>
inline enableStd<T> setColor(std::ostream &os, T const value)
{
	HANDLE h = getConsoleHandle();
	if (h)
	{
		setWinAttribute(value, current_state());
		SetConsoleTextAttribute(h, current_state());
		return os;
	}
	return os << "\033[" << static_cast<int>(value) << "m";
}
#else
template <typename T>
inline enableStd<T> setColor(std::ostream &os, T const value)
{
	return os << "\033[" << static_cast<int>(value) << "m";
}
#endif

template <typename T>
using enableControl = typename std::enable_if
	<
		std::is_same<T, rang::control>::value,
		std::ostream&
	>::type;

template <typename T>
inline enableStd<T> operator<<(std::ostream &os, T const value)
{
	std::streambuf const *osbuf = os.rdbuf();
	return (os.iword(getIword()) || ((supportsColor()) && (isTerminal(osbuf))))
		? setColor(os, value) : os;
}

template <typename T>
inline enableControl<T> operator<<(std::ostream &os, T const value)
{
	if (value == rang::control::forceColor) {
		os.iword(getIword()) = 1;
	} else if (value == rang::control::autoColor) {
		os.iword(getIword()) = 0;
	}

	return os;
}
}

#undef OS_LINUX
#undef OS_WIN
#undef OS_MAC

#endif /* ifndef RANG_DOT_HPP */
<commit_msg>Removed duplicate rang.hpp<commit_after><|endoftext|>


<commit_before>/*******************************************************
 SPIDAR-mouse-server for Linux, FreeBSD and Mac OS X
 Copyright (c) 2013 Naoto Hieda
 March 3, 2013
 Licensed under MIT license.
 https://github.com/micuat/SPIDAR-mouse-UNIX

 ofxSpidarMouse.cpp

 ********************************************************/

#include "ofxSpidarMouse.h"
#include "hidapi.h"

ofxSpidarMouse::ofxSpidarMouse()
{
	isInitialized = false;
	isOpened = false;
}

int ofxSpidarMouse::init()
{
	isInitialized = false;

	int res;
	unsigned char buf[256];
#define MAX_STR 255
	wchar_t wstr[MAX_STR];

	devs = hid_enumerate(0x0, 0x0);
	cur_dev = devs;
	while (cur_dev) {
		printf("Device Found\n  type: %04hx %04hx\n  path: %s\n  serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
		printf("\n");
		printf("  Manufacturer: %ls\n", cur_dev->manufacturer_string);
		printf("  Product:      %ls\n", cur_dev->product_string);
		printf("  Release:      %hx\n", cur_dev->release_number);
		printf("  Interface:    %d\n",  cur_dev->interface_number);
		printf("\n");
		cur_dev = cur_dev->next;
	}
	hid_free_enumeration(devs);

	// Set up the command buffer.
	memset(buf,0x00,sizeof(buf));
	buf[0] = 0x01;
	buf[1] = 0x81;

	// Open the device using the VID, PID,
	// and optionally the Serial number.
	////handle = hid_open(0x4d8, 0x3f, L"12345");
	handle = hid_open(0x4d8, 0x3f, NULL);
	if (!handle) {
		printf("unable to open device\n");
 		return 1;
	}

	// Read the Manufacturer String
	wstr[0] = 0x0000;
	res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
	if (res < 0)
		printf("Unable to read manufacturer string\n");
	printf("Manufacturer String: %ls\n", wstr);

	// Read the Product String
	wstr[0] = 0x0000;
	res = hid_get_product_string(handle, wstr, MAX_STR);
	if (res < 0)
		printf("Unable to read product string\n");
	printf("Product String: %ls\n", wstr);

	// Read the Serial Number String
	wstr[0] = 0x0000;
	res = hid_get_serial_number_string(handle, wstr, MAX_STR);
	if (res < 0)
		printf("Unable to read serial number string\n");
	printf("Serial Number String: (%d) %ls", wstr[0], wstr);
	printf("\n");

	// Read Indexed String 1
	wstr[0] = 0x0000;
	res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
	if (res < 0)
		printf("Unable to read indexed string 1\n");
	printf("Indexed String 1: %ls\n", wstr);

	// Set the hid_read() function to be non-blocking.
	hid_set_nonblocking(handle, 1);

	// Try to read from the device. There shoud be no
	// data here, but execution should not block.
	res = hid_read(handle, buf, 17);

	// Send a Feature Report to the device
	buf[0] = 0x2;
	buf[1] = 0xa0;
	buf[2] = 0x0a;
	buf[3] = 0x00;
	buf[4] = 0x00;
	res = hid_send_feature_report(handle, buf, 17);
	if (res < 0) {
		printf("Unable to send a feature report.\n");
	}

	memset(buf,0,sizeof(buf));

	// Read a Feature Report from the device
	buf[0] = 0x2;
	res = hid_get_feature_report(handle, buf, sizeof(buf));
	if (res < 0) {
		printf("Unable to get a feature report.\n");
		printf("%ls\n", hid_error(handle));
	}
	else {
		// Print out the returned buffer.
		printf("Feature Report\n   ");
		for (int i = 0; i < res; i++)
			printf("%02hhx ", buf[i]);
		printf("\n");
	}

	isInitialized = true;
	return 0;
}

ofxSpidarMouse::~ofxSpidarMouse()
{
	if( isOpened ) {
		close();
	}

	if( isInitialized ) {
		hid_close(handle);

		/* Free static HIDAPI objects. */
		hid_exit();
	}
}

int ofxSpidarMouse::open()
{
	isOpened = false;

	if( !isInitialized ) {
		return 1;
	}

	int res;
	unsigned char buf[256];
	float conf_data[4];

	// Initialize parameters

	interG_X=0.0;
	interG_Y=0.0;
	SetForce_Mag=0.0;
	SetForce_Dirunit_X=0.0;
	SetForce_Dirunit_Y=0.0;
	Max_Force=1.5;
	defaultDuration = 4; // 4ms
	duration = 0;

	forceStack.clear();
	//////////////// Start SPIDAR-mouse

	memset(buf,0,sizeof(buf));

	buf[0] = 0x00;
	buf[1] = 0x82;
	res = hid_write(handle, buf, 65);
	if (res < 0) {
		printf("Unable to write()\n");
		printf("Error: %ls\n", hid_error(handle));
		return 1;
	}

	// Read requested state. hid_read() has been set to be
	// non-blocking by the call to hid_set_nonblocking() above.
	// This loop demonstrates the non-blocking nature of hid_read().
	res = 0;
	while (res == 0) {
		res = hid_read(handle, buf, sizeof(buf));
		if (res == 0)
			printf("waiting...\n");
		if (res < 0)
			printf("Unable to read()\n");
#ifdef WIN32
		Sleep(500);
#else
		usleep(500*1000);
#endif
	}

	printf("Data read:\n   ");
	// Print out the returned buffer.
	for (int i = 0; i < res; i++)
		printf("%02hhx ", buf[i]);
	printf("\n");

	memset(buf,0,sizeof(buf));

	conf_data[0] = 0.3;
	conf_data[1] = 1.5;
	conf_data[2] = 47.9867;
	conf_data[3] = 13.1215;
	MinForce_Bytes = (unsigned char *)&conf_data[0];
	MaxForce_Bytes = (unsigned char *)&conf_data[1];
	Fun_a_Bytes = (unsigned char *)&conf_data[2];
	Fun_b_Bytes = (unsigned char *)&conf_data[3];

	buf[0] = 0;
	buf[1] = 0x80;
	buf[2] = MinForce_Bytes[0];
	buf[3] = MinForce_Bytes[1];
	buf[4] = MinForce_Bytes[2];
	buf[5] = MinForce_Bytes[3];

	buf[6] = MaxForce_Bytes[0];
	buf[7] = MaxForce_Bytes[1];
	buf[8] = MaxForce_Bytes[2];
	buf[9] = MaxForce_Bytes[3];

	buf[10] = Fun_a_Bytes[0];
	buf[11] = Fun_a_Bytes[1];
	buf[12] = Fun_a_Bytes[2];
	buf[13] = Fun_a_Bytes[3];

	buf[14] = Fun_b_Bytes[0];
	buf[15] = Fun_b_Bytes[1];
	buf[16] = Fun_b_Bytes[2];
	buf[17] = Fun_b_Bytes[3];
	res = hid_write(handle, buf, 65);
	if (res < 0) {
		printf("Unable to start SPIDAR-mouse: %ls\n", hid_error(handle));
		return 1;
	}

	setForce(0.0, 0.0);

	isOpened = true;
	return 0;
}

int ofxSpidarMouse::close()
{
	if( !isOpened ) {
		return 1;
	}

	int res;
	unsigned char buf[256];

	buf[0] = 0;
	buf[1] = 0x99;
	res = hid_write(handle, buf, 65);
	if (res < 0) {
		printf("Unable to close SPIDAR-mouse: %d %ls\n", res, hid_error(handle));
	}

	return 0;
}

int ofxSpidarMouse::update()
{
	unsigned long curTime = ofGetSystemTime();

	if( duration != 0 && (curTime - startTime) > duration ) {
		setForce(0.0, 0.0);
		duration = 0;
	}

	forceStack.push_back(ofVec2f(Force_XScale, Force_YScale));
	if( forceStack.size() > 30 ) {
		forceStack.erase(forceStack.begin());
	}

	return 0;
}

int ofxSpidarMouse::setForce(float fx, float fy)
{
	duration = 0; // infinite

	return sForce(fx, fy);
}

int ofxSpidarMouse::setForce(float fx, float fy, int d)
{
	startTime = ofGetSystemTime();

	if( d < defaultDuration ) {
		duration = defaultDuration;
	} else {
		duration = d;
	}

	return sForce(fx, fy);
}

int ofxSpidarMouse::sForce(float fx, float fy)
{
	if( fabsf(fx) > 1.0 ) {
		Force_XScale = fx / fabsf(fx);
	} else {
		Force_XScale = fx;
	}

	if( fabsf(fy) > 1.0 ) {
		Force_YScale = fy / fabsf(fy);
	} else {
		Force_YScale = fy;
	}

	if( !isOpened ) {
		return 1;
	}

	Force_X = Force_XScale * Max_Force;
	Force_Y = Force_YScale * Max_Force;
	SetForce_Mag = sqrt(Force_X * Force_X + Force_Y * Force_Y);
	if ( SetForce_Mag > 0.0 ) {
		SetForce_Dirunit_X = Force_X / SetForce_Mag;
		SetForce_Dirunit_Y = Force_Y / SetForce_Mag;
	}

	int res;
	unsigned char buf[256];

	memset(buf,0x00,sizeof(buf));

	buf[0] = 0;
	buf[1] = 0x97;
	interG_X_Bytes = (unsigned char *)&interG_X;
	interG_Y_Bytes = (unsigned char *)&interG_Y;
	Force_Mag_Bytes = (unsigned char *)&SetForce_Mag;
	Force_Dirunit_X_Bytes = (unsigned char *)&SetForce_Dirunit_X;
	Force_Dirunit_Y_Bytes = (unsigned char *)&SetForce_Dirunit_Y;

	buf[2] = interG_X_Bytes[0];
	buf[3] = interG_X_Bytes[1];
	buf[4] = interG_X_Bytes[2];
	buf[5] = interG_X_Bytes[3];

	buf[6] = interG_Y_Bytes[0];
	buf[7] = interG_Y_Bytes[1];
	buf[8] = interG_Y_Bytes[2];
	buf[9] = interG_Y_Bytes[3];

	buf[10] = Force_Mag_Bytes[0] ;
	buf[11] = Force_Mag_Bytes[1] ;
	buf[12] = Force_Mag_Bytes[2] ;
	buf[13] = Force_Mag_Bytes[3] ;

	buf[14] = Force_Dirunit_X_Bytes[0] ;
	buf[15] = Force_Dirunit_X_Bytes[1] ;
	buf[16] = Force_Dirunit_X_Bytes[2] ;
	buf[17] = Force_Dirunit_X_Bytes[3] ;

	buf[18] = Force_Dirunit_Y_Bytes[0] ;
	buf[19] = Force_Dirunit_Y_Bytes[1] ;
	buf[20] = Force_Dirunit_Y_Bytes[2] ;
	buf[21] = Force_Dirunit_Y_Bytes[3] ;
	res = hid_write(handle, buf, 65);
	if (res < 0) {
		printf("Unable to make feedback: %ls\n", hid_error(handle));
		return 1;
	}

	return 0;
}

void ofxSpidarMouse::draw(int col = 0x000000)
{
	string info = "";
	info += "X Value: "+ofToString(Force_XScale, 3)+"\n";
	info += "Y Value: "+ofToString(Force_YScale, 3)+"\n\n";
 	ofSetHexColor(col);
	ofDrawBitmapString(info, 30, 30);

	ofFill();
	ofVec2f circleCenter(80, 100);

	for( int i = 0 ; i < forceStack.size() ; i++ ) {
		ofSetColor(i * 6);
		ofCircle(circleCenter + forceStack[i] * 30, 3);
	}
 	ofSetHexColor(col);
	ofCircle(circleCenter, 3);
}<commit_msg>Improved design<commit_after>/*******************************************************
 SPIDAR-mouse-server for Linux, FreeBSD and Mac OS X
 Copyright (c) 2013 Naoto Hieda
 March 3, 2013
 Licensed under MIT license.
 https://github.com/micuat/SPIDAR-mouse-UNIX

 ofxSpidarMouse.cpp

 ********************************************************/

#include "ofxSpidarMouse.h"
#include "hidapi.h"

ofxSpidarMouse::ofxSpidarMouse()
{
	isInitialized = false;
	isOpened = false;
}

int ofxSpidarMouse::init()
{
	isInitialized = false;

	int res;
	unsigned char buf[256];
#define MAX_STR 255
	wchar_t wstr[MAX_STR];

	devs = hid_enumerate(0x0, 0x0);
	cur_dev = devs;
	while (cur_dev) {
		printf("Device Found\n  type: %04hx %04hx\n  path: %s\n  serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
		printf("\n");
		printf("  Manufacturer: %ls\n", cur_dev->manufacturer_string);
		printf("  Product:      %ls\n", cur_dev->product_string);
		printf("  Release:      %hx\n", cur_dev->release_number);
		printf("  Interface:    %d\n",  cur_dev->interface_number);
		printf("\n");
		cur_dev = cur_dev->next;
	}
	hid_free_enumeration(devs);

	// Set up the command buffer.
	memset(buf,0x00,sizeof(buf));
	buf[0] = 0x01;
	buf[1] = 0x81;

	// Open the device using the VID, PID,
	// and optionally the Serial number.
	////handle = hid_open(0x4d8, 0x3f, L"12345");
	handle = hid_open(0x4d8, 0x3f, NULL);
	if (!handle) {
		printf("unable to open device\n");
 		return 1;
	}

	// Read the Manufacturer String
	wstr[0] = 0x0000;
	res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
	if (res < 0)
		printf("Unable to read manufacturer string\n");
	printf("Manufacturer String: %ls\n", wstr);

	// Read the Product String
	wstr[0] = 0x0000;
	res = hid_get_product_string(handle, wstr, MAX_STR);
	if (res < 0)
		printf("Unable to read product string\n");
	printf("Product String: %ls\n", wstr);

	// Read the Serial Number String
	wstr[0] = 0x0000;
	res = hid_get_serial_number_string(handle, wstr, MAX_STR);
	if (res < 0)
		printf("Unable to read serial number string\n");
	printf("Serial Number String: (%d) %ls", wstr[0], wstr);
	printf("\n");

	// Read Indexed String 1
	wstr[0] = 0x0000;
	res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
	if (res < 0)
		printf("Unable to read indexed string 1\n");
	printf("Indexed String 1: %ls\n", wstr);

	// Set the hid_read() function to be non-blocking.
	hid_set_nonblocking(handle, 1);

	// Try to read from the device. There shoud be no
	// data here, but execution should not block.
	res = hid_read(handle, buf, 17);

	// Send a Feature Report to the device
	buf[0] = 0x2;
	buf[1] = 0xa0;
	buf[2] = 0x0a;
	buf[3] = 0x00;
	buf[4] = 0x00;
	res = hid_send_feature_report(handle, buf, 17);
	if (res < 0) {
		printf("Unable to send a feature report.\n");
	}

	memset(buf,0,sizeof(buf));

	// Read a Feature Report from the device
	buf[0] = 0x2;
	res = hid_get_feature_report(handle, buf, sizeof(buf));
	if (res < 0) {
		printf("Unable to get a feature report.\n");
		printf("%ls\n", hid_error(handle));
	}
	else {
		// Print out the returned buffer.
		printf("Feature Report\n   ");
		for (int i = 0; i < res; i++)
			printf("%02hhx ", buf[i]);
		printf("\n");
	}

	isInitialized = true;
	return 0;
}

ofxSpidarMouse::~ofxSpidarMouse()
{
	if( isOpened ) {
		close();
	}

	if( isInitialized ) {
		hid_close(handle);

		/* Free static HIDAPI objects. */
		hid_exit();
	}
}

int ofxSpidarMouse::open()
{
	isOpened = false;

	if( !isInitialized ) {
		return 1;
	}

	int res;
	unsigned char buf[256];
	float conf_data[4];

	// Initialize parameters

	interG_X=0.0;
	interG_Y=0.0;
	SetForce_Mag=0.0;
	SetForce_Dirunit_X=0.0;
	SetForce_Dirunit_Y=0.0;
	Max_Force=1.5;
	defaultDuration = 4; // 4ms
	duration = 0;

	forceStack.clear();
	//////////////// Start SPIDAR-mouse

	memset(buf,0,sizeof(buf));

	buf[0] = 0x00;
	buf[1] = 0x82;
	res = hid_write(handle, buf, 65);
	if (res < 0) {
		printf("Unable to write()\n");
		printf("Error: %ls\n", hid_error(handle));
		return 1;
	}

	// Read requested state. hid_read() has been set to be
	// non-blocking by the call to hid_set_nonblocking() above.
	// This loop demonstrates the non-blocking nature of hid_read().
	res = 0;
	while (res == 0) {
		res = hid_read(handle, buf, sizeof(buf));
		if (res == 0)
			printf("waiting...\n");
		if (res < 0)
			printf("Unable to read()\n");
#ifdef WIN32
		Sleep(500);
#else
		usleep(500*1000);
#endif
	}

	printf("Data read:\n   ");
	// Print out the returned buffer.
	for (int i = 0; i < res; i++)
		printf("%02hhx ", buf[i]);
	printf("\n");

	memset(buf,0,sizeof(buf));

	conf_data[0] = 0.3;
	conf_data[1] = 1.5;
	conf_data[2] = 47.9867;
	conf_data[3] = 13.1215;
	MinForce_Bytes = (unsigned char *)&conf_data[0];
	MaxForce_Bytes = (unsigned char *)&conf_data[1];
	Fun_a_Bytes = (unsigned char *)&conf_data[2];
	Fun_b_Bytes = (unsigned char *)&conf_data[3];

	buf[0] = 0;
	buf[1] = 0x80;
	buf[2] = MinForce_Bytes[0];
	buf[3] = MinForce_Bytes[1];
	buf[4] = MinForce_Bytes[2];
	buf[5] = MinForce_Bytes[3];

	buf[6] = MaxForce_Bytes[0];
	buf[7] = MaxForce_Bytes[1];
	buf[8] = MaxForce_Bytes[2];
	buf[9] = MaxForce_Bytes[3];

	buf[10] = Fun_a_Bytes[0];
	buf[11] = Fun_a_Bytes[1];
	buf[12] = Fun_a_Bytes[2];
	buf[13] = Fun_a_Bytes[3];

	buf[14] = Fun_b_Bytes[0];
	buf[15] = Fun_b_Bytes[1];
	buf[16] = Fun_b_Bytes[2];
	buf[17] = Fun_b_Bytes[3];
	res = hid_write(handle, buf, 65);
	if (res < 0) {
		printf("Unable to start SPIDAR-mouse: %ls\n", hid_error(handle));
		return 1;
	}

	setForce(0.0, 0.0);

	isOpened = true;
	return 0;
}

int ofxSpidarMouse::close()
{
	if( !isOpened ) {
		return 1;
	}

	int res;
	unsigned char buf[256];

	buf[0] = 0;
	buf[1] = 0x99;
	res = hid_write(handle, buf, 65);
	if (res < 0) {
		printf("Unable to close SPIDAR-mouse: %d %ls\n", res, hid_error(handle));
	}

	return 0;
}

int ofxSpidarMouse::update()
{
	unsigned long curTime = ofGetSystemTime();

	if( duration != 0 && (curTime - startTime) > duration ) {
		setForce(0.0, 0.0);
		duration = 0;
	}

	forceStack.push_back(ofVec2f(Force_XScale, Force_YScale));
	if( forceStack.size() > 30 ) {
		forceStack.erase(forceStack.begin());
	}

	return 0;
}

int ofxSpidarMouse::setForce(float fx, float fy)
{
	duration = 0; // infinite

	return sForce(fx, fy);
}

int ofxSpidarMouse::setForce(float fx, float fy, int d)
{
	startTime = ofGetSystemTime();

	if( d < defaultDuration ) {
		duration = defaultDuration;
	} else {
		duration = d;
	}

	return sForce(fx, fy);
}

int ofxSpidarMouse::sForce(float fx, float fy)
{
	if( fabsf(fx) > 1.0 ) {
		Force_XScale = fx / fabsf(fx);
	} else {
		Force_XScale = fx;
	}

	if( fabsf(fy) > 1.0 ) {
		Force_YScale = fy / fabsf(fy);
	} else {
		Force_YScale = fy;
	}

	if( !isOpened ) {
		return 1;
	}

	Force_X = Force_XScale * Max_Force;
	Force_Y = Force_YScale * Max_Force;
	SetForce_Mag = sqrt(Force_X * Force_X + Force_Y * Force_Y);
	if ( SetForce_Mag > 0.0 ) {
		SetForce_Dirunit_X = Force_X / SetForce_Mag;
		SetForce_Dirunit_Y = Force_Y / SetForce_Mag;
	}

	int res;
	unsigned char buf[256];

	memset(buf,0x00,sizeof(buf));

	buf[0] = 0;
	buf[1] = 0x97;
	interG_X_Bytes = (unsigned char *)&interG_X;
	interG_Y_Bytes = (unsigned char *)&interG_Y;
	Force_Mag_Bytes = (unsigned char *)&SetForce_Mag;
	Force_Dirunit_X_Bytes = (unsigned char *)&SetForce_Dirunit_X;
	Force_Dirunit_Y_Bytes = (unsigned char *)&SetForce_Dirunit_Y;

	buf[2] = interG_X_Bytes[0];
	buf[3] = interG_X_Bytes[1];
	buf[4] = interG_X_Bytes[2];
	buf[5] = interG_X_Bytes[3];

	buf[6] = interG_Y_Bytes[0];
	buf[7] = interG_Y_Bytes[1];
	buf[8] = interG_Y_Bytes[2];
	buf[9] = interG_Y_Bytes[3];

	buf[10] = Force_Mag_Bytes[0] ;
	buf[11] = Force_Mag_Bytes[1] ;
	buf[12] = Force_Mag_Bytes[2] ;
	buf[13] = Force_Mag_Bytes[3] ;

	buf[14] = Force_Dirunit_X_Bytes[0] ;
	buf[15] = Force_Dirunit_X_Bytes[1] ;
	buf[16] = Force_Dirunit_X_Bytes[2] ;
	buf[17] = Force_Dirunit_X_Bytes[3] ;

	buf[18] = Force_Dirunit_Y_Bytes[0] ;
	buf[19] = Force_Dirunit_Y_Bytes[1] ;
	buf[20] = Force_Dirunit_Y_Bytes[2] ;
	buf[21] = Force_Dirunit_Y_Bytes[3] ;
	res = hid_write(handle, buf, 65);
	if (res < 0) {
		printf("Unable to make feedback: %ls\n", hid_error(handle));
		return 1;
	}

	return 0;
}

void ofxSpidarMouse::draw(int col = 0x000000)
{
	ofVec2f circleCenter(80, 100);
	int r = 30;

	string info = "";
	info += "X Value: "+ofToString(Force_XScale, 3)+"\n";
	info += "Y Value: "+ofToString(Force_YScale, 3)+"\n\n";
 	ofSetHexColor(col);
	ofDrawBitmapString(info, 30, 30);

	ofFill();

	for( int i = 0 ; i < forceStack.size() ; i++ ) {
		ofSetColor(i * 6);
		ofCircle(circleCenter + forceStack[i] * r, 3);
	}

	ofNoFill();
 	ofSetHexColor(col);
	ofCircle(circleCenter, r * 1.41);
	ofCircle(circleCenter, r * 1.41 / 2);
}<|endoftext|>


<commit_before>// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "coins.h"
#include "random.h"
#include "uint256.h"
#include "test/test_bitcoin.h"

#include <vector>
#include <map>

#include <boost/test/unit_test.hpp>

namespace
{
class CCoinsViewTest : public CCoinsView
{
    uint256 hashBestBlock_;
    std::map<uint256, CCoins> map_;

public:
    bool GetCoins(const uint256& txid, CCoins& coins) const
    {
        std::map<uint256, CCoins>::const_iterator it = map_.find(txid);
        if (it == map_.end()) {
            return false;
        }
        coins = it->second;
        if (coins.IsPruned() && insecure_rand() % 2 == 0) {
            // Randomly return false in case of an empty entry.
            return false;
        }
        return true;
    }

    bool HaveCoins(const uint256& txid) const
    {
        CCoins coins;
        return GetCoins(txid, coins);
    }

    uint256 GetBestBlock() const { return hashBestBlock_; }

    bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock)
    {
        for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
            map_[it->first] = it->second.coins;
            if (it->second.coins.IsPruned() && insecure_rand() % 3 == 0) {
                // Randomly delete empty entries on write.
                map_.erase(it->first);
            }
            mapCoins.erase(it++);
        }
        mapCoins.clear();
        hashBestBlock_ = hashBlock;
        return true;
    }

    bool GetStats(CCoinsStats& stats) const { return false; }
};

class CCoinsViewCacheTest : public CCoinsViewCache
{
public:
    CCoinsViewCacheTest(CCoinsView* base) : CCoinsViewCache(base) {}

    void SelfTest() const
    {
        // Manually recompute the dynamic usage of the whole data, and compare it.
        size_t ret = memusage::DynamicUsage(cacheCoins);
        for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
            ret += it->second.coins.DynamicMemoryUsage();
        }
        BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
    }

};

}

BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)

static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;

// This is a large randomized insert/remove simulation test on a variable-size
// stack of caches on top of CCoinsViewTest.
//
// It will randomly create/update/delete CCoins entries to a tip of caches, with
// txids picked from a limited list of random 256-bit hashes. Occasionally, a
// new tip is added to the stack of caches, or the tip is flushed and removed.
//
// During the process, booleans are kept to make sure that the randomized
// operation hits all branches.
BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
{
    // Various coverage trackers.
    bool removed_all_caches = false;
    bool reached_4_caches = false;
    bool added_an_entry = false;
    bool removed_an_entry = false;
    bool updated_an_entry = false;
    bool found_an_entry = false;
    bool missed_an_entry = false;

    // A simple map to track what we expect the cache stack to represent.
    std::map<uint256, CCoins> result;

    // The cache stack.
    CCoinsViewTest base; // A CCoinsViewTest at the bottom.
    std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
    stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.

    // Use a limited set of random transaction ids, so we do test overwriting entries.
    std::vector<uint256> txids;
    txids.resize(NUM_SIMULATION_ITERATIONS / 8);
    for (unsigned int i = 0; i < txids.size(); i++) {
        txids[i] = GetRandHash();
    }

    for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
        // Do a random modification.
        {
            uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration.
            CCoins& coins = result[txid];
            CCoinsModifier entry = stack.back()->ModifyCoins(txid);
            BOOST_CHECK(coins == *entry);
            if (insecure_rand() % 5 == 0 || coins.IsPruned()) {
                if (coins.IsPruned()) {
                    added_an_entry = true;
                } else {
                    updated_an_entry = true;
                }
                coins.nVersion = insecure_rand();
                coins.vout.resize(1);
                coins.vout[0].nValue = insecure_rand();
                *entry = coins;
            } else {
                coins.Clear();
                entry->Clear();
                removed_an_entry = true;
            }
        }

        // Once every 1000 iterations and at the end, verify the full cache.
        if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
            for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) {
                const CCoins* coins = stack.back()->AccessCoins(it->first);
                if (coins) {
                    BOOST_CHECK(*coins == it->second);
                    found_an_entry = true;
                } else {
                    BOOST_CHECK(it->second.IsPruned());
                    missed_an_entry = true;
                }
            }
            BOOST_FOREACH(const CCoinsViewCacheTest *test, stack) {
                test->SelfTest();
            }
        }

        if (insecure_rand() % 100 == 0) {
            // Every 100 iterations, change the cache stack.
            if (stack.size() > 0 && insecure_rand() % 2 == 0) {
                stack.back()->Flush();
                delete stack.back();
                stack.pop_back();
            }
            if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) {
                CCoinsView* tip = &base;
                if (stack.size() > 0) {
                    tip = stack.back();
                } else {
                    removed_all_caches = true;
                }
                stack.push_back(new CCoinsViewCacheTest(tip));
                if (stack.size() == 4) {
                    reached_4_caches = true;
                }
            }
        }
    }

    // Clean up the stack.
    while (stack.size() > 0) {
        delete stack.back();
        stack.pop_back();
    }

    // Verify coverage.
    BOOST_CHECK(removed_all_caches);
    BOOST_CHECK(reached_4_caches);
    BOOST_CHECK(added_an_entry);
    BOOST_CHECK(removed_an_entry);
    BOOST_CHECK(updated_an_entry);
    BOOST_CHECK(found_an_entry);
    BOOST_CHECK(missed_an_entry);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Make CCoinsViewTest behave like CCoinsViewDB<commit_after>// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "coins.h"
#include "random.h"
#include "uint256.h"
#include "test/test_bitcoin.h"

#include <vector>
#include <map>

#include <boost/test/unit_test.hpp>

namespace
{
class CCoinsViewTest : public CCoinsView
{
    uint256 hashBestBlock_;
    std::map<uint256, CCoins> map_;

public:
    bool GetCoins(const uint256& txid, CCoins& coins) const
    {
        std::map<uint256, CCoins>::const_iterator it = map_.find(txid);
        if (it == map_.end()) {
            return false;
        }
        coins = it->second;
        if (coins.IsPruned() && insecure_rand() % 2 == 0) {
            // Randomly return false in case of an empty entry.
            return false;
        }
        return true;
    }

    bool HaveCoins(const uint256& txid) const
    {
        CCoins coins;
        return GetCoins(txid, coins);
    }

    uint256 GetBestBlock() const { return hashBestBlock_; }

    bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock)
    {
        for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
            if (it->second.flags & CCoinsCacheEntry::DIRTY) {
                // Same optimization used in CCoinsViewDB is to only write dirty entries.
                map_[it->first] = it->second.coins;
                if (it->second.coins.IsPruned() && insecure_rand() % 3 == 0) {
                    // Randomly delete empty entries on write.
                    map_.erase(it->first);
                }
            }
            mapCoins.erase(it++);
        }
        if (!hashBlock.IsNull())
            hashBestBlock_ = hashBlock;
        return true;
    }

    bool GetStats(CCoinsStats& stats) const { return false; }
};

class CCoinsViewCacheTest : public CCoinsViewCache
{
public:
    CCoinsViewCacheTest(CCoinsView* base) : CCoinsViewCache(base) {}

    void SelfTest() const
    {
        // Manually recompute the dynamic usage of the whole data, and compare it.
        size_t ret = memusage::DynamicUsage(cacheCoins);
        for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
            ret += it->second.coins.DynamicMemoryUsage();
        }
        BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
    }

};

}

BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)

static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;

// This is a large randomized insert/remove simulation test on a variable-size
// stack of caches on top of CCoinsViewTest.
//
// It will randomly create/update/delete CCoins entries to a tip of caches, with
// txids picked from a limited list of random 256-bit hashes. Occasionally, a
// new tip is added to the stack of caches, or the tip is flushed and removed.
//
// During the process, booleans are kept to make sure that the randomized
// operation hits all branches.
BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
{
    // Various coverage trackers.
    bool removed_all_caches = false;
    bool reached_4_caches = false;
    bool added_an_entry = false;
    bool removed_an_entry = false;
    bool updated_an_entry = false;
    bool found_an_entry = false;
    bool missed_an_entry = false;

    // A simple map to track what we expect the cache stack to represent.
    std::map<uint256, CCoins> result;

    // The cache stack.
    CCoinsViewTest base; // A CCoinsViewTest at the bottom.
    std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
    stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.

    // Use a limited set of random transaction ids, so we do test overwriting entries.
    std::vector<uint256> txids;
    txids.resize(NUM_SIMULATION_ITERATIONS / 8);
    for (unsigned int i = 0; i < txids.size(); i++) {
        txids[i] = GetRandHash();
    }

    for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
        // Do a random modification.
        {
            uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration.
            CCoins& coins = result[txid];
            CCoinsModifier entry = stack.back()->ModifyCoins(txid);
            BOOST_CHECK(coins == *entry);
            if (insecure_rand() % 5 == 0 || coins.IsPruned()) {
                if (coins.IsPruned()) {
                    added_an_entry = true;
                } else {
                    updated_an_entry = true;
                }
                coins.nVersion = insecure_rand();
                coins.vout.resize(1);
                coins.vout[0].nValue = insecure_rand();
                *entry = coins;
            } else {
                coins.Clear();
                entry->Clear();
                removed_an_entry = true;
            }
        }

        // Once every 1000 iterations and at the end, verify the full cache.
        if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
            for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) {
                const CCoins* coins = stack.back()->AccessCoins(it->first);
                if (coins) {
                    BOOST_CHECK(*coins == it->second);
                    found_an_entry = true;
                } else {
                    BOOST_CHECK(it->second.IsPruned());
                    missed_an_entry = true;
                }
            }
            BOOST_FOREACH(const CCoinsViewCacheTest *test, stack) {
                test->SelfTest();
            }
        }

        if (insecure_rand() % 100 == 0) {
            // Every 100 iterations, change the cache stack.
            if (stack.size() > 0 && insecure_rand() % 2 == 0) {
                stack.back()->Flush();
                delete stack.back();
                stack.pop_back();
            }
            if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) {
                CCoinsView* tip = &base;
                if (stack.size() > 0) {
                    tip = stack.back();
                } else {
                    removed_all_caches = true;
                }
                stack.push_back(new CCoinsViewCacheTest(tip));
                if (stack.size() == 4) {
                    reached_4_caches = true;
                }
            }
        }
    }

    // Clean up the stack.
    while (stack.size() > 0) {
        delete stack.back();
        stack.pop_back();
    }

    // Verify coverage.
    BOOST_CHECK(removed_all_caches);
    BOOST_CHECK(reached_4_caches);
    BOOST_CHECK(added_an_entry);
    BOOST_CHECK(removed_an_entry);
    BOOST_CHECK(updated_an_entry);
    BOOST_CHECK(found_an_entry);
    BOOST_CHECK(missed_an_entry);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>
#include "ogr_layer.hpp"
#include "ogr_datasource.hpp"
#include "ogr_geometry.hpp"
#include "ogr_common.hpp"

Persistent<FunctionTemplate> Datasource::constructor;

void Datasource::Initialize(Handle<Object> target) {
    HandleScope scope;

    constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Datasource::New));
    constructor->InstanceTemplate()->SetInternalFieldCount(1);
    constructor->SetClassName(String::NewSymbol("Datasource"));

    NODE_SET_PROTOTYPE_METHOD(constructor, "toString", toString);
    NODE_SET_PROTOTYPE_METHOD(constructor, "getName", getName);
    NODE_SET_PROTOTYPE_METHOD(constructor, "getLayerByName", getLayerByName);
    NODE_SET_PROTOTYPE_METHOD(constructor, "getLayerCount", getLayerCount);
    NODE_SET_PROTOTYPE_METHOD(constructor, "getLayer", getLayer);
    NODE_SET_PROTOTYPE_METHOD(constructor, "deleteLayer", deleteLayer);
    NODE_SET_PROTOTYPE_METHOD(constructor, "testCapability", testCapability);
    NODE_SET_PROTOTYPE_METHOD(constructor, "executeSQL", executeSQL);
    NODE_SET_PROTOTYPE_METHOD(constructor, "syncToDisk", syncToDisk);
    NODE_SET_PROTOTYPE_METHOD(constructor, "createLayer", createLayer);
    NODE_SET_PROTOTYPE_METHOD(constructor, "copyLayer", copyLayer);
    NODE_SET_PROTOTYPE_METHOD(constructor, "releaseResultSet", releaseResultSet);

    target->Set(String::NewSymbol("Datasource"), constructor->GetFunction());
}

Datasource::Datasource(OGRDataSource *ds)
: ObjectWrap(),
  this_(ds)
{}

Datasource::Datasource()
: ObjectWrap(),
  this_(0)
{
}

Datasource::~Datasource()
{
  //OGRDataSource::DestroyDataSource(this_);
}

Handle<Value> Datasource::New(const Arguments& args)
{
  HandleScope scope;

  if (!args.IsConstructCall())
      return ThrowException(String::New("Cannot call constructor as function, you need to use 'new' keyword"));

  if (args[0]->IsExternal()) {
      Local<External> ext = Local<External>::Cast(args[0]);
      void* ptr = ext->Value();
      Datasource *f =  static_cast<Datasource *>(ptr);
      f->Wrap(args.This());
      return args.This();
  }

  return args.This();
}

Handle<Value> Datasource::New(OGRDataSource *ds) {
  return ClosedPtr<Datasource, OGRDataSource>::Closed(ds);
}

Handle<Value> Datasource::toString(const Arguments& args)
{
  HandleScope scope;
  return scope.Close(String::New("Datasource"));
}

NODE_WRAPPED_METHOD_WITH_RESULT(Datasource, getName, String, GetName);
NODE_WRAPPED_METHOD_WITH_RESULT(Datasource, getLayerCount, Integer, GetLayerCount);
NODE_WRAPPED_METHOD_WITH_RESULT(Datasource, syncToDisk, Integer, SyncToDisk);
NODE_WRAPPED_METHOD_WITH_RESULT_1_STRING_PARAM(Datasource, getLayerByName, Layer, GetLayerByName, "layer name");
NODE_WRAPPED_METHOD_WITH_RESULT_1_INTEGER_PARAM(Datasource, getLayer, Layer, GetLayer, "layer index to fetch");
NODE_WRAPPED_METHOD_WITH_RESULT_1_INTEGER_PARAM(Datasource, deleteLayer, Integer, DeleteLayer, "layer index to delete");
NODE_WRAPPED_METHOD_WITH_RESULT_1_STRING_PARAM(Datasource, testCapability, Boolean, TestCapability, "capability to test");
NODE_WRAPPED_METHOD_WITH_1_WRAPPED_PARAM(Datasource, releaseResultSet, ReleaseResultSet, Layer, "layer to release");

Handle<Value> Datasource::executeSQL(const Arguments& args)
{
  HandleScope scope;
  std::string sql;
  std::string sql_dialect;
  Geometry *spatial_filter = NULL;

  NODE_ARG_STR(0, "sql text", sql);
  NODE_ARG_WRAPPED_OPT(1, "spatial filter geometry", Geometry, spatial_filter);
  NODE_ARG_OPT_STR(2, "sql dialect", sql_dialect);

  Datasource *ds = ObjectWrap::Unwrap<Datasource>(args.This());

  try {
    OGRLayer *layer = ds->this_->ExecuteSQL(sql.c_str(),
                                            spatial_filter ? spatial_filter->get() : NULL,
                                            sql_dialect.empty() ? NULL : sql_dialect.c_str());

    if (layer)
      return scope.Close(Layer::New(layer));

  } catch (...) {
    return NODE_THROW("Unknown error. Does the layer exist?");
  }

  return Undefined();
}

Handle<Value> Datasource::createLayer(const Arguments& args)
{
  HandleScope scope;
  std::string layer_name;
  std::string spatial_ref = "";
  OGRwkbGeometryType geom_type = wkbUnknown;
  Handle<Array> layer_options = Array::New(0);

  NODE_ARG_STR(0, "layer name", layer_name);
  NODE_ARG_OPT_STR(1, "spatial reference", spatial_ref);
  NODE_ARG_ENUM_OPT(2, "geometry type", OGRwkbGeometryType, geom_type);
  NODE_ARG_ARRAY_OPT(3, "layer creation options", layer_options);

  Datasource *ds = ObjectWrap::Unwrap<Datasource>(args.This());

  char **options = NULL;

  if (layer_options->Length() > 0) {
    options = new char* [layer_options->Length()];
    for (unsigned int i = 0; i < layer_options->Length(); ++i) {
      options[i] = TOSTR(layer_options->Get(i));
    }
  }

  OGRLayer *layer = ds->this_->CreateLayer(layer_name.c_str(),
                                           NULL,
                                           geom_type,
                                           options);

  if (options) {
    delete [] options;
  }

  if (layer) {
    return scope.Close(Layer::New(layer));
  }

  return Undefined();
}

Handle<Value> Datasource::copyLayer(const Arguments& args)
{
  HandleScope scope;
  Layer *layer_to_copy;
  std::string new_name = "";
  Handle<Array> layer_options = Array::New(0);

  NODE_ARG_WRAPPED(0, "layer to copy", Layer, layer_to_copy);
  NODE_ARG_STR(1, "new layer name", new_name);
  NODE_ARG_ARRAY_OPT(2, "layer creation options", layer_options);

  Datasource *ds = ObjectWrap::Unwrap<Datasource>(args.This());

  char **options = NULL;

  if (layer_options->Length() > 0) {
    options = new char* [layer_options->Length()];
    for (unsigned int i = 0; i < layer_options->Length(); ++i) {
      options[i] = TOSTR(layer_options->Get(i));
    }
  }

  OGRLayer *layer = ds->this_->CopyLayer(layer_to_copy->get(),
                                         new_name.c_str(),
                                         options);

  if (options) {
    delete [] options;
  }

  if (layer) {
    return scope.Close(Layer::New(layer));
  }

  return Undefined();
}
<commit_msg>Remove try/catch for -fno-exceptions<commit_after>
#include "ogr_layer.hpp"
#include "ogr_datasource.hpp"
#include "ogr_geometry.hpp"
#include "ogr_common.hpp"

Persistent<FunctionTemplate> Datasource::constructor;

void Datasource::Initialize(Handle<Object> target) {
    HandleScope scope;

    constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Datasource::New));
    constructor->InstanceTemplate()->SetInternalFieldCount(1);
    constructor->SetClassName(String::NewSymbol("Datasource"));

    NODE_SET_PROTOTYPE_METHOD(constructor, "toString", toString);
    NODE_SET_PROTOTYPE_METHOD(constructor, "getName", getName);
    NODE_SET_PROTOTYPE_METHOD(constructor, "getLayerByName", getLayerByName);
    NODE_SET_PROTOTYPE_METHOD(constructor, "getLayerCount", getLayerCount);
    NODE_SET_PROTOTYPE_METHOD(constructor, "getLayer", getLayer);
    NODE_SET_PROTOTYPE_METHOD(constructor, "deleteLayer", deleteLayer);
    NODE_SET_PROTOTYPE_METHOD(constructor, "testCapability", testCapability);
    NODE_SET_PROTOTYPE_METHOD(constructor, "executeSQL", executeSQL);
    NODE_SET_PROTOTYPE_METHOD(constructor, "syncToDisk", syncToDisk);
    NODE_SET_PROTOTYPE_METHOD(constructor, "createLayer", createLayer);
    NODE_SET_PROTOTYPE_METHOD(constructor, "copyLayer", copyLayer);
    NODE_SET_PROTOTYPE_METHOD(constructor, "releaseResultSet", releaseResultSet);

    target->Set(String::NewSymbol("Datasource"), constructor->GetFunction());
}

Datasource::Datasource(OGRDataSource *ds)
: ObjectWrap(),
  this_(ds)
{}

Datasource::Datasource()
: ObjectWrap(),
  this_(0)
{
}

Datasource::~Datasource()
{
  //OGRDataSource::DestroyDataSource(this_);
}

Handle<Value> Datasource::New(const Arguments& args)
{
  HandleScope scope;

  if (!args.IsConstructCall())
      return ThrowException(String::New("Cannot call constructor as function, you need to use 'new' keyword"));

  if (args[0]->IsExternal()) {
      Local<External> ext = Local<External>::Cast(args[0]);
      void* ptr = ext->Value();
      Datasource *f =  static_cast<Datasource *>(ptr);
      f->Wrap(args.This());
      return args.This();
  }

  return args.This();
}

Handle<Value> Datasource::New(OGRDataSource *ds) {
  return ClosedPtr<Datasource, OGRDataSource>::Closed(ds);
}

Handle<Value> Datasource::toString(const Arguments& args)
{
  HandleScope scope;
  return scope.Close(String::New("Datasource"));
}

NODE_WRAPPED_METHOD_WITH_RESULT(Datasource, getName, String, GetName);
NODE_WRAPPED_METHOD_WITH_RESULT(Datasource, getLayerCount, Integer, GetLayerCount);
NODE_WRAPPED_METHOD_WITH_RESULT(Datasource, syncToDisk, Integer, SyncToDisk);
NODE_WRAPPED_METHOD_WITH_RESULT_1_STRING_PARAM(Datasource, getLayerByName, Layer, GetLayerByName, "layer name");
NODE_WRAPPED_METHOD_WITH_RESULT_1_INTEGER_PARAM(Datasource, getLayer, Layer, GetLayer, "layer index to fetch");
NODE_WRAPPED_METHOD_WITH_RESULT_1_INTEGER_PARAM(Datasource, deleteLayer, Integer, DeleteLayer, "layer index to delete");
NODE_WRAPPED_METHOD_WITH_RESULT_1_STRING_PARAM(Datasource, testCapability, Boolean, TestCapability, "capability to test");
NODE_WRAPPED_METHOD_WITH_1_WRAPPED_PARAM(Datasource, releaseResultSet, ReleaseResultSet, Layer, "layer to release");

Handle<Value> Datasource::executeSQL(const Arguments& args)
{
  HandleScope scope;
  std::string sql;
  std::string sql_dialect;
  Geometry *spatial_filter = NULL;

  NODE_ARG_STR(0, "sql text", sql);
  NODE_ARG_WRAPPED_OPT(1, "spatial filter geometry", Geometry, spatial_filter);
  NODE_ARG_OPT_STR(2, "sql dialect", sql_dialect);

  Datasource *ds = ObjectWrap::Unwrap<Datasource>(args.This());

  OGRLayer *layer = ds->this_->ExecuteSQL(sql.c_str(),
                                          spatial_filter ? spatial_filter->get() : NULL,
                                          sql_dialect.empty() ? NULL : sql_dialect.c_str());

  if (layer)
    return scope.Close(Layer::New(layer));

  return Undefined();
}

Handle<Value> Datasource::createLayer(const Arguments& args)
{
  HandleScope scope;
  std::string layer_name;
  std::string spatial_ref = "";
  OGRwkbGeometryType geom_type = wkbUnknown;
  Handle<Array> layer_options = Array::New(0);

  NODE_ARG_STR(0, "layer name", layer_name);
  NODE_ARG_OPT_STR(1, "spatial reference", spatial_ref);
  NODE_ARG_ENUM_OPT(2, "geometry type", OGRwkbGeometryType, geom_type);
  NODE_ARG_ARRAY_OPT(3, "layer creation options", layer_options);

  Datasource *ds = ObjectWrap::Unwrap<Datasource>(args.This());

  char **options = NULL;

  if (layer_options->Length() > 0) {
    options = new char* [layer_options->Length()];
    for (unsigned int i = 0; i < layer_options->Length(); ++i) {
      options[i] = TOSTR(layer_options->Get(i));
    }
  }

  OGRLayer *layer = ds->this_->CreateLayer(layer_name.c_str(),
                                           NULL,
                                           geom_type,
                                           options);

  if (options) {
    delete [] options;
  }

  if (layer) {
    return scope.Close(Layer::New(layer));
  }

  return Undefined();
}

Handle<Value> Datasource::copyLayer(const Arguments& args)
{
  HandleScope scope;
  Layer *layer_to_copy;
  std::string new_name = "";
  Handle<Array> layer_options = Array::New(0);

  NODE_ARG_WRAPPED(0, "layer to copy", Layer, layer_to_copy);
  NODE_ARG_STR(1, "new layer name", new_name);
  NODE_ARG_ARRAY_OPT(2, "layer creation options", layer_options);

  Datasource *ds = ObjectWrap::Unwrap<Datasource>(args.This());

  char **options = NULL;

  if (layer_options->Length() > 0) {
    options = new char* [layer_options->Length()];
    for (unsigned int i = 0; i < layer_options->Length(); ++i) {
      options[i] = TOSTR(layer_options->Get(i));
    }
  }

  OGRLayer *layer = ds->this_->CopyLayer(layer_to_copy->get(),
                                         new_name.c_str(),
                                         options);

  if (options) {
    delete [] options;
  }

  if (layer) {
    return scope.Close(Layer::New(layer));
  }

  return Undefined();
}
<|endoftext|>