import pydirectinput
import random
import cv2 as cv
import numpy as np
from window_capture import WindowCapture
from vision import Vision
from config_file import UserConfigs

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


class GameBase:

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

        self.data_value_grid = np.zeros((8, 14), dtype=int)
        self.data_coordinates = np.zeros((8, 14), dtype=object)
        self.observation = np.zeros((8, 14), dtype=int)
        self.data_score_map = np.zeros((8, 14), dtype=int)

        self.explosives = [RAINBOW, ARROW_RIGHT, ARROW_DOWN, BIGBOMB, BOMB]
        self.colors = [GREEN, YELLOW, RED, BLUE, MAGINENT, CHEMTRANT, TENESENT, CIBUTRANT, ARTISENT]

        self.next_level = cv.imread("control_elements/next_level.jpg", cv.IMREAD_COLOR)
        self.next_level_x = cv.imread("control_elements/next_level_x.jpg", cv.IMREAD_COLOR)
        self.reset_board = cv.imread("control_elements/reset_button.jpg", cv.IMREAD_COLOR)
        self.reset_confirm = cv.imread("control_elements/reset_confirm.jpg", cv.IMREAD_COLOR)

        self.reset_counter = 0

        # initialize the user-class
        self.config = UserConfigs()
        # initialize the StunWindowCapture class
        self.capture_window = WindowCapture(None, None, self.config)
        # initialize the StunVision class
        self.vision_stun = Vision()

    def fill_data_coordinates(self):
        # 230 to 2110 = 1883 / 14 = 134.5
        # 60 to 1130 = 1076 / 8 =  134.5
        dim = 134.5
        for e in range(0, 8, 1):
            for i in range(0, 14, 1):
                self.data_coordinates[e][i] = [i * dim, e * dim, dim, dim]

    def check_for_button_and_execute(self, screen, needle, offset_left=0, offset_down=0):
        rectangles = self.vision_stun.find(screen, needle, 0.70, 1)
        if len(rectangles) == 0:
            return False
        point = self.vision_stun.get_click_points(rectangles)[0]
        self.dig_point(point[0] + offset_left, point[1] + offset_down, 500)
        return True

    def check_for_next_level(self, screen, needle):
        offset_left = 230
        offset_down = 58
        rectangles = self.vision_stun.find(screen, needle, 0.70, 1)
        if len(rectangles) == 0:
            return False
        if self.detonate_explosive_when_stuck(self.observation):
            return True
        point = self.vision_stun.get_click_points(rectangles)[0]
        self.dig_point(point[0] + offset_left, point[1] + offset_down, 500)
        return True

    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):
                        src_pt = self.get_click_point(self.data_coordinates[e, i])
                        if self.local_pos_checks(state, e, i, 1, 0, self.colors):
                            dest_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):
                            dest_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):
                            dest_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):
                            dest_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_explosives(self, state, e, i):
        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.explosives):
                    src_pt = self.get_click_point(self.data_coordinates[e + 1, i])
                    self.move_tile(src_pt, dest_pt)
                elif self.local_pos_checks(state, e, i, 0, 1, self.explosives):
                    src_pt = self.get_click_point(self.data_coordinates[e, i + 1])
                    self.move_tile(src_pt, dest_pt)
                elif self.local_pos_checks(state, e, i, -1, 0, self.explosives):
                    src_pt = self.get_click_point(self.data_coordinates[e - 1, i])
                    self.move_tile(src_pt, dest_pt)
                elif self.local_pos_checks(state, e, i, 0, -1, self.explosives):
                    src_pt = self.get_click_point(self.data_coordinates[e, i - 1])
                    self.move_tile(src_pt, dest_pt)
                else:
                    continue
                return True
            else:
                continue
        return False

    def is_direction_in_bounce_and_same_color(self, state, loc, color):
        x, y = loc
        if x <= 7 and x >= 0 and y <= 13 and y >= 0:
            if state[x, y] == color:
                return 1
        return 0

    def local_pos_check(self, state, e, i, e_check, i_check, needle):
        if e + e_check >= 0 and e + e_check <= 7 and i + i_check >= 0 and i + i_check <= 13:
            if state[e + e_check, i + i_check] == needle:
                return True
            else:
                return False

    def local_pos_checks(self, state, e, i, e_check, i_check, needles):
        if e + e_check >= 0 and e + e_check <= 7 and i + i_check >= 0 and i + i_check <= 13:
            for needle in needles:
                if state[e + e_check, i + i_check] == needle:
                    return True
                else:
                    continue
        return False

    def get_click_point(self, rectangle):
        # Loop over all the rectangles
        x, y, w, h = rectangle
        # Determine the center position
        center_x = x + int(w / 2)
        center_y = y + int(h / 2)
        # Save the points
        return int(center_x), int(center_y)

    def move_tile(self, point_source, point_dest):
        offset_left = 230
        offset_down = 58
        pydirectinput.moveTo(point_source[0] + offset_left, point_source[1] + offset_down)
        # pydirectinput.moveTo(0,0)
        pydirectinput.mouseDown()
        w = random.randint(25, 50)
        cv.waitKey(100 + w)
        pydirectinput.moveTo(point_dest[0] + offset_left, point_dest[1] + offset_down)
        pydirectinput.mouseUp()
        cv.waitKey(400 + w)

    def check_for_button_and_click_it(self, button_url):
        screenshot = self.capture_window.get_screenshot()
        # gray = cv.cvtColor(screenshot, cv.COLOR_BGR2GRAY)
        # thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1]
        # gray_needle = cv.cvtColor(cv.imread(button_url, cv.IMREAD_UNCHANGED), cv.COLOR_BGR2GRAY)
        # thresh_needle = cv.threshold(gray_needle, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1]
        needle = cv.imread(button_url, cv.IMREAD_UNCHANGED)
        # rectangles = self.vision_stun.find(thresh, thresh_needle, 0.4, 1)
        rectangles = self.vision_stun.find(screenshot, needle, 0.7, 1)

        if len(rectangles) == 1:
            pointis = self.vision_stun.get_click_points(rectangles)
            for pointi in pointis:
                self.dig_point(pointi[0], pointi[1], 150)

    def dig_point(self, point1, point2, dig_time):
        pydirectinput.moveTo(point1, point2)
        cv.waitKey(dig_time)
        pydirectinput.mouseDown()
        w = random.randint(50, 100)
        cv.waitKey(w)
        pydirectinput.mouseUp()

    def move_to(self, x, y):
        point_src = (1113, 598)
        pydirectinput.moveTo(point_src[0], point_src[1])
        pydirectinput.mouseDown()
        w = random.randint(1, 100)
        cv.waitKey(150 + w)
        pydirectinput.moveTo(x, y)
        pydirectinput.mouseUp()
        cv.waitKey(500 + w)

    def point_in_rect(self, point):
        for e in range(0, 8, 1):
            for i in range(0, 14, 1):
                x1, y1, w, h = self.data_coordinates[e][i]
                x2, y2 = x1 + w, y1 + h
                x, y = point
                if x1 < x and x < x2:
                    if y1 < y and y < y2:
                        return e, i
        return None, None
