import time
import pygame
import random

import Pole
import displayControler as dCon
import Slot
import Osprzet
import Node
import Condition
import Drzewo
import neuralnetwork as nn

condition=Condition.Condition()
drzewo=Drzewo.Drzewo()

format_string = "{:<25}{:<25}{:<25}{:<10}{:<10}{:<10}{:<25}{:<15}{:<20}{:<10}{:<15}"
format_string_nn="{:<10}{:<20}{:<20}{:<15}{:<20}"


tab = [-1, 0, 0, 0, 0, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
       0, 1, 0, 1, 0, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1]


class Tractor:
    DIRECTION_NORTH = 'N'
    DIRECTION_SOUTH = 'S'
    DIRECTION_WEST = 'W'
    DIRECTION_EAST = 'E'

    def __init__(self,slot,screen, osprzet,clock,bfs2_flag):
        self.tractor_images = {
            Tractor.DIRECTION_NORTH: pygame.transform.scale(pygame.image.load('images/traktorN.png'),
                                                            (dCon.CUBE_SIZE, dCon.CUBE_SIZE)),
            Tractor.DIRECTION_SOUTH: pygame.transform.scale(pygame.image.load('images/traktorS.png'),
                                                            (dCon.CUBE_SIZE, dCon.CUBE_SIZE)),
            Tractor.DIRECTION_WEST: pygame.transform.scale(pygame.image.load('images/traktorW.png'),
                                                           (dCon.CUBE_SIZE, dCon.CUBE_SIZE)),
            Tractor.DIRECTION_EAST: pygame.transform.scale(pygame.image.load('images/traktor.png'),
                                                           (dCon.CUBE_SIZE, dCon.CUBE_SIZE))
        }
        self.direction = Tractor.DIRECTION_EAST  # początkowy kierunek wschód
        self.current_tractor_image = self.tractor_images[self.direction]
        self.screen=screen
        self.slot=slot
        self.osprzet = osprzet
        self.clock=clock
        self.slot_hydrate_dict={}
        self.bfs2_flag=bfs2_flag
        self.waterLevel=random.randint(0,100)

    def draw_tractor(self):
        self.screen.blit(self.current_tractor_image, (self.slot.x_axis * dCon.CUBE_SIZE, self.slot.y_axis * dCon.CUBE_SIZE))
        pygame.display.update()

    def turn_left(self):
        # zmiana kierunku w lewo
        direction_map = {
            Tractor.DIRECTION_EAST: Tractor.DIRECTION_NORTH,
            Tractor.DIRECTION_NORTH: Tractor.DIRECTION_WEST,
            Tractor.DIRECTION_WEST: Tractor.DIRECTION_SOUTH,
            Tractor.DIRECTION_SOUTH: Tractor.DIRECTION_EAST
        }
        self.direction = direction_map[self.direction]
        self.current_tractor_image = self.tractor_images[self.direction]
        self.draw_tractor()

    def tree_move(self, pole):
        drzewo.treeLearn()
        drzewo.plotTree()
        self.snake_move_irrigation(pole, drzewo)
    
    def get_attributes(self): 
        slot_attributes=self.slot.return_stan_for_tree()
        climate_attributes=condition.return_condition()
        attributes=[]
        attributes=attributes+slot_attributes+[self.waterLevel]+climate_attributes
        return attributes
    
    def get_attributes_for_print(self):
        slot_attributes=self.slot.return_plant().return_status_tree()
        climate_attributes=condition.getCondition()
        slot_attributes=slot_attributes+[self.waterLevel]
        return slot_attributes+climate_attributes

    def turn_right(self):
        # zmiana kierunku w prawo
        direction_map = {
            Tractor.DIRECTION_EAST: Tractor.DIRECTION_SOUTH,
            Tractor.DIRECTION_SOUTH: Tractor.DIRECTION_WEST,
            Tractor.DIRECTION_WEST: Tractor.DIRECTION_NORTH,
            Tractor.DIRECTION_NORTH: Tractor.DIRECTION_EAST
        }
        self.direction = direction_map[self.direction]
        self.current_tractor_image = self.tractor_images[self.direction]
        self.draw_tractor()

    def move_forward(self, pole, destroy = True):
        next_slot_coordinates = None
        if self.direction == Tractor.DIRECTION_EAST:
            next_slot_coordinates = (self.slot.x_axis + 1, self.slot.y_axis)
            self.current_tractor_image = self.tractor_images[self.direction]
        elif self.direction == Tractor.DIRECTION_WEST:
            next_slot_coordinates = (self.slot.x_axis - 1, self.slot.y_axis)
            self.current_tractor_image = self.tractor_images[self.direction]
        elif self.direction == Tractor.DIRECTION_SOUTH:
            next_slot_coordinates = (self.slot.x_axis, self.slot.y_axis + 1)
            self.current_tractor_image = self.tractor_images[self.direction]
        elif self.direction == Tractor.DIRECTION_NORTH:
            next_slot_coordinates = (self.slot.x_axis, self.slot.y_axis - 1)
            self.current_tractor_image = self.tractor_images[self.direction]

        # sprawdzenie czy następny slot jest dobry
        self.do_move_if_valid(pole,next_slot_coordinates, destroy)
        self.clock.tick(10)

    def do_move_if_valid(self,pole, next_slot_coordinates, destroy = True):
        if next_slot_coordinates and pole.is_valid_move(next_slot_coordinates):
            next_slot = pole.get_slot_from_cord(next_slot_coordinates)
            self.slot.redraw_image(destroy)
            self.slot = next_slot
            self.draw_tractor()
            return True
        else:
            return False

    def random_move(self, pole):
        self.clock.tick(2)
        # losowanie skrętu
        turn_direction = random.choice([self.turn_left, self.turn_right])
        turn_direction()
        self.clock.tick(5)
        # wykonanie ruchu do przodu z uwzględnieniem aktualnej orientacji
        self.move_forward(pole)

    def reset_pos(self,pole):
        self.do_move_if_valid(pole,(0,0))

    def initial_move(self,pole):
        if (self.bfs2_flag==True):
            index=0

            for y in range (0,dCon.NUM_Y):
                if(y%2==0):
                    for x in range(0,dCon.NUM_X):
                        if(pole.is_valid_move((x,y))):
                            pole.get_slot_from_cord((x,y)).setHydrate(tab[index])
                            self.snake_move(pole,x,y)
                            index=index+1
                else:
                    for x in range(dCon.NUM_X,-1,-1):
                        if(pole.is_valid_move((x,y))):
                            pole.get_slot_from_cord((x,y)).setHydrate(tab[index])
                            self.snake_move(pole,x,y)
                            index=index+1
        else:
            for y in range (0,dCon.NUM_Y):
                if(y%2==0):
                    for x in range(0,dCon.NUM_X):
                        self.snake_move(pole,x,y)
                else:
                    for x in range(dCon.NUM_X,-1,-1):
                        self.snake_move(pole,x,y)


    def snake_move_irrigation(self, pole, drzewo):
        headers=['Wspolrzedne','Czy podlac','Poziom nawodnienia','Wzrost','Choroba','Zyznosc','Poziom wody w traktorze','Temperatura','Opady','Pora Roku','Aktualny czas']
        print(format_string.format(*headers))
        initPos = (self.slot.x_axis, self.slot.y_axis)
        counter = 0
        for i in range(initPos[1], dCon.NUM_Y):
            for j in range(initPos[0], dCon.NUM_X):
                attributes=self.get_attributes()
                decision = drzewo.makeDecision(attributes)
                self.pretty_print_tree([str("({:02d}, {:02d})").format(self.slot.x_axis, self.slot.y_axis),decision,*self.get_attributes_for_print()])
                if decision == "Tak":
                    self.slot.irrigatePlant()
                    counter += 1
                condition.cycle()
                pygame.time.delay(50)
                self.waterLevel=random.randint(0,100)
                #condition.getCondition()
                self.move_forward(pole, False)
            if i % 2 == 0 and i != dCon.NUM_Y - 1:
                self.turn_right()
                self.move_forward(pole, False)
                self.turn_right()
            elif i != dCon.NUM_Y - 1:
                self.turn_left()
                self.move_forward(pole, False)
                self.turn_left()
        print("podlanych slotów: ", str(counter))

    def snake_move_predict_plant(self, pole, model, headers, actions = None):
        print(format_string_nn.format(*headers))
        initPos = (self.slot.x_axis, self.slot.y_axis)
        count = 0
        for i in range(initPos[1], dCon.NUM_Y):
            for j in range(initPos[0], dCon.NUM_X):
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        quit()
                if self.slot.imagePath != None:
                    predictedLabel = nn.predictLabel(self.slot.imagePath, model)

                    #print(str("Coords: ({:02d}, {:02d})").format(self.slot.x_axis, self.slot.y_axis), "real:", self.slot.label, "predicted:", predictedLabel, "correct" if (self.slot.label == predictedLabel) else "incorrect", 'nawożę za pomocą:', nn.fertilizer[predictedLabel])
                    # print(format_string_nn.format(f"{self.slot.x_axis,self.slot.y_axis}",self.slot.label,predictedLabel,"correct" if (self.slot.label == predictedLabel) else "incorrect",nn.fertilizer[predictedLabel]))
                    for a in actions:
                        a(predictedLabel)
                    if self.slot.label != predictedLabel:
                        # self.slot.mark_visited()
                        count += 1
                self.move_forward(pole, False)
            if i % 2 == 0 and i != dCon.NUM_Y - 1:
                self.turn_right()
                self.move_forward(pole, False)
                self.turn_right()
            elif i != dCon.NUM_Y - 1:
                self.turn_left()
                self.move_forward(pole, False)
                self.turn_left()
        print(f"Dobrze rozpoznanych roślin: {20*12-count}, źle rozpoznanych roślin: {count}")
    
    def fertilize_slot(self, predictedLabel):
        print(format_string_nn.format(f"{self.slot.x_axis,self.slot.y_axis}",self.slot.label,predictedLabel,"correct" if (self.slot.label == predictedLabel) else "incorrect",nn.fertilizer[predictedLabel]))
        if self.slot.label != predictedLabel:
            self.slot.mark_visited()

    def irigate_slot_NN(self, predictedLabel):
        attributes=self.get_attributes()
        decision = drzewo.makeDecision(attributes)
        print(format_string_nn.format(f"{self.slot.x_axis,self.slot.y_axis}",self.slot.label,predictedLabel,"correct" if (self.slot.label == predictedLabel) else "incorrect",decision))
        condition.cycle()
        self.waterLevel = random.randint(0, 100)

    def snake_move(self,pole,x,y):
        next_slot_coordinates=(x,y)
        if(self.do_move_if_valid(pole,next_slot_coordinates)):
            if (x,y) not in Pole.stoneList:
                if x == 0 and y == 0:
                    hydrateIndex = -1
                elif pole.get_slot_from_cord((x,y)).get_hydrate_stats() < 60:
                    hydrateIndex = 0
                else:
                    hydrateIndex = 1
                self.slot_hydrate_dict[(x,y)]= hydrateIndex #Budowanie slownika slotow z poziomem nawodnienia dla traktorka
        self.clock.tick(10)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit()

    def move_by_root(self, root, pole, actions = None):
        for move in root:
            self.slot.redraw_image()
            if move[1] == 'forward':
                self.move_forward(pole)
            if move[1] == 'right':
                self.turn_right()
            if move[1] == 'left':
                self.turn_left()
            for a in actions:
                a()

            self.clock.tick(3)

    #to tak zrobiłam już na później, może się przyda
    def change_osprzet(self, new_osprzet):
        self.osprzet = new_osprzet

    def print_osprzet_info(self):
        print("ID:", self.osprzet.id)
        print("Marka:", self.osprzet.marka)
        print("Model:", self.osprzet.model)
        if self.osprzet.akcje:
            print("Akcje:")
            for akcja in self.osprzet.akcje:
                print("- Typ:", akcja.typ)
        else:
            print("Brak akcji przypisanych do tego sprzętu.")

    def pretty_print_tree(self,attributes):
        print(format_string.format(*attributes))
    def irrigateSlot(self):
        try:
            self.slot.irrigatePlant()
        except:
            pass