text:
/*
 * Data:                2019-03-18
 * Autor:               Marcin Kurdziel
 * Kompilacja:          clang++ -std=c++14 -o client2_async client2_async.cpp -lpthread
 *                      Zamiast clang++ można użyć g++, ale nie jest to zalecane
 *                      Do skompilowaniu programu potrzebna jest biblioteka Boost.Asio oraz Boost.Lexical_Cast
 * Uruchamianie:        ./client2_async <adres IP> <numer portu> <wiadomość>
 */

#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/lexical_cast.hpp>
#include <array>
#include <iostream>
#include <string>
#include <thread>

namespace asio = boost::asio;

void print_error(const std::string msg, const boost::system::error_code& err)
{
    if(err == asio::error::operation_aborted) return;
    std::cerr << msg << "(): " << err.message() << std::endl;
}

int main(int argc, char** argv)
{
    if (argc != 4)
    {
        std::cerr << "Invocation: " << argv[0] << " <IPv4 ADDRESS> <PORT> <MESSAGE>" << std::endl;
        return 1;
    }

    // io_context jest niezbędnym elementem, który służy do komunikacji z systemem operacyjnym
    asio::io_context context;

    // utworzenie i otwarcie gniazda dla protokolu UDP
    asio::ip::udp::socket socket(context);
    socket.open(asio::ip::udp::v4());

    // utworzenie punktu końcowego komunikacji
    // pierwszym argumentem konstruktora jest adres IP, drugim numer portu
    asio::ip::udp::endpoint endpoint(asio::ip::make_address_v4(argv[1]),
                                    boost::lexical_cast<unsigned short>(argv[2]));

    sleep(1);

    std::cout << "Sending message to " << argv[1] << ".\nWaiting for server response..." << std::endl;

    sleep(2);

    std::string message = argv[3];
    std::array<char, 256> response;

    // wysłanie wiadomości
    socket.async_send_to(asio::buffer(message), endpoint, [&](const boost::system::error_code& err, std::size_t bytes)
    {
        if(err)
        {
            print_error("send_to", err);
            return;
        }

        // odpowiedź serwera
        socket.async_receive_from(asio::buffer(response), endpoint, [&](const boost::system::error_code& err, std::size_t bytes)
        {
            if(err)
            {
                print_error("receive_from", err);
                return;
            }
            std::cout << "Server response: " << response.data() << std::endl;
            socket.close();
        });

    });

    // odpalenie obsługi handlerów na osobnym wątku
    std::thread thread([&](){ context.run(); });
    // jesli wolimy, by komunikacja działała w tle i nie chcemy czekać, możemy zrobić tu detach()

    // tutaj możemy robić co chcemy (równolegle do obsługi komunikacji)
    // ...

    // jeżeli nie użyliśmy detach()
    thread.join();

    return 0;
}


text:
// This ray-tracing code is adapted from Hugues Thomas' project.
#include <math.h>
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <set>
#include <cstdint>
#include <cstdio>
#include <boost/algorithm/string.hpp>
#include "ray_tracing.hpp"
#include "estimation.hpp"
#include "utils.hpp"
#include "pointmatcher/PointMatcher.h"

static Eigen::Vector3d max_point(Eigen::MatrixXd & pc) {
    Eigen::Vector3d maxP = Eigen::Vector3d::Zero();
    for (uint i = 0; i < pc.cols(); ++i) {
        if (pc(0, i) > maxP(0))
            maxP(0) = pc(0, i);
        if (pc(1, i) > maxP(1))
            maxP(1) = pc(1, i);
        if (pc(2, i) > maxP(2))
            maxP(2) = pc(2, i);
    }
    return maxP;
}

static Eigen::Vector3d min_point(Eigen::MatrixXd & pc) {
    Eigen::Vector3d minP = Eigen::Vector3d::Zero();
    for (uint i = 0; i < pc.cols(); ++i) {
        if (pc(0, i) < minP(0))
            minP(0) = pc(0, i);
        if (pc(1, i) < minP(1))
            minP(1) = pc(1, i);
        if (pc(2, i) < minP(2))
            minP(2) = pc(2, i);
    }
    return minP;
}

static void cart2pol_(Eigen::MatrixXd &cart_frame, Eigen::MatrixXd &polar_frame) {
    polar_frame = Eigen::MatrixXd::Ones(cart_frame.rows(), cart_frame.cols());
    for (uint i = 0; i < cart_frame.cols(); ++i) {
        Eigen::Vector3d p = cart_frame.block(0, i, 3, 1);
        float rho = p.norm();
        float phi = atan2f(p(1), p(0));
        float theta = atan2f(sqrt(p(0) * p(0) + p(1) * p(1)), p(2));
        polar_frame(0, i) = rho;
        polar_frame(1, i) = theta;
        polar_frame(2, i) = phi + M_PI / 2;
    }
}

static Eigen::Vector3d cart2pol(Eigen::Vector3d &p) {
    float rho = p.norm();
    float phi = atan2f(p(1), p(0));
    float theta = atan2f(sqrt(p(0) * p(0) + p(1) * p(1)), p(2));
    return Eigen::Vector3d(rho, theta, phi + M_PI / 2);
}

static void compare_map_to_frame(Eigen::MatrixXd &aligned_frame, Eigen::MatrixXd &map_points,
    Eigen::MatrixXd &map_normals, Eigen::Matrix4d T_sensor_map, std::unordered_map<VoxKey, size_t> &map_samples,
    float theta_dl, float phi_dl, float map_dl, std::vector<float> &movable_probs, std::vector<int> &movable_counts) {

    float inv_theta_dl = 1.0 / theta_dl;
    float inv_phi_dl = 1.0 / phi_dl;
    float inv_map_dl = 1.0 / map_dl;
    float max_angle = 5 * M_PI / 12;
    float min_vert_cos = cos(M_PI / 3);

    // Mask of the map points not updated yet
    std::vector<bool> not_updated(map_points.cols(), true);

    // Get limits
    Eigen::Vector3d min_P = min_point(aligned_frame) - Eigen::Vector3d(map_dl, map_dl, map_dl);
    Eigen::Vector3d max_P = max_point(aligned_frame) + Eigen::Vector3d(map_dl, map_dl, map_dl);

    // Update full voxels
    // Loop over aligned_frame
    VoxKey k0, k;
    for (uint i = 0; i < aligned_frame.cols(); ++i) {
        // Corresponding key
        k0.x = (int)floor(aligned_frame(0, i) * inv_map_dl);
        k0.y = (int)floor(aligned_frame(1, i) * inv_map_dl);
        k0.z = (int)floor(aligned_frame(2, i) * inv_map_dl);
        // Update the adjacent cells
        for (k.x = k0.x - 1; k.x < k0.x + 2; k.x++) {
            for (k.y = k0.y - 1; k.y < k0.y + 2; k.y++) {
                for (k.z = k0.z - 1; k.z < k0.z + 2; k.z++) {
                    // Update count and movable at this point
                    if (map_samples.count(k) > 0) {
                        // Only update once
                        size_t i0 = map_samples[k];
                        if (not_updated[i0]) {
                            not_updated[i0] = false;
                            movable_counts[i0] += 1;
                        }
                    }
                }
            }
        }
    }

    // Create the free frustum grid

    // get frame in polar coordinates
    Eigen::MatrixXd frame = T_sensor_map * aligned_frame;
    Eigen::MatrixXd polar_frame;
    cart2pol_(frame, polar_frame);

    // Get grid limits
    Eigen::Vector3d minCorner = min_point(polar_frame);
    Eigen::Vector3d maxCorner = max_point(polar_frame);
    Eigen::Vector3d originCorner = minCorner - Eigen::Vector3d(0, 0.5 * theta_dl, 0.5 * phi_dl);

    // Dimensions of the grid
    size_t grid_n_theta = (size_t)floor((maxCorner(1) - originCorner(1)) / theta_dl) + 1;
    size_t grid_n_phi = (size_t)floor((maxCorner(2) - originCorner(2)) / phi_dl) + 1;

    // Initialize variables
    std::vector<float> frustrum_radiuses(grid_n_theta * grid_n_phi, -1.0);

    // Fill the frustrum radiuses
    for (uint i = 0; i < polar_frame.cols(); ++i) {
        // Corresponding key
        Eigen::Vector3d p = polar_frame.block(0, i, 3, 1);
        // Position of point in grid
        size_t i_theta = (size_t)floor((p(1) - originCorner(1)) * inv_theta_dl);
        size_t i_phi = (size_t)floor((p(2) - originCorner(2)) * inv_phi_dl);
        size_t gridIdx = i_theta + grid_n_theta * i_phi;

        // Update the radius in cell
        if (frustrum_radiuses[gridIdx] < 0)
            frustrum_radiuses[gridIdx] = p(0);
        else if (p(0) < frustrum_radiuses[gridIdx])
            frustrum_radiuses[gridIdx] = p(0);
    }

    // Apply margin to free ranges
    float margin = map_dl;
    float frustrum_alpha = theta_dl / 2;
    for (auto &r : frustrum_radiuses) {
        float adapt_margin = r * frustrum_alpha;
        if (margin < adapt_margin)
            r -= adapt_margin;
        else
            r -= margin;
    }

    // Apply frustum casting

    // update free pixels
    float min_r = 2 * map_dl;
    Eigen::Matrix3d C_sensor_map = T_sensor_map.block(0, 0, 3, 3);

    for (uint i = 0; i < map_points.cols(); ++i) {
        // Ignore points updated just now
        if (!not_updated[i])
            continue;
        Eigen::Vector4d p = map_points.block(0, i, 4, 1);
        // Ignore points outside area of the frame
        if (p(0) > max_P(0) || p(1) > max_P(1) || p(2) > max_P(2) ||
            p(0) < min_P(0) || p(1) < min_P(1) || p(2) < min_P(2)) {
            continue;
        }

        // Align point in frame coordinates (and normal)
        p = T_sensor_map * p;
        Eigen::Vector3d xyz = p.block(0, 0, 3, 1);
        Eigen::Vector3d nxyz = C_sensor_map * map_normals.block(0, i, 3, 1);

        // Project in polar coordinates
        Eigen::Vector3d rtp = cart2pol(xyz);

        // Position of point in grid
        size_t i_theta = (size_t)floor((rtp(1) - originCorner(1)) * inv_theta_dl);
        size_t i_phi = (size_t)floor((rtp(2) - originCorner(2)) * inv_phi_dl);
        size_t gridIdx = i_theta + grid_n_theta * i_phi;

        // Update movable prob
        if (rtp(0) > min_r && rtp(0) < frustrum_radiuses[gridIdx]) {
            // Do not update if normal is horizontal and perpendicular to ray (to avoid removing walls)
            if (abs(nxyz(2)) > min_vert_cos) {
                movable_counts[i] += 1;
                movable_probs[i] += 1.0;
            } else {
                float angle = acos(std::min(fabs(xyz.dot(nxyz) / rtp(0)), 1.0));
                if (angle < max_angle) {
                    movable_counts[i] += 1;
                    movable_probs[i] += 1.0;
                }
            }
        }
    }
}

static void getNameFromPath(std::string path, std::string &name) {
    std::vector<std::string> parts;
    boost::split(parts, path, boost::is_any_of("/"));
    std::string endy_bit = parts[parts.size() - 1];
    boost::split(parts, endy_bit, boost::is_any_of("."));
    name = parts[0];
}

int main(int argc, const char *argv[]) {
    std::string root, config;
    std::string new_map_name = "map_no_movable.ply";
    validateArgs(argc, argv, root, config);
    // Params:
    float map_dl = 0.15;
    float theta_dl = 1.29 * M_PI / 180;
    float phi_dl = 0.1 * M_PI / 180;

    // Init point map
    std::cout << "Loading map..." << std::endl;
    DP map = DP::load(root + "map/map.ply");
    std::cout << "Finished loading map" << std::endl;

    // Create the pointmap voxels
    std::unordered_map<VoxKey, size_t> map_samples;
    uint N = map.features.cols();
    map_samples.reserve(N);
    float inv_map_dl = 1.0 / map_dl;
    VoxKey k0;

    for (uint i = 0; i < N; ++i) {
        k0.x = (int)floor(map.features(0, i) * inv_map_dl);
        k0.y = (int)floor(map.features(1, i) * inv_map_dl);
        k0.z = (int)floor(map.features(2, i) * inv_map_dl);
        if (map_samples.count(k0) < 1) {
            map_samples.emplace(k0, i);
        }
    }

    // Init map movable probabilities and counts
    std::vector<float> movable_probs(N, 0);
    std::vector<int> movable_counts(N, 0);

    // Calculate normal vectors for the map
    std::shared_ptr<PM::DataPointsFilter> normalFilter = PM::get().DataPointsFilterRegistrar.create(
        "SurfaceNormalDataPointsFilter", {{"knn", toParam(10)}, {"epsilon", toParam(5)}, {"keepNormals", toParam(1)},
        {"keepDensities", toParam(0)}});

    std::cout << "Calculating normals for the entire map..." << std::endl;
    map = normalFilter->filter(map);
    std::cout << "Finished calculating normals" << std::endl;
    uint norm_row = map.getDescriptorStartingRow("normals");
    Eigen::MatrixXd map_normals = map.descriptors.block(norm_row, 0, 3, N);

    // Start movable detection

    std::vector<std::string> frame_names;
    getMapFrames(root, frame_names);

    for (uint i = 0; i < frame_names.size(); ++i) {
        std::cout << "Ray tracing frame " << i << " / " << frame_names.size() - 1 << std::endl;
        // Load frame / ply file
        DP frame = DP::load(frame_names[i]);
        // Load pose for this frame
        Eigen::Matrix4d T_map_sensor = Eigen::Matrix4d::Identity();
        std::string name;
        getNameFromPath(frame_names[i], name);
        load_transform(root + "map/frame_poses/" + name + ".txt", T_map_sensor);
        Eigen::Matrix4d T_sensor_map = get_inverse_tf(T_map_sensor);
        // Perform ray-tracing to identity movable points
        compare_map_to_frame(frame.features, map.features, map_normals, T_sensor_map,
            map_samples, theta_dl, phi_dl, map_dl, movable_probs, movable_counts);
    }

    for (uint i = 0; i < movable_probs.size(); ++i) {
        movable_probs[i] = movable_probs[i] / (movable_counts[i] + 1e-6);
        if (movable_counts[i] < 1e-6)
            movable_probs[i] = -1;
    }
    map.allocateDescriptor("movable", 1);
    uint movable_row = map.getDescriptorStartingRow("movable");
    for (uint i = 0; i < movable_probs.size(); ++i) {
        map.descriptors(movable_row, i) = movable_probs[i];
    }
    std::cout << "Adding movable descriptor to the existing map..." << std::endl;
    map.removeDescriptor("normals");  // Get rid of normals
    map.save(root + "map/map.ply");

    std::cout << "Removing movable points from the map..." << std::endl;
    uint feat_dim = map.features.rows();
    map.removeDescriptor("movable");
    uint desc_dim = map.descriptors.rows();
    uint j = 0;
    for (uint i = 0; i < movable_probs.size(); ++i) {
        if (movable_probs[i] < 0.8) {
            map.features.block(0, j, feat_dim, 1) = map.features.block(0, i, feat_dim, 1);
            map.descriptors.block(0, j, desc_dim, 1) = map.descriptors.block(0, i, desc_dim, 1);
            j++;
        }
    }
    map.conservativeResize(j);

    std::cout << "Saving the filtered map..." << std::endl;
    map.save(root + "map/" + new_map_name);
}

ext: cpp


text:
/*!
 * Title ---- km/ql/ast_adapted.hpp
 * Author --- Giacomo Trudu aka `Wicker25` - wicker25[at]gmail[dot]com
 *
 * Copyright (C) 2017 by Giacomo Trudu.
 * All rights reserved.
 *
 * This file is part of KeyMaker software.
 */

#ifndef __KM_QL_AST_ADAPTED_HPP__
#define __KM_QL_AST_ADAPTED_HPP__

#include <km.hpp>
#include <km/ql/ast.hpp>

#include <boost/fusion/include/adapt_struct.hpp>

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::add_stmt,
    (std::string, name)
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::list_stmt
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::show_stmt,
    (km::ql::ast::entry_t, entry)
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::select_stmt,
    (km::ql::ast::property_list_t, property_list)
    (km::ql::ast::entry_t,         entry)
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::update_stmt,
    (km::ql::ast::entry_t,         entry)
    (km::ql::ast::property_list_t, property_list)
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::run_stmt,
    (km::ql::ast::action_t, action)
    (km::ql::ast::entry_t,  entry)
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::copy_stmt,
    (km::ql::ast::property_list_t, property_list)
    (km::ql::ast::entry_t,         entry)
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::commit_stmt,
    (km::ql::ast::entry_t, entry)
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::push_stmt
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::pull_stmt
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::remove_stmt,
    (km::ql::ast::property_list_t, property_list)
    (km::ql::ast::entry_t,         entry)
)

BOOST_FUSION_ADAPT_STRUCT(
    km::ql::ast::query,
    (km::ql::ast::stmt, statement)
)

#endif /* __KM_QL_AST_ADAPTED_HPP__ */

ext: hpp

max_stars_repo_name: keymaker-io/keymaker-alpha

max_issues_repo_name: keymaker-io/keymaker-alpha

max_forks_repo_name: keymaker-io/keymaker-alpha


text:
/*
 * _TradeManager.cpp
 *
 *  Created on: 2013-2-25
 *      Author: fasiondog
 */

#include <boost/python.hpp>
#include <hikyuu/trade_manage/build_in.h>
#include "../_Parameter.h"
#include "../pickle_support.h"

using namespace boost::python;
using namespace hku;

#if HKU_PYTHON_SUPPORT_PICKLE
struct TradeManager_pickle_suite : bp::pickle_suite {
    static boost::python::tuple getinitargs(TradeManager const& w) {
        return boost::python::make_tuple(w.initDatetime(), w.initCash(), w.costFunc(), w.name());
    }

    static bp::object getstate(const TradeManager& param) {
        std::ostringstream os;
        OUTPUT_ARCHIVE oa(os);
        oa << param;
        return bp::str(os.str());
    }

    static void setstate(TradeManager& params, bp::object entries) {
        bp::str s = bp::extract<bp::str>(entries)();
        std::string st = bp::extract<std::string>(s)();
        std::istringstream is(st);

        INPUT_ARCHIVE ia(is);
        ia >> params;
    }
};
#endif /* HKU_PYTHON_SUPPORT_PICKLE */

FundsRecord (TradeManager::*getFunds_1)(KQuery::KType) const = &TradeManager::getFunds;
FundsRecord (TradeManager::*getFunds_2)(const Datetime&, KQuery::KType) = &TradeManager::getFunds;

PriceList (TradeManager::*getTMFundsCurve_1)(const DatetimeList&,
                                             KQuery::KType) = &TradeManager::getFundsCurve;
PriceList (TradeManager::*getTMFundsCurve_2)() = &TradeManager::getFundsCurve;

PriceList (TradeManager::*getTMProfitCurve_1)(const DatetimeList&,
                                              KQuery::KType ktype) = &TradeManager::getProfitCurve;
PriceList (TradeManager::*getTMProfitCurve_2)() = &TradeManager::getProfitCurve;

TradeCostPtr (TradeManager::*get_costFunc)() const = &TradeManager::costFunc;
void (TradeManager::*set_costFunc)(const TradeCostPtr&) = &TradeManager::costFunc;

string (TradeManager::*tm_get_name)() const = &TradeManager::name;
void (TradeManager::*tm_set_name)(const string&) = &TradeManager::name;

const TradeRecordList& (TradeManager::*_getTradeList_1)() const = &TradeManager::getTradeList;
TradeRecordList (TradeManager::*_getTradeList_2)(const Datetime&, const Datetime&) const =
  &TradeManager::getTradeList;

void export_TradeManager() {
    // size_t null_size = Null<size_t>();

    class_<TradeManager>(
      "TradeManager",
      R"(交易管理类，可理解为一个模拟账户进行模拟交易。一般使用 crtTM 创建交易管理实例。

交易管理可理解为一个模拟账户进行模拟交易。一般使用 crtTM 创建交易管理实例。

公共参数：

    - reinvest=False (bool) : 红利是否再投资
    - precision=2 (int) : 价格计算精度
    - support_borrow_cash=False (bool) : 是否自动融资
    - support_borrow_stock=False (bool) : 是否自动融券
    - save_action=True (bool) : 是否保存Python命令序列)",
      init<const Datetime&, price_t, const TradeCostPtr&, const string&>())

      .def("__str__", &TradeManager::toString)
      .def("__repr__", &TradeManager::toString)

      .add_property("name", tm_get_name, tm_set_name, "名称")
      .add_property("init_cash", &TradeManager::initCash, "（只读）初始资金")
      .add_property("current_cash", &TradeManager::currentCash, "（只读）当前资金")
      .add_property("init_datetime", &TradeManager::initDatetime, "（只读）账户建立日期")
      .add_property("first_datetime", &TradeManager::firstDatetime,
                    "（只读）第一笔买入交易发生日期，如未发生交易返回 Datetime>()")
      .add_property("last_datetime", &TradeManager::lastDatetime,
                    "（只读）最后一笔交易日期，注意和交易类型无关，如未发生交易返回账户建立日期")
      .add_property("reinvest", &TradeManager::reinvest,
                    "（只读）红利/股息/送股再投资标志，同公共参数“reinvest”")
      .add_property("precision", &TradeManager::precision,
                    "（只读）价格精度，同公共参数“precision”")
      .add_property("cost_func", get_costFunc, set_costFunc, "交易成本算法")
      .add_property("broker_last_datetime", &TradeManager::getBrokerLastDatetime,
                    &TradeManager::setBrokerLastDatetime,
                    R"(实际开始订单代理操作的时刻。

    默认情况下，TradeManager会在执行买入/卖出操作时，调用订单代理执行代理的买入/卖出动作，但这样在实盘操作时会存在问题。因为系统在计算信号指示时，需要回溯历史数据才能得到最新的信号，这样TradeManager会在历史时刻就执行买入/卖出操作，此时如果订单代理本身没有对发出买入/卖出指令的时刻进行控制，会导致代理发送错误的指令。此时，需要指定在某一个时刻之后，才允许指定订单代理的买入/卖出操作。属性 brokeLastDatetime 即用于指定该时刻。)")

      .def("getParam", &TradeManager::getParam<boost::any>, R"(get_param(self, name)

    获取指定的参数

    :param str name: 参数名称
    :return: 参数值
    :raises out_of_range: 无此参数)")

      .def("set_param", &TradeManager::setParam<object>, R"(set_param(self, name, value)

    设置参数

    :param str name: 参数名称
    :param value: 参数值
    :type value: int | bool | float | string | Query | KData | Stock | DatetimeList
    :raises logic_error: Unsupported type! 不支持的参数类型)")

      .def("have_param", &TradeManager::haveParam, "是否存在指定参数")

      .def("reset", &TradeManager::reset, "复位，清空交易、持仓记录")
      .def("clone", &TradeManager::clone, "克隆（深复制）实例")

      .def("reg_broker", &TradeManager::regBroker, R"(reg_broker(self, broker)

    注册订单代理。可执行多次该命令注册多个订单代理。

    :param OrderBrokerBase broker: 订单代理实例)")

      .def("clear_broker", &TradeManager::clearBroker, R"(clear_broker(self)

    清空所有已注册订单代理)")

      //.def("getMarginRate", &TradeManager::getMarginRate)

      .def("have", &TradeManager::have, R"(have(self, stock)

    当前是否持有指定的证券

    :param Stock stock: 指定证券
    :rtype: bool)")

      .def("get_stock_num", &TradeManager::getStockNumber, R"(get_stock_num(self)

    当前持有的证券种类数量，即当前持有几只股票（非各个股票的持仓数）

    :rtype: int)")

      //.def("getShortStockNumber", &TradeManager::getShortStockNumber)

      .def("get_hold_num", &TradeManager::getHoldNumber, R"(get_hold_num(self, datetime, stock)

        获取指定时刻指定证券的持有数量

        :param Datetime datetime: 指定时刻
        :param Stock stock: 指定的证券
        :rtype: int)")

      //.def("getShortHoldNumber", &TradeManager::getShortHoldNumber)

      .def("get_trade_list", _getTradeList_1, return_value_policy<copy_const_reference>())
      .def("get_trade_list", _getTradeList_2, R"(get_trade_list(self[, start, end])

    获取交易记录，未指定参数时，获取全部交易记录

    :param Datetime start: 起始日期
    :param Datetime end: 结束日期
    :rtype: TradeRecordList)")

      .def("get_position_list", &TradeManager::getPositionList, R"(get_position_list(self)

    获取当前全部持仓记录

    :rtype: PositionRecordList)")

      .def("get_history_position_list", &TradeManager::getHistoryPositionList,
           return_value_policy<copy_const_reference>(), R"(get_history_position_list(self)

    获取全部历史持仓记录，即已平仓记录

    :rtype: PositionRecordList)")

      .def("get_position", &TradeManager::getPosition, R"(get_position(self, stock)

    获取指定证券的当前持仓记录，如当前未持有该票，返回PositionRecord()

    :param Stock stock: 指定的证券
    :rtype: PositionRecord)")

      .def("get_buy_cost", &TradeManager::getBuyCost,
           R"(get_buy_cost(self, datetime, stock, price, num)

    计算买入成本

    :param Datetime datetime: 交易时间
    :param Stock stock:       交易的证券
    :param float price:       买入价格
    :param float num:         买入数量
    :rtype: CostRecord)")

      .def("get_sell_cost", &TradeManager::getSellCost,
           R"(get_sell_cost(self, datetime, stock, price, num)

    计算卖出成本

    :param Datetime datetime: 交易时间
    :param Stock stock:       交易的证券
    :param float price:       卖出价格
    :param float num:         卖出数量
    :rtype: CostRecord)")

      //.def("getBorrowCashCost", &TradeManager::getBorrowCashCost)
      //.def("getReturnCashCost", &TradeManager::getReturnCashCost)
      //.def("getBorrowStockCost", &TradeManager::getBorrowStockCost)
      //.def("getReturnStockCost", &TradeManager::getReturnStockCost)

      .def("cash", &TradeManager::cash, (arg("datetime"), arg("ktype") = KQuery::DAY),
           R"(cash(self, datetime[, ktype=Query.KType.DAY])

    获取指定时刻的现金。（注：如果不带日期参数，无法根据权息信息调整持仓。）

    :param Datetime datetime: 指定时刻
    :param ktype: K线类型
    :rtype: float)")

      .def("get_funds", getFunds_1, (arg("ktype") = KQuery::DAY))
      .def("get_funds", getFunds_2, (arg("datetime"), arg("ktype") = KQuery::DAY),
           R"(get_funds(self, [datetime, ktype = Query.DAY])

    获取指定时刻的资产市值详情

    :param Datetime datetime:  指定时刻
    :param Query.KType ktype: K线类型
    :rtype: FundsRecord)")

      //.def("getFunds", getFunds_1, (arg("ktype") = KQuery::DAY))
      //.def("getFunds", getFunds_2, (arg("datetime"), arg("ktype") = KQuery::DAY))
      .def("get_funds_curve", getTMFundsCurve_1, (arg("dates"), arg("ktype") = KQuery::DAY),
           R"(get_funds_curve(self, dates[, ktype = Query.DAY])

    获取资产净值曲线

    :param DatetimeList dates: 日期列表，根据该日期列表获取其对应的资产净值曲线
    :param Query.KType ktype: K线类型，必须与日期列表匹配
    :return: 资产净值列表
    :rtype: PriceList)")

      .def("get_funds_curve", getTMFundsCurve_2,
           R"(get_funds_curve(self)

    获取从账户建立日期到系统当前日期的资产净值曲线（按自然日）

    :return: 资产净值列表
    :rtype: PriceList)")

      .def("get_profit_curve", getTMProfitCurve_1, (arg("dates"), arg("ktype") = KQuery::DAY),
           R"(get_profit_curve(self, dates[, ktype = Query.DAY])

    获取收益曲线，即扣除历次存入资金后的资产净值曲线

    :param DatetimeList dates: 日期列表，根据该日期列表获取其对应的收益曲线，应为递增顺序
    :param Query.KType ktype: K线类型，必须与日期列表匹配
    :return: 收益曲线
    :rtype: PriceList)")

      .def("get_profit_curve", getTMProfitCurve_2,
           R"(get_profit_curve(self)

    获取获取从账户建立日期到系统当前日期的收益曲线，即扣除历次存入资金后的资产净值曲线

    :return: 收益曲线
    :rtype: PriceList)")

      .def("checkin", &TradeManager::checkin, R"(checkin(self, datetime, cash)

    向账户内存入现金

    :param Datetime datetime: 交易时间
    :param float cash: 存入的现金量
    :rtype: TradeRecord)")

      .def("checkout", &TradeManager::checkout, R"(checkout(self, datetime, cash)

    从账户内取出现金

    :param Datetime datetime: 交易时间
    :param float cash: 取出的资金量
    :rtype: TradeRecord)")

      //.def("checkinStock", &TradeManager::checkinStock)
      //.def("checkoutStock", &TradeManager::checkoutStock)
      //.def("borrowCash", &TradeManager::borrowCash)
      //.def("returnCash", &TradeManager::returnCash)
      //.def("borrowStock", &TradeManager::borrowStock)
      //.def("returnStock", &TradeManager::returnStock)

      .def(
        "buy", &TradeManager::buy,
        (arg("datetime"), arg("stock"), arg("real_price"), arg("num"), arg("stoploss") = 0.0,
         arg("goal_price") = 0.0, arg("plan_price") = 0.0, arg("part") = PART_INVALID),
        R"(buy(self, datetime, stock, real_price, number[, stoploss=0.0, goal_price=0.0, plan_price=0.0, part=System.INVALID])

    买入操作

    :param Datetime datetime: 买入时间
    :param Stock stock:       买入的证券
    :param float real_price:  实际买入价格
    :param float num:         买入数量
    :param float stoploss:    止损价
    :param float goal_price:  目标价格
    :param float plan_price:  计划买入价格
    :param SystemPart part:   交易指示来源
    :rtype: TradeRecord)")

      // buy_overload(args("datetime", "stock", "realPrice",
      //"num", "stoploss", "goalPrice", "planPrice","part")))

      .def(
        "sell", &TradeManager::sell,
        (arg("datetime"), arg("stock"), arg("real_price"), arg("num") = MAX_DOUBLE,
         arg("stoploss") = 0.0, arg("goal_price") = 0.0, arg("plan_price") = 0.0,
         arg("part") = PART_INVALID),
        R"(sell(self, datetime, stock, realPrice[, number=constant.max_double, stoploss=0.0, goal_price=0.0, plan_price=0.0, part=System.INVALID])

    卖出操作

    :param Datetime datetime: 卖出时间
    :param Stock stock:       卖出的证券
    :param float real_price:  实际卖出价格
    :param float num:         卖出数量，如果等于constant.max_double，表示全部卖出
    :param float stoploss:    新的止损价
    :param float goal_price:  新的目标价格
    :param float plan_price:  原计划卖出价格
    :param SystemPart part:   交易指示来源
    :rtype: TradeRecord)")
      // sell_overload(args("datetime", "stock", "realPrice", "num", "stoploss", "goalPrice",
      //                   "planPrice", "part")))
      //.def("buyShort", &TradeManager::buyShort, buyShort_overload())
      //.def("sellShort", &TradeManager::sellShort, sellShort_overload())

      .def("add_trade_record", &TradeManager::addTradeRecord, R"(add_trade_record(self, tr)

    直接加入交易记录，如果加入初始化账户记录，将清除全部已有交易及持仓记录。

    :param TradeRecord tr: 交易记录
    :return: True（成功） | False（失败）
    :rtype: bool)")

      .def("tocsv", &TradeManager::tocsv, R"(tocsv(self, path)

    以csv格式输出交易记录、未平仓记录、已平仓记录、资产净值曲线

    :param str path: 输出文件所在目录)")

#if HKU_PYTHON_SUPPORT_PICKLE
      .def_pickle(TradeManager_pickle_suite())
#endif
      ;

    register_ptr_to_python<TradeManagerPtr>();
}

max_stars_repo_name: scanfyu/hikyuu

max_issues_repo_name: scanfyu/hikyuu

max_forks_repo_name: scanfyu/hikyuu


text:
/* $Id: asio_listener.hpp 48153 2011-01-01 15:57:50Z mordante $ */

/**
 * @file
 * @brief Header file of a listener for the ana project.
 *
 * ana: Asynchronous Network API.
 * Copyright (C) 2010 - 2011 Guillermo Biset.
 *
 * This file is part of the ana project.
 *
 * System:         ana
 * Language:       C++
 *
 * Author:         Guillermo Biset
 * E-Mail:         billybiset AT gmail DOT com
 *
 * ana is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * ana is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ana.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef ASIO_LISTENER_HPP
#define ASIO_LISTENER_HPP

#include <boost/asio.hpp>
#include <memory>

#include "../api/ana.hpp"

using boost::asio::ip::tcp;

class asio_listener : public virtual ana::detail::listener
{
    public:
        asio_listener( );

        virtual void set_listener_handler( ana::listener_handler* listener);
        virtual void run_listener();

        virtual ~asio_listener();

    protected:
        virtual tcp::socket& socket() = 0;

        void wait_for_incoming_message( size_t ms_to_timeout, ana::net_id id = 0  );

    private:
        virtual void disconnect_listener()                   {}

        virtual void set_raw_buffer_max_size( size_t size );

        virtual void wait_raw_object(ana::serializer::bistream& bis, size_t size);

        void listen_one_message();

        void disconnect( boost::system::error_code error);

        void handle_header(char* header, const boost::system::error_code& );

        void handle_body( ana::read_buffer , const boost::system::error_code& );

        void handle_partial_body( ana::read_buffer,
                                  const boost::system::error_code&,
                                  size_t accumulated,
                                  size_t last_msg_size);

        void handle_timeout( const boost::system::error_code&, ana::net_id);

        void handle_raw_buffer( ana::read_buffer, const boost::system::error_code&, size_t);

        /*attr*/
        bool                       disconnected_;
        ana::listener_handler*     listener_;
        char                       header_[ana::HEADER_LENGTH];
        size_t                     raw_mode_buffer_size_;

        ana::timer*                next_message_timer_;
};

#endif

max_stars_repo_name: blackberry/Wesnoth

max_issues_repo_name: blackberry/Wesnoth

max_forks_repo_name: blackberry/Wesnoth


text:
#pragma once

#include "logger.hpp"

#include <boost/log/expressions.hpp>
#include <boost/log/keywords/auto_flush.hpp>
#include <boost/log/common.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/severity_feature.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/core/record.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/expressions/formatters.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>

#include "nameof.hpp"

namespace pgl {
	// Keywords definitions for logger
	BOOST_LOG_ATTRIBUTE_KEYWORD(log_level_attribute, "Severity", log_level);
	BOOST_LOG_ATTRIBUTE_KEYWORD(log_header_attribute, "Header", std::string);

	// thread safe logger definition
	BOOST_LOG_GLOBAL_LOGGER(boost_mt_logger, boost::log::sources::severity_logger_mt<log_level>);

	boost::log::formatter get_boost_logger_default_formatter();

	// log_level to string
	std::ostream& operator<<(std::ostream& stream, const log_level level);
}

max_stars_repo_name: InstytutXR/PlanetaMatchMaker

max_issues_repo_name: InstytutXR/PlanetaMatchMaker

max_forks_repo_name: InstytutXR/PlanetaMatchMaker


text:
/*!
@file
Forward declares `boost::hana::Set`.

@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_HANA_FWD_SET_HPP
#define BOOST_HANA_FWD_SET_HPP

#include <boost/hana/core/make.hpp>
#include <boost/hana/core/operators.hpp>
#include <boost/hana/detail/std/forward.hpp>

namespace boost { namespace hana {
    //! @ingroup group-datatypes
    //! A basic unordered container requiring `Comparable` elements.
    //!
    //! Modeled concepts
    //! ----------------
    //! 1. `Comparable` (operators provided)\n
    //! Two sets are equal iff they contain the same elements, regardless of
    //! the order.
    //! @snippet example/set.cpp comparable
    //!
    //! 2. Foldable\n
    //! Folding a `Set` is equivalent to folding the sequence of its values.
    //! However, note that the values are not required to be in any specific
    //! order, so using the folds provided here with an operation that is not
    //! both commutative and associative will yield unspecified behavior.
    //! @snippet example/set.cpp foldable
    //!
    //! 3. Searchable\n
    //! The keys and the values of a `Set` are its elements; the `Searchable`
    //! model follows naturally from that.
    //! @snippet example/set.cpp searchable
    //!
    //!
    //! Provided conversions
    //! --------------------
    //! 1. From any `Foldable`\n
    //! If the foldable structure contains duplicates, the last one will
    //! be the one appearing in the resulting set.
    struct Set { };

    //! Creates a `Set` containing the given elements.
    //! @relates Set
    //!
    //! @note
    //! There must not be duplicate elements.
    //!
    //! @todo Consider allowing duplicates elements in this constructor.
#ifdef BOOST_HANA_DOXYGEN_INVOKED
    constexpr auto set = [](auto&& ...xs) {
        return unspecified-type;
    };
#else
    template <typename Storage, typename = operators::adl>
    struct _set {
        Storage storage;
        struct hana { using datatype = Set; };
    };

    struct _make_set {
        template <typename ...Xs>
        constexpr decltype(auto) operator()(Xs&& ...xs) const;
    };

    constexpr _make_set set{};
#endif

    //! Equivalent to `set`, provided for consistency.
    //! @relates Set
    template <>
    struct make_impl<Set> {
        template <typename ...Xs>
        static constexpr decltype(auto) apply(Xs&& ...xs) {
            return hana::set(detail::std::forward<Xs>(xs)...);
        }
    };

    //! Insert an element in a `Set`.
    //! @relates Set
    //!
    //! If the set already contains an element that compares equal, then
    //! nothing is done and the set is returned as is.
    //!
    //!
    //! @param set
    //! The set in which to insert a value.
    //!
    //! @param x
    //! The value to insert.
    //!
    //!
    //! ### Example
    //! @snippet example/set.cpp insert
#ifdef BOOST_HANA_DOXYGEN_INVOKED
    constexpr auto insert = [](auto&& set, auto&& x) -> decltype(auto) {
        return tag-dispatched;
    };
#endif
}} // end namespace boost::hana

#endif // !BOOST_HANA_FWD_SET_HPP

max_stars_repo_name: josephwinston/hana

max_issues_repo_name: josephwinston/hana

max_forks_repo_name: josephwinston/hana


text:
#ifndef RWHANDLER_HPP
#define RWHANDLER_HPP

#include <functional>

#include <boost/asio.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/system/error_code.hpp>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>

#include <Exception/marcoexception.hpp>
#include <Asio/message.hpp>
#include <Asio/asiocommon.hpp>

namespace SSDK
{
    namespace Asio
    {
        namespace TCP
        {
            /**
             *  @brief RWHandler
             *
             *  @author rime
             *  @version 1.00 2017-07-07 rime
             *                note:create it
             *
             * 注意：
             *          1.需要通过enable_shared_from_this返回this指针，这样才能保证异步操作时，原来的生命周期不会结束
             *             在返回时还是有效的
             *
             *          2.异步发送没有经过严格的测试，目前不建议使用， 除此之外，还是不推荐使用异步发送
             *
             *             异步发送比异步接收等其他异步操作更复杂，需要一个发送队列来保证发送不会乱序.
             *             但是，还有一个问题需要注意就是这个发送队列是没有加限制的，如果接收端收到数据之后阻塞处理，
             *             而发送又很快的话，就会导致发送队列的内存快速增长甚至内存爆掉。解决办法有两个：
             *                      1.发慢一点，并且保证接收端不会长时间阻塞socket；
             *                      2.控制发送队列的上限。
             *              第一种方法对实际应用的约束性较强，实际可操作性不高。
             *              第二种方法需要控制队列上限，不可避免的要加锁，这样就丧失了单线程异步发送的性能优势。
             *
             *              所以建议用同步发送接口来发送数据：
             *                      一来不用发送队列，自然也不会有内存暴涨的问题，
             *                      二来也不会有复杂的循环发送过程，而且还可以通过线程池来提高发送效率。
             *
             *          总结
             *              1.不要连续发起异步发送，要等上次发送完成之后再发起下一个异步发送；
             *              2.要考虑异步发送的发送队列内存可能会暴涨的问题；
             *              3.相比复杂的异步发送，同步发送简单可靠，推荐优先使用同步发送接口。
             *
             *          异步发送没有经过严格的测试，目前不建议使用
             *
             *          3.RWHandler结构保存了客户端和服务端通用的读写接口，所以在客户端和服务端都可以调用RWHandler，
             *             它抽象了公共的读写行为
             */
            class RWHandler: public std::enable_shared_from_this<RWHandler>
            {
            public:

                //>>>----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                //constructor & deconstructor

                /**
                 * @brief RWHandler
                 * @param ios
                 *              boost::asio的io_service对象，注意，这里传入的参数是引用，说明了该对象是由外部创建
                 *      不需要在内部负责销毁
                 */
                RWHandler(boost::asio::io_service& ios);
                virtual ~RWHandler();

                //<<<----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

                //>>>----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                //get & set function

                /**
                 * @brief socket
                 *          连接的套接字
                 * @return
                 */
                boost::asio::ip::tcp::socket& socket() {return this->m_socket;}
                /**
                 * @brief ip
                 *          返回连接点的IP
                 * @return
                 */
                const std::string ip(){return this->m_socket.remote_endpoint().address().to_string();}
                /**
                 * @brief port
                 *           返回连接点的端口号
                 * @return
                 */
                unsigned short port(){return this->m_socket.remote_endpoint().port();}

                /**
                 * @brief setId
                 *          设置连接的ID号，范围取决与Server的最大连接数
                 * @param id
                 */
                void setId(uint id){this->m_Id = id;}
                /**
                 * @brief Id
                 *          返回当前连接的ID号
                 * @return
                 */
                uint Id() const {return this->m_Id;}

                /**
                 * @brief receivedMessage
                 *             返回接受到Message对象，数据存储在里面
                 * @return
                 */
                 Message& receivedMessage(){return this->m_receivedMessage;}

                /**
                 * @brief setCallBackError
                 *             设置读写错误是的回调函数
                 * @param errorHandler
                 *              读写错误时的回调函数
                 */
                void setErrorHandler(AsioCommon::RWErrorHandler errorHandler){ this->m_errorHandler = errorHandler; }

                /**
                 * @brief setReceivedHandler
                 *             设置接受到数据的处理Handler
                 * @param receivedHandler
                 *              数据接受到的回调函数
                 */
                void setReceivedHandler(AsioCommon::ReceivedHandler receivedHandler){ this->m_receivedHandler =  receivedHandler;}

                //<<<----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

                //>>>----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                //send & receive function

               /*
                 *注意：
                 *          1.异步发送没有经过严格的测试，目前不建议使用
                 *          2.异步发送比同步发送更复杂，不稳定，所以优先使用同步发送
                 *          3.接受时涉及到同时有多个数据的接收，为了不阻塞，优先使用异步接收
                 */

                /**
                 * @brief sendSync
                 *             同步发送
                 * @param pData
                 *             需要发送的数据指针
                 * @param len
                 *             需要发送的数据对象
                 */
                void sendSync(const  char* pData, int64_t len);
                void sendSync(const std::vector<char>& toSendedData);

                /**
                 * @brief receiveSync
                 *             同步接受
                 */
                void receiveSync();
                /**
                 * @brief receiveAsync
                 *              异步接收
                 */
                void receiveAsync();

                void sendAsync(const std:: vector<char>& toSendedData);
                /**
                 * @brief sendAsync
                 *             异步发送数据
                 * @param pData
                 *              数据对象指针
                 * @param len
                 *              发送的数据长度
                 */
                void sendAsync(const  char* pData, int64_t len);
                void sendAsync();

                /**
                 * @brief close
                 *             关闭当前的RWHandler对象
                 */
                void close();

                //<<<----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

                //>>>----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                //handle function

                void handleError(const boost::system::error_code& error);

                //<<<----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

            private:

                //>>>----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                //member variant

                /**
                 * @brief m_socket
                 *          连接的套接字对象
                 */
                boost::asio::ip::tcp::socket m_socket;

                /**
                 * @brief m_Id
                 *          连接的ID号
                 */
                uint m_Id;

                /**
                 * @brief m_callbackError
                 *             错误的回调函数
                 */
                AsioCommon::RWErrorHandler m_errorHandler;

                /**
                 * @brief m_receivedHandler
                 *             获取数据后执行的回调函数, 通常用于上层调用的数据处理接口
                 */
                AsioCommon::ReceivedHandler m_receivedHandler;

                /**
                 * @brief m_sendedMessage
                 *          这个Message对应接受到的信息，目前只支持char类型，封装在Message中，在进行通讯之间需要用DataConveter进行类型装换
                 */
                Message m_receivedMessage;

                /**
                 * @brief m_toSendedMessage
                 *             有待发送的Message，目前只支持char类型，封装在Message中，在进行通讯之间需要用DataConveter进行类型装换
                 */
                Message m_toSendedMessage;

                /**
                 * @brief m_toSendMessageQueue
                 *          有待发送的消息队列
                 * 异步发送的复杂性在于：
                 *
                 *          不能连续调用异步发送接口async_write，因为async_write内部是不断调用async_write_some，
                 * 直到所有的数据发送完成为止.
                 *
                 *          有可能这时第一次的async_write还在循环调用async_write_some发送，而第二次的async_write要发送的数据很小，
                 * 一下子就发出去了，这使得第一次发送的数据和第二次发送的数据交织在一起了，导致发送乱序的问题.
                 *
                 *          这个问题的方法就是在第一次发送完成之后再发送第二次的数据。具体的做法是用一个发送缓冲区，
                 * 在异步发送完成之后从缓冲区再取下一个数据包发送
                 */
                std::list<Message> m_toSendedMessageQueue;

                //<<<----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
            };//End of class RWHandler
        }//End of namespace TCP
    }//End of namespace Asio
}//End of namespace SSDK

#endif // RWHANDLER_HPP


text:
//======================================================================
/*! \file TcpReceiveThreadEnsemble.cpp
 *
 * \copydoc Copyright
 * \author Jan Christian Dittmer (jcd)
 * \date May 5, 2017
 *///-------------------------------------------------------------------

//======================================================================

#include <ibeosdk/Log.hpp>

#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/scoped_ptr.hpp>

#include <ibeosdk/devices/TcpReceiveThreadEnsemble.hpp>
#include <ibeosdk/devices/IbeoDeviceBase.hpp>
#include <ibeosdk/MsgBuffer.hpp>
#include <ibeosdk/EventMonitor.hpp>

//======================================================================

namespace ibeosdk {

//======================================================================

TcpReceiveThreadEnsemble::TcpReceiveThreadEnsemble(const boost::asio::ip::address& ip,
                                                   const unsigned short port,
                                                   ReceiveDataBlock receiveDataBlock)
  : m_ioService(),
    m_endPoint(ip, port),
    m_socket(),
    m_expiryTime(),
    m_receiveDataBlock(receiveDataBlock),
    m_mutex(),
    m_threadStateCondition(),
    m_thread(NULL),
    m_threadState(TS_NotRunning),
    m_rMutex(),
    m_replyPtr(NULL),
    m_expectedReplyId(CommandId(CommandId::CmdLuxReset)),
    m_eventMonitor(),
    m_eventReplyReceived(m_eventMonitor.newEvent()),
    m_sizeOfPrevCommand(0)
{}

//======================================================================

TcpReceiveThreadEnsemble::~TcpReceiveThreadEnsemble()
{

	try {
		Lock lock(m_mutex);
		if (m_threadState == TS_Running || m_threadState == TS_Starting) {
			// Thread has not yet been stopped.
			// Ask for termination and wait till a state change
			// has been performed.
			m_threadState = TS_Stopping;
			m_threadStateCondition.wait(lock);
		}
	}
	catch (...) {
		logError << "failed 2" << std::endl;
	}

	if (this->m_thread->joinable()) {
		this->m_thread->join();
		delete this->m_thread;
		this->m_thread = NULL;
	}
}

//======================================================================

boost::optional<boost::asio::deadline_timer::duration_type> TcpReceiveThreadEnsemble::getExpiryTime()
{
	return m_expiryTime;
}

//======================================================================

void TcpReceiveThreadEnsemble::setExpiryTime(const boost::asio::deadline_timer::duration_type expiryTime)
{
	Lock lock(m_mutex);
	m_expiryTime.reset(expiryTime);
	logDebug2 << "Set new ExpiryTime" << std::endl;
}

//======================================================================

void TcpReceiveThreadEnsemble::resetExpiryTime()
{
	Lock lock(m_mutex);
	m_expiryTime.reset();
}

//======================================================================

void TcpReceiveThreadEnsemble::run()
{
	if (m_thread)
		return;

	try {
		Lock lock(m_mutex);

		m_thread = new boost::thread(&TcpReceiveThreadEnsemble::recvThreadMain, this);
		while ((this->m_threadState==TS_NotRunning || this->m_threadState==TS_Starting)) {
			m_threadStateCondition.wait(lock);
		}
	}
	catch (const boost::lock_error& e) {
		logError << e.what() << std::endl;
	}

}

//======================================================================

bool TcpReceiveThreadEnsemble::powerUpConnection()
{

	if (!m_receiveDataBlock) {
		logError << "Failed to start TCP/IP receive thread. No onData method has be specified." << std::endl;
		return false;
	}

	try {
		Lock lock(m_mutex);
		m_threadState = TS_Starting;
		m_threadStateCondition.notify_all();
	}
	catch (const boost::lock_error& e) {
		logError << e.what() << std::endl;
		return false;
	}

	try {
		m_socket.reset(new boost::asio::ip::tcp::socket(m_ioService));
	}
	catch (const std::exception& e) {
		logError << "Failed to allocate socket. - " << e.what() << std::endl;
		Lock lock(m_mutex);
		m_threadState = TS_StartFailed;
		m_threadStateCondition.notify_all();
		return false;
	}

	boost::system::error_code ec;
	m_socket->connect(m_endPoint, ec);

	if (ec) {
		logError << "Failed to connect to device (" << m_endPoint << ") " << ec.message() << std::endl;
		Lock lock(m_mutex);
		m_threadState = TS_StartFailed;
		m_threadStateCondition.notify_all();
		return false;
	}

	logDebug << "Connected to device on " << m_endPoint << std::endl;

	try {
		boost::asio::ip::tcp::no_delay option(true);
		m_socket->set_option(option);
	}
	catch (const std::exception& e) {
		logError << "Failed to allocate socket. - "	<< e.what() << std::endl;
		Lock lock(m_mutex);
		m_threadState = TS_StartFailed;
		m_threadStateCondition.notify_all();
		return false;
	}

	Lock lock(m_mutex);
	m_threadState = TS_Running;
	m_threadStateCondition.notify_all();

	return true;
}

//======================================================================

void TcpReceiveThreadEnsemble::recvThreadMain()
{
	const bool powerUpOk= powerUpConnection();
	boost::system::error_code error;
	const char* bodyBuf;
	const IbeoDataHeader* dh;
	ibeosdk::MsgBuffer msgBuf(m_socket.get(), bufferSize);

	try {
		Lock lock(m_mutex);
		m_threadState = powerUpOk ? TS_Running : TS_RunFailed;
		m_threadStateCondition.notify_all();
	}
	catch (const boost::lock_error& e) {
		logError << e.what() << std::endl;
		return;
	}

	if (!powerUpOk) {
		return;
	}

	boost::optional<boost::asio::deadline_timer::duration_type> expiryTime;

	try {
		while (this->m_threadState != TS_Stopping && m_socket->is_open()) {

			{
				Lock lock(m_mutex);
				expiryTime = m_expiryTime;
			}

			if (msgBuf.recvMsg(dh, bodyBuf, expiryTime)) {
				logDebug << "Received DataBlock of type " << toHex(dh->getDataType()) << std::dec << std::endl;

				if ((dh->getDataType() == DataTypeId::DataType_Reply)) {
					onReceiveCommandReply(*dh, bodyBuf);
					continue; // do not inform any streamer or listener
				}

				m_receiveDataBlock(dh, bodyBuf);
			}
		}
	}
	catch (const boost::system::error_code& errorCode) {
		logError << "In receive loop: " << errorCode.message() << std::endl;
		m_threadState = TS_RunFailed;
		m_threadStateCondition.notify_all();
		return;
	}

	if (!m_socket->is_open() && this->m_threadState != TS_Stopping) {
		try {
			Lock lock(this->m_mutex);
			m_threadState = TS_RunFailed;
			m_threadStateCondition.notify_all();
		}
		catch (const boost::lock_error& e) {
			logError << e.what() << std::endl;
		}

		return;
	}

	m_threadState = TS_NotRunning;
	m_threadStateCondition.notify_all();
}

//======================================================================

void TcpReceiveThreadEnsemble::disconnect() {
	if (m_socket.get() != NULL) {
		m_socket->close();
		m_socket.reset();
	}
}

//======================================================================

void TcpReceiveThreadEnsemble::stop()
{
	if (isRunning(false)) {
		try {
			Lock lock(this->m_mutex);
			m_threadState = TS_Stopping;
		}
		catch (const boost::lock_error& e) {
			logError << e.what() << std::endl;
		}
	}
	disconnect();
}

//======================================================================

bool TcpReceiveThreadEnsemble::isRunning(const bool useMutex)
{
	ThreadState state = TS_NotRunning;
	if (useMutex) {
		try {
			Lock lock(this->m_mutex);
			state = m_threadState;
		}
		catch (const boost::lock_error& e) {
			logError << e.what() << std::endl;
		}
	}
	else {
		state = m_threadState;
	}

	if (state == TS_Starting || state == TS_Running) {
		return true;
	}
	return false;
}

//======================================================================

bool TcpReceiveThreadEnsemble::isConnected()
{
	if (m_socket == NULL)
		return false;

	if (!m_socket->is_open())
		return false;

	return true;
}

//======================================================================

bool TcpReceiveThreadEnsemble::isNotYetRunning(const bool useMutex)
{
	ThreadState state = TS_NotRunning;
	if (useMutex) {
		try {
			Lock lock(this->m_mutex);
			state = m_threadState;
		}
		catch (const boost::lock_error& e) {
			logError << e.what() << std::endl;
		}
	}
	else {
		state = m_threadState;
	}

	if (state == TS_NotRunning || state == TS_Starting) {
		return true;
	}
	return false;
}

//======================================================================

statuscodes::Codes TcpReceiveThreadEnsemble::sendCommand(const CommandBase& cmd)
{
	if (!isConnected()) {
		// cannot send a command if the device is not connected.
		logError << " command (id " << toHex(cmd.getCommandId()) << ") failed " << statuscodes::NotConnected << std::endl;
		return statuscodes::NotConnected;
	}

	// Send command
	logDebug2 << "Sending " << " command (id " << toHex(cmd.getCommandId()) << ") " << "; expecting no reply" << std::endl;

	const statuscodes::Codes errorCode = sendCommandInternal(cmd);
	logDebug2 << "Done. " << errorCode << std::endl;

	return errorCode;
}

//======================================================================

statuscodes::Codes TcpReceiveThreadEnsemble::sendCommand(const CommandBase& cmd,
                                                         CommandReplyBase& reply,
                                                         const boost::posix_time::time_duration timeOut)
{
	if (!isConnected()) {
		// cannot send a command if the device is not connected.
		logError << " command (id " << toHex(cmd.getCommandId()) << ") failed  Reply: "
				<< toHex(reply.getCommandId()) << " "
				<< statuscodes::NotConnected << std::endl;
		return statuscodes::NotConnected;
	}

	if (cmd.getCommandId() != reply.getCommandId()) {
		logError << " command (id " << toHex(cmd.getCommandId()) << ") failed  Reply: "
				<< toHex(reply.getCommandId()) << " "
				<< statuscodes::MismatchingCommandAndReply << std::endl;
		return statuscodes::MismatchingCommandAndReply;
	}

	{
		// Pass reply buffer to onReceiveCommandReply() which is running
		// in the reception thread
		boost::recursive_timed_mutex::scoped_lock criticalSection(m_rMutex);
		assert (!m_replyPtr); // previous reply must have been completely processed
		m_replyPtr = &reply;
		m_expectedReplyId = cmd.getCommandId();
	}

	statuscodes::Codes errorCode = statuscodes::EverythingOk;
	try {
		// Send command
		logDebug2 << "Sending " << " command (id " << toHex(cmd.getCommandId())
				<< ")" << std::endl;

		if (sendCommandInternal(cmd) != statuscodes::EverythingOk)
			throw statuscodes::SendingCommandFailed;

		// Wait for reply. The event m_eventReplyReceived will be signaled by
		EventMonitor::Mask event = m_eventMonitor.wait(timeOut, m_eventReplyReceived);

		if (event == EventMonitor::TimeOutEvent)
			throw statuscodes::TimeOut;
		else if (   (cmd.getCommandId() | CommandReplyBase::errorMask)
		         != (reply.getCommandId() | CommandReplyBase::errorMask))
			throw statuscodes::ReplyMismatch;
		else if (reply.isErrorReply())
			throw statuscodes::ReceiveCommandErrorReply;
	} // try
	catch (const statuscodes::Codes& ec) {
		errorCode = ec;
		logError << " command (id " << toHex(cmd.getCommandId())
				<< ") failed  Reply: " << toHex(reply.getCommandId())
				<< " " << ec << std::endl;
	} // catch

	{
		// Clear reply buffer for reception thread
		boost::recursive_timed_mutex::scoped_lock criticalSection(m_rMutex);
		assert (m_replyPtr);
		m_replyPtr = NULL;
		m_expectedReplyId = CommandId(0x7fff);
	}

	return errorCode;
}

//======================================================================

statuscodes::Codes TcpReceiveThreadEnsemble::sendCommandInternal(const CommandBase& cmd)
{
	boost::posix_time::ptime timeout
	= boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(50);
	boost::recursive_timed_mutex::scoped_timed_lock criticalSection(m_rMutex, timeout);

	if (criticalSection) { // if we have got thread-safe access in time
		if (isConnected()) {
			const uint32_t cmdLength = uint32_t(cmd.getCommandLength());
			const uint32_t sizeOfThisMsg = cmdLength;
			const uint32_t sizeOfBuffer = sizeOfThisMsg + IbeoDataHeader::getHeaderSize();

			boost::scoped_ptr<char> sendBuf(new char[sizeOfBuffer]);
			boost::iostreams::stream<boost::iostreams::array> strm(sendBuf.get(), sizeOfBuffer);
			std::ostream& s = (std::ostream&)strm;

			const IbeoDataHeader header(DataTypeId::DataType_Command,
			                            m_sizeOfPrevCommand,
			                            sizeOfThisMsg,
			                            1,
			                            NTPTime());

			logDebug2 << "Sending command with " <<  sizeOfThisMsg << " bytes"<< std::endl;

			header.serialize(s);
			cmd.serializeToStream(s);
			if (!s.good())
				return statuscodes::FailedToPrepareSendingCommand;

			const size_t nbOfBytesSend = m_socket->send(boost::asio::buffer(sendBuf.get(), sizeOfBuffer));
			if (nbOfBytesSend != size_t(sizeOfBuffer)) {
				logError << "Send " << nbOfBytesSend << " expected: " << sizeOfBuffer << std::endl;

				return statuscodes::SendingCommandFailed;
			}

			m_sizeOfPrevCommand = uint32_t(sizeOfThisMsg);

			return statuscodes::EverythingOk;
		}
		return statuscodes::NotConnected;
	}

	logError << "Timeout while locking critical section" << std::endl;
	return statuscodes::TimeOutCriticalSection;
}

//======================================================================

void TcpReceiveThreadEnsemble::onReceiveCommandReply(const IbeoDataHeader& dh, const char* bodyBuf)
{
	{
		const uint8_t* bb = reinterpret_cast<const uint8_t*>(bodyBuf);
		logDebug << "INCOMMING REPLY: " << toHex(uint16_t(*(bb+1) + ((*(bb))<<8)))
				<< "  Size: " << dh.getMessageSize() << std::endl;
	}

	if (m_replyPtr != NULL) {
		boost::iostreams::stream<boost::iostreams::array_source> strm(bodyBuf, dh.getMessageSize());
		std::istream& s = (std::istream&)strm;
		if (m_replyPtr->deserializeFromStream(s, dh)) {
			logDebug2 << "Received Reply -- good" << std::endl;
		}
		else {
			logDebug2 << "Received Reply -- failed" << std::endl;
			if (m_replyPtr)
				m_replyPtr->setErrorReply();
		}

		// Wake up main thread waiting in command()
		m_eventMonitor.signal(m_eventReplyReceived);
	}
	else {
		logDebug2 << "INCOMMING REPLY UNEXPECTED " << std::endl;
	}
}

//======================================================================

} // namespace ibeosdk

//======================================================================


text:
#include "SystemData.h"
#include "Gamelist.h"
#include "AudioManager.h"
#include "VolumeControl.h"
#include "Log.h"
#include "Settings.h"
#include "Util.h"
#include <boost/thread.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/make_shared.hpp>
#include <RHGamestationConf.h>

std::vector<SystemData *> SystemData::sSystemVector;

namespace fs = boost::filesystem;
namespace pt = boost::property_tree;

SystemData::SystemData(const std::string &name, const std::string &fullName, const std::string &startPath,
                       const std::vector<std::string> &extensions, const std::string &command,
                       const std::vector<PlatformIds::PlatformId> &platformIds, const std::string &themeFolder,
                       std::map<std::string, std::vector<std::string> *> *emulators)
  : mName(name),
    mFullName(fullName),
    mStartPath(getExpandedPath(startPath)),
    mLaunchCommand(command),
    mThemeFolder(themeFolder),
    mEmulators(emulators),
    mSearchExtensions(extensions),
    mPlatformIds(platformIds)
{
  mSortId = RHGamestationConf::getInstance()->getUInt(mName + ".sort");
  // make it absolute if needed
  {
    const std::string defaultRomsPath = getExpandedPath(Settings::getInstance()->getString("DefaultRomsPath"));

    if (!defaultRomsPath.empty())
    {
      mStartPath = fs::absolute(mStartPath, defaultRomsPath).generic_string();
    }
  }

  mRootFolder = new FileData(FOLDER, mStartPath, this);
  mRootFolder->metadata.set("name", mFullName);

  if (RHGamestationConf::getInstance()->get("emulationstation.gamelistonly") != "1")
    populateFolder(mRootFolder);

  if (!Settings::getInstance()->getBool("IgnoreGamelist"))
    parseGamelist(this);

  mIsFavorite = false;
  loadTheme();
}

SystemData::SystemData(const std::string &name, const std::string &fullName, const std::string &command,
                       const std::string &themeFolder, std::vector<SystemData *> *systems)
  : mName(name),
    mFullName(fullName),
    mStartPath(),
    mLaunchCommand(command),
    mThemeFolder(themeFolder),
    mEmulators(nullptr)
{
  mSortId = RHGamestationConf::getInstance()->getUInt(mName + ".sort");
  mRootFolder = new FileData(FOLDER, mStartPath, this);
  mRootFolder->metadata.set("name", mFullName);

  for (auto system : *systems)
  {
    std::vector<FileData *> favorites = system->getFavorites();
    for (auto favorite : favorites)
      mRootFolder->addAlreadyExistingChild(favorite);
  }

  mIsFavorite = true;
  mPlatformIds.push_back(PlatformIds::PLATFORM_IGNORE);
  loadTheme();
}

SystemData::~SystemData()
{
  //save changed game data back to xml
  if (!Settings::getInstance()->getBool("IgnoreGamelist"))
  {
    updateGamelist(this);
  }
  if (mRootFolder)
  {
    auto children = mRootFolder->getChildren();
    for (auto it : children)
    {

      mRootFolder->removeChild(it);
      delete it;
    }
    delete mRootFolder;
  }
  if (mEmulators)
  {
    for (const auto &itr : *mEmulators)
    {
      delete (itr.second);
    }
    mEmulators->clear();

    delete mEmulators;
  }
}

std::string strreplace(std::string str, const std::string &replace, const std::string &with)
{
  size_t pos;
  while ((pos = str.find(replace)) != std::string::npos)
    str = str.replace(pos, replace.length(), with.c_str(), with.length());

  return str;
}

// plaform-specific escape path function
// on windows: just puts the path in quotes
// everything else: assume bash and escape special characters with backslashes
std::string escapePath(const boost::filesystem::path &path)
{
#ifdef WIN32
  // windows escapes stuff by just putting everything in quotes
  return '"' + fs::path(path).make_preferred().string() + '"';
#else
  // a quick and dirty way to insert a backslash before most characters that would mess up a bash path
  std::string pathStr = path.string();

  const char *invalidChars = " '\"\\!$^&*(){}[]?;<>";
  for (unsigned int i = 0; i < pathStr.length(); i++)
  {
    char c;
    unsigned int charNum = 0;
    do
    {
      c = invalidChars[charNum];
      if (pathStr[i] == c)
      {
        pathStr.insert(i, "\\");
        i++;
        break;
      }
      charNum++;
    } while (c != '\0');
  }

  return pathStr;
#endif
}

void SystemData::launchGame(Window* window, FileData* game, std::string netplay, std::string core, std::string ip, std::string port)
{
  LOG(LogInfo) << "Attempting to launch game...";

  AudioManager::getInstance()->deinit();
  VolumeControl::getInstance()->deinit();

  std::string controlersConfig = InputManager::getInstance()->configureEmulators();
  LOG(LogInfo) << "Controllers config : " << controlersConfig;
  window->deinit();

  std::string command = mLaunchCommand;

  const std::string rom = escapePath(game->getPath());
  const std::string basename = game->getPath().stem().string();
  const std::string rom_raw = fs::path(game->getPath()).make_preferred().string();

  command = strreplace(command, "%ROM%", rom);
  command = strreplace(command, "%CONTROLLERSCONFIG%", controlersConfig);
  command = strreplace(command, "%SYSTEM%", game->metadata.get("system"));
  command = strreplace(command, "%BASENAME%", basename);
  command = strreplace(command, "%ROM_RAW%", rom_raw);
  command = strreplace(command, "%EMULATOR%", game->metadata.get("emulator"));
  command = strreplace(command, "%CORE%", game->metadata.get("core"));
  command = strreplace(command, "%RATIO%", game->metadata.get("ratio"));

	command = strreplace(command, "%ROM%", rom);
	command = strreplace(command, "%CONTROLLERSCONFIG%", controlersConfig);
	command = strreplace(command, "%SYSTEM%", game->metadata.get("system"));
	command = strreplace(command, "%BASENAME%", basename);
	command = strreplace(command, "%ROM_RAW%", rom_raw);
	if (core != "")
	{
		command = strreplace(command, "%EMULATOR%", "libretro");
		command = strreplace(command, "%CORE%", core);
	}
	else
	{
		command = strreplace(command, "%EMULATOR%", game->metadata.get("emulator"));
		command = strreplace(command, "%CORE%", game->metadata.get("core"));
	}
	command = strreplace(command, "%RATIO%", game->metadata.get("ratio"));

	if (netplay == "client")
	{
		command = strreplace(command, "%NETPLAY%", "-netplay client -netplay_port " + port + " -netplay_ip " + ip);
	}
	else if (netplay == "host")
	{
	    std::string hash = game->metadata.get("hash");
	    std::string hashcmd = "";
	    if (hash != "")
	    {
	        hashcmd = " -hash " + hash;
	    }
		command = strreplace(command, "%NETPLAY%", "-netplay host -netplay_port " + RHGamestationConf::getInstance()->get("global.netplay.port") + hashcmd);
	}
	else
	{
		command = strreplace(command, "%NETPLAY%", "");
	}

	LOG(LogInfo) << "	" << command;
	std::cout << "==============================================\n";
	int exitCode = runSystemCommand(command);
	std::cout << "==============================================\n";

  if( exitCode != 0)
  {
    LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!";
  }

  // Reinit
  window->init();
  VolumeControl::getInstance()->init();
  AudioManager::getInstance()->resumeMusic();
  window->normalizeNextUpdate();

  //update number of times the game has been launched
  int timesPlayed = game->metadata.getInt("playcount") + 1;
  game->metadata.set("playcount", std::to_string(static_cast<long long>(timesPlayed)));

  //update last played time
  boost::posix_time::ptime time = boost::posix_time::second_clock::universal_time();
  game->metadata.setTime("lastplayed", time);
}

void SystemData::populateFolder(FileData *folder)
{
  FileData::populateRecursiveFolder(folder, mSearchExtensions, this);
}

std::vector<std::string> readList(const std::string &str, const char *delims = " \t\r\n,")
{
  std::vector<std::string> ret;

  size_t prevOff = str.find_first_not_of(delims, 0);
  size_t off = str.find_first_of(delims, prevOff);
  while (off != std::string::npos || prevOff != std::string::npos)
  {
    ret.push_back(str.substr(prevOff, off - prevOff));

    prevOff = str.find_first_not_of(delims, off);
    off = str.find_first_of(delims, prevOff);
  }

  return ret;
}

SystemData *createSystem(const SystemData::Tree &system)
{
  std::string name, fullname, path, cmd, themeFolder;
  //PlatformIds::PlatformId platformId = PlatformIds::PLATFORM_UNKNOWN;

  name = system.get("name", "");
  fullname = system.get("fullname", "");
  path = system.get("path", "");

  // convert extensions list from a string into a vector of strings
  std::vector<std::string> extensions = readList(system.get("extension", ""));

  cmd = system.get("command", "");

  // platform id list
  const std::string platformList = system.get("platform", "");
  std::vector<std::string> platformStrs = readList(platformList);
  std::vector<PlatformIds::PlatformId> platformIds;
  for (const auto &it : platformStrs)
  {
    const char *str = it.c_str();
    PlatformIds::PlatformId platformId = PlatformIds::getPlatformId(str);

    if (platformId == PlatformIds::PLATFORM_IGNORE)
    {
      // when platform is ignore, do not allow other platforms
      platformIds.clear();
      platformIds.push_back(platformId);
      break;
    }

    // if there appears to be an actual platform ID supplied but it didn't match the list, warn
    if (str != nullptr && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN)
      LOG(LogWarning) << "  Unknown platform for system \"" << name << "\" (platform \"" << str << "\" from list \"" << platformList << "\")";
    else if (platformId != PlatformIds::PLATFORM_UNKNOWN)
      platformIds.push_back(platformId);
  }

  // theme folder
  themeFolder = system.get("theme", "");

  //validate
  if (name.empty() || path.empty() || extensions.empty() || cmd.empty())
  {
    LOG(LogError) << "System \"" << name << "\" is missing name, path, extension, or command!";
    return nullptr;
  }

  //convert path to generic directory seperators
  boost::filesystem::path genericPath(path);
  path = genericPath.generic_string();

  // emulators and cores
  auto systemEmulators = new std::map<std::string, std::vector<std::string> *>();
  //SystemData::Tree emulatorsNode = system.get("emulators", "");
  for (const SystemData::TreeNode &emuNode : system.get_child("emulators"))
  {
    const std::string &emulatorName = emuNode.second.get_child("<xmlattr>").get("name", "");
    (*systemEmulators)[emulatorName] = new std::vector<std::string>();
    for (const SystemData::TreeNode &coreNode : emuNode.second.get_child("cores"))
    {
      const std::string &corename = coreNode.second.data();
      (*systemEmulators)[emulatorName]->push_back(corename);
    }
  }

  auto newSys = new SystemData(name,
                               fullname,
                               path, extensions,
                               cmd, platformIds,
                               themeFolder,
                               systemEmulators);
  if (newSys->getRootFolder()->getDisplayableRecursive(GAME | FOLDER).empty())
  {
    LOG(LogWarning) << "System \"" << name << "\" has no games! Ignoring it.";
    delete newSys;
    return nullptr;
  } else
  {
    LOG(LogWarning) << "Adding \"" << name << "\" in system list.";
    return newSys;
  }
}

bool SystemData::loadSystemNodes(XmlNodeCollisionMap &collisionMap, XmlNodeList &nodeStore, const Tree &document)
{
  deleteSystems();
  bool result = false;
  for (const TreeNode &systemNode : document.get_child("systemList"))
  {
    // At least one node found
    result = true;

    // Get system
    const Tree &system = systemNode.second;

    // Composite key: short name + platform
    std::string key = system.get("name", "");
    key += system.get("platform", "");
    LOG(LogWarning) << "Key: " << key;

    // Already exist in the store ?
    int index = (collisionMap.find(key) != collisionMap.end()) ? collisionMap[key] : -1;

    // If the system does not exists, add it to the end of list.
    // Replace the existing entry otherwise.
    // This way, we keep the original ordering of first-in entries
    if (index < 0)
    {
      collisionMap[key] = (int) nodeStore.size();
      nodeStore.push_back(system);
    } else nodeStore[index] = system;
  }

  if (!result)
    LOG(LogError) << "missing or empty <systemList> tag!";

  return result;
}

bool SystemData::loadXmlFile(Tree &document, const std::string &filePath)
{
  try
  {
    pt::read_xml(filePath, document);
  }
  catch (std::exception &e)
  {
    LOG(LogError) << "Could not parse " << filePath << " file!";
    LOG(LogError) << e.what();
    return false;
  }
  return true;
}

bool SystemData::loadSystemList(Tree &document, XmlNodeCollisionMap &collisionMap, XmlNodeList &nodeStore, const std::string &filePath)
{
  // Load user configuration
  if (!fs::exists(filePath))
  {
    LOG(LogError) << filePath << " file does not exist!";
    return false;
  }

  LOG(LogInfo) << "Loading system config files " << filePath << "...";
  if (!loadXmlFile(document, filePath)) return false;

  bool result = loadSystemNodes(collisionMap, nodeStore, document);
  if (!result) LOG(LogWarning) << filePath << " has no systems or systemList nodes";

  return result;
}

//creates systems from information located in a config file
bool SystemData::loadConfig()
{
  deleteSystems();

  // System store
  XmlNodeCollisionMap systemMap;    // System key to vector index
  XmlNodeList systemList;  // Sorted storage, keeping original system node ordering
  // XML Documents - declared here to keep node memory allocated
  pt::ptree templateDocument;
  pt::ptree userDocument;

  // Load user systems
  bool userValid = loadSystemList(userDocument, systemMap, systemList, getUserConfigPath());
  // Load template systems
  bool templateValid = loadSystemList(templateDocument, systemMap, systemList, getTemplateConfigPath());

  // Is there at least
  if (!templateValid && !userValid)
  {
    LOG(LogError) << "No es_systems.cfg file available!";
    writeExampleConfig(getUserConfigPath());
    return false;
  }

  // THE CREATION OF EACH SYSTEM
  boost::asio::io_service ioService;
  boost::thread_group threadpool;
  boost::asio::io_service::work work(ioService);
  for (int i = 4; --i >= 0;)
    threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioService));

  // Iterate over the map using Iterator till end.
  std::vector<boost::unique_future<SystemData *>> pending_data;
  for (const Tree &system : systemList)
  {
    LOG(LogInfo) << "creating thread for system " << system.get("name", "???");
    typedef boost::packaged_task<SystemData *> task_t;
    boost::shared_ptr<task_t> task = boost::make_shared<task_t>(
      boost::bind(&createSystem, system));
    boost::unique_future<SystemData *> fut = task->get_future();
    pending_data.push_back(std::move(fut));
    ioService.post(boost::bind(&task_t::operator(), task));
  }
  boost::wait_for_all(pending_data.begin(), pending_data.end());

  for (auto &pending : pending_data)
  {
    SystemData *result = pending.get();
    if (result != nullptr)
      sSystemVector.push_back(result);
  }
  ioService.stop();
  threadpool.join_all();

  if (sSystemVector.empty()) return true;
  // Favorite system
  for (const Tree &system : systemList)
  {
    std::string name = system.get("name", "");
    std::string fullname = system.get("fullname", "");
    std::string cmd = system.get("command", "");
    std::string themeFolder = system.get("theme", "");

    if (name == "favorites")
    {
      LOG(LogInfo) << "creating favorite system";
      SystemData *newSys = new SystemData("favorites", fullname, cmd, themeFolder, &sSystemVector);
      sSystemVector.push_back(newSys);
    }
  }

  return true;
}

void SystemData::writeExampleConfig(const std::string &path)
{
  std::ofstream file(path.c_str());

  file << "<!-- This is the EmulationStation Systems configuration file.\n"
          "All systems must be contained within the <systemList> tag.-->\n"
          "\n"
          "<systemList>\n"
          "    <!-- Here's an example system to get you started. -->\n"
          "    <system>\n"
          "\n"
          "        <!-- A short name, used internally. Traditionally lower-case. -->\n"
          "        <name>nes</name>\n"
          "\n"
          "        <!-- A \"pretty\" name, displayed in menus and such. -->\n"
          "        <fullname>Nintendo Entertainment System</fullname>\n"
          "\n"
          "        <!-- The path to start searching for ROMs in. '~' will be expanded to $HOME on Linux or %HOMEPATH% on Windows. -->\n"
          "        <path>~/roms/nes</path>\n"
          "\n"
          "        <!-- A list of extensions to search for, delimited by any of the whitespace characters (\", \\r\\n\\t\").\n"
          "        You MUST include the period at the start of the extension! It's also case sensitive. -->\n"
          "        <extension>.nes .NES</extension>\n"
          "\n"
          "        <!-- The shell command executed when a game is selected. A few special tags are replaced if found in a command:\n"
          "        %ROM% is replaced by a bash-special-character-escaped absolute path to the ROM.\n"
          "        %BASENAME% is replaced by the \"base\" name of the ROM.  For example, \"/foo/bar.rom\" would have a basename of \"bar\". Useful for MAME.\n"
          "        %ROM_RAW% is the raw, unescaped path to the ROM. -->\n"
          "        <command>retroarch -L ~/cores/libretro-fceumm.so %ROM%</command>\n"
          "\n"
          "        <!-- The platform to use when scraping. You can see the full list of accepted platforms in src/PlatformIds.cpp.\n"
          "        It's case sensitive, but everything is lowercase. This tag is optional.\n"
          "        You can use multiple platforms too, delimited with any of the whitespace characters (\", \\r\\n\\t\"), eg: \"genesis, megadrive\" -->\n"
          "        <platform>nes</platform>\n"
          "\n"
          "        <!-- The theme to load from the current theme set.  See THEMES.md for more information.\n"
          "        This tag is optional. If not set, it will default to the value of <name>. -->\n"
          "        <theme>nes</theme>\n"
          "    </system>\n"
          "</systemList>\n";

  file.close();

  LOG(LogError) << "Example config written!  Go read it at \"" << path << "\"!";
}

bool deleteSystem(SystemData *system)
{
  delete system;
}

void SystemData::deleteSystems()
{
  if (!sSystemVector.empty())
  {
    // THE DELETION OF EACH SYSTEM
    boost::asio::io_service ioService;
    boost::thread_group threadpool;
    boost::asio::io_service::work work(ioService);
    //std::vector<boost::thread *> threads;
    for (int i = 0; i < 4; i++)
    {
      threadpool.create_thread(
        boost::bind(&boost::asio::io_service::run, &ioService)
      );
    }

    std::vector<boost::unique_future<bool>> pending_data;
    for (SystemData *system : sSystemVector)
    {
      typedef boost::packaged_task<bool> task_t;
      boost::shared_ptr<task_t> task = boost::make_shared<task_t>(
        boost::bind(&deleteSystem, system));
      boost::unique_future<bool> fut = task->get_future();
      pending_data.push_back(std::move(fut));
      ioService.post(boost::bind(&task_t::operator(), task));
    }

    boost::wait_for_all(pending_data.begin(), pending_data.end());

    ioService.stop();
    threadpool.join_all();
    sSystemVector.clear();
  }
}

std::string SystemData::getUserConfigPath()
{
  fs::path path = getHomePath() + mUserConfigurationRelativePath;
  return path.generic_string();
}

std::string SystemData::getTemplateConfigPath()
{
  return mTemplateConfigurationAbsolutePath;
}

std::string SystemData::getGamelistPath(bool forWrite) const
{
  fs::path filePath;

  // If we have a gamelist in the rom directory, we use it
  filePath = mRootFolder->getPath() / "gamelist.xml";
  if (fs::exists(filePath))
    return filePath.generic_string();

  // else we try to create it
  if (forWrite)
  { // make sure the directory exists if we're going to write to it, or crashes will happen
    if (fs::exists(filePath.parent_path()) || fs::create_directories(filePath.parent_path()))
    {
      return filePath.generic_string();
    }
  }
  // Unable to get or create directory in roms, fallback on ~
  filePath = getHomePath() + "/.emulationstation/gamelists/" + mName + "/gamelist.xml";
  fs::create_directories(filePath.parent_path());
  return filePath.generic_string();
}

std::string SystemData::getThemePath() const
{
  // where we check for themes, in order:
  // 1. [SYSTEM_PATH]/theme.xml
  // 2. system theme from currently selected theme set [CURRENT_THEME_PATH]/[SYSTEM]/theme.xml
  // 3. default system theme from currently selected theme set [CURRENT_THEME_PATH]/theme.xml

  // first, check game folder
  fs::path localThemePath = mRootFolder->getPath() / "theme.xml";
  if (fs::exists(localThemePath))
    return localThemePath.generic_string();

  // not in game folder, try system theme in theme sets
  localThemePath = ThemeData::getThemeFromCurrentSet(mThemeFolder);

  if (fs::exists(localThemePath))
    return localThemePath.generic_string();

  // not system theme, try default system theme in theme set
  localThemePath = localThemePath.parent_path().parent_path() / "theme.xml";

  return localThemePath.generic_string();
}

unsigned int SystemData::getGameCount() const
{
  return (unsigned int) mRootFolder->getFilesRecursive(GAME).size();
}

unsigned int SystemData::getFavoritesCount() const
{
  return (unsigned int) mRootFolder->getFavoritesRecursive(GAME).size();
}

unsigned int SystemData::getHiddenCount() const
{
  return (unsigned int) mRootFolder->getHiddenRecursive(GAME).size();
}

void SystemData::loadTheme()
{
  mTheme = std::make_shared<ThemeData>();

  std::string path = getThemePath();

  if (!fs::exists(path)) // no theme available for this platform
    return;

  try
  {
    //std::cout << "creating theme for: " << getName() << " / " << getFullName() << std::endl;
    mTheme->loadFile(getThemeFolder(), path);
    mHasFavorites = mTheme->getHasFavoritesInTheme();
  }
  catch (ThemeException &e)
  {
    LOG(LogError) << e.what();
    mTheme = std::make_shared<ThemeData>(); // reset to empty
  }
}

std::map<std::string, std::vector<std::string> *> *SystemData::getEmulators()
{
  return mEmulators;
}

std::vector<std::string> SystemData::getCores(const std::string &emulatorName)
{
  std::vector<std::string> list;
  for (auto emulator : *mEmulators)
  {
    if (emulatorName == emulator.first)
    {
      list = *emulator.second;
      break;
    }
  }
  return list;
}

SystemData *SystemData::getSystem(std::string &name)
{
  for (auto system: sSystemVector)
  {
    if (system->mName == name)
    {
      return system;
    }
  }
  return nullptr;
}

SystemData *SystemData::getFavoriteSystem()
{
  for (int i = (int) sSystemVector.size(); --i >= 0;)
    if (sSystemVector[i]->isFavorite())
      return sSystemVector[i];
  return nullptr;
}

int SystemData::getSystemIndex(const std::string &name)
{
  for (int i = (int) sSystemVector.size(); --i >= 0;)
    if (sSystemVector[i]->mName == name)
      return i;
  return -1;
}