"""Privilege SPI (callback) implementation.

Callback output format matches C++ demo printf exactly.
"""

from __future__ import annotations

import logging
import threading
import traceback
from typing import TYPE_CHECKING

import py_xlt_api as xlt

if TYPE_CHECKING:
    try:
        from .privilege_demo import XLTPrivilegeDemo
    except Exception:
        from privilege_demo import XLTPrivilegeDemo

_log = logging.getLogger(__name__)


def _cb_guard(name: str):
    """Decorator: never let exceptions escape SWIG director callbacks.

    注意: 回调函数必须快速返回，若需要异步处理数据，请先拷贝一份数据。
    回调结束之后，参数指向的内存会失效。
    NOTE: Callbacks must return quickly. If you need to process data
    asynchronously, copy the data first. Memory becomes invalid after return.
    """

    def deco(fn):
        def wrapped(self, *args, **kwargs):
            if self._shutting_down:
                return None
            try:
                return fn(self, *args, **kwargs)
            except BaseException:
                _log.error("Uncaught exception in callback %s", name)
                _log.error(traceback.format_exc())
                return None

        return wrapped

    return deco


class _PrivilegeSpi(xlt.XLTPrivilegeSpi):
    """Privilege SPI implementation.

    注意: 所有回调函数必须快速返回！参数指向的内存在回调返回后即失效。
    若需要异步处理数据，请在回调中先拷贝一份数据再返回。
    """
    def __init__(self, owner: "XLTPrivilegeDemo"):
        super().__init__()
        self._owner = owner
        self._shutting_down = False
        self.login_event = threading.Event()
        self.auth_event = threading.Event()

    # ---- connection / login ----

    @_cb_guard("on_disconnect")
    def on_disconnect(self, session_id):
        print("on_disconnect: { session_id = %d }" % int(session_id))
        self._owner._session_id = 0
        self._owner._login_success = False
        self._owner._login_finished = False

    @_cb_guard("on_login")
    def on_login(self, session_id, request_id, error_info):
        if error_info and error_info.error_code_type != 0:
            print("on_login failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            self._owner._session_id = 0
            self._owner._login_success = False
        else:
            print("on_login success: { session_id = %d, request_id = %d }"
                  % (int(session_id), int(request_id)))
            self._owner._session_id = int(session_id)
            self._owner._login_success = True
        self._owner._login_finished = True
        self.login_event.set()

    @_cb_guard("on_auth_trade")
    def on_auth_trade(self, session_id, request_id, trade_token, error_info):
        if error_info and error_info.error_code_type != 0:
            print("on_auth_trade failed: { error_code_type = %d, error_code = %d, error_msg = %s, session_id = %d, request_id = %d, trade_token = %d }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg,
                     int(session_id), int(request_id), int(trade_token)))
            self._owner._trade_token = 0
            self._owner._auth_success = False
        else:
            print("on_auth_trade success: { session_id = %d, request_id = %d, trade_token = %d }"
                  % (int(session_id), int(request_id), int(trade_token)))
            self._owner._trade_token = int(trade_token)
            self._owner._auth_success = True
        self._owner._auth_finished = True
        self.auth_event.set()

    @_cb_guard("on_subscribe_trade_message")
    def on_subscribe_trade_message(self, session_id, request_id, start_sequence, latest_sequence, error_info):
        if error_info and error_info.error_code_type != 0:
            print("on_subscribe_trade_message failed: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
        else:
            print("on_subscribe_trade_message success: { session_id = %d, request_id = %d, start_sequence = %d, latest_sequence = %d }"
                  % (int(session_id), int(request_id), int(start_sequence), int(latest_sequence)))

    # ---- REPORT callbacks ----

    @_cb_guard("on_order_response")
    def on_order_response(self, session_id, msg_desc, order_response):
        r = order_response
        print("on_order_response: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "client_id = %d, report_id = %s, xid = %d, ord_status = %s, exec_type = %s, security_code = %s, "
              "exchange_index = %d, price = %d, quantity = %d, side = %d, leaves_quantity = %d, cancel_quantity = %d, "
              "cancel_reason_id = %d, transact_time = %d }"
              % (int(session_id),
                 int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(r.client_id), r.report_id, int(r.xid),
                 chr(r.ord_status), chr(r.exec_type), r.security_code,
                 int(r.exchange_index), int(r.price), int(r.quantity),
                 int(r.side), int(r.leaves_quantity), int(r.cancel_quantity),
                 int(r.cancel_reason_id), int(r.transact_time)))

    @_cb_guard("on_cancel_response")
    def on_cancel_response(self, session_id, msg_desc, cancel_response):
        r = cancel_response
        print("on_cancel_response: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "client_id = %d, report_id = %s, xid = %d, origin_xid = %d, ord_status = %s, exec_type = %s, "
              "cancel_quantity = %d, cancel_reason_id = %d, transact_time = %d }"
              % (int(session_id),
                 int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(r.client_id), r.report_id, int(r.xid),
                 int(r.origin_xid), chr(r.ord_status), chr(r.exec_type),
                 int(r.cancel_quantity), int(r.cancel_reason_id), int(r.transact_time)))

    @_cb_guard("on_trade_report")
    def on_trade_report(self, session_id, msg_desc, trade_report):
        r = trade_report
        print("on_trade_report: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "client_id = %d, report_id = %s, xid = %d, ord_status = %s, exec_type = %s, exec_id = %s, "
              "price = %d, quantity = %d, leaves_quantity = %d, order_quantity = %d, transact_time = %d }"
              % (int(session_id),
                 int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(r.client_id), r.report_id, int(r.xid),
                 chr(r.ord_status), chr(r.exec_type), r.exec_id,
                 int(r.price), int(r.quantity), int(r.leaves_quantity),
                 int(r.order_quantity), int(r.transact_time)))

    @_cb_guard("on_order_error_response")
    def on_order_error_response(self, session_id, msg_desc, order_error_response):
        r = order_error_response
        print("on_order_error_response: { error_code_type = %d, error_code = %d, error_msg = %s, "
              "session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "client_id = %d, report_id = %s, xid = %d, origin_xid = %d, transact_time = %d }"
              % (int(r.error_info.error_code_type), int(r.error_info.error_code), r.error_info.error_msg,
                 int(session_id),
                 int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(r.client_id), r.report_id,
                 int(r.xid), int(r.origin_xid), int(r.transact_time)))

    @_cb_guard("on_order_end")
    def on_order_end(self, session_id, msg_desc, order_end):
        print("on_order_end: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "client_id = %d, report_id = %s, xid = %d }"
              % (int(session_id),
                 int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(order_end.client_id), order_end.report_id, int(order_end.xid)))

    @_cb_guard("on_raw_report")
    def on_raw_report(self, *args, **kwargs):
        return None

    # ---- DETAIL callbacks ----

    @_cb_guard("on_order_book_insert")
    def on_order_book_insert(self, session_id, msg_desc, order_insert):
        if not order_insert:
            print("on_order_book_insert: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, data = null }"
                  % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild)))
            return
        d = order_insert
        print("on_order_book_insert: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "client_id = %d, account_index = %d, token_id = %d, xid = %d, origin_xid = %d, order_status = %d, is_cancel_flag = %d, "
              "security_code = %s, exchange_index = %d, order_type = %d, side = %d, business_type = %d, "
              "price = %d, quantity = %d, order_amount = %d, order_fee = %d, insert_time = %d }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(d.client_id), int(d.account_index),
                 int(xlt.XLTPrivilege.xid_to_token_id(d.xid)),
                 int(d.xid), int(d.origin_xid), int(d.order_status), int(d.is_cancel_flag),
                 d.security_code, int(d.exchange_index),
                 int(d.order_type), int(d.side), int(d.business_type),
                 int(d.order_price), int(d.order_quantity),
                 int(d.order_amount), int(d.order_fee), int(d.insert_time)))

    @_cb_guard("on_order_book_update")
    def on_order_book_update(self, session_id, msg_desc, order_update):
        if not order_update:
            print("on_order_book_update: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, data = null }"
                  % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild)))
            return
        d = order_update
        print("on_order_book_update: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "client_id = %d, account_index = %d, token_id = %d, xid = %d, origin_xid = %d, order_status = %d, is_cancel_flag = %d, "
              "cl_order_id = %s, origin_cl_order_id = %s, "
              "trade_quantity = %d, trade_amount = %d, trade_fee = %d, trade_fix_fee = %d, trade_count = %d, "
              "leaves_quantity = %d, cancel_quantity = %d, trade_time = %d, cancel_time = %d, update_time = %d, "
              "report_type = %d }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(d.client_id), int(d.account_index),
                 int(xlt.XLTPrivilege.xid_to_token_id(d.xid)),
                 int(d.xid), int(d.origin_xid), int(d.order_status), int(d.is_cancel_flag),
                 d.cl_order_id, d.origin_cl_order_id,
                 int(d.trade_quantity), int(d.trade_amount), int(d.trade_fee),
                 int(d.trade_fix_fee), int(d.trade_count),
                 int(d.leaves_quantity), int(d.cancel_quantity),
                 int(d.trade_time), int(d.cancel_time), int(d.update_time),
                 int(d.report_type)))

        rpt = d.report
        rt = int(d.report_type)
        if rt == int(xlt.TradeReportType_ORDER_RESPONSE):
            r0 = rpt.order_response
            print("  report(order_response): { client_id = %d, report_id = %s, token_id = %d, xid = %d, ord_status = %s, exec_type = %s, security_code = %s, "
                  "exchange_index = %d, price = %d, quantity = %d, side = %d, leaves_quantity = %d, cancel_quantity = %d, "
                  "cancel_reason_id = %d, transact_time = %d }"
                  % (int(r0.client_id), r0.report_id,
                     int(xlt.XLTPrivilege.xid_to_token_id(r0.xid)),
                     int(r0.xid),
                     chr(r0.ord_status), chr(r0.exec_type), r0.security_code,
                     int(r0.exchange_index), int(r0.price), int(r0.quantity),
                     int(r0.side), int(r0.leaves_quantity), int(r0.cancel_quantity),
                     int(r0.cancel_reason_id), int(r0.transact_time)))
        elif rt == int(xlt.TradeReportType_CANCEL_RESPONSE):
            r1 = rpt.cancel_response
            print("  report(cancel_response): { client_id = %d, report_id = %s, token_id = %d, xid = %d, origin_xid = %d, ord_status = %s, exec_type = %s, "
                  "cancel_quantity = %d, cancel_reason_id = %d, transact_time = %d }"
                  % (int(r1.client_id), r1.report_id,
                     int(xlt.XLTPrivilege.xid_to_token_id(r1.xid)),
                     int(r1.xid),
                     int(r1.origin_xid), chr(r1.ord_status), chr(r1.exec_type),
                     int(r1.cancel_quantity), int(r1.cancel_reason_id), int(r1.transact_time)))
        elif rt == int(xlt.TradeReportType_TRADE_REPORT):
            r2 = rpt.trade_report
            print("  report(trade_report): { client_id = %d, report_id = %s, token_id = %d, xid = %d, ord_status = %s, exec_type = %s, exec_id = %s, "
                  "price = %d, quantity = %d, leaves_quantity = %d, order_quantity = %d, transact_time = %d }"
                  % (int(r2.client_id), r2.report_id,
                     int(xlt.XLTPrivilege.xid_to_token_id(r2.xid)),
                     int(r2.xid),
                     chr(r2.ord_status), chr(r2.exec_type), r2.exec_id,
                     int(r2.price), int(r2.quantity), int(r2.leaves_quantity),
                     int(r2.order_quantity), int(r2.transact_time)))
        else:
            print("  report: { unknown_type = %d }" % rt)

    @_cb_guard("on_order_book_error")
    def on_order_book_error(self, session_id, msg_desc, order_error):
        if not order_error:
            print("on_order_book_error: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, data = null }"
                  % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild)))
            return
        d = order_error
        oe = d.order_error
        print("on_order_book_error: { "
              "session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "client_id = %d, account_index = %d, token_id = %d, xid = %d, origin_xid = %d, order_status = %d, is_cancel_flag = %d, "
              "cl_order_id = %s, origin_cl_order_id = %s, update_time = %d, "
              "client_id = %d, report_id = %s, xid = %d, origin_xid = %d, transact_time = %d, error_code_type = %d, error_code = %d, error_msg = %s }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(d.client_id), int(d.account_index),
                 int(xlt.XLTPrivilege.xid_to_token_id(d.xid)),
                 int(d.xid), int(d.origin_xid), int(d.order_status), int(d.is_cancel_flag),
                 d.cl_order_id, d.origin_cl_order_id, int(d.update_time),
                 int(oe.client_id), oe.report_id,
                 int(oe.xid), int(oe.origin_xid), int(oe.transact_time),
                 int(oe.error_info.error_code_type), int(oe.error_info.error_code), oe.error_info.error_msg))

    @_cb_guard("on_asset")
    def on_asset(self, session_id, msg_desc, asset_info):
        if not asset_info:
            print("on_asset: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, data = null }"
                  % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild)))
            return
        a = asset_info
        print("on_asset: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "available_amount = %d, withholding_amount = %d, fund_buy_amount = %d, fund_buy_fee = %d, "
              "fund_sell_amount = %d, fund_sell_fee = %d, withdraw_amount = %d, deposit_amount = %d }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(a.available_amount), int(a.withholding_amount),
                 int(a.fund_buy_amount), int(a.fund_buy_fee),
                 int(a.fund_sell_amount), int(a.fund_sell_fee),
                 int(a.withdraw_amount), int(a.deposit_amount)))

    @_cb_guard("on_position")
    def on_position(self, session_id, msg_desc, position_info):
        if not position_info:
            print("on_position: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, data = null }"
                  % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild)))
            return
        p = position_info
        print("on_position: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "security_code = %s, exchange_index = %d, total_quantity = %d, sellable_quantity = %d, frozen_quantity = %d }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 p.security_code, int(p.exchange_index),
                 int(p.total_quantity), int(p.sellable_quantity), int(p.frozen_quantity)))

    # ---- Fund transfer callbacks ----

    @_cb_guard("on_fund_transfer_exec_result")
    def on_fund_transfer_exec_result(self, session_id, msg_desc, fund_transfer_exec_result):
        if not fund_transfer_exec_result:
            print("on_fund_transfer_exec_result: { session_id = %d, data = null }" % int(session_id))
            return
        r = fund_transfer_exec_result
        print("on_fund_transfer_exec_result: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "biz_no = %s, side = %d, amount = %d, check_flag = %d, exec_type = %d, time = %d, status = %d, "
              "error_code_type = %d, error_code = %d, error_msg = %s }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 r.biz_no, int(r.side), int(r.amount), int(r.check_flag),
                 int(r.exec_type), int(r.time), int(r.status),
                 int(r.error_info.error_code_type), int(r.error_info.error_code), r.error_info.error_msg))

    @_cb_guard("on_counter_fund_transfer_request")
    def on_counter_fund_transfer_request(self, session_id, msg_desc, fund_transfer_request):
        if not fund_transfer_request:
            print("on_counter_fund_transfer_request: { session_id = %d, data = null }" % int(session_id))
            return
        r = fund_transfer_request
        print("on_counter_fund_transfer_request: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "biz_no = %s, source = %d, side = %d, amount = %d, request_time = %d }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 r.biz_no, int(r.source), int(r.side), int(r.amount), int(r.request_time)))

    @_cb_guard("on_counter_fund_transfer_result")
    def on_counter_fund_transfer_result(self, session_id, msg_desc, fund_transfer_result):
        if not fund_transfer_result:
            print("on_counter_fund_transfer_result: { session_id = %d, data = null }" % int(session_id))
            return
        r = fund_transfer_result
        print("on_counter_fund_transfer_result: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "biz_no = %s, source = %d, side = %d, amount = %d, request_time = %d, result_time = %d, status = %d, "
              "error_code_type = %d, error_code = %d, error_msg = %s }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 r.biz_no, int(r.source), int(r.side), int(r.amount),
                 int(r.request_time), int(r.result_time), int(r.status),
                 int(r.error_info.error_code_type), int(r.error_info.error_code), r.error_info.error_msg))

    @_cb_guard("on_cluster_fund_transfer_request")
    def on_cluster_fund_transfer_request(self, session_id, msg_desc, cluster_fund_transfer_request):
        if not cluster_fund_transfer_request:
            print("on_cluster_fund_transfer_request: { session_id = %d, data = null }" % int(session_id))
            return
        r = cluster_fund_transfer_request
        print("on_cluster_fund_transfer_request: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "biz_no = %s, source = %d, amount = %d, request_time = %d }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 r.biz_no, int(r.source), int(r.amount), int(r.request_time)))

    @_cb_guard("on_cluster_fund_transfer_result")
    def on_cluster_fund_transfer_result(self, session_id, msg_desc, cluster_fund_transfer_result):
        if not cluster_fund_transfer_result:
            print("on_cluster_fund_transfer_result: { session_id = %d, data = null }" % int(session_id))
            return
        r = cluster_fund_transfer_result
        print("on_cluster_fund_transfer_result: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "biz_no = %s, source = %d, amount = %d, request_time = %d, result_time = %d, status = %d, "
              "error_code_type = %d, error_code = %d, error_msg = %s }"
              % (int(session_id), int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 r.biz_no, int(r.source), int(r.amount),
                 int(r.request_time), int(r.result_time), int(r.status),
                 int(r.error_info.error_code_type), int(r.error_info.error_code), r.error_info.error_msg))


    # ---- Query callbacks ----

    @_cb_guard("on_query_positions")
    def on_query_positions(self, session_id, request_id, positions, is_last, error_info):
        data_count = positions.data_count() if positions else 0
        print("on_query_positions: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            pos = positions.get(i)
            print("  [%d]: { account_index = %d, security_code = %s, exchange_index = %d, total_quantity = %d, sellable_quantity = %d, frozen_quantity = %d }"
                  % (i, int(pos.account_index), pos.security_code, int(pos.exchange_index),
                     int(pos.total_quantity), int(pos.sellable_quantity), int(pos.frozen_quantity)))

    @_cb_guard("on_query_assets")
    def on_query_assets(self, session_id, request_id, assets, is_last, error_info):
        data_count = assets.data_count() if assets else 0
        print("on_query_assets: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            a = assets.get(i)
            print("  [%d]: { account_index = %d, available_amount = %d, withholding_amount = %d, fund_buy_amount = %d, fund_buy_fee = %d, fund_sell_amount = %d, fund_sell_fee = %d, withdraw_amount = %d, deposit_amount = %d, withdrawable_amount = %d }"
                  % (i, int(a.account_index), int(a.available_amount), int(a.withholding_amount),
                     int(a.fund_buy_amount), int(a.fund_buy_fee), int(a.fund_sell_amount), int(a.fund_sell_fee),
                     int(a.withdraw_amount), int(a.deposit_amount), int(a.withdrawable_amount)))

    @_cb_guard("on_query_orders")
    def on_query_orders(self, session_id, request_id, orders, is_last, error_info):
        data_count = orders.data_count() if orders else 0
        print("on_query_orders: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            o = orders.get(i)
            print("  [%d]: { xid = %d, origin_xid = %d, order_status = %d, security_code = %s, exchange_index = %d, "
                  "side = %d, business_type = %d, order_price = %d, order_quantity = %d, order_amount = %d, order_fee = %d, "
                  "trade_quantity = %d, trade_amount = %d, trade_fee = %d, trade_fix_fee = %d, trade_count = %d, "
                  "leaves_quantity = %d, cancel_quantity = %d, insert_time = %d, trade_time = %d, "
                  "error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (i, int(o.xid), int(o.origin_xid), int(o.order_status), o.security_code, int(o.exchange_index),
                     int(o.side), int(o.business_type), int(o.order_price), int(o.order_quantity),
                     int(o.order_amount), int(o.order_fee), int(o.trade_quantity), int(o.trade_amount),
                     int(o.trade_fee), int(o.trade_fix_fee), int(o.trade_count),
                     int(o.leaves_quantity), int(o.cancel_quantity), int(o.insert_time), int(o.trade_time),
                     int(o.error_info.error_code_type), int(o.error_info.error_code), o.error_info.error_msg))

    @_cb_guard("on_query_orders_by_page")
    def on_query_orders_by_page(self, session_id, request_id, order_infos, req_count, rsp_count, query_reference, is_last, error_info):
        data_count = order_infos.data_count() if order_infos else 0
        print("on_query_orders_by_page: { session_id = %d, request_id = %d, data_count = %d, req_count = %d, rsp_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(req_count), int(rsp_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            o = order_infos.get(i)
            print("  [%d]: { xid = %d, origin_xid = %d, order_status = %d, security_code = %s, exchange_index = %d, "
                  "side = %d, business_type = %d, order_price = %d, order_quantity = %d, order_amount = %d, order_fee = %d, "
                  "trade_quantity = %d, trade_amount = %d, trade_fee = %d, trade_fix_fee = %d, trade_count = %d, "
                  "leaves_quantity = %d, cancel_quantity = %d, insert_time = %d, trade_time = %d, "
                  "error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (i, int(o.xid), int(o.origin_xid), int(o.order_status), o.security_code, int(o.exchange_index),
                     int(o.side), int(o.business_type), int(o.order_price), int(o.order_quantity),
                     int(o.order_amount), int(o.order_fee), int(o.trade_quantity), int(o.trade_amount),
                     int(o.trade_fee), int(o.trade_fix_fee), int(o.trade_count),
                     int(o.leaves_quantity), int(o.cancel_quantity), int(o.insert_time), int(o.trade_time),
                     int(o.error_info.error_code_type), int(o.error_info.error_code), o.error_info.error_msg))
        if is_last:
            if int(rsp_count) < int(req_count):
                print("all orders got")
            else:
                print("maybe more orders, call query_orders_by_page again with query_reference")

    @_cb_guard("on_query_trades")
    def on_query_trades(self, session_id, request_id, trade_reports, is_last, error_info):
        data_count = trade_reports.data_count() if trade_reports else 0
        print("on_query_trades: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            t = trade_reports.get(i)
            print("  [%d]: { report_id = %s, xid = %d, ord_status = %d, price = %d, quantity = %d, leaves_quantity = %d }"
                  % (i, t.report_id, int(t.xid), int(t.ord_status),
                     int(t.price), int(t.quantity), int(t.leaves_quantity)))

    @_cb_guard("on_query_init_assets")
    def on_query_init_assets(self, session_id, request_id, init_assets, is_last, error_info):
        data_count = init_assets.data_count() if init_assets else 0
        print("on_query_init_assets: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            a = init_assets.get(i)
            print("  [%d]: { account_index = %d, initial_amount = %d, fund_balance = %d }"
                  % (i, int(a.account_index), int(a.initial_amount), int(a.fund_balance)))

    @_cb_guard("on_query_init_positions")
    def on_query_init_positions(self, session_id, request_id, init_positions, is_last, error_info):
        data_count = init_positions.data_count() if init_positions else 0
        print("on_query_init_positions: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            p = init_positions.get(i)
            print("  [%d]: { account_index = %d, security_code = %s, exchange_index = %d, initial_quantity = %d, total_quantity = %d, frozen_quantity = %d }"
                  % (i, int(p.account_index), p.security_code, int(p.exchange_index),
                     int(p.initial_quantity), int(p.total_quantity), int(p.frozen_quantity)))

    @_cb_guard("on_query_trade_account_info")
    def on_query_trade_account_info(self, session_id, request_id, trade_account_infos, is_last, error_info):
        data_count = trade_account_infos.data_count() if trade_account_infos else 0
        print("on_query_trade_account_info: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            info = trade_account_infos.get(i)
            print("  [%d]: { account_index = %d, account_type = %d, account_name = %s, fund_account_index = %d, fund_account_name = %s }"
                  % (i, int(info.account_index), int(info.account_type), info.account_name,
                     int(info.fund_account_index), info.fund_account_name))

    # ---- Privilege-only callbacks ----

    @_cb_guard("on_token")
    def on_token(self, session_id, msg_desc, token):
        print("on_token: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "token_session_id = %d, trade_way = %d, token_id = %d, login_account_index = %d, trade_account_index = %d, "
              "client_id = %d, terminal_info = %s, api_version = %s }"
              % (int(session_id),
                 int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(token.session_id), int(token.trade_way), int(token.token_id),
                 int(token.login_account_index), int(token.trade_account_index),
                 int(token.client_id), token.terminal_info, token.api_version))

    @_cb_guard("on_quote_login_info")
    def on_quote_login_info(self, session_id, msg_desc, quote_login_info):
        print("on_quote_login_info: { session_id = %d, account_index = %d, sequence = %d, is_rebuild = %d, "
              "info_account_index = %d, sh_l2_flag = %d, sz_l2_flag = %d, client_ip = %s, client_port = %d, "
              "detail_type = %d, time = %d }"
              % (int(session_id),
                 int(msg_desc.account_index), int(msg_desc.sequence), int(msg_desc.is_rebuild),
                 int(quote_login_info.account_index),
                 int(quote_login_info.sh_l2_flag), int(quote_login_info.sz_l2_flag),
                 quote_login_info.client_ip, int(quote_login_info.client_port),
                 int(quote_login_info.detail_type), int(quote_login_info.time)))

    @_cb_guard("on_dump_final_file")
    def on_dump_final_file(self, session_id, request_id, success, error_info):
        if error_info and error_info.error_code_type != 0:
            print("on_dump_final_file failed: { error_code_type = %d, error_code = %d, error_msg = %s, session_id = %d, request_id = %d }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg,
                     int(session_id), int(request_id)))
        else:
            print("on_dump_final_file success: { session_id = %d, request_id = %d }"
                  % (int(session_id), int(request_id)))

    @_cb_guard("on_unbind_user_tgw")
    def on_unbind_user_tgw(self, session_id, request_id, account_index, error_info):
        if error_info and error_info.error_code_type != 0:
            print("on_unbind_user_tgw failed: { error_code_type = %d, error_code = %d, error_msg = %s, session_id = %d, request_id = %d, account_index = %d }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg,
                     int(session_id), int(request_id), int(account_index)))
        else:
            print("on_unbind_user_tgw success: { session_id = %d, request_id = %d, account_index = %d }"
                  % (int(session_id), int(request_id), int(account_index)))

    @_cb_guard("on_query_brief_assets")
    def on_query_brief_assets(self, session_id, request_id, brief_assets, is_last, error_info):
        data_count = brief_assets.data_count() if brief_assets else 0
        print("on_query_brief_assets: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            a = brief_assets.get(i)
            print("  [%d]: { account_index = %d, available_amount = %d, withdraw_amount = %d, deposit_amount = %d }"
                  % (i, int(a.account_index), int(a.available_amount), int(a.withdraw_amount), int(a.deposit_amount)))

    @_cb_guard("on_query_brief_positions")
    def on_query_brief_positions(self, session_id, request_id, brief_positions, is_last, error_info):
        data_count = brief_positions.data_count() if brief_positions else 0
        print("on_query_brief_positions: { session_id = %d, request_id = %d, data_count = %d, is_last = %d }"
              % (int(session_id), int(request_id), int(data_count), int(is_last)))
        if error_info and error_info.error_code_type != 0:
            print("  error: { error_code_type = %d, error_code = %d, error_msg = %s }"
                  % (int(error_info.error_code_type), int(error_info.error_code), error_info.error_msg))
            return
        for i in range(int(data_count)):
            p = brief_positions.get(i)
            print("  [%d]: { account_index = %d, security_code = %s, exchange_index = %d, total_quantity = %d, sellable_quantity = %d }"
                  % (i, int(p.account_index), p.security_code, int(p.exchange_index),
                     int(p.total_quantity), int(p.sellable_quantity)))
