import os
import traceback
from core.map_loader import MapLoader
from core.object_handler import ObjectHandler
from layers.stories_layer import StoriesLayer#bu object layer için
from layers.important_coords import ImportantCoordsLayer#bu object layer
from layers.ladders_layer import LaddersLayer#bu object layer
from layers.dangerous_layer import DangerousLayer#bu object layer
from layers.mission_layer import MissionLayer#bu object layer
from layers.texts_layer import TextsLayer#bu object layer
from entities.enemy_placer import EnemyPlacer#bu object layer
from entities.police_placer import PolicePlacer#bu object layer
from entities.worker_placer import WorkerPlacer#bu object layer
from entities.mover_placer import MoverPlacer#bu object layer
from entities.boss_placer import BossPlacer#bu object layer
#from entities.TileLayer import TileLayer bu tile layer 
from layers.bg_layer import BackgroundLayer # tilelayer

class MapGenerator:
    def __init__(self, map_path):
        self.map_loader = MapLoader(map_path)
        self.object_handler = ObjectHandler()
        self.layers = {
            'background': BackgroundLayer(),
            'stories': StoriesLayer(),
            'important_coords': ImportantCoordsLayer(),
            'ladders': LaddersLayer(),
            'dangerous': DangerousLayer(),
            'mission': MissionLayer(),
            'texts': TextsLayer(),
            'enemies': EnemyPlacer(),
            'police': PolicePlacer(),
            'workers': WorkerPlacer(),
            'movers': MoverPlacer(),
            'bosses': BossPlacer()
        }

    def generate(self):
        """Generate the complete map with all layers and entities"""
        try:
            map_data = self.map_loader.load()
            if map_data is None:
                print("Failed to load map data.")
                return

            # Process each layer
            for layer_name, layer in self.layers.items():
                try:
                    layer.process(map_data)
                    print(f"Processed {layer_name} layer successfully.")
                except Exception as e:
                    print(f"Error processing {layer_name} layer: {e}")
                    traceback.print_exc()  # Print detailed traceback for debugging

            self.map_loader.save(map_data)
            print("Map generation completed successfully.")
        
        except Exception as main_exception:
            print(f"Failed to generate the map: {main_exception}")
            traceback.print_exc()

if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="Generate game map with all layers and entities")
    parser.add_argument("map_path", help="Path to the TMX map file")
    
    args = parser.parse_args()
    
    generator = MapGenerator(args.map_path)
    generator.generate()

_____
import numpy as np
from sklearn.preprocessing import MinMaxScaler
import logging
import traceback
from pathlib import Path
from main import MapGenerator
import xml.etree.ElementTree as ET

logging.basicConfig(level=logging.INFO)

class AIMapController:
    def __init__(self):
        self.logger = logging.getLogger(__name__)
        self.scaler = MinMaxScaler()
        
        # Parameters for different entity types
        self.params = {
            'enemy_density': 0.15,  # Enemies per tile area
            'police_density': 0.08,  # Police per tile area
            'worker_density': 0.2,   # Workers per tile area
            'trap_density': 0.1,     # Traps per tile area
            'min_distance': 100,     # Minimum distance between entities
            'view_range': {
                'enemy': 200,
                'police': 250,
                'worker': 150,
                'boss': 400
            }
        }

    def generate_map(self, map_path, difficulty=0.5):
        """Generate a complete map with AI-controlled placement"""
        try:
            self.logger.info(f"Starting AI map generation with difficulty {difficulty}")
            
            # Initialize map generator
            generator = MapGenerator(map_path)
            self.logger.info(f"Map path: {map_path}")
            # Analyze map structure
            map_data = self._analyze_map(map_path)
            
            # Adjust parameters based on difficulty
            self._adjust_parameters(difficulty)
            
            # Generate entity positions
            positions = self._generate_positions(map_data)
            
            # Apply positions to map
            self._apply_positions(generator, positions)
            
            # Generate the final map
            generator.generate()
            
            self.logger.info("AI map generation completed successfully")
            print("Map generation successful")  # Print for Node.js to capture
            return True
            
        except Exception as e:
            error_message = f"AI map generation failed: {e}\n{traceback.format_exc()}"
            self.logger.error(error_message)
            print(f"Map generation failed: {e}")
            return False

    def _analyze_map(self, map_path):
        """Analyze map structure and return useful data"""
        self.logger.info("Analyzing map...")
        try:
            # Get map dimensions and walkable areas
            map_size = self._get_map_size(map_path)
            walkable_areas = self._get_walkable_areas(map_path)

            if not walkable_areas:
                self.logger.error("No walkable areas found in the map.")
                self._log_error_to_file("No walkable areas found in the map.", map_path)
                raise ValueError("No walkable areas found in the map.")

            return {
                'size': map_size,
                'walkable_areas': walkable_areas,
                'complexity': self._calculate_complexity(walkable_areas)
            }
        except Exception as e:
            error_message = f"Map analysis failed: {e}\n{traceback.format_exc()}"
            self.logger.error(error_message)
            self._log_error_to_file(error_message, map_path)
            raise

    def _get_walkable_areas(self, map_path):
        """Get walkable areas from the collision layer"""
        try:
            tree = ET.parse(map_path)
            root = tree.getroot()

            walkable_areas = []
            width = int(root.attrib['width'])
            height = int(root.attrib['height'])
            tile_width = int(root.attrib['tilewidth'])
            tile_height = int(root.attrib['tileheight'])

            empty_layer = True

            for layer in root.findall('layer'):
                layer_name = layer.get('name')
                if layer_name == 'Tile Layer 1':
                    empty_layer = False
                    
                    data = layer.find('data').text.strip().split(',')
                    for index, tile_id in enumerate(data):
                        if int(tile_id) == 0:  # Walkable tile
                            x = (index % width) * tile_width
                            y = (index // width) * tile_height
                            walkable_areas.append({
                                'x': x,
                                'y': y,
                                'width': tile_width,
                                'height': tile_height
                            })

            # If layer is empty, consider all tiles walkable
            if empty_layer:
                for y in range(height):
                    for x in range(width):
                        walkable_areas.append({
                            'x': x * tile_width,
                            'y': y * tile_height,
                            'width': tile_width,
                            'height': tile_height
                        })

            return walkable_areas
        except Exception as e:
            error_message = f"Error getting walkable areas: {e}\n{traceback.format_exc()}"
            self.logger.error(error_message)
            self._log_error_to_file(error_message, map_path)
            return []

    def _adjust_parameters(self, difficulty):
        """Adjust entity parameters based on difficulty"""
        try:
            scale = 0.5 + difficulty  # Scale from 0.5 to 1.5
            
            self.params['enemy_density'] *= scale
            self.params['police_density'] *= scale
            self.params['trap_density'] *= scale
            
            # Adjust view ranges
            for entity_type in self.params['view_range']:
                self.params['view_range'][entity_type] = int(
                    self.params['view_range'][entity_type] * scale
                )
        except Exception as e:
            self.logger.error(f"Error adjusting parameters: {e}\n{traceback.format_exc()}")
            raise

    def _generate_positions(self, map_data):
        """Generate optimal positions for all entities"""
        try:
            positions = {
                'enemies': [],
                'police': [],
                'workers': [],
                'traps': [],
                'bosses': []
            }
            
            map_size = map_data['size']
            walkable = map_data['walkable_areas']
            
            # Calculate number of entities based on density and map size
            area = map_size[0] * map_size[1]
            
            num_enemies = int(area * self.params['enemy_density'])
            num_police = int(area * self.params['police_density'])
            num_workers = int(area * self.params['worker_density'])
            
            # Generate positions ensuring minimum distance between entities
            positions['enemies'] = self._place_entities(
                num_enemies, walkable, self.params['min_distance']
            )
            positions['police'] = self._place_entities(
                num_police, walkable, self.params['min_distance']
            )
            positions['workers'] = self._place_entities(
                num_workers, walkable, self.params['min_distance']
            )
            
            # Place boss at strategic location
            positions['bosses'] = self._place_boss(walkable, positions)
            
            return positions
        except Exception as e:
            self.logger.error(f"Error generating positions: {e}\n{traceback.format_exc()}")
            raise

    def _place_entities(self, num_entities, walkable_areas, min_distance):
        """Place entities with minimum distance constraints"""
        try:
            positions = []
            attempts = 0
            max_attempts = num_entities * 10
            
            while len(positions) < num_entities and attempts < max_attempts:
                pos = self._get_random_walkable_position(walkable_areas)
                
                # Check minimum distance from other entities
                if all(self._calculate_distance(pos, p) >= min_distance for p in positions):
                    positions.append(pos)
                
                attempts += 1
            
            return positions
        except Exception as e:
            self.logger.error(f"Error placing entities: {e}\n{traceback.format_exc()}")
            raise

    def _place_boss(self, walkable_areas, existing_positions):
        """Place boss in strategic location"""
        try:
            # Find position far from other entities but in walkable area
            best_pos = None
            max_min_distance = 0
            
            for _ in range(100):  # Try 100 random positions
                pos = self._get_random_walkable_position(walkable_areas)
                min_distance = float('inf')
                
                # Check distance from all other entities
                for positions in existing_positions.values():
                    for p in positions:
                        dist = self._calculate_distance(pos, p)
                        min_distance = min(min_distance, dist)
                
                if min_distance > max_min_distance:
                    max_min_distance = min_distance
                    best_pos = pos
            
            return [best_pos] if best_pos else []
        except Exception as e:
            self.logger.error(f"Error placing boss: {e}\n{traceback.format_exc()}")
            raise

    def _get_random_walkable_position(self, walkable_areas):
        """Get random position in walkable area"""
        try:
            area = np.random.choice(walkable_areas)
            x = np.random.uniform(area['x'], area['x'] + area['width'])
            y = np.random.uniform(area['y'], area['y'] + area['height'])
            return {'x': x, 'y': y}
        except Exception as e:
            self.logger.error(f"Error getting random walkable position: {e}\n{traceback.format_exc()}")
            raise

    def _calculate_distance(self, pos1, pos2):
        """Calculate Euclidean distance between two positions"""
        try:
            return np.sqrt(
                (pos1['x'] - pos2['x'])**2 + 
                (pos1['y'] - pos2['y'])**2
            )
        except Exception as e:
            self.logger.error(f"Error calculating distance: {e}\n{traceback.format_exc()}")
            raise

    def _apply_positions(self, generator, positions):
        """Apply generated positions to the map"""
        try:
            for entity_type, pos_list in positions.items():
                for pos in pos_list:
                    if entity_type == 'enemies':
                        generator.layers['enemies'].add_enemy(pos['x'], pos['y'])
                    elif entity_type == 'police':
                        generator.layers['police'].add_police(pos['x'], pos['y'])
                    elif entity_type == 'workers':
                        generator.layers['workers'].add_worker(pos['x'], pos['y'])
                    elif entity_type == 'bosses':
                        generator.layers['bosses'].add_boss(pos['x'], pos['y'])
        except Exception as e:
            self.logger.error(f"Error applying positions: {e}\n{traceback.format_exc()}")
            raise

    def _get_map_size(self, map_path):
        """Get map dimensions from TMX file"""
        try:
            tree = ET.parse(map_path)
            root = tree.getroot()
            return (
                int(root.get('width', 0)) * int(root.get('tilewidth', 0)),
                int(root.get('height', 0)) * int(root.get('tileheight', 0))
            )
        except Exception as e:
            self.logger.error(f"Error getting map size: {e}\n{traceback.format_exc()}")
            raise

    def _log_error_to_file(self, error_message, map_path):
        """Log error message to a file for further analysis."""
        error_log_path = 'error_log.txt'
        error_msg = f"Error in {map_path}: {error_message}\n"
        with open(error_log_path, 'a') as file:
            file.write(error_msg)
        self.logger.info(f"Error logged to {error_log_path}")

    def _calculate_complexity(self, walkable_areas):
        """Calculate map complexity score"""
        try:
            return len(walkable_areas)  # Complexity based on number of walkable areas
        except Exception as e:
            self.logger.error(f"Error calculating map complexity: {e}\n{traceback.format_exc()}")
            raise

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="Generate AI maps.")
    parser.add_argument("--map", help="Path to the TMX map file", required=True)
    parser.add_argument("--difficulty", type=float, default=0.5, help="Difficulty level for map generation")
    
    args = parser.parse_args()
    
    controller = AIMapController()
    success = controller.generate_map(args.map, args.difficulty)

    if success:
        print("Map generation successful.")
    else:
        print("Map generation failed.")
______
bu ai sınıfı bu ikisini düzenle

ve yeni bir bunları değiştir ama sadece ekleme yap

buradaki tilelayer1 sınıfını üret ama bu dikkat tile layer olarak geçer yani objet layer değil

bu ikisi de
#from entities.TileLayer import TileLayer bu tile layer 
from layers.bg_layer import BackgroundLayer # tilelayer

bunlar boş bunları şu mümkünse elimizdeki kütüphaneler ile harita üretelim tile lardan tileset deki hangi tileset ise artık onu bulsun