# THIS IS A SOURCE CODE FILE FROM I'M NOT EVEN HUMAN THE GAME.
# IT COULD BE USED IN A DIFFERENT PIECE OF SOFTWARE ( LIKE A
# DIFFERENT GAME ), BUT IT WAS ORIGINALLY WRITTEN FOR I'M NOT
# EVEN HUMAN THE GAME.

# THE DEVELOPERS OF THE GAME ARE : (C) J.Y.AMIHUD, AYYZEE AND 
# OTHER CONTRIBUTORS. THIS AND OTHER FILES IN THIS GAME,
# UNLESS SPECIFICALLY NOTED, COULD BE USED UNDER THE TERMS OF
# GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER VERSION.

import os
import json

# GTK module ( Graphical interface
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import cairo
import math

from modules import ui
from modules import world

def layer(game):
    
    # Setting up a cairo layer
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
                                 game.current['w'],
                                 game.current['h'])
    layer = cairo.Context(surface)
    layer.set_antialias(cairo.ANTIALIAS_NONE) # Disable AA to prevent blured pixels


    # Factor to change speed dependand on FPS
    withFPS = (1/game.FPS*60)

    
    # Prepare the layer for gameplay and do not draw the editor grid.
    world.draw(game, layer, 0, 0,
               game.current["w"],
               game.current["h"], grid=False)

    

    ### DRAWING SELECTED CHARACTERS ###

    for n, asset in enumerate(game.current["state"]["controlling"]):


        if game.current["state"]["controlling"][asset]:
            ui.color(game, layer, "orange")
            layer.rectangle(
                14,
                14 + (n*15),
                12,12)
            layer.fill()

        if "following" in game.current["state"]["objects"][asset]:
            target = game.current["state"]["objects"][asset]["following"]

            # Tiny Visualizer
            
            ui.color(game, layer, "orange")
            layer.set_line_width(1)
            layer.move_to(
                26,
                14 + (n*15) + 10)
            layer.line_to(26 + (n*5) + 5,
                          14 + (n*15) + 10)
            layer.line_to(26 + (n*5) + 5,
                          14 + (target*15) + 2)
            layer.line_to(26,
                          14 + (target*15) + 2)

            layer.stroke()
            layer.move_to(28,
                          14 + (target*15))
            layer.line_to(28,
                          14 + (target*15)+3)
            layer.stroke()
            

        asset_draw = game.current["state"]["objects"][asset]["asset"]
        ui.image(game, layer,
                 15,                     
                 15 + (n*15) ,
                 "assets/elements/"+asset_draw+".png",
                 "icon")

    # Rotations is sequence for the automatic rotation mechanism
    rotations = {
        "normal":[
            "Right",
            "Down-Right",
            "Down",
            "Down-Left",
            "Left",
            "Up-Left",
            "Up",
            "Up-Right"]}
        
    #### MOVING CHARACTERS AROUND LOGIC ####


    for asset, t in enumerate(game.current["state"]["objects"]):
        try:
            if "target-orientation" in game.current["state"]["objects"][asset]\
                and game.current["frame"] % max(1, int(game.FPS/10)) == 0:

                target = game.current["state"]["objects"][asset]["target-orientation"]
                current = game.current["state"]["objects"][asset]["orientation"]

                if target != current:
                
                    tlist = rotations["normal"].copy()
                    for i in range(2):
                        for b in rotations["normal"]:
                            tlist.append(b)
                    
                    tlist[tlist.index(current)] = "blank"
                    tlist[tlist.index(current)] = "current"
                    tlist[tlist.index(current)] = "blank"

                    tlist[tlist.index(target)] = "option1"
                    tlist[tlist.index(target)] = "option2"
                    tlist[tlist.index(target)] = "option3"

                    add = 1
                    closest = len(tlist)
                    
                    for i in ["option1", "option2", "option3"]:
                        t = tlist.index(i)
                        c = tlist.index("current")

                        if max(t,c) - min(t,c) < closest:
                            closest = max(t,c) - min(t,c)
                            if t < c:
                                add = -1
                            else:
                                add = 1

                    index = (rotations["normal"].index(current)+add) % len(rotations["normal"])
                    put = rotations["normal"][index]
                    game.current["state"]["objects"][asset]["orientation"] = put
            
            elif game.current["frame"] % max(1, int(game.FPS/10)) == 0:
                del game.current["state"]["objects"][asset]["target-orientation"]
            
        except:
            pass


        xob = game.current["state"]["objects"][asset]["xyz"][0]
        yob = game.current["state"]["objects"][asset]["xyz"][1]

        
        asset_draw = game.current["state"]["objects"][asset]["asset"]
        orientation = "Down-Left"
        try:
            
            image = game.images["assets/elements/"+asset_draw+".png"][orientation][0]["binary"]
            image_height = image.get_height()
        except:
            image_height = 61

        # Healthbar

        if game.current["state"]["objects"][asset]["health"] <= 0:
            game.current["state"]["objects"][asset]["health"] = 0
            game.current["state"]["objects"][asset]["orientation"] = "Dead"
            try:
                del game.current["state"]["controlling"][asset]
            except:
                pass
            continue
        else:
            #Regenerate
            game.current["state"]["objects"][asset]["health"] += 0.0001 * withFPS
            if game.current["state"]["objects"][asset]["health"] >= 1:
                game.current["state"]["objects"][asset]["health"] = 1 
        if game.current["state"]["controlling"].get(asset, False):

            ui.color(game, layer, "orange")
            ui.text(game,layer, game.images["assets/elements/"+asset_draw+".png"].get("title", "Unknown"),
                            15,
                            game.current["h"]-40)
            
            ui.fancy_health_bar(game,
                                15,
                                game.current["h"]-20,
                                game.current["w"]/3,
                                15,
                                layer,
                                game.current["state"]["objects"][asset]["health"])
        else:
            ui.color(game, layer, "red")
            layer.set_line_width(2)
            layer.move_to(xob + game.current["camera"][0]-10,
                          yob + game.current["camera"][1] - image_height-5)
            layer.line_to(xob + game.current["camera"][0]+10,
                          yob + game.current["camera"][1] - image_height-5)

            layer.stroke()
            ui.color(game, layer, "green")
            layer.set_line_width(2)
            layer.move_to(xob + game.current["camera"][0]-10,
                          yob + game.current["camera"][1] - image_height-5)
            layer.line_to(xob + game.current["camera"][0]-10+(20*game.current["state"]["objects"][asset]["health"]),
                          yob + game.current["camera"][1] - image_height-5)

            layer.stroke()

        # THE PLAYER CONTROLLS THIS CHRACTER DIRECTLY
        if game.current["state"]["controlling"].get(asset, False):
            
            x79 = game.current["state"]["objects"][asset]["xyz"][0]
            y79 = game.current["state"]["objects"][asset]["xyz"][1]
            z79 = game.current["state"]["objects"][asset]["xyz"][2]

            

            ui.image(game, layer,
                        x79 + game.current["camera"][0],
                        y79 + game.current["camera"][1] + 61 - image_height,
                        "assets/elements/characters/shadow.png",
                        "arrow",
                        offset=True, dynamic=True)
            
            # SELECT FOLLOWING
            def do():
                if "following" in game.current["state"]["objects"][asset]:
                    del game.current["state"]["objects"][asset]["following"]
                else:
                    follow_closest(game, asset)

            ui.button(game, layer,
                      int(game.current["w"]) - 36,
                      15, 
                      12,
                      12,
                      icon="radio_transition",
                      func=do)
            
            # VISUALIZING THIS CHARACTERS FOLLOWING
            
            if "following" in game.current["state"]["objects"][asset]:
 
                target_asset = game.current["state"]["objects"][asset]["following"]
                xtar = 0
                ytar = 0
                ztar = 0
                try:
                    xtar = game.current["state"]["objects"][target_asset]["xyz"][0]
                    ytar = game.current["state"]["objects"][target_asset]["xyz"][1]
                    ztar = game.current["state"]["objects"][target_asset]["xyz"][2]
                except:
                    pass

                camx = game.current["camera"][0]
                camy = game.current["camera"][1]



                ui.color(game, layer, "orange")
                layer.set_line_width(2)
                # layer.arc(xtar+camx, ytar+camy, 20, 0, math.pi)
                # layer.stroke()
                ui.image(game, layer,
                    xtar + game.current["camera"][0],
                    ytar + game.current["camera"][1],
                    "assets/elements/characters/shadow.png",
                    "following",
                    offset=True, dynamic=True)

            # CONTROLLS
            
            ori = ""
            
            # UP
            if 65362 in game.current["keys"]:
                game.current["state"]["objects"][asset]["xyz"][1] -= 4*0.57142857142857142857*withFPS
                ori = "Up"
            # DOWN
            if 65364 in game.current["keys"]:
                game.current["state"]["objects"][asset]["xyz"][1] += 4*0.57142857142857142857*withFPS
                ori = "Down"
            # LEFT
            if 65361 in game.current["keys"]:
                game.current["state"]["objects"][asset]["xyz"][0] -= 4 *withFPS
                if ori:
                    ori = ori + "-Left"
                else:
                    ori = "Left"
                
            # RIGHT
            if 65363 in game.current["keys"]:
                game.current["state"]["objects"][asset]["xyz"][0] += 4 *withFPS
                if ori:
                    ori = ori + "-Right"
                else:
                    ori = "Right"

            if ori and ori not in rotations["normal"]:
                ori = "Down"
            
            # SPACE ( JUMP )
            if 32 in game.current["keys"]:

                if game.current["state"]["objects"][asset]["grounded"]:
                    # game.current["state"]["objects"][asset]["xyz"][2] += 0.8
                    # game.current["state"]["objects"][asset]["xyz"][1] += 0.8*34
                    game.current["state"]["objects"][asset]["velocity"][2] = 0.05

            # F ( toggle Following )

            if 102 in game.current["keys"]:
                if "following" in game.current["state"]["objects"][asset]:
                    del game.current["state"]["objects"][asset]["following"]

                else:
                    follow_closest(game, asset)

                game.current["keys"].remove(102)
                
            
            if ori:
                game.current["state"]["objects"][asset]["target-orientation"] = ori
                    
            


        # THE OTHER ( FOLLOWING ) CHARACTERS
            
        elif "following" in game.current["state"]["objects"][asset]:
            target_asset = game.current["state"]["objects"][asset]["following"]
            xtar = 0
            ytar = 0
            ztar = 0
            try:
                xtar = game.current["state"]["objects"][target_asset]["xyz"][0]
                ytar = game.current["state"]["objects"][target_asset]["xyz"][1]
                ztar = game.current["state"]["objects"][target_asset]["xyz"][2]
            except:
                pass

                    
            xob = game.current["state"]["objects"][asset]["xyz"][0]
            yob = game.current["state"]["objects"][asset]["xyz"][1]
            zob = game.current["state"]["objects"][asset]["xyz"][2]

            
            distance = ui.distance([xob, yob], [xtar, ytar])
            
            if distance > 50:

                motions = ["Right",
                           "Down",
                           "Left",
                           "Up",
                           "Up-Right",
                           "Down-Right",
                           "Down-Left",
                           "Up-Left"]

                possibles = [[xob+4*withFPS,yob, zob],
                             [xob, yob+4*0.57142857142857142857*withFPS, zob],
                             [xob-4*withFPS, yob, zob],
                             [xob, yob-4*0.57142857142857142857*withFPS, zob],
                             
                             [xob+4*withFPS, yob-4*0.57142857142857142857*withFPS, zob],
                             [xob+4*withFPS, yob+4*0.57142857142857142857*withFPS, zob],
                             [xob-4*withFPS, yob+4*0.57142857142857142857*withFPS, zob],
                             [xob-4*withFPS, yob-4*0.57142857142857142857*withFPS, zob]]

                closest = 0
                
                for n, i in enumerate(possibles):
                    thistance = ui.distance([i[0],i[1]], [xtar, ytar])
                    if thistance < distance:
                        distance = thistance
                        closest = n

                    game.current["state"]["objects"][asset]["xyz"] = possibles[closest]
                    game.current["state"]["objects"][asset]["target-orientation"] = motions[closest]

            if ztar > zob+0.5 and game.current["state"]["objects"][asset]["grounded"]:
                game.current["state"]["objects"][asset]["velocity"][2] = 0.05

        

        # VISUALIZE THIS CHARACTERS FOLLOWING COURSE

        if game.current["testing"] and "following" in game.current["state"]["objects"][asset]:
            xob = game.current["state"]["objects"][asset]["xyz"][0]
            yob = game.current["state"]["objects"][asset]["xyz"][1]
            zob = game.current["state"]["objects"][asset]["xyz"][2]
            target_asset = game.current["state"]["objects"][asset]["following"]
            xtar = 0
            ytar = 0
            ztar = 0
            try:
                xtar = game.current["state"]["objects"][target_asset]["xyz"][0]
                ytar = game.current["state"]["objects"][target_asset]["xyz"][1]
                ztar = game.current["state"]["objects"][target_asset]["xyz"][2]
            except:
                pass

            camx = game.current["camera"][0]
            camy = game.current["camera"][1]



            ui.color(game, layer, "orange")
            layer.set_line_width(2)
            layer.move_to(xob+camx, yob+camy)

            di = ui.distance([xob, yob], [xtar, ytar])
            
            xlar = xob + ((xtar-xob) / di * (di-20))
            ylar = yob + ((ytar-yob) / di * (di-20))
            layer.line_to(xlar+camx, ylar+camy)
            layer.stroke()

            layer.arc(xtar+camx, ytar+camy, 20, 0, math.pi)

            layer.stroke()

        
                
        # Respawn if falling for more than specified time
        if game.current["state"]["objects"][asset]["grounded"]:
            game.current["state"]["objects"][asset]["time-falling"] = 0
            if game.current["frame"] % 20 == 0:
                game.current["state"]["objects"][asset]["check_mark"] = game.current["state"]["objects"][asset]["xyz"].copy()
        else:
            game.current["state"]["objects"][asset]["time-falling"] += 1

        if game.current["state"]["objects"][asset]["time-falling"] > 100:
            game.current["state"]["objects"][asset]["time-falling"] = 0
            game.current["state"]["objects"][asset]["xyz"] = game.current["state"]["objects"][asset]["check_mark"].copy()
            game.current["state"]["objects"][asset]["xyz"][2] += 1
            game.current["state"]["objects"][asset]["xyz"][1] -= 34
            game.current["state"]["objects"][asset]["velocity"] = [0,0,0]
        
        # Character switching
        # TAB                            
        if 65289 in game.current["keys"]:
            #print("asdf")
            found = 0 
            for i, t in enumerate(game.current["state"]["controlling"]):
                if game.current["state"]["controlling"][t]:
                   game.current["state"]["controlling"][t] = False
                   found = i

            found += 1
            found = found % len(game.current["state"]["controlling"])
            found = list(game.current["state"]["controlling"].keys())[found]
            game.current["state"]["controlling"][found] = True
            game.current["keys"].remove(65289)

        
        
    # Camera binding
    try:
        targetx = 0 - x79 + (game.current["w"] / 2)
        targety = 0 - y79 + (game.current["h"] / 2)
        game.current["camera"][2] = int(round(z79))
    

        game.current["camera"][0] += (targetx - game.current["camera"][0])/4
        game.current["camera"][1] += (targety - game.current["camera"][1])/4
    except:
        pass
    
    return surface


def follow_closest(game, asset):

    follow = 0
    dis = 1000000000

    xthis = game.current["state"]["objects"][asset]["xyz"][0]
    ythis = game.current["state"]["objects"][asset]["xyz"][1]
    
    for n, target_asset in enumerate(game.current["state"]["controlling"]):

        if target_asset == asset:
            continue
        
        xtar = game.current["state"]["objects"][target_asset]["xyz"][0]
        ytar = game.current["state"]["objects"][target_asset]["xyz"][1]

        di = ui.distance([xtar,ytar],[xthis,ythis])

        if di < dis:
            dis = di
            follow = target_asset
        
        
    game.current["state"]["objects"][asset]["following"] = follow
    
def follow_last(game, asset):

    last = list(game.current["state"]["controlling"].keys())[-1]
    game.current["state"]["objects"][asset]["following"] = last
