from PIL import Image, ImageFilter, ImageDraw
import os
import numpy as np
import time
import random

# 该数值为1080x1920上的,可能需要微调
# 调整方法:
# 先把下方的DEVICE_SCREEN参数改成你手机的分辨率
# 用adb命令 ‘adb shell input swipe 580 1600 580 1600 XXX’ 尝试跳第一个点,记录准确的XXX
# DISTANCE_ARG 为 XXX 除以 跳第一个点算出来的距离
# 例如,我运行第一遍算出来的distance为562.5,记录到的XXX为720
# 那么此处的DISTANCE_ARG 为 720/562.5 = 1.28
# 还没在很多机型上试过,后期会将该过程封装起来,目前大概是这么调整
DISTANCE_ARG = 1.3925
# 设备型号
DEVICE_SCREEN = (1080, 1920)
# 每次跳的停等时间,如果前期纪录较低建议设为2以防止“超越”字样的影响
WAIT_TIME = 2

# ----------------------------------------------------------

# 临时文件位置
TEMP_FILE_PATH = 'temp.png'
# 棋子底端中心点到棋子边缘的距离
CHESS_WIDTH = int(DEVICE_SCREEN[0] * 0.032407)
# 屏蔽区域厚度
IGNORE_HEIGHT = (int(DEVICE_SCREEN[1] / 4), int(DEVICE_SCREEN[1] / 2))
# 棋子的RGB数值,可能因为设备不同有偏差,可能需要微调
SELF_RGB = (62, 56, 79)


def get_pic(_pic_path: '临时图片路径'):
    """ 从设备中获取截图 """
    os.system('adb shell screencap -p /sdcard/wx.png')
    os.system('adb pull /sdcard/wx.png {}'.format(_pic_path))


def calculate_time(dis: '距离'):
    """ 根据距离计算时间 """
    _result = int(dis * DISTANCE_ARG)
    return _result if _result > 200 else 200


def get_distance(point1, point2):
    """ 计算两点间距离 """
    draw = ImageDraw.Draw(Image.open('temp.png'))
    draw.arc((point2[0], point2[1], point2[0] + 20, point2[1] + 20), 0, 360, fill=150)

    return ((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2) ** 0.5


def get_self_position(_img_path: '临时图片路径'):
    """ 获取自身位置 """
    _img = Image.open(_img_path)

    point_list = list()
    for x in range(DEVICE_SCREEN[0]):
        for y in range(DEVICE_SCREEN[1]):
            each_point = _img.getpixel((x, y))
            if rgb_compare(SELF_RGB, each_point):
                point_list.append((x, y))

    return point_list[-1][0]-CHESS_WIDTH, point_list[-1][1]


def rgb_compare(a, b):
    """ 两个RGB点的比较 """
    for i in range(3):
        if abs(a[i] - b[i]) >= 5:
            return False
    else:
        return True


def get_des_position(_img_path: '临时图片路径', _self_point: '起始点坐标'):
    """ 获取目标点位置 """
    _img = Image.open(_img_path)
    # 两次边缘检测
    _img = _img.filter(ImageFilter.FIND_EDGES)
    _img = _img.filter(ImageFilter.FIND_EDGES)
    # 2 value
    _img = _img.convert('1')
    _img.save('temp1.png')
    # 排除顶端的干扰
    _img = np.array(_img)[IGNORE_HEIGHT[0]:]
    # 按行扫描图片
    for index, each in enumerate(_img):
        old_line = _img[index-1]
        # 如果有变化说明检测到顶端
        if (each[1:-1] - old_line[1:-1]).any():
            # black line
            if any(map(lambda x: list(x).count(True) > int(len(each)/2), (each, old_line))):
                continue
            else:
                des_x = _get_des_x(each, old_line)
                des_y = _get_des_y(index, des_x, _img)
                if abs(des_x - self_point[0]) < CHESS_WIDTH * 2:
                    continue
                else:
                    break
    else:
        raise ValueError('Something error.')
    return des_x, des_y


def _get_des_x(line1, line2):
    """ 获取目标点横坐标,通过前后行的差异匹配 """
    for i, a in enumerate(zip(line1[1:-1], line2[1:-1])):
        if a[0] != a[1]:
            return i + 1
    else:
        raise ValueError('Nothing different.')


def _get_des_y(_cur_row: '目标顶端所在行', _des_x: '目标点横坐标', _img: '图片矩阵'):
    """ 目标顶端从上往下扫描,如果右侧边缘不继续递增说明到达边界 """
    _rows = _img[_cur_row:]
    _des_x += list(_rows[0][_des_x::]).index(False)
    for row_num, each_row in enumerate(_rows[1:]):
        _next = list(_rows[row_num+1][_des_x:]).index(True) if True in list(_rows[row_num+1][_des_x:]) else 0
        if _next > 15:
            _next = list(_rows[row_num+2][_des_x:]).index(True)
            if _next > 15:
                return row_num + IGNORE_HEIGHT[0] + _cur_row + 1
            else:
                _des_x += _next
        elif _next == 0:
            _des_x += 1
        else:
            _des_x += _next
        if _des_x >= DEVICE_SCREEN[0]:
            return row_num + IGNORE_HEIGHT[0] + _cur_row + 1
    else:
        raise ValueError('NO DES POINT FOUND.')


def print_log(_self_point, _des_point, _distance, _t):
    """ 打印计算结果方便调试 """
    print('self location: {}, {}'.format(_self_point[0], _self_point[1]))
    print('des location: {}, {}'.format(_des_point[0], _des_point[1]))
    print('x distance: {}'.format(_distance))
    print('press time: {}'.format(_t))


def apply_to_adb(_t: '长按时间'):
    """ 用adb操作手机 """
    r_x, r_y = random.uniform(DEVICE_SCREEN[0]/2, DEVICE_SCREEN[0]/2 + 100), \
               random.uniform(DEVICE_SCREEN[1]/6, DEVICE_SCREEN[1]/6 - 100)
    os.system('adb shell input swipe {} {} {} {} {}'.format(r_x, r_y, r_x, r_y, _t))
    time.sleep(WAIT_TIME + random.random())


if __name__ == '__main__':
    while True:
        # get screen pic
        get_pic(TEMP_FILE_PATH)

        # get self location
        self_point = get_self_position(TEMP_FILE_PATH)

        # get des location
        des_point = get_des_position(TEMP_FILE_PATH, self_point)

        # get distance
        distance = get_distance(self_point, des_point)

        # cal press time
        t = calculate_time(distance)

        # print log
        print_log(self_point, des_point, distance, t)

        # DO
        apply_to_adb(t)