import numpy as np
from sklearn.preprocessing import MinMaxScaler
import logging
import traceback
from pathlib import Path
import xml.etree.ElementTree as ET
from PIL import Image
import cv2
import numpy as np
from sklearn.cluster import KMeans
from main import MapGenerator

#from core.map_loader import MapLoader
#from layers.bg_layer import BackgroundLayer

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,
            'police_density': 0.08,
            'worker_density': 0.2,
            'trap_density': 0.1,
            'min_distance': 100,
            'view_range': {
                'enemy': 200,
                'police': 250,
                'worker': 150,
                'boss': 400
            }
        }
        
        self.tileset_cache = {}
        self.pattern_cache = {}

    def analyze_tileset(self, tileset_path):
        """Tileset'i analiz et ve benzer desenleri bul"""
        try:
            if tileset_path in self.tileset_cache:
                return self.tileset_cache[tileset_path]

            # Tileset'i yükle
            tileset_img = Image.open(tileset_path)
            tileset_array = np.array(tileset_img)

            # Tile'ları parçala
            tile_size = 24  # varsayılan tile boyutu
            tiles = []
            
            for y in range(0, tileset_array.shape[0], tile_size):
                for x in range(0, tileset_array.shape[1], tile_size):
                    if y + tile_size <= tileset_array.shape[0] and x + tile_size <= tileset_array.shape[1]:
                        tile = tileset_array[y:y + tile_size, x:x + tile_size]
                        tiles.append(tile)

            # Tile özelliklerini çıkar
            tile_features = self._extract_tile_features(tiles)

            # Benzer tile'ları grupla
            kmeans = KMeans(n_clusters=min(self.params['tile_analysis']['max_patterns'], len(tiles)))
            clusters = kmeans.fit_predict(tile_features)

            # Sonuçları cache'le
            analysis_result = {
                'tiles': tiles,
                'features': tile_features,
                'clusters': clusters,
                'tile_size': tile_size
            }
            
            self.tileset_cache[tileset_path] = analysis_result
            return analysis_result

        except Exception as e:
            self.logger.error(f"Tileset analizi başarısız: {e}\n{traceback.format_exc()}")
            raise

    def _extract_tile_features(self, tiles):
        """Tile'lardan özellik çıkarımı yap"""
        features = []
        for tile in tiles:
            # Renk histogramı
            hist = cv2.calcHist([tile], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
            hist = cv2.normalize(hist, hist).flatten()
            
            # Kenar özellikleri
            gray = cv2.cvtColor(tile, cv2.COLOR_RGB2GRAY)
            edges = cv2.Canny(gray, 100, 200)
            edge_density = np.sum(edges > 0) / (edges.shape[0] * edges.shape[1])
            
            # Doku özellikleri
            texture_features = self._calculate_texture_features(gray)
            
            # Tüm özellikleri birleştir
            combined_features = np.concatenate([hist, [edge_density], texture_features])
            features.append(combined_features)
            
        return np.array(features)

    def _calculate_texture_features(self, gray_image):
        """Gri seviye görüntüden doku özellikleri hesapla"""
        glcm = cv2.calcHist([gray_image], [0], None, [16], [0, 256])
        glcm = cv2.normalize(glcm, glcm).flatten()
        return glcm

    def generate_tilemap(self, map_size, tileset_path):
        """AI ile tilemap oluştur"""
        try:
            # Tileset'i analiz et
            tileset_analysis = self.analyze_tileset(tileset_path)
            tiles = tileset_analysis['tiles']
            clusters = tileset_analysis['clusters']
            
            # Boş harita oluştur
            height, width = map_size
            tilemap = np.zeros((height, width), dtype=int)
            
            # Pattern tabanlı yerleştirme
            for y in range(height):
                for x in range(width):
                    # Komşu tile'ları kontrol et
                    neighbors = self._get_neighbor_tiles(tilemap, x, y)
                    
                    # En uygun tile'ı seç
                    best_tile_idx = self._select_best_tile(neighbors, tiles, clusters)
                    tilemap[y, x] = best_tile_idx
            
            return tilemap

        except Exception as e:
            self.logger.error(f"Tilemap oluşturma başarısız: {e}\n{traceback.format_exc()}")
            raise

    def _get_neighbor_tiles(self, tilemap, x, y):
        """Belirtilen konumdaki komşu tile'ları getir"""
        neighbors = []
        directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]  # yukarı, aşağı, sol, sağ
        
        for dx, dy in directions:
            new_x, new_y = x + dx, y + dy
            if 0 <= new_x < tilemap.shape[1] and 0 <= new_y < tilemap.shape[0]:
                neighbors.append(tilemap[new_y, new_x])
        
        return neighbors

    def _select_best_tile(self, neighbors, tiles, clusters):
        """Komşulara göre en uygun tile'ı seç"""
        if not neighbors or all(n == 0 for n in neighbors):
            return np.random.randint(0, len(tiles))
            
        # Komşu tile'ların cluster'larına bak
        neighbor_clusters = [clusters[n] for n in neighbors if n < len(clusters)]
        
        if not neighbor_clusters:
            return np.random.randint(0, len(tiles))
            
        # En çok tekrar eden cluster'ı bul
        most_common_cluster = max(set(neighbor_clusters), key=neighbor_clusters.count)
        
        # Bu cluster'daki tile'lardan birini seç
        possible_tiles = [i for i, c in enumerate(clusters) if c == most_common_cluster]
        return np.random.choice(possible_tiles)
#apply_tilemap
#generate_tilemap

    def apply_tilemap(self, generator, tilemap, tileset_analysis):
        """Oluşturulan tilemap'i uygula"""
        try:
            tile_size = tileset_analysis['tile_size']
            tiles = tileset_analysis['tiles']
            
            height, width = tilemap.shape
            for y in range(height):
                for x in range(width):
                    tile_idx = tilemap[y, x]
                    if tile_idx < len(tiles):
                        tile = tiles[tile_idx]
                        pos_x = x * tile_size
                        pos_y = y * tile_size
                        generator.layers['ground'].add_tile(pos_x, pos_y, tile)
                        
        except Exception as e:
            self.logger.error(f"Tilemap uygulama başarısız: {e}\n{traceback.format_exc()}")
            raise


    def _process_tiles(self, generator, map_data):
        """Tile işlemlerini yap"""
        try:
            bg_layer = generator.layers['background']
            map_loader = generator.map_loader
            
            # TMX dosyasından tileset bilgisini al
            tree = ET.parse(map_data['map_path'])
            root = tree.getroot()
            
            for tileset in root.findall('tileset'):
                tileset_source = tileset.get('source')
                if tileset_source:
                    tileset_path = Path(map_data['map_path']).parent / tileset_source
                    first_gid = int(tileset.get('firstgid', 1))
                    
                    # Tileset'i analiz et
                    self._analyze_and_place_tiles(bg_layer, tileset_path, first_gid, map_data)
                    
        except Exception as e:
            self.logger.error(f"Error processing tiles: {e}\n{traceback.format_exc()}")
            raise

    def _analyze_and_place_tiles(self, bg_layer, tileset_path, first_gid, map_data):
        """Tileset analizi ve yerleştirme"""
        try:
            # Tileset görüntüsünü yükle
            tileset_img = Image.open(tileset_path)
            tile_width = int(map_data['tile_width'])
            tile_height = int(map_data['tile_height'])
            
            # Harita boyutlarını al
            map_width = int(map_data['width'])
            map_height = int(map_data['height'])
            
            # Her grid pozisyonu için uygun tile seç
            for y in range(map_height):
                for x in range(map_width):
                    # Grid pozisyonunu piksel koordinatlarına çevir
                    pixel_x = x * tile_width
                    pixel_y = y * tile_height
                    
                    # Tile seç ve yerleştir
                    tile_id = self._select_appropriate_tile(first_gid, x, y, map_width, map_height)
                    bg_layer.add_tile(pixel_x, pixel_y, tile_id)
                    
        except Exception as e:
            self.logger.error(f"Error analyzing and placing tiles: {e}\n{traceback.format_exc()}")
            raise

    def _select_appropriate_tile(self, first_gid, x, y, map_width, map_height):
        """Konuma göre uygun tile'ı seç"""
        try:
            # Basit bir örnek: Kenar ve köşelere farklı tile'lar yerleştir
            if x == 0 or x == map_width - 1 or y == 0 or y == map_height - 1:
                return first_gid + 1  # Kenar tile'ı
            else:
                return first_gid  # Normal zemin tile'ı
                
        except Exception as e:
            self.logger.error(f"Error selecting tile: {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 _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



    def _generate_positions(self, map_data):
        """Entity pozisyonlarını oluştur"""
        try:
            positions = {
                'enemies': [],
                'police': [],
                'workers': [],
                'traps': [],
                'bosses': []
            }
            
            # Harita boyutlarını al
            map_width = map_data['width'] * map_data['tile_width']
            map_height = map_data['height'] * map_data['tile_height']
            area = map_width * map_height
            
            # Entity sayılarını hesapla
            num_enemies = int(area * self.params['enemy_density'] / 10000)  # Yoğunluğu ayarla
            num_police = int(area * self.params['police_density'] / 10000)
            num_workers = int(area * self.params['worker_density'] / 10000)
            
            # Walkable alanları hesapla
            walkable_areas = self._get_walkable_areas(map_data)
            
            # Entity'leri yerleştir
            positions['enemies'] = self._place_entities(num_enemies, walkable_areas, self.params['min_distance'])
            positions['police'] = self._place_entities(num_police, walkable_areas, self.params['min_distance'])
            positions['workers'] = self._place_entities(num_workers, walkable_areas, self.params['min_distance'])
            
            # Boss'u yerleştir
            positions['bosses'] = self._place_boss(walkable_areas, positions)
            
            return positions
            
        except Exception as e:
            self.logger.error(f"Error generating positions: {e}\n{traceback.format_exc()}")
            raise



    def _get_walkable_areas(self, map_data):
        """Yürünebilir alanları hesapla"""
        try:
            walkable_areas = []
            tile_width = map_data['tile_width']
            tile_height = map_data['tile_height']
            
            # Basit yaklaşım: Tüm haritayı yürünebilir kabul et
            for y in range(map_data['height']):
                for x in range(map_data['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:
            self.logger.error(f"Error getting walkable areas: {e}\n{traceback.format_exc()}")
            raise

    def generate_map(self, map_path, difficulty=0.5):
        """AI kontrollü harita oluşturma"""
        try:
            self.logger.info(f"Starting AI map generation with difficulty {difficulty}")
            
            # MapGenerator başlat
            generator = MapGenerator(map_path)
            
            # Harita yapısını analiz et
            map_data = self._analyze_map(map_path)
            if not map_data:
                raise ValueError("Map analysis failed")
            
            # Zorluk seviyesine göre parametreleri ayarla
            self._adjust_parameters(difficulty)
            
            # Entity pozisyonlarını oluştur
            positions = self._generate_positions(map_data)
            
            # Entity pozisyonlarını uygula
            self._apply_positions(generator, positions)
            
            # Son haritayı oluştur
            generator.generate()
            
            self.logger.info("AI map generation completed successfully")
            print("Map generation successful")
            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):
        """Harita yapısını analiz et"""
        try:
            tree = ET.parse(map_path)
            root = tree.getroot()
            
            map_data = {
                'map_path': map_path,
                'width': int(root.get('width', 0)),
                'height': int(root.get('height', 0)),
                'tile_width': int(root.get('tilewidth', 0)),
                'tile_height': int(root.get('tileheight', 0))
            }
            
            # Geçerlilik kontrolü
            if any(v == 0 for v in map_data.values() if isinstance(v, int)):
                raise ValueError("Invalid map dimensions")
                
            return map_data
            
        except Exception as e:
            self.logger.error(f"Error analyzing map: {e}\n{traceback.format_exc()}")
            raise

    def _place_entities(self, num_entities, walkable_areas, min_distance):
        """Minimum mesafe kısıtlamasıyla entity'leri yerleştir"""
        try:
            positions = []
            attempts = 0
            max_attempts = num_entities * 10
            
            while len(positions) < num_entities and attempts < max_attempts:
                area = np.random.choice(walkable_areas)
                pos = {
                    'x': np.random.uniform(area['x'], area['x'] + area['width']),
                    'y': np.random.uniform(area['y'], area['y'] + area['height'])
                }
                
                # Minimum mesafe kontrolü
                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):
        """Boss'u stratejik konuma yerleştir"""
        try:
            best_pos = None
            max_min_distance = 0
            
            for _ in range(100):
                area = np.random.choice(walkable_areas)
                pos = {
                    'x': np.random.uniform(area['x'], area['x'] + area['width']),
                    'y': np.random.uniform(area['y'], area['y'] + area['height'])
                }
                
                min_distance = float('inf')
                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 _calculate_distance(self, pos1, pos2):
        """İki pozisyon arasındaki mesafeyi hesapla"""
        return np.sqrt((pos1['x'] - pos2['x'])**2 + (pos1['y'] - pos2['y'])**2)

    def _adjust_parameters(self, difficulty):
        """Zorluğa göre parametreleri ayarla"""
        try:
            scale = 0.5 + difficulty
            self.params['enemy_density'] *= scale
            self.params['police_density'] *= scale
            self.params['trap_density'] *= scale
            
            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 _apply_positions(self, generator, positions):
        """Oluşturulan pozisyonları haritaya uygula"""
        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


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


burada bu iki

#apply_tilemap
#generate_tilemap


fonksyion çağrılmıyor nerede çağırmalıyım