import random
import cv2 as cv
import numpy as np
import pydirectinput
from utils import mse
from utils import get_click_point
from hsvfilter import HsvFilter
from game_base_class import GameBase

GREEN = 1
YELLOW = 2
RED = 3
BLUE = 4
PURPLE = 5
RAINBOW = 6
BIGBOMB = 7
BOMB = 8
ARROW_DOWN = 9
ARROW_RIGHT = 10
ROCK_1 = 11
ROCK_2 = 12
ROCK_3 = 13
BURGER = 14
PAB1 = 15
GOLDBAR = 16
MAGINENT = 21
CHEMTRANT = 22
TENESENT = 23
CIBUTRANT = 24
ARTISENT = 25

STATUS_FOUND_CONTINUATION = "1"
STATUS_FOUND_DEADEND = "2"
STATUS_FOUND_ENDING = "3"

RAINBOW_STRATEGY = 11
BIGBOMB_STRATEGY = 9
ROCKET_STRATEGY = 7
BOMB_STRATEGY = 5


class Crops(GameBase):

    def __init__(self, overlay):
        super().__init__(overlay)

        self.screenshot = []

        self.needles = {}
        self.hsh_needles = {}

        self.colors.append(PURPLE)

        self.current_strategy = overlay.get_strategy_value_as_int()

        self.fill_data_coordinates()

        self.set_color_order((GREEN, YELLOW, RED, BLUE, PURPLE))

    def execute_main_loop(self):
        while True:
            if self.overlay.run_mode == 'paused':
                cv.waitKey(1)
                continue
            elif self.overlay.run_mode == 'stopped':
                break

            self.assess_playfield_and_make_move()
            cv.waitKey(500)

    def set_color_order(self, order):
        self.needles.clear()
        self.needles = {order[0]: cv.imread("crop/green.jpg", cv.IMREAD_COLOR),
                        order[1]: cv.imread("crop/yellow.jpg", cv.IMREAD_COLOR),
                        order[2]: cv.imread("crop/red.jpg", cv.IMREAD_COLOR),
                        order[3]: cv.imread("crop/blue.jpg", cv.IMREAD_COLOR),
                        order[4]: cv.imread("crop/purple.jpg", cv.IMREAD_COLOR),

                        RAINBOW: cv.imread("crop/rainbow.jpg", cv.IMREAD_COLOR),
                        BIGBOMB: cv.imread("crop/bigbomb.jpg", cv.IMREAD_COLOR),
                        BOMB: cv.imread("crop/bomb.jpg", cv.IMREAD_COLOR),
                        ARROW_DOWN: cv.imread("crop/arrow_down.jpg", cv.IMREAD_COLOR),
                        ARROW_RIGHT: cv.imread("crop/arrow_right.jpg", cv.IMREAD_COLOR),

                        MAGINENT: cv.imread("crop/maginent.jpg", cv.IMREAD_COLOR),
                        CHEMTRANT: cv.imread("crop/chemtrant.jpg", cv.IMREAD_COLOR),
                        TENESENT: cv.imread("crop/tenesent.jpg", cv.IMREAD_COLOR),
                        CIBUTRANT: cv.imread("crop/cibutrant.jpg", cv.IMREAD_COLOR),
                        ARTISENT: cv.imread("crop/artisent.jpg", cv.IMREAD_COLOR)
                        }
        self.hsh_needles.clear()
        self.hsh_needles = {order[0]: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            order[1]: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            order[2]: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            order[3]: HsvFilter(60, 40, 0, 115, 255, 255, 0, 0, 0, 0),
                            order[4]: HsvFilter(120, 45, 0, 170, 255, 255, 0, 0, 0, 0),

                            RAINBOW: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            BIGBOMB: HsvFilter(60, 40, 0, 179, 129, 129, 0, 0, 0, 0),
                            BOMB: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            ARROW_DOWN: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            ARROW_RIGHT: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),

                            MAGINENT: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            CHEMTRANT: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            TENESENT: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            CIBUTRANT: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0),
                            ARTISENT: HsvFilter(0, 0, 0, 179, 255, 255, 0, 0, 0, 0)
                            }

    def assess_playfield_and_make_move(self):

        new_observation, new_screenshot = self.get_current_board_state()

        # wrong movement detection
        # last board state is same as actual
        if mse(new_observation, self.observation) == 0.0:
            # no movement detected -> blow explosives or reset

            self.reset_counter += 1
            if self.reset_counter == 1:
                pass
            elif self.reset_counter == 2:
                if self.detonate_explosive_when_stuck(new_observation):
                    new_observation, new_screenshot = self.get_current_board_state()
                    if mse(new_observation, self.observation) != 0.0:
                        self.reset_counter = 0
                return
            elif self.reset_counter >= 3:
                screenshot = self.capture_window.get_screenshot()
                if self.check_for_button_and_execute(screenshot, self.reset_board):
                    cv.waitKey(500)
                screenshot = self.capture_window.get_screenshot()
                if self.check_for_button_and_execute(screenshot, self.reset_confirm):
                    cv.waitKey(500)

                color_list = [PURPLE, BLUE, RED, YELLOW, GREEN]
                random.shuffle(color_list)
                self.set_color_order(color_list)
                self.current_strategy = random.choice([RAINBOW_STRATEGY, BIGBOMB_STRATEGY, ROCKET_STRATEGY, BOMB_STRATEGY])
                self.reset_counter = 0
                return
            else:
                return

        self.find_patterns_and_valid_moves(new_observation)
        self.observation = new_observation
        return new_observation

    def get_current_board_state(self):

        # get an updated image of the game
        screenshot = self.capture_window.get_screenshot()
        # screenshot = cv.imread("playfield.jpg")
        screenshot = screenshot[58:1134, 230:2113]  # 1883,1076
        self.screenshot = screenshot
        # gray = cv.cvtColor(screenshot, cv.COLOR_BGR2GRAY)
        # thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1]
        offset_left = 230
        offset_down = 58
        if self.check_for_button_and_execute(screenshot, self.next_level, offset_left, offset_down):
            self.set_color_order((GREEN, YELLOW, RED, BLUE, PURPLE))
            self.current_strategy = RAINBOW_STRATEGY
            cv.waitKey(500)
            screenshot = self.capture_window.get_screenshot()
            screenshot = screenshot[58:1134, 230:2113]
        if self.check_for_button_and_execute(screenshot, self.next_level_x, offset_left, offset_down):
            cv.waitKey(500)
            screenshot = self.capture_window.get_screenshot()
            screenshot = screenshot[58:1134, 230:2113]

        # cv.imshow("screenshot", screenshot)
        # cv.waitKey(150)
        # continue
        data_coords = np.zeros((8, 14), dtype=object)
        # field = Field()

        for needle_key in self.needles.keys():
            # gray_needle = cv.cvtColor(self.needles[needle_key], cv.COLOR_BGR2GRAY)
            # thresh_needle = cv.threshold(gray_needle, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1]
            processed_screenshot = self.vision_stun.apply_hsv_filter(screenshot, self.hsh_needles[needle_key])
            processed_needle = self.vision_stun.apply_hsv_filter(self.needles[needle_key], self.hsh_needles[needle_key])
            rectangles = self.vision_stun.find(processed_screenshot, processed_needle, 0.70, 56)
            # rectangles = self.vision_stun.find(screenshot, self.needles[needle_key], 0.70, 56)
            if len(rectangles) == 0:
                continue
            points = self.vision_stun.get_click_points(rectangles)

            for point in points:
                x, y = self.point_in_rect(point)
                if x is not None and y is not None:
                    data_coords[x][y] = int(needle_key)
                    # self.change_value(x, y, int(needle_key))
                    # print(field.data_value_grid)
                    # cv.circle(screenshot, points[0], 7, (0, 255, 0), -1)
                    # output_image = vision_stun.draw_rectangles(screenshot, rectangles)
                    # cv.imshow("output_image", output_image)
                    # cv.waitKey(150)

        score_map = np.zeros((8, 14), dtype=object)
        for x in range(0, 8, 1):
            for y in range(0, 14, 1):
                score_map[x, y] = self.score_for_attached_same_color_all_directions(data_coords, x, y)
                #cv.putText(screenshot, str(score_map[x, y]),
                #           self.get_click_point(self.data_coordinates[x, y]), cv.FONT_HERSHEY_SIMPLEX,
                #           1, (0, 0, 0), 3, 2)
        #cv.imwrite('screenshot_scored.jpg', screenshot)

        return data_coords, screenshot

    def find_patterns_and_valid_moves(self, state):
        # score_map = np.zeros((8, 14), dtype=object)
        for x in range(0, 8, 1):
            for y in range(0, 14, 1):
                self.data_score_map[x, y] = self.score_for_attached_same_color_all_directions(state, x, y)
                if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused':
                    break
            if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused':
                break

        full_moves = []
        reserve_moves = []
        for x in range(0, 8, 1):
            for y in range(0, 14, 1):
                if self.check_explosives(state, x, y):
                    return
                if self.data_score_map[x, y] >= 4:
                    path_option = [[x, y]]
                    recursion_reminder = [[x, y]]
                    result = self.find_next_same_color_all_directions_recursion(state, x, y, path_option,
                                                                                recursion_reminder, True)
                    if result == STATUS_FOUND_ENDING:
                        path_option.append([x, y])
                    if len(path_option) >= 5:
                        full_moves.append((state[x, y], path_option))
                    if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused':
                        break
                if self.data_score_map[x, y] >= 1:
                    path_option = [[x, y]]
                    self.find_next_same_color_all_directions_recursion2(state, x, y, path_option)
                    if len(path_option) >= 3:
                        reserve_moves.append((state[x, y], path_option))
                    if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused':
                        break
                if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused':
                    break
            if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused':
                break
        # print(self.data_score_map)

        # screenshot = cv.imread("screenshot_scored.jpg")
        full_dic = self.convert_moves_to_dic_by_color(full_moves)
        res_dic = self.convert_moves_to_dic_by_color(reserve_moves)

        full_moves.sort(key=len, reverse=True)
        reserve_moves.sort(key=len, reverse=True)

        if len(full_dic) >= 1:
            for key in sorted(full_dic):
                if len(full_dic[key]) >= self.current_strategy:
                    #self.print_move(full_dic[key])
                    self.execute_move(full_dic[key])
                    return
        if len(reserve_moves) >= 1:
            for key in sorted(res_dic):
                #self.print_move(res_dic[key])
                self.execute_move(res_dic[key])
                break

    def convert_moves_to_dic_by_color(self, reserve_moves):
        dic = {}
        for color, move in reserve_moves:
            if color in dic:
                if len(move) > len(dic[color]):
                    dic[color] = move
            else:
                dic[color] = move
        return dic

    def print_move(self, move):
        mp = []
        for point in move:
            cp = self.get_click_point(self.data_coordinates[point[0], point[1]])
            mp.append(cp)
        cv.polylines(self.screenshot,
                     [np.array(mp)],
                     isClosed=False,
                     color=(0, 255, 0),
                     thickness=3)

        cv.imwrite('screenshot_pathed.jpg', self.screenshot)

    def execute_move(self, move):
        offset_left = 230
        offset_down = 58

        first = True
        for cords in move:
            square = self.data_coordinates[cords[0], cords[1]]
            pydirectinput.moveTo(get_click_point(square)[0] + offset_left, get_click_point(square)[1] + offset_down)
            cv.waitKey(50)
            if first:
                pydirectinput.mouseDown()
                cv.waitKey(100)
                first = False
        pydirectinput.mouseUp()
        cv.waitKey(50)

    def get_directions_array(self, current_x, current_y):
        left_x = current_x
        left_y = current_y - 1

        right_x = current_x
        right_y = current_y + 1

        upper_x = current_x - 1
        upper_y = current_y

        lower_x = current_x + 1
        lower_y = current_y

        lower_left_x = current_x + 1
        lower_left_y = current_y - 1

        lower_right_x = current_x + 1
        lower_right_y = current_y + 1

        upper_left_x = current_x - 1
        upper_left_y = current_y - 1

        upper_right_x = current_x - 1
        upper_right_y = current_y + 1

        directions = [[left_x, left_y], [lower_left_x, lower_left_y], [lower_x, lower_y],
                      [lower_right_x, lower_right_y], [right_x, right_y], [upper_right_x, upper_right_y],
                      [upper_x, upper_y], [upper_left_x, upper_left_y]]

        return directions

    def find_next_same_color_all_directions_recursion(self, state, current_x, current_y, path_store, recursion_reminder,
                                                      look_for_ending):
        directions = self.get_directions_array(current_x, current_y)
        color = state[current_x, current_y]

        for direction in directions:
            if self.is_direction_in_bounce_and_same_color(state, direction, color) == 1:
                if (self.data_score_map[direction[0], direction[1]] >= 2) and (direction not in recursion_reminder):
                    recursion_reminder.append(direction)
                    result = self.find_next_same_color_all_directions_recursion(state, direction[0], direction[1],
                                                                                path_store, recursion_reminder,
                                                                                look_for_ending)
                    if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused':
                        break
                    if result == STATUS_FOUND_CONTINUATION:
                        path_store.append(direction)
                        return STATUS_FOUND_CONTINUATION
                    elif result == STATUS_FOUND_DEADEND:
                        if look_for_ending:
                            continue
                        else:
                            path_store.append(direction)
                            return STATUS_FOUND_DEADEND
                    elif result == STATUS_FOUND_ENDING:
                        path_store.append(direction)
                        return STATUS_FOUND_ENDING

        if look_for_ending:
            for direction in directions:
                if direction == path_store[0]:
                    # path_store.append(direction)
                    return STATUS_FOUND_ENDING

        return STATUS_FOUND_DEADEND

    def find_next_same_color_all_directions_recursion2(self, state, current_x, current_y, path_store):
        directions = self.get_directions_array(current_x, current_y)
        color = state[current_x, current_y]

        for direction in directions:
            if self.is_direction_in_bounce_and_same_color(state, direction, color) == 1:
                if (self.data_score_map[direction[0], direction[1]] >= 1) and (direction not in path_store):
                    path_store.append(direction)
                    result = self.find_next_same_color_all_directions_recursion2(state, direction[0], direction[1],
                                                                                 path_store)
                    if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused':
                        break
                    if result == STATUS_FOUND_DEADEND:
                        return STATUS_FOUND_DEADEND

        return STATUS_FOUND_DEADEND

    def score_for_attached_same_color_all_directions(self, state, current_x, current_y):
        directions = self.get_directions_array(current_x, current_y)

        score = 0
        color = state[current_x, current_y]
        if color not in self.colors:
            return score
        for direction in directions:
            score = score + self.is_direction_in_bounce_and_same_color(state, direction, color)

        return score
