;================================================================
; mahjong/mahjong_play 麻将游戏 实际业务逻辑
; mahjong_handle.erb 准备对局
;================================================================


;;;使用 Fisher-Yates 洗牌算法重新洗牌
;;;- (option)int tiles_type 使用的麻将牌类型
;;;@return 没有返回值
@MahjongShuffleTilesWall(tiles_type=0)
    #DIM tiles_type  ; 0-标准四麻 1-三麻 2-川麻
    #DIM DYNAMIC array_length
    #DIM DYNAMIC i
    array_length = tiles_type ? 108 # 136
    VARSET MJ_TILES_WALL, 0
    SELECTCASE tiles_type
        CASE 1
            FOR i, 0, array_length
                MJ_TILES_WALL:i = MJ_ENUM_TILE_SM:i
            NEXT
        CASE 2
            FOR i, 0, array_length
                MJ_TILES_WALL:i = MJ_ENUM_TILE_SC:i
            NEXT
        CASEELSE
            FOR i, 0, array_length
                MJ_TILES_WALL:i = MJ_ENUM_TILE:i
            NEXT
    ENDSELECT
    FOR i, 0, array_length - 1
        SWAP MJ_TILES_WALL:i, MJ_TILES_WALL:(RAND(i, array_length))
    NEXT
    CALL PrintLog("洗牌完成。")
    RETURN


;;;获取牌山余牌数量
;;;@return 余牌数量
@mj_count_wall()
#FUNCTION
    #DIM DYNAMIC remove_count
    SELECTCASE MJ_GAME_RULE
        CASE 0  ; 四麻
            remove_count = 14  ; 去掉 14 张王牌（宝牌1 + 里宝牌1 + 杠宝牌4 + 杠里宝牌4 + 岭上牌4）
        CASE 1  ; 三麻：没有二万～八万 7 * 4 = 28
            remove_count = 28
        CASE 2  ; 川麻：没有字牌（东南西北白发中）7 * 4 = 28
            remove_count = 28
        CASEELSE
            remove_count = MATCH(MJ_TILES_WALL, 0)
    ENDSELECT
    RETURNF 136 - remove_count - MJ_KAN_COUNT - MJ_TILES_CURSOR


;;;给某家发一张麻将牌
;;;- int seat 座次
;;;- int tile_id 麻将牌ID
;;;@return 没有返回值
@MahjongDealTileTo(seat, tile_id)
    #DIM seat
    #DIM tile_id
    #DIM DYNAMIC i
    FOR i, 0, MJ_HAND_LIMIT
        IF MJ_HAND:seat:i < 100 || MJ_HAND:seat:i > 500
            MJ_HAND:seat:i = tile_id
            BREAK
        ENDIF
    NEXT
    RETURN


;;;某家主动摸一张牌
;;;- int seat 座次
;;;@return 摸到的麻将牌ID
@MahjongDrawTo(seat)
    #DIM seat
    #DIM DYNAMIC i
    #DIM DYNAMIC new_tile_id
    SIF mj_count_hand(seat) % 3 != 1
        RETURN 0
    new_tile_id = MJ_TILES_WALL:MJ_TILES_CURSOR
    CALL MahjongSortHand(seat)
    CALL MahjongDealTileTo(seat, new_tile_id)
    MJ_TILES_CURSOR += 1
    FOR i, 0, VARSIZE("MJ_GAIN_HISTORY", 1)
        IF MJ_GAIN_HISTORY:seat:i == ""
            MJ_GAIN_HISTORY:seat:i '= TOSTR(mj_id_to_value(new_tile_id))
            BREAK
        ENDIF
    NEXT
    RETURN new_tile_id


;;;某家尝试切一张牌
;;;- int seat 座次
;;;- str discard_code 想要切掉的麻将牌代码
;;;@return 切掉的麻将牌ID
@mj_discard_by(seat, discard_code)
#FUNCTION
    #DIM seat
    #DIMS discard_code
    #DIM DYNAMIC i
    #DIM DYNAMIC discard_id
    SIF mj_count_hand(seat) % 3 != 2
        RETURNF 0
    FOR i, 0, mj_count_hand(seat)
        IF mj_to_code(MJ_HAND:seat:i) == discard_code
            discard_id = MJ_HAND:seat:i
            MJ_HAND:seat:i = 999
            BREAK
        ENDIF
    NEXT
    FOR i, 0, VARSIZE("MJ_DISCARD_PILE", 1)
        IF MJ_DISCARD_PILE:seat:i == ""
            MJ_DISCARD_PILE:seat:i '= TOSTR(mj_id_to_value(discard_id))
            BREAK
        ENDIF
    NEXT
    MJ_LAST_DISCARD = discard_id
    MJ_LAST_DISCARD_SEAT = seat
    RETURNF discard_id


;;;按座次发放手牌（模拟各家摸起始手牌）
;;;@return 没有返回值
@MahjongDealStartingHand()
    #DIM CONST TileColor, 4 = 0xd32f2f, 0xf57c00, 0xfbc02d, 0x388e3c
    #DIM DYNAMIC i
    #DIM DYNAMIC j
    #DIM DYNAMIC k
    #DIM DYNAMIC temp_cursor
    #DIM DYNAMIC seat
    VARSET MJ_HAND:0:0, 999  ; 重置各家手牌
    MJ_TILES_CURSOR = 0      ; 重置当前牌山游标
    ; 每家各摸 12 张
    FOR i, 0, MJ_PLAYER_COUNT
        FOR j, 0, 3
            FOR k, 0, 4
                temp_cursor = i * 4 + j * MJ_PLAYER_COUNT * 4 + k
                CALL MahjongDealTileTo(i, MJ_TILES_WALL:temp_cursor)
                MJ_TILES_CURSOR += 1
            NEXT
        NEXT
    NEXT
    ; 各家跳牌
    seat = 0
    FOR i, 0, MJ_PLAYER_COUNT + 1
        IF i == 2
            CALL MahjongDealTileTo(0, MJ_TILES_WALL:MJ_TILES_CURSOR)
        ELSE
            CALL MahjongDealTileTo(seat, MJ_TILES_WALL:MJ_TILES_CURSOR)
            seat += 1
        ENDIF
        MJ_TILES_CURSOR += 1
    NEXT
    CALL PrintLog("起始手牌发放完成。")
    RETURN


;;;排序某家手牌
;;;- (option)int seat
;;;@return 没有返回值
@MahjongSortHand(seat=-1)
    #DIM seat
    #DIM DYNAMIC idx
    #DIM DYNAMIC hand, MJ_HAND_LIMIT
    #DIM DYNAMIC hand_count
    SIF seat < 0 || seat >= MJ_PLAYER_COUNT
        seat = MJ_PLAYER_SEAT
    FOR idx, 0, VARSIZE("MJ_HAND", 1)
        IF mj_to_code(MJ_HAND:seat:idx) == ""
            hand:idx = 999
        ELSE
            hand:idx = MJ_HAND:seat:idx
            hand_count += 1
        ENDIF
    NEXT
    ARRAYSORT hand
    FOR idx, 0, VARSIZE("MJ_HAND", 1)
        MJ_HAND:seat:idx = hand:idx
    NEXT
    RETURN


;;;获取某家的副露
;;;- int seat 座次
;;;@return 副露数组(字符串形式)
@mj_get_melds(seat)
#FUNCTIONS
    #DIM seat
    #DIM DYNAMIC i
    #DIMS DYNAMIC melds
    FOR i, 0, VARSIZE("MJ_GAIN_HISTORY", 1)
        SIF MJ_GAIN_HISTORY:seat:i == ""
            BREAK
        SIF !ISNUMERIC(MJ_GAIN_HISTORY:seat:i)
            melds += (melds == "" ? "" # ",") + MJ_GAIN_HISTORY:seat:i
    NEXT
    RETURNF melds


;;;获取某家的可操作项
;;;- int seat 座次
;;;@return 可操作项
@mj_get_options(seat)
#FUNCTION
    #DIM seat
    #DIM DYNAMIC playing
    #DIM DYNAMIC allow_pass
    #DIM DYNAMIC options
    playing = seat == MJ_CURRENT_PLAYER && mj_count_hand(seat) % 3 == 2
    IF playing && mj_can_tsumo(seat)
        options |= MJ_ENUM_WIN
    ELSEIF mj_call_ron(mj_get_hand_code(seat), mj_to_code(MJ_LAST_DISCARD))
        options |= MJ_ENUM_WIN
        allow_pass = 1
    ENDIF
    SIF playing && mj_can_riichi(seat)
        options |= MJ_ENUM_RIICHI
    IF playing && mj_can_kan(seat)
        options |= MJ_ENUM_KAN
    ELSEIF mj_call_kan(mj_get_hand_code(seat), mj_to_code(MJ_LAST_DISCARD)) != ""
        options |= MJ_ENUM_KAN
        allow_pass = 1
    ENDIF
    IF !playing && mj_call_pon(mj_get_hand_code(seat), mj_to_code(MJ_LAST_DISCARD)) != ""
        options |= MJ_ENUM_PON
        allow_pass = 1
    ENDIF
    IF !playing && seat == mj_get_next_seat(MJ_CURRENT_PLAYER) && mj_call_chii(mj_get_hand_code(seat), mj_to_code(MJ_LAST_DISCARD)) != ""
        options |= MJ_ENUM_CHII
        allow_pass = 1
    ENDIF
    SIF allow_pass
        options |= MJ_ENUM_SKIP
    SIF playing && mj_can_ryuukyoku(seat)
        options |= MJ_ENUM_DRAW
    RETURNF options


;;;相对位置转绝对位置
;;;- int aspect 相对位置(自家/下家/对家/上家)
;;;@return 绝对位置(东家/南家/西家/北家)
@mj_get_seat_from_aspect(aspect)
#FUNCTION
    #DIM aspect
    RETURNF (aspect + MJ_PLAYER_SEAT) % 4


;;;绝对位置转相对位置
;;;- int seat 绝对位置(东家/南家/西家/北家)
;;;@return 相对位置(自家/下家/对家/上家)
@mj_get_aspect_from_seat(seat)
#FUNCTION
    #DIM seat
    RETURNF (seat + 4 - MJ_PLAYER_SEAT) % 4


;;;获取下一家的绝对位置
;;;- int seat 绝对位置(东家/南家/西家/北家)
;;;@return 下一家的绝对位置(南家/西家/北家/东家)
@mj_get_next_seat(seat=-1)
#FUNCTION
    #DIM seat
    SIF seat < 0 || seat >= MJ_PLAYER_COUNT
        seat = MJ_PLAYER_SEAT
    RETURNF (seat + 1) % 4
