# encoding: UTF-8

import json
from collections import defaultdict

import jrpc_server

from vnpy.event import Event
from vnpy.trader.vtFunction import getJsonPath
from vnpy.trader.vtObject import VtLogData, VtOrderReq, VtCancelOrderReq
from vnpy.trader.vtConstant import *
from vnpy.trader.vtEvent import EVENT_ACCOUNT


EVENT_JS_LOG = 'eJsLog'

ACTION_MAP = {}
ACTION_MAP['Buy'] = (DIRECTION_LONG, OFFSET_OPEN)
ACTION_MAP['Sell'] = (DIRECTION_SHORT, OFFSET_CLOSE)
ACTION_MAP['Short'] = (DIRECTION_SHORT, OFFSET_OPEN)
ACTION_MAP['Cover'] = (DIRECTION_LONG, OFFSET_CLOSE)
ACTION_MAP['CoverYesterday'] = (DIRECTION_LONG, OFFSET_CLOSEYESTERDAY)
ACTION_MAP['SellYesterday'] = (DIRECTION_SHORT, OFFSET_CLOSEYESTERDAY)
ACTION_MAP_REVERSE = {v:k for k,v in ACTION_MAP.items()}

STATUS_MAP_REVERSE = {}
STATUS_MAP_REVERSE[STATUS_NOTTRADED] = 'Accepted'
STATUS_MAP_REVERSE[STATUS_PARTTRADED] = 'Accepted'
STATUS_MAP_REVERSE[STATUS_ALLTRADED] = 'Filled'
STATUS_MAP_REVERSE[STATUS_CANCELLED] = 'Cancelled'
STATUS_MAP_REVERSE[STATUS_REJECTED] = 'Rejected'
STATUS_MAP_REVERSE[STATUS_UNKNOWN] = 'New'


EXCHANGE_MAP = {}
EXCHANGE_MAP['SH'] = EXCHANGE_SSE
EXCHANGE_MAP['SZ'] = EXCHANGE_SZSE
EXCHANGE_MAP['CFE'] = EXCHANGE_CFFEX
EXCHANGE_MAP['SHF'] = EXCHANGE_SHFE
EXCHANGE_MAP['DCE'] = EXCHANGE_DCE
EXCHANGE_MAP['CZC'] = EXCHANGE_CZCE
EXCHANGE_MAP_REVERSE = {v:k for k, v in EXCHANGE_MAP.items()}


########################################################################
class JsEngine(object):
    """JAQS服务引擎"""
    settingFileName = 'JS_setting.json'
    settingfilePath = getJsonPath(settingFileName, __file__)    

    #----------------------------------------------------------------------
    def __init__(self, mainEngine, eventEngine):
        """Constructor"""
        self.mainEngine = mainEngine
        self.eventEngine = eventEngine
        
        self.server = None      # RPC服务器
        self.cbDict = {}        # 回调函数字典
        self.account = None     # 账户对象
        
        # 注册日志事件类型
        self.mainEngine.registerLogEvent(EVENT_JS_LOG)
        
        # 初始化
        self.initCallback()
        self.initServer()
        self.registerEvent()
        
    #----------------------------------------------------------------------
    def registerEvent(self):
        """注册事件监听"""
        self.eventEngine.register(EVENT_ACCOUNT, self.processAccountEvent)
        
    #----------------------------------------------------------------------
    def initCallback(self):
        """初始化回调函数映射"""
        self.cbDict['.sys.heartbeat'] = self.onHeartbeat
        self.cbDict['auth.login'] = self.onLogin
        self.cbDict['auth.use_strategy'] = self.onUseStrategy
        self.cbDict['oms.query_position'] = self.onQueryPosition
        self.cbDict['oms.query_order'] = self.onQueryOrder
        self.cbDict['oms.place_order'] = self.onPlaceOrder
        self.cbDict['oms.cancel_order'] = self.onCancelOrder
        self.cbDict['oms.query_account'] = self.onQueryAccount
        self.cbDict['oms.query_universe'] = self.onQueryUniverse
        
    #----------------------------------------------------------------------
    def initServer(self):
        """初始化"""
        with open(self.settingfilePath) as f:
            setting = json.load(f)
            host = setting['host']
            port = setting['port']
            addr = "tcp://%s:%s" %(host, port)
            
            # 初始化RPC服务器
            self.server = jrpc_server.JRpcServer()
            self.server.on_call = self.onCall
            self.server.listen(addr)
            
            self.writeLog(u'Jaqs服务器启动成功')
            
    #----------------------------------------------------------------------
    def onCall(self, clientId, req):
        """RPC服务回调函数"""
        method = req['method']
        cb = self.cbDict.get(method, None)
        if not cb:
            self.writeLog(u'无法找到方法%s对应的回调函数' %method)
            return
        
        self.writeLog(u'收到请求：%s' %req)
        
        cb(clientId, req)
    
    #----------------------------------------------------------------------
    def onHeartbeat(self, clientId, req):
        """心跳"""
        pass
    
    #----------------------------------------------------------------------
    def onLogin(self, clientId, req):
        """登录"""
        params = req['params']
        
        result = {
            'username': params['username'],
            'name': params['username'],
            'strategies': [1],
            'broker_strategies': [1]
        }
        
        error = [0, '']
        
        self.server.send_rsp(clientId, req, result, error)
        
        self.writeLog(u'发出响应：%s' %result)
    
    #----------------------------------------------------------------------
    def onUseStrategy(self, clientId, req):
        """使用策略"""
        result = req['params']['account_id']
        error = [0, '']
        self.server.send_rsp(clientId, req, result, error)
        
        self.writeLog(u'发出响应：%s' %result)
    
    #----------------------------------------------------------------------
    def onQueryPosition(self, clientId, req):
        """查询持仓"""
        l = self.mainEngine.getAllPositionDetails()
        
        result = defaultdict(list)
        for detail in l:
            security = self.converSymbol(detail.vtSymbol)
            
            # 多头
            if detail.longPos:
                result['security'].append(security)
                result['side'].append('Long')
                
                result['cost_price'].append(0)
                result['float_pnl'].append(0)
                result['close_pnl'].append(0)
                result['trading_pnl'].append(0)
                result['holding_pnl'].append(0)
                result['commission'].append(0)
                result['init_size'].append(0)
                result['current_size'].append(detail.longPos)
                result['enable_size'].append(detail.longPos-detail.longPosFrozen)
                result['frozen_size'].append(detail.longPosFrozen)
                result['uncome_size'].append(0)
                result['pre_size'].append(detail.longYd)
                result['today_size'].append(detail.longTd)
                
            # 空头
            if detail.shortPos:
                result['security'].append(security)
                result['side'].append('Short')
                
                result['cost_price'].append(0)
                result['float_pnl'].append(0)
                result['close_pnl'].append(0)
                result['trading_pnl'].append(0)
                result['holding_pnl'].append(0)
                result['commission'].append(0)
                result['init_size'].append(0)
                result['current_size'].append(detail.shortPos)
                result['enable_size'].append(detail.shortPos-detail.shortPosFrozen)
                result['frozen_size'].append(detail.shortPosFrozen)
                result['uncome_size'].append(0)
                result['pre_size'].append(detail.shortYd)
                result['today_size'].append(detail.shortTd)                
                
        error = [0, '']
        
        self.server.send_rsp(clientId, req, result, error)
        
        self.writeLog(u'发出响应：%s' %result)
    
    #----------------------------------------------------------------------
    def onQueryOrder(self, clientId, req):
        """查询委托"""
        l = self.mainEngine.getAllOrders()
        
        result = defaultdict(list)
        for order in l:
            result['task_id'].append(order.vtOrderID)
            result['entrust_no'].append(order.vtOrderID)
            
            result['entrust_price'].append(order.price)
            result['entrust_size'].append(order.totalVolume)
            result['sub_seq'].append(0)
            result['sub_total'].append(0)
            result['batch_no'].append(0)
            
            result['fill_price'].append(order.price)
            result['fill_size'].append(order.tradedVolume)
            result['algo'].append('')
            result['entrust_action'].append(ACTION_MAP_REVERSE[(order.direction, order.offset)])
            result['order_status'].append(STATUS_MAP_REVERSE[order.status])
            result['security'].append(self.converSymbol(order.vtSymbol))
            
            hh, mm, ss = order.orderTime.split(':')
            result['entrust_time'].append(int(hh)*10000000+
                                          int(mm)*100000+
                                          int(ss)*1000)
            
        error = [0, '']
        
        self.server.send_rsp(clientId, req, result, error)
        
        self.writeLog(u'发出响应：%s' %result)
    
    #----------------------------------------------------------------------
    def onPlaceOrder(self, clientId, req):
        """委托"""
        params = req['params']
        s, e = params['security'].split('.')
        
        contract = self.mainEngine.getContract(s)        

        if not contract:
            vtOrderID = ''
            error = [-1, u'委托失败，找不到合约%s' %params['security']]
        else:
            vor = VtOrderReq()
            vor.symbol = s
            vor.exchange = EXCHANGE_MAP[e]
            vor.direction, vor.offset = ACTION_MAP[params['action']]
            vor.price = float(params['price'])
            vor.volume = int(params['size'])
            vor.priceType = PRICETYPE_LIMITPRICE
            
            vtOrderID = self.mainEngine.sendOrder(vor, contract.gatewayName)
            error = [0, '']
        
        self.server.send_rsp(clientId, req, vtOrderID, error)
        
        self.writeLog(u'发出响应：%s' %vtOrderID)
    
    #----------------------------------------------------------------------
    def onCancelOrder(self, clientId, req):
        """撤单"""
        params = req['params']
        vtOrderID = params['task_id']
        gatewayName, orderID = vtOrderID.split('.')
        
        vcor = VtCancelOrderReq()
        vcor.orderID = vtOrderID
        self.mainEngine.cancelOrder(vcor, gatewayName)
        
        error = [0, '']
        self.server.send_rsp(clientId, req, 'successful', error)
        
        self.writeLog(u'发出响应：%s' %result)

    #----------------------------------------------------------------------
    def onQueryAccount(self, clientId, req):
        """查询账户"""
        params = req['params']
        
        account = self.account
        
        result = defaultdict(list)
        result['id'].append(account.accountID)
        result['init_balance'].append(account.preBalance)
        result['enable_balance'].append(account.available)
        result['margin'].append(account.margin)
        result['float_pnl'].append(0.0)
        result['close_pnl'].append(account.closeProfit)
        result['holding_pnl'].append(0.0)
        result['trading_pnl'].append(0.0)
        result['type'].append('BA')
        
        error = [0, '']
        self.server.send_rsp(clientId, req, result, error)
        
        self.writeLog(u'发出响应：%s' %result)
     
    #----------------------------------------------------------------------
    def onQueryUniverse(self, clientId, req):
        """查询账户"""
        params = req['params']
        
        l = self.mainEngine.getAllContracts()
        
        result = defaultdict(list)
        for contract in l:        
            result['ba_id'].append(1000)
            result['security'].append(self.converSymbol(contract.vtSymbol))
        
        error = [0, '']
        self.server.send_rsp(clientId, req, result, error)
        
        self.writeLog(u'发出响应：%s' %result)
          
    #----------------------------------------------------------------------
    def writeLog(self, content):
        """发出日志事件"""
        log = VtLogData()
        log.logContent = content
        log.gatewayName = 'JAQS_SERVICE'
        event = Event(type_=EVENT_JS_LOG)
        event.dict_['data'] = log
        self.eventEngine.put(event)         
    
    #----------------------------------------------------------------------
    def converSymbol(self, vtSymbol):
        """转换合约代码"""
        contract = self.mainEngine.getContract(vtSymbol)
        if not contract:
            return ''
        
        e = EXCHANGE_MAP_REVERSE.get(contract.exchange, contract.exchange)
        return '.'.join([contract.symbol, e])
        
    #----------------------------------------------------------------------
    def processAccountEvent(self, event):
        """账户资金更新"""
        self.account = event.dict_['data']
    