#!/usr/bin/env python3
"""
Parser dla plików .mar (MultiArray) z gier Reksio
"""

import struct
import sys
from pathlib import Path


class MultiArrayParser:
    def __init__(self, filepath):
        self.filepath = Path(filepath)
        self.dimensions = []
        self.total_elements = 0
        self.data = {}  # sparse dictionary: index -> value
        
    def read_int(self, f):
        """Czyta int32 little-endian"""
        return struct.unpack('<i', f.read(4))[0]
    
    def read_double(self, f):
        """Czyta double jako int32/10000"""
        raw = struct.unpack('<i', f.read(4))[0]
        return raw / 10000.0
    
    def read_bool(self, f):
        """Czyta bool jako byte"""
        return struct.unpack('<?', f.read(1))[0]
    
    def read_string(self, f):
        """Czyta string: [int32 length][bytes data]"""
        length = self.read_int(f)
        if length <= 0:
            return ""
        data = f.read(length)
        # Usuń null terminatory
        return data.decode('utf-8', errors='ignore').rstrip('\x00')
    
    def read_variable(self, f):
        """Czyta zmienną: [int32 type][data]"""
        data_type = self.read_int(f)
        
        if data_type == 1:  # INTEGER
            value = self.read_int(f)
            return ('INTEGER', value)
        elif data_type == 2:  # STRING
            value = self.read_string(f)
            return ('STRING', value)
        elif data_type == 3:  # BOOL
            value = self.read_bool(f)
            return ('BOOL', value)
        elif data_type == 4:  # DOUBLE
            value = self.read_double(f)
            return ('DOUBLE', value)
        else:
            raise ValueError(f"Unknown data type: {data_type}")
    
    def flat_to_indices(self, flat_index):
        """Konwertuje flat index na wielowymiarowe indeksy"""
        indices = []
        remaining = flat_index
        
        for i in range(len(self.dimensions) - 1, -1, -1):
            indices.insert(0, remaining % self.dimensions[i])
            remaining //= self.dimensions[i]
        
        return indices
    
    def indices_to_flat(self, indices):
        """Konwertuje wielowymiarowe indeksy na flat index"""
        flat_index = 0
        multiplier = 1
        
        for i in range(len(self.dimensions) - 1, -1, -1):
            flat_index += indices[i] * multiplier
            multiplier *= self.dimensions[i]
        
        return flat_index
    
    def parse(self):
        """Parsuje plik .mar"""
        with open(self.filepath, 'rb') as f:
            # Czytaj liczbę wymiarów
            dimensions_count = self.read_int(f)
            print(f"Dimensions count: {dimensions_count}")
            
            # Czytaj rozmiary wymiarów
            self.total_elements = 1
            for i in range(dimensions_count):
                dim_size = self.read_int(f)
                self.dimensions.append(dim_size)
                self.total_elements *= dim_size
                print(f"  Dimension {i}: {dim_size}")
            
            print(f"Total elements: {self.total_elements}")
            print(f"Array shape: {self.dimensions}")
            print()
            
            # Czytaj elementy (sparse format)
            loaded_count = 0
            try:
                while True:
                    # Sprawdź czy są jeszcze dane
                    pos = f.tell()
                    if f.read(1) == b'':
                        break
                    f.seek(pos)
                    
                    # Czytaj indeks
                    flat_index = self.read_int(f)
                    
                    if flat_index < 0 or flat_index >= self.total_elements:
                        print(f"WARNING: Index out of bounds: {flat_index}")
                        break
                    
                    # Czytaj zmienną
                    var_type, var_value = self.read_variable(f)
                    
                    # Zapisz w sparse dictionary
                    self.data[flat_index] = (var_type, var_value)
                    loaded_count += 1
                    
            except struct.error:
                pass  # Koniec pliku
            
            print(f"Loaded {loaded_count}/{self.total_elements} elements ({100*loaded_count/self.total_elements:.1f}% filled)")
    
    def print_summary(self):
        """Wyświetla podsumowanie"""
        print("\n" + "="*80)
        print(f"File: {self.filepath.name}")
        print(f"Dimensions: {len(self.dimensions)}D array")
        print(f"Shape: {' × '.join(map(str, self.dimensions))}")
        print(f"Total slots: {self.total_elements}")
        print(f"Filled slots: {len(self.data)}")
        print(f"Empty slots: {self.total_elements - len(self.data)}")
        print(f"Fill rate: {100*len(self.data)/self.total_elements:.1f}%")
        print("="*80)
    
    def print_data(self, max_items=50):
        """Wyświetla dane"""
        print("\nData:")
        print("-" * 80)
        
        if not self.data:
            print("  (empty)")
            return
        
        for i, (flat_index, (var_type, var_value)) in enumerate(sorted(self.data.items())):
            if i >= max_items:
                remaining = len(self.data) - max_items
                print(f"  ... and {remaining} more items")
                break
            
            indices = self.flat_to_indices(flat_index)
            indices_str = '[' + ']['.join(map(str, indices)) + ']'
            
            # Formatuj wartość
            if var_type == 'STRING':
                value_str = f'"{var_value}"'
            elif var_type == 'BOOL':
                value_str = 'true' if var_value else 'false'
            else:
                value_str = str(var_value)
            
            print(f"  {indices_str:20s} (flat: {flat_index:5d}) = {var_type:8s} {value_str}")
    
    def export_to_python(self, output_file=None):
        """Eksportuje do Pythona jako nested lists"""
        if output_file is None:
            output_file = self.filepath.with_suffix('.py')
        
        def create_nested_structure(dims):
            """Tworzy zagnieżdżoną strukturę list"""
            if len(dims) == 1:
                return [None] * dims[0]
            else:
                return [create_nested_structure(dims[1:]) for _ in range(dims[0])]
        
        def set_value(arr, indices, value):
            """Ustawia wartość w zagnieżdżonej strukturze"""
            for idx in indices[:-1]:
                arr = arr[idx]
            arr[indices[-1]] = value
        
        # Stwórz strukturę
        nested = create_nested_structure(self.dimensions)
        
        # Wypełnij danymi
        for flat_index, (var_type, var_value) in self.data.items():
            indices = self.flat_to_indices(flat_index)
            set_value(nested, indices, (var_type, var_value))
        
        # Zapisz do pliku
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(f"# Generated from {self.filepath.name}\n")
            f.write(f"# Dimensions: {self.dimensions}\n\n")
            f.write(f"data = {nested!r}\n")
        
        print(f"\nExported to: {output_file}")


def main():
    if len(sys.argv) < 2:
        print("Usage: mar_parser.py <file.mar> [--export] [--verbose] [--max-items N]")
        print()
        print("Options:")
        print("  --export       Export to Python file")
        print("  --verbose      Show all data items")
        print("  --max-items N  Show max N items (default: 50)")
        sys.exit(1)
    
    filepath = sys.argv[1]
    export = '--export' in sys.argv
    verbose = '--verbose' in sys.argv
    
    max_items = 50
    if '--max-items' in sys.argv:
        idx = sys.argv.index('--max-items')
        if idx + 1 < len(sys.argv):
            max_items = int(sys.argv[idx + 1])
    
    if verbose:
        max_items = 999999
    
    # Parsuj plik
    parser = MultiArrayParser(filepath)
    parser.parse()
    
    # Pokaż wyniki
    parser.print_summary()
    parser.print_data(max_items=max_items)
    
    # Eksportuj jeśli trzeba
    if export:
        parser.export_to_python()


if __name__ == '__main__':
    main()
