//五子棋应用

#include "stdlib.h"

#include "sys.h"
#include "systick.h"

#include "key.h"
#include "lcd.h"
#include "hc12.h"

#include "GameEngine.h"

#include "APP_Gobang.h"

/**************************************** 私有定义 ****************************************/

#define MAP_COLOR 0xdc84

#define BROADWIDTH 15
#define WIDTH (BROADWIDTH + 1)

#define BLACK_TURN 0
#define WHITE_TURN 1

#define NO_CHESS 0
#define BLACK_CHESS 1
#define WHITE_CHESS 2

#define CHECK_X 0
#define CHECK_Y 1
#define CHECK_DIAG_LEFT 2
#define CHECK_DIAG_RIGHT 3

#define CHESS_RADIUS 5

#define CONNETING_STATUS 0
#define BATTLING_STATUS 1

#define NONE_MSG 0
#define REQUEST_MSG 'R'
#define AGREE_MSG 'A'
#define REFUSE_MSG 'N'
#define CONFIRM_MSG 'X'

#define START_BYTE '#'
#define END_BYTE '&'

#define Random() rand()

/*****************************************************************************************/

/**************************************** 全局变量 ****************************************/
//棋盘交点阵
int map[WIDTH][WIDTH] = {{0, 0}};

//光标位置
int cursor_x = 7;
int cursor_y = 7;

//记录执子方
int turn = BLACK_TURN;

//棋子类型
int chess_kind = BLACK_CHESS;

//我方执子类型
int my_turn = BLACK_TURN;

//当前所处状态
int status = CONNETING_STATUS;

//联机消息
uint8_t connetion_msg = NONE_MSG;

uint8_t interval = 240 / WIDTH;
uint16_t x_start, x_end, y_start, y_end;

/*****************************************************************************************/

/**************************************** 私有函数 ****************************************/

void APP_Gobang_Init(void);
void APP_Gobang_DispGobang(void);
void APP_Gobang_DispText(void);
void APP_Gobang_DispMap(void);
void APP_Gobang_DispChess(void);
void APP_Gobang_MoveChess(void);
void APP_Gobang_DispCursor(void);
int APP_Gobang_CheckNum(int check_type);
void APP_Gobang_Msg(uint8_t *head, uint8_t *content);
void APP_Gobang_SendPosition(void);
void APP_Gobang_RevPosition(void);
uint8_t APP_Gobang_Connect(void);
uint8_t APP_Gobang_InitiateConnet(void);
uint8_t APP_Gobang_ReplyConnect(void);
void APP_Gobang_DecideMyTurn(void);
void APP_Gobang_RevMyTurn(void);
void APP_Gobang_ConfirmBeforeStart(void);
void APP_Gobang_ConnectRevHandler(uint8_t byte);

/*****************************************************************************************/

/**
  * @brief  启动五子棋
  */
void APP_Gobang_Launcher(void)
{
    srand(SysTick->VAL);
    APP_Gobang_Init();
    APP_Gobang_DispGobang();
    if (APP_Gobang_Connect() == 0)
        return;
    status = BATTLING_STATUS;
    HC12_BindReceiveHandle(NULL);
    HC12_ClearReceive;
    KEY_ClearKey();

    while (1)
    {
        APP_Gobang_DispGobang();

        if (turn == my_turn)
            APP_Gobang_MoveChess();
        else
            APP_Gobang_RevPosition();

        turn = !turn; //更换执子方

        //判断胜负
        if (
            APP_Gobang_CheckNum(CHECK_X) >= 5 ||
            APP_Gobang_CheckNum(CHECK_Y) >= 5 ||
            APP_Gobang_CheckNum(CHECK_DIAG_LEFT) >= 5 ||
            APP_Gobang_CheckNum(CHECK_DIAG_RIGHT) >= 5)
        {
            if (chess_kind == BLACK_CHESS ? 1 : 0)
                APP_Gobang_Msg("游戏结束", "黑棋获得胜利！");
            else
                APP_Gobang_Msg("游戏结束", "白棋获得胜利！");

            return;
        }
    }
}

/**
  * @brief  初始化变量
  */
void APP_Gobang_Init(void)
{
    ge_font_print_set.font_size = FONT_16;

    for (int i = 0; i < WIDTH; i++)
        for (int j = 0; j < WIDTH; j++)
            map[i][j] = NO_CHESS;

    cursor_x = cursor_y = 7;
    turn = BLACK_TURN;
    chess_kind = BLACK_CHESS;
    status = CONNETING_STATUS;
    connetion_msg = NONE_MSG;

    x_start = (LCD_WIDTH - LCD_HEIGHT) / 2 + interval / 2;
    x_end = x_start + (WIDTH - 1) * interval;
    y_start = interval / 2;
    y_end = y_start + (WIDTH - 1) * interval;
}

/**
  * @brief  绘制游戏
  */
void APP_Gobang_DispGobang(void)
{
    GE_Draw_ClrAll(MAP_COLOR);
    APP_Gobang_DispMap();
    APP_Gobang_DispChess();
    APP_Gobang_DispCursor();
    if (status == BATTLING_STATUS)
        APP_Gobang_DispText();
    GE_Draw_Disp();
}

/**
  * @brief  绘制文字
  * @param  head: 标题
  * @param  content: 内容
  */
void APP_Gobang_DispText(void)
{
    if (turn == my_turn)
        GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, interval / 2, BORDER_MAX, BORDER_MAX, "我方");
    else
        GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, interval / 2, BORDER_MAX, BORDER_MAX, "对方");
    GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, interval / 2 + FONT_16, BORDER_MAX, BORDER_MAX, "回合");

    GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, LCD_HEIGHT - interval / 2 - 2 * FONT_16, BORDER_MAX, BORDER_MAX, "我方");
    if (my_turn == BLACK_TURN)
        GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, LCD_HEIGHT - interval / 2 - FONT_16, BORDER_MAX, BORDER_MAX, "执黑");
    else
        GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, LCD_HEIGHT - interval / 2 - FONT_16, BORDER_MAX, BORDER_MAX, "执白");
}

/**
  * @brief  绘制棋盘
  */
void APP_Gobang_DispMap(void)
{
    uint8_t i = 0;
    for (i = 0; i < WIDTH; i++)
    {
        GE_Draw_Line(x_start, y_start + i * interval, x_end, y_start + i * interval, BLACK);
        GE_Draw_Line(x_start + i * interval, y_start, x_start + i * interval, y_end, BLACK);
    }
}

/**
  * @brief  绘制棋子
  */
void APP_Gobang_DispChess(void)
{
    uint8_t i, j;
    for (i = 0; i < WIDTH; i++)
    {
        for (j = 0; j < WIDTH; j++)
        {
            if (map[i][j] == BLACK_CHESS)
            {
                GE_Draw_FillCircle(x_start + j * interval, y_start + i * interval, CHESS_RADIUS, BLACK);
            }
            else if (map[i][j] == WHITE_CHESS)
            {
                GE_Draw_FillCircle(x_start + j * interval, y_start + i * interval, CHESS_RADIUS, WHITE);
            }
        }
    }
}

/**
  * @brief  移动棋子
  */
void APP_Gobang_MoveChess(void)
{
    while (1)
    {
        switch (KEY_GetKeyWait())
        {
        case JOY_U_DOWN:
        {
            cursor_x--;

            if (cursor_x < 0)
                cursor_x = WIDTH - 1;

            APP_Gobang_DispGobang();
        }
        break;

        case JOY_D_DOWN:
        {
            cursor_x++;

            if (cursor_x > WIDTH - 1)
                cursor_x = 0;

            APP_Gobang_DispGobang();
        }
        break;

        case JOY_L_DOWN:
        {
            cursor_y--;

            if (cursor_y < 0)
                cursor_y = WIDTH - 1;

            APP_Gobang_DispGobang();
        }
        break;

        case JOY_R_DOWN:
        {
            cursor_y++;

            if (cursor_y > WIDTH - 1)
                cursor_y = 0;

            APP_Gobang_DispGobang();
        }
        break;

        case JOY_OK_DOWN:
        {
            if (map[cursor_x][cursor_y] != NO_CHESS)
                break;

            chess_kind = map[cursor_x][cursor_y] = turn == BLACK_TURN ? BLACK_CHESS : WHITE_CHESS;
            APP_Gobang_SendPosition();
            APP_Gobang_DispGobang();
            return;
        }
        }
    }
}

/**
  * @brief  显示光标
  */
void APP_Gobang_DispCursor(void)
{
    GE_Draw_Circle(x_start + cursor_y * interval, y_start + cursor_x * interval, CHESS_RADIUS, RED);
}

/**
  * @brief  检查某个方向上的连子数
  * @param  check_type: 检查方向
  * @retval 连子数
  */
int APP_Gobang_CheckNum(int check_type)
{
    int temp_x = cursor_x, temp_y = cursor_y;
    int num = 0;

    while (temp_x >= 0 && temp_x < WIDTH && temp_y >= 0 && temp_y < WIDTH && map[temp_x][temp_y] == chess_kind)
    {
        num++;

        switch (check_type)
        {
        case CHECK_X:
            temp_x--;
            break;
        case CHECK_Y:
            temp_y--;
            break;
        case CHECK_DIAG_LEFT:
            temp_x--;
            temp_y--;
            break;
        case CHECK_DIAG_RIGHT:
            temp_x--;
            temp_y++;
        }
    }

    switch (check_type)
    {
    case CHECK_X:
        temp_x = cursor_x + 1;
        break;
    case CHECK_Y:
        temp_y = cursor_y + 1;
        ;
        break;
    case CHECK_DIAG_LEFT:
        temp_x = cursor_x + 1;
        temp_y = cursor_y + 1;
        break;
    case CHECK_DIAG_RIGHT:
        temp_x = cursor_x + 1;
        temp_y = cursor_y - 1;
    }

    while (temp_x >= 0 && temp_x < WIDTH && temp_y >= 0 && temp_y < WIDTH && map[temp_x][temp_y] == chess_kind)
    {
        num++;

        switch (check_type)
        {
        case CHECK_X:
            temp_x++;
            break;
        case CHECK_Y:
            temp_y++;
            break;
        case CHECK_DIAG_LEFT:
            temp_x++;
            temp_y++;
            break;
        case CHECK_DIAG_RIGHT:
            temp_x++;
            temp_y--;
        }
    }

    return num;
}

/**
  * @brief  消息框，任意键按下后退出
  * @param  head: 标题
  * @param  content: 内容
  */
void APP_Gobang_Msg(uint8_t *head, uint8_t *content)
{
    GE_Draw_Fill(60, 75, 200, 90, WHITE);
    GE_GUI_MsgBox(60, 75, 200, 90, head, content, NULL);
    KEY_WaitKey(JOY_L);
}

void APP_Gobang_SendPosition(void)
{
    uint8_t position[6];
    position[0] = START_BYTE;
    position[1] = '0' + cursor_x / 10;
    position[2] = '0' + cursor_x % 10;
    position[3] = '0' + cursor_y / 10;
    position[4] = '0' + cursor_y % 10;
    position[5] = END_BYTE;

    HC12_SendBuff(position, 6);
}

void APP_Gobang_RevPosition(void)
{
    uint8_t byte;
    uint8_t position[5];
    while (1)
    {
        if (HC12_Receive(&byte) == 1)
        {
            if (byte == START_BYTE)
            {
                HC12_ReceiveBuffUntil(position, END_BYTE, 500);
                cursor_x = (position[0] - '0') * 10 + position[1] - '0';
                cursor_y = (position[2] - '0') * 10 + position[3] - '0';

                if (map[cursor_x][cursor_y] == NO_CHESS)
                {
                    chess_kind = map[cursor_x][cursor_y] = turn == BLACK_TURN ? BLACK_CHESS : WHITE_CHESS;
                    APP_Gobang_DispGobang();
                    break;
                }
            }
        }
    }
}

uint8_t APP_Gobang_Connect(void)
{
    HC12_BindReceiveHandle(APP_Gobang_ConnectRevHandler);

    GE_Draw_Fill(60, 75, 200, 90, WHITE);
    GE_GUI_MsgBox(60, 75, 200, 90, "当前未联机", "按“OK”以发起联机", NULL);

    while (1)
    {
        switch (KEY_GetKey())
        {
        case JOY_OK_DOWN:
        {
            if (APP_Gobang_InitiateConnet())
            {
                uint8_t *head = (my_turn == BLACK_TURN ? "本局我方执黑" : "本局我方执白");
                GE_Draw_Fill(60, 75, 200, 90, WHITE);
                GE_GUI_MsgBox(60, 75, 200, 90, head, "按“OK”开始游戏", NULL);
                Delay_ms(500);
                KEY_WaitKey(JOY_OK);
                APP_Gobang_ConfirmBeforeStart();
                return 1;
            }
            else
            {
                GE_Draw_Fill(60, 75, 200, 90, WHITE);
                GE_GUI_MsgBox(60, 75, 200, 90, "当前未联机", "按“OK”以发起联机", NULL);
            }
        }
        break;

        case JOY_L_DOWN:
        {
            return 0;
        }
        break;
        }

        if (connetion_msg == REQUEST_MSG)
        {
            if (APP_Gobang_ReplyConnect())
            {
                uint8_t *head = (my_turn == BLACK_TURN ? "本局我方执黑" : "本局我方执白");
                GE_Draw_Fill(60, 75, 200, 90, WHITE);
                GE_GUI_MsgBox(60, 75, 200, 90, head, "按“OK”开始游戏", NULL);
                Delay_ms(500);
                KEY_WaitKey(JOY_OK);
                APP_Gobang_ConfirmBeforeStart();
                return 1;
            }
            else
            {
                GE_Draw_Fill(60, 75, 200, 90, WHITE);
                GE_GUI_MsgBox(60, 75, 200, 90, "当前未联机", "按“OK”以发起联机", NULL);
            }
        }
    }
}

uint8_t APP_Gobang_InitiateConnet(void)
{
    int32_t start_time = SysTick_GetRunTime();

    uint8_t msg[] = {START_BYTE, REQUEST_MSG};
    HC12_SendBuff(msg, 2);
    HC12_ClearReceive;

    GE_Draw_Fill(60, 75, 200, 90, WHITE);
    GE_GUI_MsgBox(60, 75, 200, 90, "当前未联机", "请求已发送，等待对方响应...", NULL);

    while (1)
    {
        if (SysTick_CheckRunTime(start_time) > 10000)
        {
            GE_Draw_Fill(60, 75, 200, 90, WHITE);
            GE_GUI_MsgBox(60, 75, 200, 90, "联机失败", "对方无响应,按“OK”退出", NULL);
            KEY_WaitKey(JOY_OK);
            connetion_msg = NONE_MSG;
            return 0;
        }

        if (connetion_msg == AGREE_MSG)
        {
            GE_Draw_Fill(60, 75, 200, 90, WHITE);
            GE_GUI_MsgBox(60, 75, 200, 90, "联机成功", "按“OK”进入游戏", NULL);
            //connetion_msg = NONE_MSG;
            KEY_WaitKey(JOY_OK);
            return 1;
        }

        else if (connetion_msg == REFUSE_MSG)
        {
            GE_Draw_Fill(60, 75, 200, 90, WHITE);
            GE_GUI_MsgBox(60, 75, 200, 90, "联机失败", "对方拒绝联机,按“OK”退出", NULL);
            KEY_WaitKey(JOY_OK);
            connetion_msg = NONE_MSG;
            return 0;
        }
    }
}

uint8_t APP_Gobang_ReplyConnect(void)
{
    uint8_t content[2][GE_GUI_MENUBOX_CONTENT_LEN] = {"是", "否"};
    GE_Draw_Fill(60, 75, 200, 90, WHITE);

    switch (GE_GUI_MenuBox(60, 75, 200, 90, "对方请求联机，是否接受？", 2, content, NULL))
    {
    case 0:
        return 0;
        break;

    case 1:
    {
        uint8_t msg[] = {START_BYTE, AGREE_MSG};
        HC12_SendBuff(msg, 2);
        APP_Gobang_DecideMyTurn();

        GE_Draw_Fill(60, 75, 200, 90, WHITE);
        GE_GUI_MsgBox(60, 75, 200, 90, "联机成功", "按“OK”进入游戏", NULL);
        KEY_WaitKey(JOY_OK);
        return 1;
    }
    break;

    case 2:
    {
        uint8_t msg[] = {START_BYTE, REFUSE_MSG};
        HC12_SendBuff(msg, 2);
        GE_Draw_Fill(60, 75, 200, 90, WHITE);
        GE_GUI_MsgBox(60, 75, 200, 90, "联机失败", "已拒绝联机,按“OK”退出", NULL);
        KEY_WaitKey(JOY_OK);
        return 0;
    }
    break;
    }
}

void APP_Gobang_DecideMyTurn(void)
{
    my_turn = Random() % 2;

    uint8_t msg[] = {START_BYTE, !my_turn};
    HC12_SendBuff(msg, 2);
}

void APP_Gobang_ConfirmBeforeStart(void)
{
    if (my_turn == BLACK_TURN)
    {
        GE_Draw_Fill(60, 75, 200, 90, WHITE);
        GE_GUI_MsgBox(60, 75, 200, 90, "游戏即将开始", "正在确认白色方设备状态...", NULL);
        while (connetion_msg != CONFIRM_MSG)
        {
            Delay_ms(100);
        }
        GE_Draw_Fill(60, 75, 200, 90, WHITE);
        GE_GUI_MsgBox(60, 75, 200, 90, "准备就绪", "按“OK”开始游戏", NULL);
        Delay_ms(500);
        KEY_WaitKey(JOY_OK);
    }
    else
    {
        uint8_t msg[] = {START_BYTE, CONFIRM_MSG};
        HC12_SendBuff(msg, 2);
    }
}

void APP_Gobang_ConnectRevHandler(uint8_t byte)
{
    static uint8_t is_receiving = FALSE;

    if (is_receiving == FALSE && byte == START_BYTE)
    {
        is_receiving = TRUE;
        return;
    }

    if (is_receiving == TRUE)
    {
        if (byte == REQUEST_MSG || byte == AGREE_MSG || byte == REFUSE_MSG || byte == CONFIRM_MSG)
        {
            connetion_msg = byte;
            is_receiving = FALSE;
        }
        else if (byte == BLACK_TURN || byte == WHITE_TURN)
        {
            my_turn = byte;
            is_receiving = FALSE;
        }
    }
}