import cv2 as cv
import numpy as np
from utils import mse
from game_base_class import GameBase

GREEN = 1
YELLOW = 2
BLUE = 3
RED = 4
PINK = 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


class Farm(GameBase):

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

        self.colors.append(PINK)

        self.fill_data_coordinates()

        self.needles = {GREEN: cv.imread("farm/green.jpg", cv.IMREAD_COLOR),
                        YELLOW: cv.imread("farm/yellow.jpg", cv.IMREAD_COLOR),
                        BLUE: cv.imread("farm/blue.jpg", cv.IMREAD_COLOR),
                        RED: cv.imread("farm/red.jpg", cv.IMREAD_COLOR),
                        PINK: cv.imread("farm/pink.jpg", cv.IMREAD_COLOR),
                        RAINBOW: cv.imread("farm/rainbow.jpg", cv.IMREAD_COLOR),
                        BIGBOMB: cv.imread("farm/bigbomb.jpg", cv.IMREAD_COLOR),
                        BOMB: cv.imread("farm/bomb.jpg", cv.IMREAD_COLOR),
                        ARROW_DOWN: cv.imread("farm/arrow_down.jpg", cv.IMREAD_COLOR),
                        ARROW_RIGHT: cv.imread("farm/arrow_right.jpg", cv.IMREAD_COLOR),
                        ROCK_1: cv.imread("farm/rock1.jpg", cv.IMREAD_COLOR),
                        ROCK_2: cv.imread("farm/rock2.jpg", cv.IMREAD_COLOR),
                        ROCK_3: cv.imread("farm/rock3.jpg", cv.IMREAD_COLOR),
                        BURGER: cv.imread("farm/burger.jpg", cv.IMREAD_COLOR),
                        GOLDBAR: cv.imread("farm/burger.jpg", cv.IMREAD_COLOR),
                        PAB1: cv.imread("farm/pab1.jpg", cv.IMREAD_COLOR),
                        MAGINENT: cv.imread("farm/maginent.jpg", cv.IMREAD_COLOR),
                        CHEMTRANT: cv.imread("farm/chemtrant.jpg", cv.IMREAD_COLOR),
                        TENESENT: cv.imread("farm/tenesent.jpg", cv.IMREAD_COLOR),
                        CIBUTRANT: cv.imread("farm/cibutrant.jpg", cv.IMREAD_COLOR),
                        ARTISENT: cv.imread("farm/artisent.jpg", cv.IMREAD_COLOR)
                        }

    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()

    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)

                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("field_farm.jpg")
        screenshot = screenshot[58:1134, 230:2113]  # 1883,1076
        # gray = cv.cvtColor(screenshot, cv.COLOR_BGR2GRAY)
        # thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1]
        if self.check_for_next_level(screenshot, self.next_level):
            cv.waitKey(500)
            screenshot = self.capture_window.get_screenshot()
            screenshot = screenshot[58:1134, 230:2113]
        if self.check_for_next_level(screenshot, self.next_level_x):
            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]
            rectangles = self.vision_stun.find(screenshot, self.needles[needle_key], 0.74, 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)
        return data_coords, screenshot

    def find_patterns_and_valid_moves(self, state):
        for e in range(0, 8, 1):
            for i in range(0, 14, 1):
                if self.check_explosives(state, e, i):
                    return
                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

        for e in range(0, 8, 1):
            for i in range(0, 14, 1):
                for color in self.colors:
                    if self.check_5_horizontal(state, e, i, color):
                        return
                    if self.check_5_vertical(state, e, i, color):
                        return
                    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

        for e in range(0, 8, 1):
            for i in range(0, 14, 1):
                for color in self.colors:
                    if self.check_3_horizontal(state, e, i, color):
                        return
                    if self.check_3_vertical(state, e, i, color):
                        return
                    if self.check_3_with_gap(state, e, i, color):
                        return
                    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

    def detonate_explosive_when_stuck(self, state):
        for e in range(0, 8, 1):
            for i in range(0, 14, 1):
                for explosive in self.explosives:
                    if self.local_pos_check(state, e, i, 0, 0, explosive):
                        dest_pt = self.get_click_point(self.data_coordinates[e, i])
                        if self.local_pos_checks(state, e, i, 1, 0, self.colors):
                            src_pt = self.get_click_point(self.data_coordinates[e + 1, i])
                            self.move_tile(src_pt, dest_pt)
                            return True
                        elif self.local_pos_checks(state, e, i, 0, 1, self.colors):
                            src_pt = self.get_click_point(self.data_coordinates[e, i + 1])
                            self.move_tile(src_pt, dest_pt)
                            return True
                        elif self.local_pos_checks(state, e, i, -1, 0, self.colors):
                            src_pt = self.get_click_point(self.data_coordinates[e - 1, i])
                            self.move_tile(src_pt, dest_pt)
                            return True
                        elif self.local_pos_checks(state, e, i, 0, -1, self.colors):
                            src_pt = self.get_click_point(self.data_coordinates[e, i - 1])
                            self.move_tile(src_pt, dest_pt)
                            return True
                        else:
                            continue
        return False

    def check_5_horizontal(self, state, e, i, color):
        try:
            #
            if state[e, i] == color and state[e, i + 1] == color and state[e, i + 3] == color and state[
                e, i + 4] == color:
                if state[e, i + 2] <= 0 or state[e, i + 2] >= 6:
                    return False
                #
                if e - 1 >= 0 and i + 2 <= 13:
                    if state[e - 1, i + 2] == color:
                        return True
            return False
        except:
            return False

    def check_5_vertical(self, state, e, i, color):
        try:
            #
            if state[e, i] == color and state[e + 1, i] == color and state[e + 3, i] == color and state[
                e + 4, i] == color:
                if state[e + 2, i] <= 0 or state[e + 2, i] >= 6:
                    return False
                # third left upper
                if e + 2 <= 7 and i - 1 >= 0:
                    if state[e + 2, i - 1] == color:
                        # print("upper left", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e + 2, i - 1])
                        dest_pt = self.get_click_point(self.data_coordinates[e + 2, i])
                        self.move_tile(src_pt, dest_pt)
                        return True
                if e + 2 <= 7 and i + 1 <= 13:
                    if state[e + 2, i + 1] == color:
                        # print("upper left", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e + 2, i + 1])
                        dest_pt = self.get_click_point(self.data_coordinates[e + 2, i])
                        self.move_tile(src_pt, dest_pt)
                        return True
            return False
        except:
            return False

    def check_3_with_gap(self, state, e, i, color):
        try:
            # second color next to starting point
            if i + 2 <= 13:
                if state[e, i] == color and state[e, i + 2] == color:
                    # third upper
                    if e - 1 >= 0 and i + 1 <= 13:
                        # if state[e - 1, i - 1] == color and (state[e, i - 1] >= 1 and state[e, i - 1] <= 5):
                        if state[e - 1, i + 1] == color and (state[e, i + 1] in self.colors):
                            src_pt = self.get_click_point(self.data_coordinates[e - 1, i + 1])
                            dest_pt = self.get_click_point(self.data_coordinates[e, i + 1])
                            self.move_tile(src_pt, dest_pt)
                            return True
                    # third left lower
                    if e + 1 <= 7 and i + 1 <= 13:
                        if state[e + 1, i + 1] == color and (state[e, i + 1] in self.colors):
                            src_pt = self.get_click_point(self.data_coordinates[e + 1, i + 1])
                            dest_pt = self.get_click_point(self.data_coordinates[e, i + 1])
                            self.move_tile(src_pt, dest_pt)
                            return True
            if e + 2 <= 7:
                if state[e, i] == color and state[e + 2, i] == color:
                    # third upper
                    if e + 1 >= 0 and i + 1 <= 13:
                        # if state[e - 1, i - 1] == color and (state[e, i - 1] >= 1 and state[e, i - 1] <= 5):
                        if state[e + 1, i + 1] == color and (state[e + 1, i] in self.colors):
                            src_pt = self.get_click_point(self.data_coordinates[e + 1, i + 1])
                            dest_pt = self.get_click_point(self.data_coordinates[e + 1, i])
                            self.move_tile(src_pt, dest_pt)
                            return True
                    # third left lower
                    if e + 1 <= 7 and i - 1 >= 0:
                        if state[e + 1, i - 1] == color and (state[e + 1, i] in self.colors):
                            src_pt = self.get_click_point(self.data_coordinates[e + 1, i - 1])
                            dest_pt = self.get_click_point(self.data_coordinates[e + 1, i])
                            self.move_tile(src_pt, dest_pt)
                            return True
        except:
            return False

    def check_3_horizontal(self, state, e, i, color):
        try:
            # second color next to starting point
            if state[e, i] == color and state[e, i + 1] == color:
                # third left upper
                if e - 1 >= 0 and i - 1 >= 0:
                    if state[e - 1, i - 1] == color and (state[e, i - 1] in self.colors):
                        # print("upper left", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e - 1, i - 1])
                        dest_pt = self.get_click_point(self.data_coordinates[e, i - 1])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third left lower
                if e + 1 <= 7 and i - 1 >= 0:
                    if state[e + 1, i - 1] == color and (state[e, i - 1] in self.colors):
                        # print("lower left", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e + 1, i - 1])
                        dest_pt = self.get_click_point(self.data_coordinates[e, i - 1])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third left with gap
                if i - 2 >= 0:
                    if state[e, i - 2] == color and (state[e, i - 1] in self.colors):
                        # print("left gap ", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e, i - 2])
                        dest_pt = self.get_click_point(self.data_coordinates[e, i - 1])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third right upper
                if e - 1 >= 0 and i + 2 <= 13:
                    if state[e - 1, i + 2] == color and (state[e, i + 2] in self.colors):
                        # print("upper right", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e - 1, i + 2])
                        dest_pt = self.get_click_point(self.data_coordinates[e, i + 2])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third right lower
                if e + 1 <= 7 and i + 2 <= 13:
                    if state[e + 1, i + 2] == color and (state[e, i + 2] in self.colors):
                        # print("upper lower", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e + 1, i + 2])
                        dest_pt = self.get_click_point(self.data_coordinates[e, i + 2])
                        self.move_tile(src_pt, dest_pt)
                        return True

                # third right with gap
                if i + 3 <= 13:
                    if state[e, i + 3] == color and (state[e, i + 2] in self.colors):
                        # print("right gap ", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e, i + 3])
                        dest_pt = self.get_click_point(self.data_coordinates[e, i + 2])
                        self.move_tile(src_pt, dest_pt)
                        return True
        except:
            return False

    def check_3_vertical(self, state, e, i, color):
        try:
            # second color next to starting point o
            if state[e, i] == color and state[e + 1, i] == color:
                # third left upper
                if e - 1 >= 0 and i - 1 >= 0:
                    if state[e - 1, i - 1] == color and (state[e - 1, i] in self.colors):
                        # print("upper left", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e - 1, i - 1])
                        dest_pt = self.get_click_point(self.data_coordinates[e - 1, i])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third left lower
                if e + 2 <= 7 and i - 1 >= 0:
                    if state[e + 2, i - 1] == color and (state[e + 2, i] in self.colors):
                        print("lower left", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e + 2, i - 1])
                        dest_pt = self.get_click_point(self.data_coordinates[e + 2, i])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third right upper
                if e - 1 >= 0 and i + 1 <= 13:
                    if state[e - 1, i + 1] == color and (state[e - 1, i] in self.colors):
                        # print("upper right", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e - 1, i + 1])
                        dest_pt = self.get_click_point(self.data_coordinates[e - 1, i])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third right lower
                if e + 2 <= 7 and i + 1 <= 13:
                    if state[e + 2, i + 1] == color and (state[e + 2, i] in self.colors):
                        # print("upper lower", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e + 2, i + 1])
                        dest_pt = self.get_click_point(self.data_coordinates[e + 2, i])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third upper with gap
                if e - 2 >= 0:
                    if state[e - 2, i] == color and (state[e - 1, i] in self.colors):
                        # print("upper gap ", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e - 2, i])
                        dest_pt = self.get_click_point(self.data_coordinates[e - 1, i])
                        self.move_tile(src_pt, dest_pt)
                        return True
                # third lower with gap
                if e + 3 <= 7:
                    if state[e + 3, i] == color and (state[e + 2, i] in self.colors):
                        # print("lower gap ", color, e, i)
                        src_pt = self.get_click_point(self.data_coordinates[e + 3, i])
                        dest_pt = self.get_click_point(self.data_coordinates[e + 2, i])
                        self.move_tile(src_pt, dest_pt)
                        return True
        except:
            return False
