from settings import *
from pygame.draw import line, circle
import pygame as pg
from pytmx.util_pygame import load_pygame
from support import load_all_images, player_is_blocked_by_object, Evolution
import sys
import os
from math import cos, sin, radians

class Sproot:
    def __init__(self, name, x, y, width, height, images, direction="left"):
        self.name = name
        # attributes defining the rectangle
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        # attributes controlling how the sproot gets drawn on screen
        self.all_images = images # list of all images for this sprite
        self.direction = direction
        self.direction_index_map = {"left": 0, "right": 4, "down": 8, "up": 12}
        self.current_image = self.all_images[self.direction_index_map[self.direction]] # set current image
        self.active = False

def load_objects(map):
    
    # get terrain tiles
    objects = map.get_layer_by_name("Objects")
    object_sproots = []
    for object in objects:
        s = Sproot("obj", object.x, object.y, object.width, object.height, [object.image])
        object_sproots += [s]
    return object_sproots

def insert_player(player_sproot, sproots):
    
    # change this code to insert the player at the correct position in O(n) time
    i = 0
    while i < len(sproots) and sproots[i].y < player_sproot.y:
        i = i + 1
    return sproots[0:i] + [player_sproot] + sproots[i: len(sproots)]

def sort_sproots(sproots):

    # code for sorting sproots in O(n^2) time
    for i in range(len(sproots)):
        min_index = i
        for j in range(min_index, len(sproots)):
            if sproots[j].y < sproots[min_index].y:
                min_index = j
        tmp = sproots[i]
        sproots[i] = sproots[min_index]
        sproots[min_index] = tmp
    return sproots

def move_player(player_sproot, object_sproots, key_pressed_map, time, walking_speed):
    # code to move player
    add_x = 0
    add_y = 0
    if key_pressed_map[pg.K_LEFT]:
        add_x -= 1
        player_sproot.direction = "left"
    elif key_pressed_map[pg.K_RIGHT]:
        add_x += 1
        player_sproot.direction = "right"
    elif key_pressed_map[pg.K_DOWN]:
        add_y += 1
        player_sproot.direction = "down"
    elif key_pressed_map[pg.K_UP]:
        add_y -= 1
        player_sproot.direction = "up"

    if not player_is_blocked_by_object(player_sproot, object_sproots):
        player_sproot.x += add_x * time * walking_speed
        player_sproot.y += add_y * time * walking_speed
    return player_sproot

def update_sproot_image(sproot, frame):
    # code to update image of a sproot based on the given frame and direction the sproot is facing
    index = sproot.direction_index_map[sproot.direction] + int(frame%4)
    sproot.current_image = sproot.all_images[index]
    return sproot

def draw(image, screen, pos_x, pos_y):
    screen.blit(image, (pos_x, pos_y))

# A few greens for branches
GREENS = [
    (0, 60, 0),    # dark green
    (0, 100, 0),
    (0, 140, 80),
    (0, 180, 60),
    (80, 220, 80),
]

PURPLES = [
    (40, 0, 60),     # very dark purple
    (75, 0, 110),    # deep royal purple
    (110, 0, 155),   # classic purple
    (150, 40, 200),  # bright violet
    (190, 110, 255), # light lavender-purple
]

YELLOWS = [
    (60, 60, 0),     # very dark yellow / olive-gold
    (110, 110, 0),   # mustard-gold
    (160, 160, 20),  # earthy yellow
    (210, 210, 40),  # warm bright yellow
    (255, 255, 120), # pale soft yellow
]

TRANSLATION_MAP = {"ATG": "L", "GTA": "Z", "GGG": "A", "TAG": "R", "GAT": "I", "AAA": "G", "TTT": "Y", "CCC": "E", "CGG": "D", "GGC": "N"}

BUD_COLOR = (180, 255, 180)


def draw_line(screen, color, start_x, start_y, length, angle, width):

    end_x = start_x
    end_y = start_y

    return (end_x, end_y)

def draw_circle(screen, x, y, radius, color):
    circle(screen, color, (x, y), radius)

def draw_hypnosis(screen, x, y, angle, depth, max_depth):

    draw_circle(screen, x, y, 4, PURPLES[4])

def draw_tree(screen, x, y, length, angle, depth, max_depth):
    
    draw_circle(screen, x, y, 4, BUD_COLOR)


def translate_dna(dna, position, direction):

    return ""
    

class Node:
    def __init__(self, data, left_child, right_child):
        self.data = data
        self.left_child = left_child
        self.right_child = right_child

    def __str__(self):
        return self.data

    def __repr__(self):
        return self.data


def find_all_root_nodes(nodes):
    roots = []
    
    return roots


def query_is_descendant_of_node(node, query):
    
    return False
    

def run_game(argument):

    # initial setup code
    pg.init()
    screen = pg.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
    pg.display.set_caption("Nomékop")
    clock = pg.time.Clock()
    world_map = load_pygame(os.path.join("..", "data", "maps", "world.tmx"))

    # loading player and friolera images
    player_image_path = os.path.join("..", "graphics", "characters", PLAYER_FILE)
    draem_image_path = os.path.join("..", "graphics", "nomekop", "Draem.png")
    plumette_image_path = os.path.join("..", "graphics", "nomekop", "Plumette.png")
    player_images = load_all_images(player_image_path, 4, 4)
    draem_images = load_all_images(draem_image_path, 2, 4)
    plumette_images = load_all_images(plumette_image_path, 2, 4)

    
    # 0a create player and friolera sproots here
    player_sproot = Sproot("Player", 960, 832, 128, 128, player_images, "right")
    draem_sproot = Sproot("Draem", 1160, 800, 150, 150, draem_images)
    plumette_sproot = Sproot("Plumette", 1160, 800, 150, 150, plumette_images)
    player_sproot.active = True
    object_sproots = [draem_sproot, plumette_sproot]
    trunk_length = 120
    max_depth = 1

    frame = 0
    timer = 0
    num = 0
    e = None
    TRANSLATION_MAP = {"ATG": "L", "GTA": "Z", "GGG": "A", "TAG": "R", "GAT": "I", "AAA": "G", "TTT": "Y", "CCC": "E", "CGG": "D", "GGC": "N"}

    if sys.argv[1] == "legend":
        sparchu_node = Node("Sparchu", None, None)
        cindrill_node = Node("Cindrill", sparchu_node, None)
        jacana_node = Node("Jacana", None, None)
        charmadillo_node = Node("Charmadillo", cindrill_node, jacana_node)
        charmadillo_dna = "CGGTAGGGGGTAGATATGTTTTAGGGGCGGGGCCCCAAACCCATG"
        if query_is_descendant_of_node(charmadillo_node, "Sparchu") and translate_dna(charmadillo_dna, 0, "reverse") == "LEGENDARYLIZARD":
            print("Charmadillo DNA CGGTAGGGGGTAGATATGTTTTAGGGGCGGGGCCCCAAACCCATG forward translates to", translate_dna(charmadillo_dna, 0, "forward"))
            print("Charmadillo DNA CGGTAGGGGGTAGATATGTTTTAGGGGCGGGGCCCCAAACCCATG reverse translates to", translate_dna(charmadillo_dna, 0, "reverse"))
            print("Prof: Of course!!! Yes! Sparchu is descended from the legendary lizard nomekop Charmadillo. We have a fighting chance now against the Evil CEO!!!")
            player_sproot.active = False
            font = pg.font.SysFont('Arial', 32)
            audio_path = os.path.join("..", "data", "audio", "evolution.wav")
            victory = pg.mixer.Sound(audio_path)
            victory.play(loops=0)
            def end():
                pass
            e = Evolution("Sparchu", "Charmadillo", font, end)
        else:
            print("Prof: Hmmm.... something's not quite right")

    while True:
        
        time = clock.tick() / 1000
        # event loop
        for event in pg.event.get():
            if event.type == pg.QUIT:
                 pg.quit()
                 sys.exit()

        # update the frame based on time and animation speed
        frame += time * ANIMATION_SPEED

        timer += time * ANIMATION_SPEED
        if timer >= 5:
            timer = 0
            max_depth += 1

        if timer >= 10:
            timer = 0
            num += 1

        # player movement logic and animation logic
        key_pressed_map = pg.key.get_pressed()
        player_sproot = move_player(player_sproot, object_sproots, key_pressed_map, time, WALKING_SPEED)
        if key_pressed_map[pg.K_LEFT] or key_pressed_map[pg.K_RIGHT] or key_pressed_map[pg.K_DOWN] or key_pressed_map[pg.K_UP]:
            player_sproot = update_sproot_image(player_sproot, frame)
        draem_sproot = update_sproot_image(draem_sproot, frame)
        plumette_sproot = update_sproot_image(plumette_sproot, frame)

        # drawing logic
        x_offset = -(player_sproot.x + player_sproot.width/2 - WINDOW_WIDTH/2)
        y_offset = -(player_sproot.y + player_sproot.height/2 - WINDOW_HEIGHT/2)  

        # always reset the screen with a blank slate first
        screen.fill("black")
        
    
        all_sproots = insert_player(player_sproot, object_sproots)

        for s in all_sproots:
            if s.active:
                draw(s.current_image, screen, s.x + x_offset, s.y + y_offset)

        if argument == "connections":
            draem_sproot.active = True
            draw_line(screen, PURPLES[4], player_sproot.x+100+x_offset, player_sproot.y+64+y_offset, 125, 0, 2)
            draw_line(screen, YELLOWS[4], player_sproot.x+100+x_offset, player_sproot.y+64+y_offset, 125, 45, 2)
            draw_line(screen, GREENS[4], player_sproot.x+100+x_offset, player_sproot.y+64+y_offset, 125, 315, 2)
       
        elif argument == "hypnosis":
            draem_sproot.active = True
            draw_hypnosis(screen, player_sproot.x + 50 + x_offset, player_sproot.y + 50 + y_offset, 0, 0, min(360, num))
            num += 1
       
        elif argument == "memorial":
            plumette_sproot.active = True
            draw_tree(screen, 1000 + x_offset, 1000 + y_offset, trunk_length, 90, 0, max_depth)
        
        elif e:
            e.update(time)
    
        pg.display.update()


if __name__ == "__main__":
    
    argument = sys.argv[1]

    if argument in ["connections", "hypnosis", "memorial", "legend"]:
        run_game(argument)

    elif argument == "translate":
        print()
        print("ATGGGGGTATAG forward translates to", translate_dna("ATGGGGGTATAG", 0, "forward"))
        print("ATGGGGGTATAG reverse translates to", translate_dna("ATGGGGGTATAG", 0, "reverse"))
    
    elif argument == "ancestry":
        # SIMPLE TREE TEST
        print()
        plumette_node = Node("Plumette", None, None)
        larvea_node = Node("Larvea", None, None)
        atrox_node = Node("Atrox", None, None)
        ivieron_node = Node("Ivieron", plumette_node, None)
        pluma_node = Node("Pluma", ivieron_node, atrox_node)
        cleaf_node = Node("Cleaf", larvea_node, pluma_node)
        print("Plumette is a descendant of Cleaf =", query_is_descendant_of_node(cleaf_node, "Plumette"))
        print("Atrox is a descendant of Cleaf =", query_is_descendant_of_node(cleaf_node, "Atrox"))
        print("Cleaf is a descendant of Cleaf =", query_is_descendant_of_node(cleaf_node, "Cleaf"))
        print("Plumette is a descendant of Larvea =", query_is_descendant_of_node(larvea_node, "Plumette"))
        print()
    
    elif argument == "roots":
        # FULL TREE TEST
        plumette_node = Node("Plumette", None, None)
        larvea_node = Node("Larvea", None, None)
        atrox_node = Node("Atrox", None, None)
        ivieron_node = Node("Ivieron", plumette_node, None)
        pluma_node = Node("Pluma", ivieron_node, atrox_node)
        cleaf_node = Node("Cleaf", larvea_node, pluma_node)

        pouch_node = Node("Pouch", None, None)
        finsta_node = Node("Finsta", None, None)
        draem_node = Node("Draem", None, pouch_node)
        gulfin_node = Node("Gulfin", None, finsta_node)
        finiette_node = Node("Finiette", draem_node, gulfin_node)
        friolera_node = Node("Friolera", cleaf_node, finiette_node)

        sparchu_node = Node("Sparchu", None, None)
        cindrill_node = Node("Cindrill", sparchu_node, None)
        jacana_node = Node("Jacana", None, None)
        charmadillo_node = Node("Charmadillo", cindrill_node, jacana_node)

        all_nodes = [plumette_node, larvea_node, atrox_node, ivieron_node, pluma_node, cleaf_node, pouch_node,
                     finsta_node, draem_node, gulfin_node, finiette_node, friolera_node,
                     sparchu_node, cindrill_node, charmadillo_node, jacana_node]

        print(find_all_root_nodes(all_nodes))