import xml.etree.ElementTree as ET

class BossPlacer:
    def __init__(self):
        self.view_range = 400  # Larger view range for bosses
    
    def process(self, map_data):
        """Process boss robot placement"""
        obj_group = map_data.find(".//objectgroup[@name='Battle_Mecha_Robots']")
        if obj_group is None:
            obj_group = ET.SubElement(map_data.getroot(), 'objectgroup')
            obj_group.set('name', 'Battle_Mecha_Robots')
        
        for boss in obj_group.findall(".//object"):
            self._add_boss_properties(boss)
    
    def _add_boss_properties(self, boss):
        x = float(boss.get('x'))
        y = float(boss.get('y'))
        width = float(boss.get('width', 64))  # Larger size for bosses
        height = float(boss.get('height', 64))
        
        # Center the boss
        boss.set('x', str(x + width/2))
        boss.set('y', str(y + height/2))
        
        properties = boss.find('properties')
        if properties is None:
            properties = ET.SubElement(boss, 'properties')
        
        # Add view area
        view_area = {
            'x': x - self.view_range/2,
            'y': y - self.view_range/2,
            'width': self.view_range,
            'height': self.view_range
        }
        
        view_prop = ET.SubElement(properties, 'property')
        view_prop.set('name', 'view_area')
        view_prop.set('value', str(view_area))
        
        # Add boss-specific properties
        health_prop = ET.SubElement(properties, 'property')
        health_prop.set('name', 'health')
        health_prop.set('value', '1000')
        
        damage_prop = ET.SubElement(properties, 'property')
        damage_prop.set('name', 'damage')
        damage_prop.set('value', '50')
        
        attack_range_prop = ET.SubElement(properties, 'property')
        attack_range_prop.set('name', 'attack_range')
        attack_range_prop.set('value', '150')

____




import xml.etree.ElementTree as ET

class EnemyPlacer:
    def __init__(self):
        self.view_range = 200  # Default view range in pixels

    def process(self, map_data):
        """Process enemy placement in the map"""
        for obj_group in map_data.findall(".//objectgroup[@name='enemies']"):
            for enemy in obj_group.findall(".//object"):
                self._add_view_area(enemy)
    def add_enemy(self, x, y):
        """Add enemy position to the list"""
        self.enemies.append({'x': x, 'y': y})

    def _add_view_area(self, enemy):
        """Add view area for enemy"""
        x = float(enemy.get('x'))
        y = float(enemy.get('y'))
        
        # Default dimensions if not specified in the object
        width = float(enemy.get('width', 32))  # Default width (assuming 32 if not specified)
        height = float(enemy.get('height', 32))  # Default height (assuming 32 if not specified)

        # Create view area rectangle based on the enemy's current position and view range
        view_area = {
            'x': x - width,
            'y': y - height,
            'width': width,
            'height': height
        }
        
        # Add view area properties to enemy object
        properties = enemy.find('properties')
        if properties is None:
            properties = ET.SubElement(enemy, 'properties')
        
        view_prop = ET.SubElement(properties, 'property')
        view_prop.set('name', 'view_area')
        view_prop.set('value', f"{view_area['x']},{view_area['y']},{view_area['width']},{view_area['height']}")

        # Log or print the view area for debugging
        print(f"Enemy at ({x},{y}) has view area: {view_area}")

_____

import xml.etree.ElementTree as ET

class MoverPlacer:
    def process(self, map_data):
        """Process moving objects with paths"""
        obj_group = map_data.find(".//objectgroup[@name='movers']")
        if obj_group is None:
            obj_group = ET.SubElement(map_data.getroot(), 'objectgroup')
            obj_group.set('name', 'movers')
        
        for mover in obj_group.findall(".//object"):
            self._add_mover_properties(mover)
    
    def _add_mover_properties(self, mover):
        properties = mover.find('properties')
        if properties is None:
            properties = ET.SubElement(mover, 'properties')
        
        # Add movement path
        path_prop = ET.SubElement(properties, 'property')
        path_prop.set('name', 'path_points')
        
        # Get start and end points from object
        start_x = float(mover.get('x'))
        start_y = float(mover.get('y'))
        width = float(mover.get('width', 32))
        
        # Create a simple back-and-forth path
        end_x = start_x + width
        path_points = f"{start_x},{start_y} {end_x},{start_y}"
        path_prop.set('value', path_points)
        
        # Add velocity
        velocity_prop = ET.SubElement(properties, 'property')
        velocity_prop.set('name', 'velocity')
        velocity_prop.set('value', '100')


_____


import xml.etree.ElementTree as ET

class PolicePlacer:
    def __init__(self):
        self.view_range = 250  # Larger view range for cyber police
    
    def process(self, map_data):
        """Process cyber police placement"""
        obj_group = map_data.find(".//objectgroup[@name='polices']")
        if obj_group is None:
            obj_group = ET.SubElement(map_data.getroot(), 'objectgroup')
            obj_group.set('name', 'polices')
        
        for police in obj_group.findall(".//object"):
            self._add_police_properties(police)
    
    def _add_police_properties(self, police):
        x = float(police.get('x'))
        y = float(police.get('y'))
        width = float(police.get('width', 48))  # Larger size for police
        height = float(police.get('height', 48))
        
        # Center the police
        police.set('x', str(x + width/2))
        police.set('y', str(y + height/2))
        
        # Add view area and behavior properties
        properties = police.find('properties')
        if properties is None:
            properties = ET.SubElement(police, 'property')
        
        # View area
        view_area = {
            'x': x - self.view_range/2,
            'y': y - self.view_range/2,
            'width': self.view_range,
            'height': self.view_range
        }
        
        view_prop = ET.SubElement(properties, 'property')
        view_prop.set('name', 'view_area')
        view_prop.set('value', str(view_area))
        
        # Patrol behavior
        patrol_prop = ET.SubElement(properties, 'property')
        patrol_prop.set('name', 'patrol_type')
        patrol_prop.set('value', 'aggressive')

_____
import xml.etree.ElementTree as ET

class WorkerPlacer:
    def __init__(self):
        self.view_range = 150  # Default view range for workers
    
    def process(self, map_data):
        """Process worker placement"""
        obj_group = map_data.find(".//objectgroup[@name='workers']")
        if obj_group is None:
            obj_group = ET.SubElement(map_data.getroot(), 'objectgroup')
            obj_group.set('name', 'workers')
        
        for worker in obj_group.findall(".//object"):
            self._add_worker_properties(worker)
    
    def _add_worker_properties(self, worker):
        x = float(worker.get('x'))
        y = float(worker.get('y'))
        width = float(worker.get('width', 32))
        height = float(worker.get('height', 32))
        
        # Center the worker
        worker.set('x', str(x + width/2))
        worker.set('y', str(y + height/2))
        
        properties = worker.find('properties')
        if properties is None:
            properties = ET.SubElement(worker, 'properties')
        
        # Add view area
        view_area = {
            'x': x - self.view_range/2,
            'y': y - self.view_range/2,
            'width': self.view_range,
            'height': self.view_range
        }
        
        view_prop = ET.SubElement(properties, 'property')
        view_prop.set('name', 'view_area')
        view_prop.set('value', str(view_area))
        
        # Add behavior properties
        behavior_prop = ET.SubElement(properties, 'property')
        behavior_prop.set('name', 'behavior')
        behavior_prop.set('value', 'patrol')


_____




class ObjectHandler:
    def __init__(self):
        self.objects = {}

    def add_object(self, layer, obj_type, x, y, properties=None):
        """Add object to specified layer"""
        if layer not in self.objects:
            self.objects[layer] = []
        
        # Center the object
        centered_x = x + (properties.get('width', 0) / 2 if properties else 0)
        centered_y = y + (properties.get('height', 0) / 2 if properties else 0)
        
        obj = {
            'type': obj_type,
            'x': centered_x,
            'y': centered_y,
            'properties': properties or {}
        }
        self.objects[layer].append(obj)
        return obj

    def get_objects(self, layer):
        """Get all objects in a layer"""
        return self.objects.get(layer, [])



______


import xml.etree.ElementTree as ET
from pathlib import Path
import traceback

class MapLoader:
    def __init__(self, map_path):
        self.map_path = Path(map_path)

    def load(self):
        """Load TMX map file"""
        try:
            tree = ET.parse(self.map_path)
            return tree
        except Exception as e:
            print(f"Error loading map: {e}")
            traceback.print_exc()  # Traceback ekle
            return None

    def save(self, map_data):
        """Save modified map"""
        try:
            map_data.write(self.map_path)
        except Exception as e:
            print(f"Error saving map: {e}")
            traceback.print_exc()  # Traceback ekle
_____


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.")


hatalar var apply_position fonksyionu ile uyumsuzluk anlamında düzelt