import json
import re
import math
import os
import traceback
from pathlib import Path

def format_float(value):
    """Correctly formats floating point values"""
    if isinstance(value, str) and any(c.isalpha() for c in value):
        return value  
        
    if isinstance(value, str) and 'e' in value.lower():
        try:
            value = float(value)
        except ValueError:
            return value 
    
    if isinstance(value, (int, float, str)):
        try:
            value = float(value)
            if abs(value) == 180.0:
                value = -180.0

            if abs(abs(value) - 0.7071068) < 0.0000001:
                return round(value, 4)  
            
            if value == int(value):
                return int(value)
            else:
                if abs(value) < 0.001:  
                    return round(value, 8)
                else:
                    return round(value, 4) 
        except ValueError:
            return value  
    
    return value


def clean_array(arr):
    """Cleans value arrays"""
    if not isinstance(arr, list):
        return arr
    
    if not arr:
        return []
    
    arr = [format_float(val) for val in arr]
    
    return arr

def optimize_json(data):
    """Recursively optimizes JSON structure"""
    if isinstance(data, dict):
        result = {}
        for key, value in data.items():
            if key in ["rotation", "position", "scale"]:
                if isinstance(value, dict):
                    result[key] = {k: format_float(v) for k, v in value.items()}
                elif isinstance(value, list):
                    result[key] = clean_array(value)
                else:
                    result[key] = value
            else:
                value = optimize_json(value)
                
                result[key] = value
        return result
    elif isinstance(data, list):
        return [optimize_json(item) for item in data]
    elif isinstance(data, (int, float, str)):
        return format_float(data)
    else:
        return data



def fix_json_format(content):
    """Fixes JSON format for multiple objects"""

    content = content.strip()
    
    if content.startswith('{') and '}' in content[1:] and len(content) > content.find('}')+1:
        print("Multiple JSON objects detected - Converting to array...")

        json_objects = []
        depth = 0
        current_obj = ""
        in_string = False
        escape_next = False
        
        for char in content:
            if escape_next:
                escape_next = False
                current_obj += char
                continue
                
            if char == '"' and not escape_next:
                in_string = not in_string
                current_obj += char
                continue
                
            if in_string:
                if char == '\\':
                    escape_next = True
                current_obj += char
                continue
                
            if char == '{':
                depth += 1
                current_obj += char
            elif char == '}':
                depth -= 1
                current_obj += char
                if depth == 0:
                    json_objects.append(current_obj)
                    current_obj = ""
            elif depth > 0:
                current_obj += char
        

        json_objects = [obj for obj in json_objects if obj.strip()]
        
        if json_objects:
            return "[" + ",".join(json_objects) + "]"
    
    return content

def process_jison_file(input_file, output_file=None):
    """Processes complete JSON file"""
    if output_file is None:

        path_obj = Path(input_file)
        output_file = path_obj.with_name(f"{path_obj.stem}_optimized{path_obj.suffix}")
    
    print(f"Reading file: {input_file}")
    try:
        with open(input_file, 'r', encoding='utf-8') as f:
            content = f.read()
            

        content = re.sub(r',\s*\}', '}', content)  
        content = re.sub(r',\s*\]', ']', content)  
        
        content = fix_json_format(content)
        
        print("Parsing JSON...")
        data = json.loads(content)
        
        print("Optimizing data...")
        optimized_data = optimize_json(data)
        
        if not isinstance(optimized_data, list):
            optimized_data = [optimized_data]
        
        print(f"Writing optimized data to: {output_file}")
        with open(output_file, 'w', encoding='utf-8') as f:
            for item in optimized_data:
                json_line = json.dumps(item, ensure_ascii=False, separators=(',', ':'))
                f.write(json_line + "\n")
        
        original_size = os.path.getsize(input_file) / (1024 * 1024) 
        optimized_size = os.path.getsize(output_file) / (1024 * 1024) 
        reduction = ((original_size - optimized_size) / original_size) * 100
        
        print(f"Optimization completed!")
        print(f"Original size: {original_size:.2f} MB")
        print(f"New size: {optimized_size:.2f} MB")
        print(f"Reduction: {reduction:.2f}%")
        print(f"File saved: {output_file}")
        return True
    except json.JSONDecodeError as e:
        print(f"Error during JSON parsing: {str(e)}")
        print(f"Position: line {e.lineno}, column {e.colno}")
        
        print("Attempting line by line analysis...")
        try:
            with open(input_file, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            
            valid_objects = []
            for i, line in enumerate(lines):
                if line.strip():
                    try:
                        obj = json.loads(line)
                        valid_objects.append(obj)
                    except json.JSONDecodeError:
                        print(f"Error at line {i+1}: {line[:50]}...")
            
            if valid_objects:
                print(f"Found {len(valid_objects)} valid JSON objects")
                
                optimized_data = [optimize_json(obj) for obj in valid_objects]
                
                with open(output_file, 'w', encoding='utf-8') as f:
                    for item in optimized_data:
                        json_line = json.dumps(item, ensure_ascii=False, separators=(',', ':'))
                        f.write(json_line + "\n")
                
                original_size = os.path.getsize(input_file) / (1024 * 1024)  
                optimized_size = os.path.getsize(output_file) / (1024 * 1024)  
                reduction = ((original_size - optimized_size) / original_size) * 100
                
                print(f"Optimization completed!")
                print(f"Original size: {original_size:.2f} MB")
                print(f"New size: {optimized_size:.2f} MB")
                print(f"Reduction: {reduction:.2f}%")
                print(f"File saved: {output_file}")
                return True
            else:
                print("No valid JSON objects found")
                return False
        except Exception as e2:
            print(f"Alternative analysis failed: {str(e2)}")
            return False
            
        return False
    except Exception as e:
        print(f"Error during processing: {str(e)}")
        print(traceback.format_exc())
        return False


try:
    print("Starting JSON optimization program...")
    
    default_path = r"C:\Users\yurri\Desktop\RockFord Sauvegardes\Map Optimizer\rockford-finalopti.bpm"
    
    if not os.path.exists(default_path):
        print(f"Default file not found: {default_path}")
        input_file = input("Please enter the correct path to the JSON file: ")
        if not input_file:
            print("No file specified. Stopping the program.")
            input("Press Enter to exit...")
            exit()
    else:
        input_file = default_path
        print(f"File found: {input_file}")
    
    output_file = input("Output file path (leave empty to auto-generate): ") or None
    
    process_jison_file(input_file, output_file)

except Exception as e:
    print(f"An error occurred: {str(e)}")
    print(traceback.format_exc())  

finally:
    print("\n--- End of program ---")
    input("Press Enter to exit...")
