extends Node2D

# camera stuff
@onready var camera = $Camera2D

var drag_start = Vector2()
var drag_active = false
var zoom_min = 0.1
var zoom_max = 4.0
var zoom_speed = 0.1
var initial_camera_position = Vector2(0, 0)
var initial_camera_zoom = Vector2(1, 1)


var GRID_SIZE_WIDTH = 50 # play area size x
var GRID_SIZE_LENGTH = 30 # play area size y
var GRID_SIZE_HEIGHT = 30 # play area size z
var TILE_SIZE = 32 # in px
var TILE_SIZE_ISOMETRIC_X = 32 # in px
var TILE_SIZE_ISOMETRIC_Y = 16 # in px
var ROTATION = 0
const MAIN_SOURCE_ID = 0
const BLUE_ISOMETRICTILE_ATLAS_POSITION = Vector2i(0,0)
const RED_ISOMETRICTILE_ATLAS_POSITION = Vector2i(1,0)
const GREEN_ISOMETRICTILE_ATLAS_POSITION = Vector2i(2,0)
const WHITE_ISOMETRICTILE_ATLAS_POSITION = Vector2i(3,0)
const BLACK_ISOMETRICTILE_ATLAS_POSITION = Vector2i(4,0)
const PURPLE_ISOMETRICTILE_ATLAS_POSITION = Vector2i(5,0)
const ORANGE_ISOMETRICTILE_ATLAS_POSITION = Vector2i(6,0)
const OFFSET = 0

var MAX_ARRAY_SIZE = 0

func _set_max_array_size():
    MAX_ARRAY_SIZE = 0
    
    if MAX_ARRAY_SIZE < GRID_SIZE_WIDTH:
        MAX_ARRAY_SIZE = GRID_SIZE_WIDTH
    if MAX_ARRAY_SIZE < GRID_SIZE_LENGTH:
        MAX_ARRAY_SIZE = GRID_SIZE_LENGTH
    if MAX_ARRAY_SIZE < GRID_SIZE_HEIGHT:
        MAX_ARRAY_SIZE = GRID_SIZE_HEIGHT


func create_debug_map_array():
    var _debug_map = []
    
       
    _set_max_array_size()
    for x in MAX_ARRAY_SIZE:
        var y_array = []
        for y in MAX_ARRAY_SIZE:
            var z_array = []
            for z in MAX_ARRAY_SIZE:
                z_array.append(null)  
            y_array.append(z_array)
        _debug_map.append(y_array)
        
    for z in GRID_SIZE_HEIGHT:
        for y in GRID_SIZE_LENGTH:
            for x in GRID_SIZE_WIDTH:
                var coord_x = x + (-1 * z) + OFFSET
                var coord_y = y + (-1 * z) - OFFSET
                
                var tile_data = {
                    "x": coord_x,
                    "y": coord_y,
                    "z": z,
                    "atlas_position": null,
                    "hp": 100,
                    "armour": 0,
                    "destroyable": false,
                    "visibility": false,
                    "unit": null,
                    "gravity": null
                }
                _debug_map[x][y][z] = tile_data
    for y in GRID_SIZE_LENGTH:
        for x in GRID_SIZE_WIDTH:
            var tile = _debug_map[x][y][0]
            tile["atlas_position"] = BLUE_ISOMETRICTILE_ATLAS_POSITION
            tile["visibility"] = true
            
    for y in GRID_SIZE_LENGTH:
        for x in GRID_SIZE_WIDTH:
            var tile = _debug_map[x][y][GRID_SIZE_HEIGHT-1]
            tile["atlas_position"] = BLUE_ISOMETRICTILE_ATLAS_POSITION
            tile["visibility"] = true
            
    for z in range(1,GRID_SIZE_HEIGHT-1):
        for x in GRID_SIZE_WIDTH:
            var tile = _debug_map[x][0][z]
            tile["atlas_position"] = GREEN_ISOMETRICTILE_ATLAS_POSITION
            tile["visibility"] = true
    for z in range(1,GRID_SIZE_HEIGHT-1):
        for x in GRID_SIZE_WIDTH:
            var tile = _debug_map[x][GRID_SIZE_LENGTH-1][z]
            tile["atlas_position"] = GREEN_ISOMETRICTILE_ATLAS_POSITION
            tile["visibility"] = true
            
    for z in range(1,GRID_SIZE_HEIGHT-1):
        for y in range(1,GRID_SIZE_LENGTH-1):
            var tile = _debug_map[0][y][z]
            tile["atlas_position"] = BLACK_ISOMETRICTILE_ATLAS_POSITION
            tile["visibility"] = true
    for z in range(1,GRID_SIZE_HEIGHT-1):
        for y in range(1,GRID_SIZE_LENGTH-1):
            var tile = _debug_map[GRID_SIZE_WIDTH-1][y][z]
            tile["atlas_position"] = RED_ISOMETRICTILE_ATLAS_POSITION
            tile["visibility"] = true
    
    return _debug_map

func rotate_map_around_x_axis(rotation_steps = 1):
    rotation_steps = rotation_steps % 4
    
    if rotation_steps == 0:
        return
    
    var temp_map = []
    # Initialize temp_map with same structure
    
    _set_max_array_size()
    for x in MAX_ARRAY_SIZE:
        var y_array = []
        for y in MAX_ARRAY_SIZE:
            var z_array = []
            for z in MAX_ARRAY_SIZE:
                z_array.append(null)
            y_array.append(z_array)
        temp_map.append(y_array)
    
    # Create a new map with adjusted dimensions
    var new_height = int(GRID_SIZE_LENGTH) if rotation_steps % 2 == 1 else int(GRID_SIZE_HEIGHT)
    var new_length = int(GRID_SIZE_HEIGHT) if rotation_steps % 2 == 1 else int(GRID_SIZE_LENGTH)
    
    # Store the original dimensions
    var original_height = GRID_SIZE_HEIGHT
    var original_length = GRID_SIZE_LENGTH
    
    # Temporarily adjust grid dimensions for coordinate calculation
    GRID_SIZE_HEIGHT = new_height
    GRID_SIZE_LENGTH = new_length
    
    for x in GRID_SIZE_WIDTH:
        for y in original_length:
            for z in original_height:
                if debug_map[x][y][z] == null:
                    continue
                
                var new_y = 0
                var new_z = 0
                
                match rotation_steps:
                    1:  # 90 degrees
                        new_y = z
                        new_z = original_length - 1 - y
                    2:  # 180 degrees
                        new_y = original_length - 1 - y
                        new_z = original_height - 1 - z
                    3:  # 270 degrees
                        new_y = original_height - 1 - z
                        new_z = y
                
                if new_y >= 0 and new_y < new_length and new_z >= 0 and new_z < new_height:
                    temp_map[x][new_y][new_z] = debug_map[x][y][z].duplicate()
                    
                    var new_coord_x = x + (-1 * new_z) + OFFSET
                    var new_coord_y = new_y + (-1 * new_z) - OFFSET
                    
                    temp_map[x][new_y][new_z]["x"] = new_coord_x
                    temp_map[x][new_y][new_z]["y"] = new_coord_y
                    temp_map[x][new_y][new_z]["z"] = new_z
    
    # Update the grid dimensions
    GRID_SIZE_HEIGHT = new_height
    GRID_SIZE_LENGTH = new_length
    
    debug_map = temp_map

func rotate_map_around_y_axis(rotation_steps = 1):
    rotation_steps = rotation_steps % 4
    
    if rotation_steps == 0:
        return
    
    var temp_map = []
    # Initialize temp_map with same structure
    _set_max_array_size()
    for x in MAX_ARRAY_SIZE:
        var y_array = []
        for y in MAX_ARRAY_SIZE:
            var z_array = []
            for z in MAX_ARRAY_SIZE:
                z_array.append(null)
            y_array.append(z_array)
        temp_map.append(y_array)
    
    # Create a new map with adjusted dimensions
    var new_width = int(GRID_SIZE_HEIGHT) if rotation_steps % 2 == 1 else int(GRID_SIZE_WIDTH)
    var new_height = int(GRID_SIZE_WIDTH) if rotation_steps % 2 == 1 else int(GRID_SIZE_HEIGHT)
    
    # Store the original dimensions
    var original_width = GRID_SIZE_WIDTH
    var original_height = GRID_SIZE_HEIGHT
    
    # Temporarily adjust grid dimensions for coordinate calculation
    GRID_SIZE_WIDTH = new_width
    GRID_SIZE_HEIGHT = new_height
    
    for x in original_width:
        for y in GRID_SIZE_LENGTH:
            for z in original_height:
                if debug_map[x][y][z] == null:
                    continue
                
                var new_x = 0
                var new_z = 0
                
                match rotation_steps:
                    1:  # 90 degrees clockwise around Y axis
                        new_x = original_height - 1 - z
                        new_z = x
                    2:  # 180 degrees
                        new_x = original_width - 1 - x
                        new_z = original_height - 1 - z
                    3:  # 270 degrees
                        new_x = z
                        new_z = original_width - 1 - x
                
                if new_x >= 0 and new_x < new_width and new_z >= 0 and new_z < new_height:
                    temp_map[new_x][y][new_z] = debug_map[x][y][z].duplicate()
                    
                    var new_coord_x = new_x + (-1 * new_z) + OFFSET
                    var new_coord_y = y + (-1 * new_z) - OFFSET
                    
                    temp_map[new_x][y][new_z]["x"] = new_coord_x
                    temp_map[new_x][y][new_z]["y"] = new_coord_y
                    temp_map[new_x][y][new_z]["z"] = new_z
    
    # Update the grid dimensions
    GRID_SIZE_WIDTH = new_width
    GRID_SIZE_HEIGHT = new_height
    
    debug_map = temp_map

func rotate_map_around_z_axis(rotation_steps = 1):
    rotation_steps = rotation_steps % 4
    
    if rotation_steps == 0:
        return
    
    var temp_map = []
    # Initialize temp_map with same structure
    _set_max_array_size()
    for x in MAX_ARRAY_SIZE:
        var y_array = []
        for y in MAX_ARRAY_SIZE:
            var z_array = []
            for z in MAX_ARRAY_SIZE:
                z_array.append(null)
            y_array.append(z_array)
        temp_map.append(y_array)
    
    # Create a new map with adjusted dimensions based on rotation
    var new_width = int(GRID_SIZE_LENGTH) if rotation_steps % 2 == 1 else int(GRID_SIZE_WIDTH)
    var new_length = int(GRID_SIZE_WIDTH) if rotation_steps % 2 == 1 else int(GRID_SIZE_LENGTH)
    
    # Store the original dimensions
    var original_width = GRID_SIZE_WIDTH
    var original_length = GRID_SIZE_LENGTH
    
    # Temporarily adjust grid dimensions for coordinate calculation
    GRID_SIZE_WIDTH = new_width
    GRID_SIZE_LENGTH = new_length
    
    for x in original_width:
        for y in original_length:
            for z in GRID_SIZE_HEIGHT:
                if debug_map[x][y][z] == null:
                    continue
                
                var new_x = 0
                var new_y = 0
                
                match rotation_steps:
                    1:  # 90 degrees
                        new_x = y
                        new_y = original_width - 1 - x
                    2:  # 180 degrees
                        new_x = original_width - 1 - x
                        new_y = original_length - 1 - y
                    3:  # 270 degrees
                        new_x = original_length - 1 - y
                        new_y = x
                
                if new_x >= 0 and new_x < new_width and new_y >= 0 and new_y < new_length:
                    temp_map[new_x][new_y][z] = debug_map[x][y][z].duplicate()
                    
                    var new_coord_x = new_x + (-1 * z) + OFFSET
                    var new_coord_y = new_y + (-1 * z) - OFFSET
                    
                    temp_map[new_x][new_y][z]["x"] = new_coord_x
                    temp_map[new_x][new_y][z]["y"] = new_coord_y
                    temp_map[new_x][new_y][z]["z"] = z
    
    # Update the grid dimensions
    GRID_SIZE_WIDTH = new_width
    GRID_SIZE_LENGTH = new_length
    
    debug_map = temp_map
var isometric_map_layers = []

func initialize_map_layers():
    # Clear any existing layers
    for layer in isometric_map_layers:
        if is_instance_valid(layer):
            layer.queue_free()
    
    isometric_map_layers.clear()
    
    # Create new layers for each height level
    for z in GRID_SIZE_HEIGHT:
        var new_layer = TileMapLayer.new()
        new_layer.set_name("IsometricMapLayer_" + str(z))
        new_layer.tile_set = $TileMapLayer.tile_set  # Set your tileset
        new_layer.z_index = z  # Set the z-index to match the layer's height
        
        # Add other TileMap settings as needed
        
        add_child(new_layer)
        isometric_map_layers.append(new_layer)

var debug_map = create_debug_map_array()
# Draw the map
func draw_visible_tiles():
    # clear all previously drawn tiles on each layer
    for layer in isometric_map_layers:
        layer.clear()
    # draw the map
    for z in GRID_SIZE_HEIGHT:
        for y in GRID_SIZE_LENGTH:
            for x in GRID_SIZE_WIDTH:
                var tile = debug_map[x][y][z]
                if tile != null and tile["visibility"]:
                    # Use the layer that corresponds to the tile's z-value
                    if z < isometric_map_layers.size():
                        isometric_map_layers[z].set_cell(
                            Vector2i(tile["x"], tile["y"]), 
                            MAIN_SOURCE_ID, 
                            tile["atlas_position"]
                        )


func _ready() -> void:    
    # Store initial camera position
    initial_camera_position = camera.position
    initial_camera_zoom = camera.zoom
    initialize_map_layers()
    draw_visible_tiles()

func _input(event):
    # Camera drag (pan) control
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT:
            if event.pressed:
                drag_start = event.position
                drag_active = true
            else:
                drag_active = false
        
        # Zoom control with mouse wheel
        if event.button_index == MOUSE_BUTTON_WHEEL_UP:
            zoom_camera(-zoom_speed)
        elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
            zoom_camera(zoom_speed)
    
    # Handle camera movement while dragging
    if event is InputEventMouseMotion and drag_active:
        camera.position -= event.relative / camera.zoom

func _on_reset_button_pressed() -> void:
    # Reset camera position and zoom
    
    # initialize_map_layers()
    # debug_map = create_debug_map_array()
    # draw_visible_tiles()
    camera.position = initial_camera_position
    camera.zoom = initial_camera_zoom
    
func zoom_camera(zoom_amount):
    var new_zoom = camera.zoom.x + zoom_amount
    new_zoom = clamp(new_zoom, zoom_min, zoom_max)
    camera.zoom = Vector2(new_zoom, new_zoom)
    
func _on_cw_pressed() -> void:
    rotate_map_around_z_axis(3)
    initialize_map_layers()
    draw_visible_tiles()

func _on_ccw_pressed() -> void:
    rotate_map_around_z_axis(1)
    initialize_map_layers()
    draw_visible_tiles()

func _on_x_cw_pressed() -> void:
    rotate_map_around_x_axis(1)
    initialize_map_layers()
    draw_visible_tiles()

func _on_x_ccw_pressed() -> void:
    rotate_map_around_x_axis(3)
    initialize_map_layers()
    draw_visible_tiles()

func _on_y_cw_pressed() -> void:
    rotate_map_around_y_axis(3)
    initialize_map_layers()
    draw_visible_tiles()

func _on_y_ccw_pressed() -> void:
    rotate_map_around_y_axis(1)
    initialize_map_layers()
    draw_visible_tiles()
