# -*- coding=utf-8
"""
Created on 2017年3月7日
麻将的逻辑类
@author: nick.kai.lee
"""
import copy

import poker.util.timestamp as pktimestamp
from difang.majiang2.ai.play_mode import MPlayMode
from difang.majiang2.chi_rule.chi_rule import MChiRule
from difang.majiang2.dealer.dealer_sanmen_nofeng import SanMenNoFengDealer
from difang.majiang2.gang_rule.gang_rule import MGangRule
from difang.majiang2.peng_rule.peng_rule import MPengRule
from difang.majiang2.player.hand.hand import MHand
from difang.majiang2.player.player import MPlayer, MPlayerTileGang
from difang.majiang2.table.friend_table_define import MFTDefine
from difang.majiang2.table.run_mode import MRunMode
from difang.majiang2.table.table_config_define import MTDefine
from difang.majiang2.table_state.state import MTableState
from difang.majiang2.table_state_processor.add_card_processor import MAddCardProcessor
from difang.majiang2.table_state_processor.drop_card_processor import MDropCardProcessor
from difang.majiang2.table_state_processor.extend_info import MTableStateExtendInfo
from difang.majiang2.table_state_processor.qiang_gang_hu_processor import MQiangGangHuProcessor
from difang.majiang2.table_statistic.statistic import MTableStatistic
from difang.majiang2.win_loose_result.one_result import MOneResult
from hengyangmj.banker.banker import HYBanker
from hengyangmj.hengyang_log import HYLog
from hengyangmj.msg_handler.msg_longnet import HYMsgLongNet as HYMSGLONGNET
from hengyangmj.table_state.state_hengyang import MTableStateHengYang
from hengyangmj.table_tile.table_tile_hengyang import MTableTileHengYang
from hengyangmj.win_lose_result.final_budget import HYFinalBudget
from hengyangmj.win_lose_result.kong_budget import HYKongBudget
from hengyangmj.win_lose_result.round_budget import HYRoundBudget
from hengyangmj.win_lose_result.win_budget import HYWinBudget
from hengyangmj.win_rule.win_rule_hengyang import MWinRuleHengYang
from poker.entity.dao import sessiondata


class HYMajiangTableLogic(object):
    def __init__(self, playerCount, playMode, runMode):
        super(HYMajiangTableLogic, self).__init__()
        # 用户数量
        self.__playerCount = playerCount
        # 玩法
        self.__playMode = playMode
        # 运行方式
        self.__run_mode = runMode
        # 牌桌配置
        self.__table_config = {}

        # 根据玩法获取发牌器 设置dealer为三门无风
        self.__table_tile_mgr = MTableTileHengYang(self.__playerCount, self.__playMode, runMode)
        self.__table_tile_mgr.setDealer(SanMenNoFengDealer())

        # 本局玩家
        self.__players = [None for _ in range(self.playerCount)]
        # 手牌张数
        self.__hand_card_count = 13
        # 庄家
        self.__banker_mgr = HYBanker()

        # 当前操作座位号
        self.__cur_seat = 0
        # 上牌状态
        self.__add_card_processor = MAddCardProcessor()
        # 出牌状态
        self.__drop_card_processor = MDropCardProcessor(self.playerCount, playMode)
        self.__drop_card_processor.setTableTileMgr(self.tableTileMgr)
        # 抢杠和状态
        self.__qiang_gang_hu_processor = MQiangGangHuProcessor(self.playerCount)
        # 和牌状态
        self.__table_win_state = MTableState.TABLE_STATE_NONE
        # 吃牌AI
        self.__chi_rule_mgr = MChiRule()
        self.__chi_rule_mgr.setTableTileMgr(self.__table_tile_mgr)
        # 碰牌AI
        self.__peng_rule_mgr = MPengRule()
        self.__peng_rule_mgr.setTableTileMgr(self.__table_tile_mgr)
        # 杠牌AI
        self.__gang_rule_mgr = MGangRule()
        self.__gang_rule_mgr.setTableTileMgr(self.__table_tile_mgr)
        # 消息处理者
        self.__msg_processor = HYMSGLONGNET()
        self.__msg_processor.setTableTileMgr(self.tableTileMgr)
        self.__msg_processor.setGangRuleMgr(self.__gang_rule_mgr)
        # 和牌管理器
        self.__win_rule_mgr = MWinRuleHengYang()
        self.__win_rule_mgr.setTableTileMgr(self.__table_tile_mgr)
        # 牌桌状态机
        self.__table_stater = MTableStateHengYang()

        # 听牌管理器
        self.__ting_rule_mgr = None

        # 牌桌最新操作标记，摸牌actionID加1，出牌actionID加1
        self.__action_id = 0
        # 圈/风
        self.__quan_men_feng = 0
        # 算分结果
        self.__round_result = None
        # 牌桌结果
        self.__table_result = HYFinalBudget()
        # 记录上一局的结果
        self.__win_loose = 0
        # 记录上一局的胜利者
        self.__last_win_seatId = 0
        # 杠牌记录,用来判定杠上花和杠上炮,每次成功杠牌后设置,每次出牌后或者抢杠胡后清空,设置值为座位号,默认值-1
        self.__latest_gang_state = -1
        # 牌桌观察者
        self.__table_observer = None
        # 一炮多响时用于存储胡牌人位置的数组
        self.__win_seats = []
        # 漏胡时用于存储出牌时能胡的人的数组
        self.__pass_hu_seats = []
        # 用于存储本局牌局上传地址的数组
        self.__record_urls = []
        # 用于存储每人网络状况的字典
        self.__player_ping = {}
        HYLog.debug('qujingLogicTableInit')

    @property
    def recordUrls(self):
        return self.__record_urls

    def setLatestGangState(self, state):
        self.__latest_gang_state = state

    @property
    def latestGangState(self):
        return self.__latest_gang_state

    @property
    def tableResult(self):
        return self.__table_result

    def nextRound(self):
        """下一把"""
        self.__cur_seat = 0
        self.__add_card_processor.reset()
        self.__drop_card_processor.reset()
        self.__qiang_gang_hu_processor.reset()
        self.__table_win_state = MTableState.TABLE_STATE_NONE
        HYLog.debug("kazhuocheck1", self.__table_win_state)
        self.__action_id = 0
        self.__quan_men_feng = 0
        self.__win_seats = []
        self.__pass_hu_seats = []
        self.__table_tile_mgr.reset()

        for player in self.player:
            if player:
                player.reset()

    @property
    def tableObserver(self):
        return self.__table_observer

    def setTableObserver(self, observer):
        self.__table_observer = observer

    @property
    def roundResult(self):
        return self.__round_result

    @property
    def runMode(self):
        return self.__run_mode

    def resetGame(self, winLoose):
        """重置游戏"""
        self.__win_loose = winLoose
        self.__last_win_seatId = self.curSeat
        # 当前游戏信息备忘
        nowBanker = self.queryBanker()
        userIds = self.getBroadCastUIDs()
        # 确定下一局的庄家
        curRoundCount = self.tableConfig.get(MFTDefine.CUR_ROUND_COUNT, 0)
        banker, remains, noresults = self.__banker_mgr.getBanker(self.__playerCount
                                                                 , (curRoundCount == 0)
                                                                 , self.__win_loose
                                                                 , self.__last_win_seatId)
        HYLog.debug('Remains:', remains, 'Banker:', banker, 'Noresults:', noresults, ' OldBanker:', nowBanker)
        if self.checkTableState(MTableState.TABLE_STATE_XUEZHAN):
            for player in self.player:
                player.setHasHu(False)
        # 标记游戏结束状态
        self.__table_win_state = MTableState.TABLE_STATE_GAME_OVER

        # 调整到gameover状态之后
        recordName = self.getCreateTableRecordName()
        self.msgProcessor.saveRecord(recordName, self.__record_urls)
        # 游戏结束后，记录牌局事件
        if self.tableObserver:
            # 游戏事件记录
            self.tableObserver.onBeginGame(userIds, nowBanker)
        #             self.tableObserver.onGameEvent(MTableStatistic.TABLE_WIN, self.player)
        # 清空__round_result 否则在一局结束下局未开始时断线重连会取到错误的积分数据
        self.__round_result = HYRoundBudget()
        # 清空上一局胜者数据
        self.__win_seats = []
        # 清空出牌时漏胡者数据
        self.__pass_hu_seats = []

    def reset(self):
        """重置"""
        self.nextRound()
        self.__banker_mgr.reset()
        self.__players = [None for _ in range(self.playerCount)]
        self.__round_result = None
        self.__table_result.reset()
        self.__record_urls = []
        HYLog.debug('MajiangTableLogic.reset call.....')

    @property
    def tingRule(self):
        """听牌规则管理器"""
        return self.__ting_rule_mgr

    @property
    def addCardProcessor(self):
        """摸牌管理器"""
        return self.__add_card_processor

    @property
    def dropCardProcessor(self):
        """出牌管理器"""
        return self.__drop_card_processor

    @property
    def qiangGangHuProcessor(self):
        return self.__qiang_gang_hu_processor

    @property
    def playerCount(self):
        """获取本局玩家数量"""
        return self.__playerCount

    @property
    def msgProcessor(self):
        """获取消息处理对象"""
        return self.__msg_processor

    @property
    def actionID(self):
        """获取当前的操作标记"""
        return self.__action_id

    @property
    def playMode(self):
        """获取本局玩法"""
        return self.__playMode

    @property
    def player(self):
        """获取玩家"""
        return self.__players

    @property
    def quanMenFeng(self):
        """获取圈/风设置"""
        return self.__quan_men_feng

    @property
    def handCardCount(self):
        """获取初始手牌张数
        """
        return self.__hand_card_count

    @property
    def curSeat(self):
        """当前操作座位号
        """
        return self.__cur_seat

    @property
    def tableTileMgr(self):
        return self.__table_tile_mgr

    def isFriendTablePlaying(self):
        if self.getTableConfig(MFTDefine.IS_CREATE, 0):
            curCount = self.getTableConfig(MFTDefine.CUR_ROUND_COUNT, 0)
            HYLog.debug('MajiangTableLogic.isPlaying friendTable curCount:', curCount
                        , ' totalCount:', self.getTableConfig(MFTDefine.ROUND_COUNT, 0))
            return (curCount > 0) and (curCount != self.getTableConfig(MFTDefine.ROUND_COUNT, 0))

        return self.isPlaying()

    def isPlaying(self):
        """游戏是否开始"""
        # HYLog.debug('tableLogic.isPlaying... self.__table_win_state:', self.__table_win_state)
        if self.__table_win_state == MTableState.TABLE_STATE_NEXT:
            return True
        return False

    def curState(self):
        """当前状态
        """
        return self.__add_card_processor.getState() \
               + self.__drop_card_processor.getState() \
               + self.__table_win_state \
               + self.__qiang_gang_hu_processor.getState()

    def nowPlayerCount(self):
        """座位上的人数
        """
        return len(self.__players)

    def setCurSeat(self, seat):
        """设置当前操作座位号
        """
        self.__cur_seat = seat

    def setHandCardCount(self, count):
        """设置初始手牌张数
        """
        self.__hand_card_count = count

    def setTableConfig(self, config):
        """设置牌桌配置"""
        self.__table_config = config
        HYLog.info('MajiangTableLogic.setTableConfig:', config)

        # 将TableConfig传递到tableTileMgr，方便各种特殊操作的判断
        self.tableTileMgr.setTableConfig(config)

        cardCount = self.getTableConfig(MTDefine.HAND_COUNT, MTDefine.HAND_COUNT_DEFAULT)
        self.setHandCardCount(cardCount)

        if self.checkTableState(MTableState.TABLE_STATE_GANG) and MTDefine.GANG_BASE not in self.__table_config:
            self.__table_config[MTDefine.GANG_BASE] = 1  # 杠牌分基数 1

        if MFTDefine.ROUND_COUNT not in self.__table_config:
            self.__table_config[MFTDefine.ROUND_COUNT] = 0

        if MFTDefine.CUR_ROUND_COUNT not in self.__table_config:
            self.__table_config[MFTDefine.CUR_ROUND_COUNT] = 0

        if MFTDefine.CARD_COUNT not in self.__table_config:
            self.__table_config[MFTDefine.CARD_COUNT] = 0

        if MFTDefine.LEFT_CARD_COUNT not in self.__table_config:
            self.__table_config[MFTDefine.LEFT_CARD_COUNT] = 0

        if MTDefine.TRUSTTEE_TIMEOUT not in self.__table_config:
            self.__table_config[MTDefine.TRUSTTEE_TIMEOUT] = 1

        if self.checkTableState(MTableState.TABLE_STATE_TING):
            self.__ting_rule_mgr.setTableConfig(config)

    def getTableConfig(self, key, default):
        """获取牌桌配置"""
        value = self.__table_config.get(key, default)
        # HYLog.info('MajiangTableLogic.getTableConfig key:', key, ' value:', value)
        return value

    @property
    def tableConfig(self):
        return self.__table_config

    def queryBanker(self):
        """查询庄家
        """
        return self.__banker_mgr.queryBanker()

    def getPlayerState(self, seatId):
        """获取用户状态"""
        if seatId >= self.__playerCount:
            return None

        return self.__players[seatId].state

    def getPlayer(self, seatId):
        """获取用户名称"""
        return self.__players[seatId]

    def addPlayer(self, player, seatId, isReady=True, isAutoDecide=False):
        """添加玩家"""
        if player in self.__players:
            HYLog.debug('already in table...')
            return
        if seatId >= self.__playerCount:
            HYLog.debug('no seat any more...')
            return

        HYLog.debug('MajiangTableLogic.addPlayer name:', player.name, ' seatId:', seatId, ' isReady:', isReady,
                    ' isAutoDecide:', isAutoDecide)

        self.__players[seatId] = player
        self.__msg_processor.setPlayers(self.player)
        self.__add_card_processor.setPlayers(self.player)
        self.__drop_card_processor.setPlayers(self.player)
        self.__qiang_gang_hu_processor.setPlayers(self.player)
        player.setSeatId(seatId)
        player.setAutoDecide(isAutoDecide)
        self.playerReady(seatId, isReady)

    def removePlayer(self, seatId):
        """删除玩家"""
        self.__players[seatId] = None
        self.__msg_processor.setPlayers(self.__players)
        if self.isEmpty():
            self.reset()

    def isEmpty(self):
        """是否空桌"""
        for player in self.__players:
            if player:
                return False
        return True

    def setAutoDecideValue(self, seatId, adValue):
        """设置玩家的托管状态"""
        if self.__players[seatId]:
            if not self.getTableConfig(MFTDefine.IS_CREATE, 0):
                HYLog.debug('MajiangTableLogic.setAutoDecideValue not in createTable Mode')
                self.__players[seatId].setAutoDecide(adValue)

    def getBroadCastUIDs(self, filter_id=-1):
        """获取待广播的UID集合，不包括filter_id及机器人
        不需要向机器人发送消息
        """
        uids = []
        for player in self.__players:
            if player and (not player.isRobot()) and (player.userId != filter_id):
                uids.append(player.userId)
        return uids

    def getSeats(self):
        seats = [0 for _ in range(self.playerCount)]
        for index, _ in enumerate(seats):
            if self.__players[index]:
                seats[index] = self.__players[index].userId
        return seats

    def isGameOver(self):
        """是否已结束"""
        return self.__table_win_state == MTableState.TABLE_STATE_GAME_OVER

    def checkTableState(self, state):
        """校验牌桌状态机
        """
        return state & self.__table_stater.states

    def haveRestTile(self):
        """检查牌堆剩余的牌"""
        restTilesCount = self.tableTileMgr.getCheckFlowCount()
        if restTilesCount <= 0:
            HYLog.debug("haveRestTile no tile left")
            return False
        else:
            return True

    def nextSeatId(self, seatId):
        """
        计算下一个seatId
        """
        HYLog.debug("nextSeatId.seatId:", seatId)
        seatId = (seatId + 1) % self.__playerCount
        return seatId

    def preSeatId(self, seatId):
        """
        计算上家seatId
        """
        seatId = seatId - 1
        if seatId < 0:
            seatId += self.__playerCount
        return seatId

    def processAddTile(self, cp, state, special_tile=None):
        """上一张牌并处理
        参数：
            cp - 当前玩家
            tile - 当前上牌
        """

    def getCreateTableRecordName(self):
        """获取牌桌记录信息"""
        if self.runMode == MRunMode.CONSOLE:
            return 'console.json'

        curCount = self.tableConfig[MFTDefine.CUR_ROUND_COUNT]
        totalCount = self.tableConfig[MFTDefine.ROUND_COUNT]
        recordName = '%s-%s-%d-%d-%d' % (
        self.playMode, self.tableConfig[MFTDefine.FTID], curCount, totalCount, pktimestamp.getCurrentTimestamp())
        HYLog.debug('MajiangTableLogic.getCreateTableRecordName recordName:', recordName)
        return recordName

    # 大结算时需要返回给客户端的统计信息
    def getCreateExtendBudgets(self, score):
        createExtendBudgets = [{} for _ in range(self.playerCount)]
        # roundResult 列表
        allResults = []
        tableResults = self.__table_result.results
        HYLog.debug('MajiangTableLogic.getCreateExtendBudgets roundResult count', len(tableResults))
        for roundResult in self.__table_result.results:
            HYLog.debug('MajiangTableLogic.getCreateExtendBudgets roundResult ...')
            for oneResult in roundResult.roundResults:
                allResults.append(oneResult)
                if MOneResult.KEY_SCORE in oneResult.results:
                    score.append(oneResult.results[MOneResult.KEY_SCORE])
                HYLog.debug('MajiangTableLogic.getCreateExtendBudgets oneResult ...score:', score)

        ziMoMaxValue = 0
        ziMoMaxSeatId = -1
        dianPaoMaxValue = 0
        dianPaoMaxSeatId = -1
        for seatId in range(self.playerCount):
            extendBudget = {}
            extendBudget["sid"] = seatId
            ziMoValue = 0
            dianPaoValue = 0

            statisticInfo = []
            # one result
            for oneResult in allResults:
                HYLog.debug('MajiangTableLogic.getCreateExtendBudgets seatId:', seatId)
                # statScore = oneResult.results[MOneResult.KEY_SCORE]
                # totalDeltaScore += statScore[seatId]
                stats = [[] for _ in range(self.__playerCount)]
                if MOneResult.KEY_STAT in oneResult.results:
                    stats = oneResult.results[MOneResult.KEY_STAT]
                    playerStats = stats[seatId]
                for stat in playerStats:
                    if MOneResult.STAT_ZIMO in stat:
                        ziMoValue += stat[MOneResult.STAT_ZIMO]

                    if MOneResult.STAT_DIANPAO in stat:
                        dianPaoValue += stat[MOneResult.STAT_DIANPAO]

                    if self.playMode == MPlayMode.HAERBIN:
                        if MOneResult.STAT_MINGGANG in stat:
                            mingGangValue += stat[MOneResult.STAT_MINGGANG]

                        if MOneResult.STAT_ANGANG in stat:
                            anGangValue += stat[MOneResult.STAT_ANGANG]

                    if MOneResult.STAT_ZUIDAFAN in stat:
                        if stat[MOneResult.STAT_ZUIDAFAN] > zuidaFanValue:
                            zuidaFanValue = stat[MOneResult.STAT_ZUIDAFAN]

            oneResultForName = MOneResult()
            statisticInfo.append({"desc": "自摸", "value": ziMoValue})
            HYLog.debug('MTableLogic.createExtendBudgets seatId', seatId, ' ziMoValue:', ziMoValue)
            statisticInfo.append({"desc": "点炮", "value": dianPaoValue})
            HYLog.debug('MTableLogic.createExtendBudgets seatId', seatId, ' dianPaoValue:', dianPaoValue)
            # extendBudget["total_delta_score"] = totalDeltaScore
            if self.__table_result.score and (len(self.__table_result.score) > seatId):
                extendBudget["total_delta_score"] = self.__table_result.score[seatId]
            else:
                extendBudget["total_delta_score"] = 0
            extendBudget["statistics"] = statisticInfo
            # dianpao_most zimo_most
            extendBudget["head_mark"] = ""
            createExtendBudgets[seatId] = extendBudget
            if ziMoValue > ziMoMaxValue:
                ziMoMaxValue = ziMoValue
                ziMoMaxSeatId = seatId
            if dianPaoValue > dianPaoMaxValue:
                dianPaoMaxValue = dianPaoValue
                dianPaoMaxSeatId = seatId
        if ziMoMaxSeatId >= 0:
            createExtendBudgets[ziMoMaxSeatId]["head_mark"] = "zimo_most"
            HYLog.debug('MTableLogic.createExtendBudgets zimo_most seat:', ziMoMaxSeatId)
        if dianPaoMaxSeatId >= 0:
            createExtendBudgets[dianPaoMaxSeatId]["head_mark"] = "dianpao_most"
            HYLog.debug('MTableLogic.createExtendBudgets dianpao_most seat:', dianPaoMaxSeatId)
        return createExtendBudgets

    def sendCreateExtendBudgetsInfo(self, terminate, score):
        """
        本桌牌局结束进行大结算 (框架方法,friend table里调用)
        @param terminate:
        @param score:
        add by taoxc
        """
        cebInfo = self.getCreateExtendBudgets(score)
        # 结算，局数不加1
        self.__msg_processor.table_call_game_all_stat(terminate, cebInfo, self.__init_custom_table_info(
            self.tableConfig[MFTDefine.CUR_ROUND_COUNT]))

    def sendMsgTableInfo(self, active_seat_id, is_reconnect=False):
        """
        游戏拉玩家入桌 (框架方法,friend table里调用)
        @param active_seat_id: 入桌玩家座位号
        @param is_reconnect: boolean 是否断线重连
        """
        HYLog.debug('sendMsgTableInfo seatId:', active_seat_id, ' isReconnect:', is_reconnect)
        if not self.__players[active_seat_id]:
            HYLog.error('sendMsgTableInfo player info error! players: ', self.__players)

        self.__round_result = self.__round_result or HYRoundBudget()

        self.__process_table_info(active_seat_id, is_reconnect)
        # 广播"得分"详情
        self.__process_score()

    def playerReady(self, seatId, isReady):
        """
        玩家准备(框架方法,friend table里调用)
        """
        HYLog.debug('playerReady seatId:', seatId, ' isReady:', isReady, ' tableState:', self.__table_win_state)
        if seatId < 0:
            return False

        if seatId == 0:
            self.refixTableStateByConfig()
            self.refixTableMultipleByConfig()
            self.refixTableBonusTilesSwitchByConfig()
            self.reloadTableConfig()

        if self.__table_win_state == MTableState.TABLE_STATE_NONE or self.__table_win_state == MTableState.TABLE_STATE_GAME_OVER:
            if isReady:
                self.player[seatId].ready()
            else:
                self.player[seatId].wait()

            return self.__process_begin_game()

    def gameNext(self):
        """
        下一步，游戏的主循环 框架方法(public api)
        action_handler里调用
        """
        HYLog.debug('table.gameNext...')
        if self.curState() == MTableState.TABLE_STATE_NEXT:
            self.__cur_seat = self.nextSeatId(self.__cur_seat)
            cp = self.player[self.__cur_seat]
            self.__process_send_tile(cp, MTableState.TABLE_STATE_NEXT)

    def __init_custom_table_info(self, current_round_index):
        """
        初始化自建桌独有信息
        (非框架方法) (private api)
        @param current_round_index: 当前回合数

        PS:该字段在"table_info"&"win"&"lose"&"gaming_leave_display_budget"协议里携带
        """
        max_round_count = self.tableConfig[MFTDefine.ROUND_COUNT]
        current_round_index = max_round_count if current_round_index >= max_round_count else current_round_index

        if self.tableConfig.get(MFTDefine.IS_CREATE, 0):
            return {
                "create_table_no": self.tableConfig[MFTDefine.FTID],  # 自建桌的桌子number
                "time": pktimestamp.getCurrentTimestamp(),
                "create_final": 1 if max_round_count == current_round_index else 0,  # 是否是最后一场
                "create_now_cardcount": current_round_index,  # 当前房卡数量
                "create_total_cardcount": self.tableConfig[MFTDefine.ROUND_COUNT],  # 总共房卡数量
                "itemParams": self.getTableConfig(MFTDefine.ITEMPARAMS, {}),
                "hostUserId": self.getTableConfig(MFTDefine.FTOWNER, 0),
                'create_table_desc_list': self.getTableConfig(MFTDefine.CREATE_TABLE_DESCS, []),
                'create_table_play_desc_list': self.getTableConfig(MFTDefine.CREATE_TABLE_PLAY_DESCS, []),
            }
        else:
            return None

    def __process_table_info(self, active_seat_id, is_reconnect):
        """
        拉玩家进桌子
        (非框架方法) (private api)
        @param active_seat_id: 入桌玩家座位号
        @param is_reconnect: boolean 是否断线重连
        """
        self.__msg_processor.setActionId(self.__action_id)

        self.__msg_processor.table_call_table_info(self.__players, active_seat_id, self.__banker_mgr.queryBanker(),
                                                   self.__playerCount, \
                                                   self.__process_player_info(active_seat_id, is_reconnect),
                                                   self.__playMode, self.__table_tile_mgr.getTilesLeftCount(),
                                                   self.__action_id, is_reconnect, \
                                                   self.__init_custom_table_info(
                                                       self.tableConfig[MFTDefine.CUR_ROUND_COUNT] + 1))

    def __process_player_info(self, active_seat_id, is_reconnect=False):
        """
        处理玩家信息(table_info里用到)
        (非框架方法) (private api)
        @param active_seat_id: 当前玩家座位号
        @param is_reconnect: boolean 是否断线重连
        """
        players = []
        for i in range(self.__playerCount):
            if not self.__players[i]:
                continue
            player = {}
            player['ip'] = sessiondata.getClientIp(self.__players[i].userId)
            player['userId'] = self.__players[i].userId
            player['name'] = self.__players[i].name
            player['pic'] = self.__players[i].purl
            player['sex'] = self.__players[i].sex
            player['coin'] = self.__players[i].coin
            player['seatId'] = i
            player['state'] = self.__players[i].state
            player['ting'] = self.__players[i].isTing()

            if is_reconnect:
                player['trustee'] = self.__players[i].autoDecide
                hand_tiles = self.__players[i].copyHandTiles()

                player['standup_tiles'] = hand_tiles

                # 如果胡了,add_tile一定是显示胡的牌 need to add
                # if self.__players[i].hasHu:
                # 	if hasHuData:
                # 		playerData = hasHuData.get(i,{})
                # 		if playerData:
                # 			player['won_tiles'] = [playerData.get('winTile', 0)]
                # 			player['winMode'] = playerData.get('winMode', 1)

                # 客户端需要把该牌恢复成摸牌
                player['add_tile'] = [self.__players[i].curTile] \
                    if self.__players[i].curTile and not self.__players[i].hasHu else []

                if i != active_seat_id:
                    # 未亮牌情况下，非本家的牌不能下发
                    player['standup_tiles'] = [0 for _ in range(len(hand_tiles))] if not self.__players[i].hasHu else \
                    player['standup_tiles']
                    player['add_tile'] = [0] if len(player['add_tile']) > 0 else player['add_tile']

                if len(player['add_tile']) > 0 and len(player['standup_tiles']) % 3 == 2:
                    for temp in player['add_tile']:
                        if temp in player['standup_tiles']:
                            player['standup_tiles'].remove(temp)

                player['gang_tiles'] = self.__players[i].copyGangArray()
                player['peng_tiles'] = self.__players[i].copyPengArray()
                player['chi_tiles'] = self.__players[i].copyChiArray()
                player['ting_tiles'] = self.__players[i].copyTingArray()
                player['drop_tiles'] = copy.deepcopy(self.__table_tile_mgr.menTiles[self.__players[i].curSeatId])
                pass
            players.append(player)
            pass
        return players

    def __process_begin_game(self):
        """
        开始游戏
        (非框架方法) (private api)

        玩家准备完毕, 准备开始游戏
        """
        for seat_id in range(self.__playerCount):
            if not self.__players[seat_id] or self.__players[seat_id].state != MPlayer.PLAYER_STATE_READY:
                HYLog.error('__process_begin_game: player is not ready, game can not begin!! seat_id:', seat_id)
                return False

        # 初始化本局结果
        self.tableConfig[MFTDefine.CUR_ROUND_COUNT] += 1
        if self.tableConfig[MFTDefine.CUR_ROUND_COUNT] % 8 == 1 and self.tableConfig[MFTDefine.LEFT_CARD_COUNT] > 0:
            self.tableConfig[MFTDefine.LEFT_CARD_COUNT] -= 1

        HYLog.debug('__process_begin_game: host fangka rest card count:', self.tableConfig[MFTDefine.LEFT_CARD_COUNT])

        self.__round_result = self.__round_result or HYRoundBudget()
        self.__round_result.set_round_index(self.tableConfig.get(MFTDefine.CUR_ROUND_COUNT, 0))

        for player in self.__players:
            player.play()
        self.__table_tile_mgr.setPlayers(self.__players)

        # 1.起手发牌
        if not self.__process_init_tiles():
            return False

        # 2.给庄家发一张牌，等待庄家出牌
        if not self.__process_send_tile(self.__players[self.__banker_mgr.queryBanker()], MTableState.TABLE_STATE_NEXT):
            HYLog.error('__process_begin_game: __process_send_tile error!')
            return False

        # 3.修改牌桌状态
        self.__table_win_state = MTableState.TABLE_STATE_NEXT
        if self.tableObserver:
            self.tableObserver.onGameEvent(MTableStatistic.TABLE_START, self.player)
        return True

    def __process_reset_game(self, win_lose):
        """
        重置游戏
        (非框架方法) (private api)

        游戏结束,重置游戏数据
        """
        self.__win_loose = win_lose
        self.__last_win_seatId = self.curSeat

        # 当前游戏信息备忘
        # nowBanker = self.queryBanker()
        # userIds = self.getBroadCastUIDs()

        # 确定下一局的庄家
        # curRoundCount = self.tableConfig.get(MFTDefine.CUR_ROUND_COUNT, 0)
        # banker, remains, noresults = self.__banker_mgr.getBanker(self.__playerCount
        # 		, (curRoundCount == 0)
        # 		, self.__win_loose
        # 		, self.__last_win_seatId)
        # HYLog.debug('Remains:', remains, 'Banker:', banker, 'Noresults:', noresults, ' OldBanker:', nowBanker)

        # 标记游戏结束状态
        self.__table_win_state = MTableState.TABLE_STATE_GAME_OVER
        # 上传牌局记录
        self.__msg_processor.upload_game_record_to_cdn(self.getCreateTableRecordName(), self.__record_urls)

        # 游戏结束 打点
        # if self.tableObserver:
        # self.tableObserver.onBeginGame(userIds, nowBanker)

        del self.__round_result
        self.__round_result = HYRoundBudget()

    def __process_init_tiles(self):
        """
        起手发牌(private api)

        @return boolean True发牌成功, False流局或者牌张错误
        """
        if len(self.__players) != self.__playerCount:
            HYLog.debug("__process_init_tiles: not enough players...")
            return False

            # 摆牌器
        # hr = MTrickHandTiles()
        # trickTiles = hr.getTiles(self.__playMode)

        # 根据需要与规则计算好牌点和初始张数
        # 好牌点1，放在发牌的最前面，table负责将好牌派发给正确的人
        # 手牌张数13
        self.__table_tile_mgr.shuffle(1, self.__hand_card_count)
        HYLog.debug("__process_init_tiles: tiles pool tiles info:", self.__table_tile_mgr.getTiles())
        HYLog.debug("__process_init_tiles: tiles pool tiles count:", len(self.__table_tile_mgr.getTiles()))

        # self.tableTileMgr.trick(hr.getTiles(self.__playMode))

        # 发牌
        self.setCurSeat(self.__banker_mgr.queryBanker())

        for seat_id in range(self.__playerCount):
            self.__table_tile_mgr.clearPassHuBySeatId(seat_id)
            hand_cards = self.__table_tile_mgr.send_tiles(seat_id, self.__hand_card_count)  # 起手发指定张数的牌，13
            self.__players[seat_id].actionBegin(hand_cards)

            # 发送发牌的消息
            self.__msg_processor.table_call_init_tiles(self.__players, seat_id, hand_cards \
                                                       , self.__banker_mgr.queryBanker())
            pass
        return True

    def __check_action_available(self, state):
        """
        检查是否可以执行某个动作
        @param state MTableState里注册的值

        @return boolean True or False
        PS:
        self.__table_stater.states注册了若干状态值0,1,2,4,8,16...
        注册过的状态值相加 s = s | [1,2,4,8...]
        查询是否注册某个值, s & value, 注册过返回 value, 没注册过返回0
        """
        return (state & self.__table_stater.states != 0)

    def __install_if_kong_action(self, cp, tile, actions):
        """
        装载玩家是否可以执行杠牌操作,如果有
        private api
        @param cp: currentPlayer
        @param tile: 摸牌或者打牌
        @param actions: 下发玩家客户端执行的action数据, eg: gang_action, chi_action etc.

        @return 可杠的状态
        """
        kongs = self.__gang_rule_mgr.hasGang(cp.copyTiles(), tile, MTableState.TABLE_STATE_NEXT)
        HYLog.debug("__install_if_kong_action: check gang_action exist:", kongs)
        if len(kongs) > 0:
            for kong in kongs:
                if cp.canGang(kong, True):
                    actions.appendInfo(MTableState.TABLE_STATE_GANG, kong)
            return MTableState.TABLE_STATE_GANG
        else:
            return MTableState.TABLE_STATE_NEXT

    def __install_if_win_action(self, cp, tile, actions):
        """
        装载玩家是否可以执行胡牌操作,如果有
        private api
        @param cp: currentPlayer
        @param tile: 摸牌或者打牌
        @param actions: 下发玩家客户端执行的action数据, eg: gang_action, chi_action etc.

        @return 可胡的状态
        """
        win = self.__win_rule_mgr.is_win01(cp.copyTiles())
        HYLog.debug("__install_if_win_action: check win_action exist:", win)
        if win:  # 可以和，给用户和的选择
            actions.appendInfo(MTableState.TABLE_STATE_HU, {"tile": tile})
            return MTableState.TABLE_STATE_HU
        else:
            return MTableState.TABLE_STATE_NEXT

    def __process_send_tile(self, cp, state):
        """
        处理摸牌(private api)
        @param cp: currentPlayer
        @param state: 由该状态导致的发牌

        @return boolean True发牌成功, False流局或者牌张错误
        """
        actions = MTableStateExtendInfo()  # 客户端执行的action数据
        tile_count = self.__table_tile_mgr.getCheckFlowCount()  # 获取用于流局判定的剩余牌数 (子类需要重写)
        cur_state = MTableState.TABLE_STATE_NEXT

        if self.__check_action_available(MTableState.TABLE_STATE_DROP):  # 是否允许出牌
            cur_state = MTableState.TABLE_STATE_DROP  # 牌桌标记等待出牌状态

        HYLog.debug("__process_send_tile: rest tile count&state", tile_count, cur_state)
        if tile_count > 0:
            tile = self.__table_tile_mgr.send_tiles(cp.curSeatId, 1)[0]
            if tile:
                HYLog.info("__process_send_tile: tile to send:", tile, ", to seatId:", cp.curSeatId)
                self.__table_tile_mgr.setAddTileInfo(tile, cp.curSeatId)
                cp.actionAdd(tile)
            else:
                HYLog.error("__process_send_tile: tile error:", tile, ", to seatId:", cp.curSeatId)
                return False
        else:
            self.gameFlow(cp.curSeatId)  # 处理流局
            return False

        self.__action_id += 1

        cur_state += self.__install_if_kong_action(cp, tile, actions)
        cur_state += self.__install_if_win_action(cp, tile, actions)

        self.__add_card_processor.initProcessor(self.__action_id, cur_state, cp.curSeatId, tile, actions)
        self.__drop_card_processor.reset()

        self.__msg_processor.table_call_send_tile(self.__players, tile, self.__cur_seat,
                                                  self.__table_stater.getTimeOutByState(state), self.__action_id,
                                                  actions)
        return True

    def dropTile(self, active_seat_id, drop_tile):
        """
        打牌 框架方法(public api)
        action_handler里调用
        @param active_seat_id 打牌的玩家的 座位号
        @param drop_tile 打出牌的花色
        """
        HYLog.debug('dropTile: seatId:', active_seat_id, ' dropTile:', drop_tile)
        self.__table_win_state = MTableState.TABLE_STATE_DROP

        if self.__cur_seat != active_seat_id:
            self.__table_win_state = MTableState.TABLE_STATE_NEXT
            HYLog.debug('dropTile: wrong seatId! current player seat id:', self.__cur_seat)
            return

        # 当前玩家
        cp = self.__players[active_seat_id]
        if not cp.actionDrop(drop_tile):  # 检查"打牌"是否在玩家手牌中
            self.__table_win_state = MTableState.TABLE_STATE_NEXT
            HYLog.error('dropTile: player does not have the drop tile! drop tile:', drop_tile)
            return

        # 设置出牌信息
        self.__table_tile_mgr.setDropTileInfo(drop_tile, active_seat_id)

        # 设置出牌
        self.__add_card_processor.reset()
        self.__drop_card_processor.reset()
        self.__qiang_gang_hu_processor.reset()
        self.__drop_card_processor.initTile(drop_tile, self.__cur_seat)

        # 修改操作标记
        self.__action_id += 1

        # 测试其他玩家对于这张牌的处理
        for seat_id in range(self.__playerCount):
            if seat_id != active_seat_id:
                self.__process_play_tile_follow_up(active_seat_id, drop_tile, self.__players[seat_id])

        # 先加入门前牌堆,如果有其它处理再从牌堆里拿掉
        self.__table_tile_mgr.setMenTileInfo(drop_tile, active_seat_id)

        # 向打牌玩家发送出牌结果
        self.__msg_processor.table_call_play(active_seat_id, drop_tile, self.__players, active_seat_id, 0, \
                                             MTableState.TABLE_STATE_NEXT, self.__action_id, None)
        self.__table_win_state = MTableState.TABLE_STATE_NEXT

    def __process_play_tile_follow_up(self, active_seat_id, tile, other):
        """
        处理玩家"打牌"后,其他玩家对此动作的可操作行为 (private api)
        打出的牌是否能被其他玩家进行吃碰杠胡等行为
        @param active_seat_id: (cp:打牌玩家)的座位号
        @param tile: 打牌玩家(cp)打出的牌
        @param other: 其他玩家 对打出的牌有潜在操作可能的"其他玩家"; (非打出tile的玩家)
        """
        HYLog.debug('__process_play_tile_follow_up: params:', active_seat_id, tile, other.curSeatId)

        # 如果玩家已经胡牌,则不可以对"打牌"继续进行操作
        if other.state == MPlayer.PLAYER_STATE_WON:
            self.__msg_processor.table_call_play(active_seat_id, tile, self.__players, other.curSeatId, 0, \
                                                 MTableState.TABLE_STATE_NEXT, self.__action_id, None)
            return other.state

        # 取出所有牌数据(二维数组)
        tiles = other.copyTiles()
        tiles[MHand.TYPE_HAND].append(tile)  # 将打牌先加入玩家手牌

        state = MTableState.TABLE_STATE_NEXT
        actions = MTableStateExtendInfo()  # 协议里可以携带的玩家可操作 actions

        """
        吃牌
        1.只有下家可以吃上家的牌
        2.下家不能处于听牌状态
        """
        # if self.__win_rule_mgr.canWinAfterChiPengGang(tiles):
        if self.nextSeatId(self.__cur_seat) == other.curSeatId and not other.isTing() and \
                self.checkTableState(MTableState.TABLE_STATE_CHI):
            # 检测是否可吃 "chi_action"
            chow_results = self.__chi_rule_mgr.hasChi(tiles, tile)
            if len(chow_results) > 0:
                state |= MTableState.TABLE_STATE_CHI
                HYLog.debug('__process_play_tile_follow_up: seatId:', other.curSeatId, ', chi_action:', chow_results)
                actions.setInfo(MTableState.TABLE_STATE_CHI, chow_results)
            pass
        """
        碰牌
        1.碰牌玩家不能处于听牌状态
        """
        if not other.isTing() and self.checkTableState(MTableState.TABLE_STATE_PENG):
            pong_results = self.__peng_rule_mgr.hasPeng(tiles, tile)
            if len(pong_results) > 0:
                # 可以碰，给用户碰的选择
                state |= MTableState.TABLE_STATE_PENG
                HYLog.debug('__process_play_tile_follow_up: seatId:', other.curSeatId, ', peng_action:', pong_results)
                actions.setInfo(MTableState.TABLE_STATE_PENG, pong_results)
            pass
        """
        杠牌
        只能明杠
        """
        if self.checkTableState(MTableState.TABLE_STATE_GANG):
            kong_results = self.__gang_rule_mgr.hasGang(tiles, tile, MTableState.TABLE_STATE_DROP)
            if len(kong_results) > 0:
                for kong in kong_results:
                    if kong['style'] != MPlayerTileGang.MING_GANG:
                        continue
                    # 可以杠，给用户杠的选择，听牌后，不改变听牌的听口
                    if other.canGang(kong, True):
                        state |= MTableState.TABLE_STATE_GANG
                        HYLog.debug('__process_play_tile_follow_up: seatId:', other.curSeatId, ', gang_action: ', kong)
                        actions.appendInfo(MTableState.TABLE_STATE_GANG, kong)
            pass
        """
        胡牌
        """
        if self.checkTableState(MTableState.TABLE_STATE_HU) and self.__win_rule_mgr.is_win01(tiles):
            state |= MTableState.TABLE_STATE_HU
            HYLog.debug('__process_play_tile_follow_up seatId:', other.curSeatId, ', win_degree: True')
            actions.appendInfo(MTableState.TABLE_STATE_HU, {'tile': tile})
            pass
        timeout = self.__table_stater.getTimeOutByState(state)
        self.__drop_card_processor.initProcessor(self.__action_id, other.curSeatId, state, actions, timeout)
        # 向当前cp发送打牌玩家打出的牌,并且告知cp可以接下来可以对该牌进行的处理操作actions,eg:吃碰杠胡等.
        self.__msg_processor.table_call_play(active_seat_id, tile, self.__players, other.curSeatId,
                                             timeout, state, self.__action_id, actions)
        # 返回结果
        return state

    def updateBao(self):
        """通知已听牌玩家宝牌"""

    """
    以下四种情况为别人打出的牌，其他人可以有的行为
    分别是
        吃
        碰
        杠
        胡
    同一人或者多个人有不同的选择，状态机的大小代表优先级。
    响应的规则是：
    优先响应最高优先级的操作，最高优先级的操作取消，响应次高优先级的操作。
    一人放弃响应，此人的状态机重置

    特殊说明：
        此时当前座位还是出牌的人
        获取出牌之外的人的状态进行比较
    """

    def do_chow_tile(self, seat_id, tile, pattern, state=MTableState.TABLE_STATE_CHI):
        """
        "吃牌"动作 框架方法(public api)
        HYActionHandlerLongNet里调用

        @param seat_id: 吃牌玩家座位号
        @param tile: 吃的牌 eg:17 (18,19吃17)
        @param pattern: 吃的牌型 eg: [17, 18, 19]
        @param state: 吃牌状态
        """
        cp = self.__players[seat_id]  # 吃牌玩家
        passive_seat_id = self.__cur_seat
        # 只传吃牌的组合，如果在听牌吃牌中，自动听牌，暂时做的是这样
        if self.__drop_card_processor.updateProcessor(self.__action_id, seat_id, state, tile, pattern):
            if self.__drop_card_processor.allResponsed():
                extend_info = self.__drop_card_processor.getExtendResultBySeatId(seat_id)
                self.__drop_card_processor.reset()

                cp.actionAdd(tile)
                cp.actionChi(pattern, tile, self.__action_id)

                self.tableTileMgr.removeMenTile(tile, passive_seat_id)  # 移除被吃牌玩家门前的牌

                self.__cur_seat = cp.curSeatId
                self.__action_id += 1

                timeout = self.__table_stater.getTimeOutByState(state)
                self.__add_card_processor.initProcessor(self.__action_id, MTableState.TABLE_STATE_DROP, \
                                                        cp.curSeatId, tile, extend_info, timeout)
                HYLog.debug('do_chow_tile: state:', state, ' tile:', tile, ' extend_info:', extend_info)

                actions = {}
                # 吃碰完能补杠
                kong = self.__gang_rule_mgr.hasGang(cp.copyTiles(), 0, state)
                if kong:
                    actions['gang_action'] = kong
                HYLog.debug("do_chow_tile: actions info: ", actions)

                # 吃完出牌，广播吃牌，如果吃听，通知用户出牌听牌
                for player in self.__players:
                    self.__msg_processor.table_call_chow(self.__players, player.curSeatId, seat_id, passive_seat_id,
                                                         tile, pattern, \
                                                         timeout, self.__action_id,
                                                         actions if player.curSeatId == seat_id else {})
        else:
            HYLog.debug('do_chow_tile: chow tile error! args:', seat_id, tile, pattern, state)

    def do_pong_tile(self, seat_id, tile, pattern, state=MTableState.TABLE_STATE_PENG):
        """
        "碰牌"动作 框架方法(public api)
        HYActionHandlerLongNet里调用

        @param seat_id: 碰牌玩家座位号
        @param tile: 碰的牌 eg:17 (17,17碰17)
        @param pattern: 碰的牌型 eg: [17, 17, 17]
        @param state: 碰牌状态
        """
        cp = self.__players[seat_id]
        passive_seat_id = self.__cur_seat
        if self.__drop_card_processor.updateProcessor(self.__action_id, seat_id, state, tile, pattern):
            if self.__drop_card_processor.allResponsed():
                extend_info = self.__drop_card_processor.getExtendResultBySeatId(seat_id)
                self.__drop_card_processor.reset()

                cp.actionAdd(tile)
                cp.actionPeng(tile, pattern, self.__action_id)

                self.__table_tile_mgr.removeMenTile(tile, passive_seat_id)  # 移除被吃牌玩家门前的牌
                self.__cur_seat = cp.curSeatId
                self.__action_id += 1

                timeout = self.__table_stater.getTimeOutByState(state)
                self.__add_card_processor.initProcessor(self.actionID, MTableState.TABLE_STATE_DROP, cp.curSeatId, tile,
                                                        extend_info, timeout)
                HYLog.debug('do_pong_tile: state:', state, ' tile:', tile, ' extend_info:', extend_info)

                actions = {}
                # 吃碰完能补杠
                kong = self.__gang_rule_mgr.hasGang(cp.copyTiles(), 0, state)
                if kong:
                    actions['gang_action'] = kong
                HYLog.debug("do_pong_tile: actions info: ", actions)

                # 碰消息广播
                for player in self.player:
                    self.__msg_processor.table_call_pong(self.__players, player.curSeatId, seat_id, passive_seat_id,
                                                         tile, pattern, \
                                                         timeout, self.__action_id,
                                                         actions if player.curSeatId == seat_id else {})
        else:
            HYLog.debug('do_pong_tile: peng tile error! args:', seat_id, tile, pattern, state)

    def do_kong_tile(self, active_seat_id, tile, pattern, style, state=MTableState.TABLE_STATE_GANG):
        """
        "杠牌"动作 框架方法(public api)
        HYActionHandlerLongNet里调用

        @param active_seat_id: 杠牌玩家座位号
        @param tile: 杠牌的牌 eg:17 (17,17,17杠17)
        @param pattern: 杠牌的牌型 eg: [17, 17, 17, 17]
        @param style: 0暗杠, 1明杠
        @param state: 杠牌状态
        """
        cp = self.__players[active_seat_id]  # 杠牌者
        passive_seat_id = self.__cur_seat
        if self.__add_card_processor.getState() != MTableState.TABLE_STATE_NEXT and cp.actionGangByAddCard(tile,
                                                                                                           pattern,
                                                                                                           style,
                                                                                                           self.__action_id,
                                                                                                           []):
            self.__add_card_processor.reset()
            # 直接杠牌
            self.__process_kong(active_seat_id, self.__cur_seat, tile, pattern, style, state, "send_tile")
        elif self.__drop_card_processor.getState() != MTableState.TABLE_STATE_NEXT:
            # 自己打牌后,自己不能杠牌,从后端逻辑上限制
            if active_seat_id == self.__cur_seat:
                HYLog.error("do_kong_tile: one cannot kong tile after tile played by oneself! seat_id:", active_seat_id)
                return

            cp.actionAdd(tile)
            self.__table_tile_mgr.removeMenTile(tile, passive_seat_id)
            if cp.actionGangByDropCard(tile, pattern, self.__action_id) and \
                    self.__drop_card_processor.updateProcessor(self.__action_id, active_seat_id, state, tile,
                                                               {"tile": tile, "pattern": pattern, "style": style}):
                if self.__drop_card_processor.allResponsed():
                    # 直接杠牌
                    self.__process_kong(active_seat_id, passive_seat_id, tile, pattern, style, state, "play")

                    # 不用检查抢杠和 直接杠牌 包含明杠和暗杠

    def __process_kong(self, active_seat_id, passive_seat_id, tile, pattern, style, state, mode):
        """
        处理"杠牌"的动作 (private api)
        @param active_seat_id 杠牌者座位号(主动) start from 0
        @param passive_seat_id 被杠牌玩家座位号(被动) start from 0
        @param tile: 杠的牌 eg:17 (17,17,17杠17)
        @param pattern: 杠的牌型 eg: 	[17, 17, 17, 17]
        @param style: 0暗杠, 1明杠
        @param state: 杠牌状态
        @param mode: 杠自己("send_tile":摸牌cmd)或者杠别人("play":打牌cmd)
        """
        cp = self.__players[active_seat_id]  # 杠牌者

        # 设置本次杠牌状态
        self.setLatestGangState(active_seat_id)
        self.__win_rule_mgr.setLastGangSeat(active_seat_id)

        self.__action_id += 1
        self.__drop_card_processor.reset()

        # 给所有人发送杠牌结果(只有结果 没有抢杠和信息)
        actions = {}
        for player in self.player:
            self.__msg_processor.table_call_kong(self.__players, player.curSeatId, active_seat_id, passive_seat_id,
                                                 tile, pattern, style, \
                                                 [], 0, self.__action_id,
                                                 actions if player.curSeatId == active_seat_id else {})

        # 杠牌之后设置当前位置为杠牌人的位置
        self.__cur_seat = cp.curSeatId

        # 杠完给玩家补发牌
        self.__process_send_tile(cp, state)

        kong_base = self.getTableConfig(MTDefine.GANG_BASE, 0)
        HYLog.debug('__process_kong kong_base: ', kong_base)
        if kong_base > 0:
            #  记录杠牌得分
            self.__round_result.add_one_budget(HYKongBudget().set_budget_base(self.__players, active_seat_id, \
                                                                              passive_seat_id, tile, pattern,
                                                                              style).calculate_budget())
            self.__process_score()  # 广播"得分"详情

    def __process_score(self):
        """
        广播"得分"详情 (private api)

        @returns [总积分数组, 当局积分变化数组]
        """
        # 加上牌桌上局数总分
        score = self.__table_result.get_score()
        score = score if score and len(score) > 0 else [0 for _ in range(self.__playerCount)]
        delta = [0 for _ in range(self.__playerCount)]
        for i in range(self.__playerCount):
            score[i] = score[i] + self.__round_result.get_score_by_seat_id(i)
            delta[i] = self.__round_result.get_delta_by_seat_id(i)

        HYLog.debug("__process_score: score&delta:", score, delta)
        self.__msg_processor.table_call_score(self.__players, score, delta)
        return score, delta

    def do_win_tile(self, active_seat_id, tile):
        """
        "胡牌"动作 框架方法(public api)
        action_handler里调用

        @param active_seat_id: 胡牌玩家座位号
        @param tile: 胡牌的牌 eg:17
        """
        passive_seat_id = self.__cur_seat  # 被胡的人(自摸者,或者点炮者)
        cp = self.__players[active_seat_id]
        # 结算的类型
        # 1 吃和
        # 0 自摸
        # -1 输牌

        loser_seat_ids = []  # 输家
        if self.__add_card_processor.getState() != MTableState.TABLE_STATE_NEXT:  # 自摸
            self.__add_card_processor.updateProcessor(self.__action_id, MTableState.TABLE_STATE_HU, active_seat_id)
            cp.actionHuByMyself(tile, True)  # 将胡牌从赢家手牌数组里移除,放在胡牌数组里,方便客户端结算显示
            # 自摸，一个人和，其他人都输
            for player in self.__players:
                if player.curSeatId != active_seat_id:
                    loser_seat_ids.append(player.curSeatId)

        elif self.__drop_card_processor.getState() != MTableState.TABLE_STATE_NEXT:  # 接炮
            if self.__drop_card_processor.updateProcessor(self.__action_id, active_seat_id, MTableState.TABLE_STATE_HU,
                                                          tile, None):
                self.__drop_card_processor.reset()
                cp.actionHuFromOthers(tile)  # 将胡牌放在胡牌数组里
                self.__table_tile_mgr.removeMenTile(tile, passive_seat_id)  # 讲胡牌从点炮人手中移除
                loser_seat_ids.append(passive_seat_id)

        self.__cur_seat = active_seat_id  # 牌权交给胡牌玩家

        # 记录杠牌得分
        win_base = self.getTableConfig(MTDefine.WIN_BASE, 1)
        HYLog.debug('do_win_tile win_base: ', win_base)

        if win_base > 0:
            self.__win_rule_mgr.is_win01(cp.copyTiles())
            self.__round_result.add_one_budget(HYWinBudget().set_budget_base(self.__players, active_seat_id, \
                                                                             passive_seat_id, loser_seat_ids, tile,
                                                                             False).calculate_budget())  # 将当前结算记录到局结算中
            self.__table_result.add_round_budget(self.__round_result)
            pass

        # 广播"得分"详情
        score, delta = self.__process_score()

        win_mode = 0

        extend_info = self.__init_custom_table_info(self.tableConfig[MFTDefine.CUR_ROUND_COUNT])

        # 广播 赢家 信息
        self.__msg_processor.table_call_budget("win", self.__players, active_seat_id, loser_seat_ids, win_mode, tile, \
                                               win_base, score[active_seat_id], delta[active_seat_id], [], False, True,
                                               extend_info)

        # 广播 输家 信息
        for seat_id in loser_seat_ids:
            self.__msg_processor.table_call_budget("lose", self.__players, seat_id, [], win_mode, 0, \
                                                   win_base, score[seat_id], delta[seat_id], [], False, True,
                                                   extend_info)

        # 处理游戏结束，判断游戏是否结束，没结束继续游戏
        self.__process_finish_game()

    def gameFlow(self, active_seat_id):
        """
        "流局"动作 框架方法(public api)
        action_handler里调用

        @param active_seat_id: 上次操作的玩家座位号
        """
        loser_seat_ids = []  # 输家
        for player in self.__players:
            loser_seat_ids.append(player.curSeatId)

        if self.__add_card_processor.getState() != MTableState.TABLE_STATE_NEXT:
            pass
        elif self.__drop_card_processor.getState() != MTableState.TABLE_STATE_NEXT:
            self.__drop_card_processor.updateProcessor(self.__action_id, active_seat_id, MTableState.TABLE_STATE_HU, 0,
                                                       None)
            self.__drop_card_processor.reset()

        win_base = self.getTableConfig(MTDefine.WIN_BASE, 1)
        HYLog.debug('gameFlow: winBase: ', win_base)

        if win_base > 0:
            self.__round_result.add_one_budget(HYWinBudget().set_budget_base(self.__players, active_seat_id, \
                                                                             active_seat_id, loser_seat_ids, 0,
                                                                             True).calculate_budget())  # 将当前结算记录到局结算中
            self.__table_result.add_round_budget(self.__round_result)
            pass

        # 广播"得分"详情
        score, delta = self.__process_score()
        win_mode = -1
        extend_info = self.__init_custom_table_info(self.tableConfig[MFTDefine.CUR_ROUND_COUNT])
        # 广播 输家 信息
        for seat_id in loser_seat_ids:
            self.__msg_processor.table_call_budget("lose", self.__players, seat_id, [], win_mode, 0, \
                                                   win_base, score[seat_id], delta[seat_id], [], True, True,
                                                   extend_info)

        # 处理游戏结束，判断游戏是否结束，没结束继续游戏
        self.__process_finish_game()

    def __process_finish_game(self):
        """
        处理"游戏结束判断" (private api)

        @return boolean True游戏结束, False游戏还不能结束
        """
        self.__process_reset_game()
        return True

    def grabHuGang(self, active_seat_id, win_tile):
        """
        "抢杠胡"动作 框架方法(public api)
        action_handler里调用

        @param active_seat_id: 抢杠胡牌玩家座位号
        @param win_tile:胡牌花色
        """

    def ting(self, active_seat_id, drop_tile):
        """
        "听牌"动作 框架方法(public api)
        action_handler里调用

        @param active_seat_id: 抢杠胡牌玩家座位号
        @param drop_tile:听口牌花色
        """

        # 不用检查抢杠和 直接杠牌 包含明杠和暗杠

    def __gangTile(self, lastSeatId, seatId, gangTile, gangPattern, style, state, afterAdd, special_tile=None,
                   qiangGangSeats=[]):
        HYLog.debug('MTableLogic.__gangTile lastSeatId', lastSeatId,
                    'seatId:', seatId, ' gangTile:', gangTile,
                    'gangPattern:', gangPattern, 'style:', style, ' state:', state)

    def getWinPlayerCount(self):
        count = 0
        for player in self.__players:
            if player.state == MPlayer.PLAYER_STATE_WON:
                count += 1
        return count

    def playerCancel(self, seatId, needSendMsg=False, userId=0):
        """用户选择放弃
        """
        tile = 0
        if self.__drop_card_processor.getState() != 0:
            HYLog.debug("playerCancel Drop", seatId)
            tile = self.__drop_card_processor.getTile()
            self.__drop_card_processor.resetSeatId(seatId)
        elif self.__add_card_processor.getState() != 0:
            HYLog.debug("playerCancel Add", seatId)
            tile = copy.deepcopy(self.__add_card_processor.getTile())
            addState = copy.deepcopy(self.__add_card_processor.state)
            cancelPlayer = self.player[seatId]
            # 成功取消才继续
            if self.__add_card_processor.updateProcessor(self.actionID, 0, seatId):
                HYLog.debug('MajiangTableLogic.playerCancel tile:', tile, ' addState:', addState)
                if cancelPlayer.isTing() and addState & MTableState.TABLE_STATE_HU:
                    HYLog.debug('MajiangTableLogic.playerCancel, user pass win, drop tile directly....')
                    self.dropTile(seatId, tile)
        elif self.__qiang_gang_hu_processor.getState() != 0:
            HYLog.debug("playerCancel QiangGangHu", seatId)
            tile = self.__qiang_gang_hu_processor.tile
            self.__qiang_gang_hu_processor.resetSeatId(seatId)
            if self.__qiang_gang_hu_processor.getState() == 0:
                HYLog.debug('__qiang_gang_hu_processor all player check')
                # 恢复挂起的杠牌状态 允许原来杠牌的玩家继续杠牌
                gangSeatId = self.__qiang_gang_hu_processor.curSeatId
                gangState = self.__qiang_gang_hu_processor.gangState
                gangSpecialTile = self.__qiang_gang_hu_processor.specialTile
                gangTile = self.__qiang_gang_hu_processor.tile
                gangPattern = self.__qiang_gang_hu_processor.gangPattern
                gangStyle = self.__qiang_gang_hu_processor.style
                self.__gangTile(self.curSeat, gangSeatId, gangTile, gangPattern, gangStyle, gangState, True,
                                gangSpecialTile, self.__qiang_gang_hu_processor.qiangGangSeats)
                HYLog.debug('__qiang_gang_hu_processor.qiangGangSeats = ',
                            self.__qiang_gang_hu_processor.qiangGangSeats)
                self.__qiang_gang_hu_processor.clearQiangGangSeats()
            if self.curState() == 0:
                self.__cur_seat = (self.__cur_seat - 1) % self.playerCount

        if self.__win_rule_mgr.isPassHu:
            # 别人出牌检查漏胡
            if self.checkTableState(MTableState.TABLE_STATE_HU) and seatId in self.__pass_hu_seats:
                # pass后将漏胡的牌加入漏胡牌数组,下次轮到自己回合时清空
                HYLog.debug("addPassHuTileByDrop", seatId, tile)
                self.tableTileMgr.addPassHuBySeatId(seatId, tile)

    def appendGangFanPattern(self, fanPattern):
        for ri in range(0, len(self.roundResult.roundResults) - 1)[::-1]:
            if self.roundResult.roundResults[ri].results[MOneResult.KEY_TYPE] == MOneResult.KEY_TYPE_NAME_HU:
                # 倒序统计杠牌信息
                break
            else:
                # 本局的杠牌记录
                if MOneResult.KEY_STAT in self.roundResult.roundResults[ri].results:
                    roundStat = self.roundResult.roundResults[ri].results[MOneResult.KEY_STAT]
                    for rsi in range(len(roundStat)):
                        for statItems in roundStat[rsi]:
                            for oneStatItemKey in statItems.keys():
                                if oneStatItemKey == MOneResult.STAT_MINGGANG:
                                    mingGangName = self.roundResult.roundResults[ri].statType[MOneResult.STAT_MINGGANG][
                                        "name"]
                                    mingGangFanPattern = [mingGangName, str(1) + "番"]
                                    if mingGangFanPattern not in fanPattern[rsi]:
                                        fanPattern[rsi].append(mingGangFanPattern)

                                if oneStatItemKey == MOneResult.STAT_ANGANG:
                                    anGangName = self.roundResult.roundResults[ri].statType[MOneResult.STAT_ANGANG][
                                        "name"]
                                    anGangFanPattern = [anGangName, str(1) + "番"]
                                    if anGangFanPattern not in fanPattern[rsi]:
                                        fanPattern[rsi].append(anGangFanPattern)
        return fanPattern

    def printTableTiles(self):
        """打印牌桌的所有手牌信息"""
        for player in self.player:
            player.printTiles()
        self.tableTileMgr.printTiles()

    def refixTableStateByConfig(self):
        """根据自建房配置调整牌桌状态"""
        itemParams = self.getTableConfig(MFTDefine.ITEMPARAMS, {})
        chipengSetting = itemParams.get('chipengsetting', 0)
        if chipengSetting == 2:
            if self.checkTableState(self.__table_stater.TABLE_STATE_CHI):
                self.__table_stater.clearState(self.__table_stater.TABLE_STATE_CHI)
                HYLog.debug("refixTableStateByConfig remove TABLE_STATE_CHI, now chi state =",
                            self.checkTableState(self.__table_stater.TABLE_STATE_CHI))
        elif chipengSetting == 1:
            if not self.checkTableState(self.__table_stater.TABLE_STATE_CHI):
                self.__table_stater.setState(self.__table_stater.TABLE_STATE_CHI)
                HYLog.debug("refixTableStateByConfig add TABLE_STATE_CHI, now chi state =",
                            self.checkTableState(self.__table_stater.TABLE_STATE_CHI))
            # 默认不改动吃的状态

    def refixTableMultipleByConfig(self):
        """根据传入配置调整输赢倍数"""
        itemParams = self.getTableConfig(MFTDefine.ITEMPARAMS, {})
        multiple = itemParams.get('multiple', 1)
        if multiple >= 1 and multiple <= 8:
            self.__win_rule_mgr.setMultiple(multiple)

    def refixTableBonusTilesSwitchByConfig(self):
        """根据传入配置调整癞子数目"""
        itemParams = self.getTableConfig(MFTDefine.ITEMPARAMS, {})
        HYLog.info("refixTableBonusTilesSwitchByConfig itemParams=", itemParams)
        bonusTilesSwitch = itemParams.get('bonusTilesSwitch', 1)
        HYLog.info("refixTableBonusTilesSwitchByConfig ", bonusTilesSwitch)
        if bonusTilesSwitch >= 1 and bonusTilesSwitch <= 2:
            self.__win_rule_mgr.setBonusTilesSwitch(bonusTilesSwitch)

    def reloadTableConfig(self):
        """根据传入配置调整参数"""
        itemParams = self.getTableConfig(MFTDefine.ITEMPARAMS, {})
        HYLog.info("loadTableConfig itemParams=", itemParams)
        if itemParams:
            self.__win_rule_mgr.setItemParams(itemParams)

    def sendLeaveMessage(self, userId):
        """userId:离线者的userId"""
        onlineInfo = []
        if self.player:
            for index in range(len(self.player)):
                onlineInfoTemp = {}
                onlineInfoTemp['seatId'] = index
                if self.player[index]:
                    if self.player[index].userId == userId:
                        onlineInfoTemp["online"] = 0
                    else:
                        if self.player[index].playerLeave:
                            onlineInfoTemp["online"] = 0
                        else:
                            onlineInfoTemp["online"] = 1
                else:
                    onlineInfoTemp["online"] = 0
                onlineInfo.append(onlineInfoTemp)
        uids = self.getBroadCastUIDs(userId)
        HYLog.info("sendLeaveMessage onlineInfo:", onlineInfo)
        self.__msg_processor.table_call_online_state(uids, onlineInfo)

    def sendEnterMessage(self):
        """userId:离线者的userId"""
        onlineInfo = []
        if self.player:
            for index in range(len(self.player)):
                onlineInfoTemp = {}
                onlineInfoTemp['seatId'] = index
                if self.player[index]:
                    HYLog.info("sendLeaveMessage self.player[index].playerLeave:", self.player[index].playerLeave)
                    if self.player[index].playerLeave:
                        onlineInfoTemp["online"] = 0
                    else:
                        onlineInfoTemp["online"] = 1
                else:
                    HYLog.info("sendLeaveMessage self.player:", self.player)
                    onlineInfoTemp["online"] = 0
                onlineInfo.append(onlineInfoTemp)
        uids = self.getBroadCastUIDs()
        HYLog.info("sendLeaveMessage onlineInfo:", onlineInfo)
        self.__msg_processor.table_call_online_state(uids, onlineInfo)

    def setPlayerLeave(self, seatId, leave):
        """设置某个玩家的离开状态"""
        if seatId < 0 or seatId > self.playerCount - 1:
            return
        if self.player:
            if self.player[seatId]:
                if leave:
                    self.player[seatId].setPlayerLeave(True)
                    HYLog.debug("table_logic.setPlayerLeaveTrue:", self.player[seatId].playerLeave)
                else:
                    self.player[seatId].setPlayerLeave(False)
                    HYLog.debug("table_logic.setPlayerLeaveFalse:", self.player[seatId].playerLeave)

    def sendNetStateToUser(self, userId, seatId, timeStamp, delta):
        """处理玩家网络状态并发送"""
        if self.__player_ping.has_key(userId):
            userData = self.__player_ping.get(userId, {})
            userData = {
                "lastTs": timeStamp,
                "delta": delta
            }
            self.__player_ping[userId] = userData
            HYLog.debug("curTimestemp:", timeStamp
                        , "delta", delta)
        else:
            self.__player_ping[userId] = {
                "lastTs": timeStamp,
                "delta": delta
            }

        pingArr = []
        for index in range(self.playerCount):
            if self.player[index]:
                if self.__player_ping.has_key(self.player[index].userId):
                    pingArr.append(self.__player_ping[self.player[index].userId].get("delta", 0))
                else:
                    pingArr.append(0)
            else:
                pingArr.append(-1)
        self.__msg_processor.table_call_ping(userId, pingArr, timeStamp)

    def isStart(self):
        if self.__table_win_state != MTableState.TABLE_STATE_NONE and self.__table_win_state != MTableState.TABLE_STATE_GAME_OVER:
            return True
        return False
