import numpy as np
from stl import mesh
import struct
import sys
import os

def simplify_stl(input_path, output_path, keep_ratio=0.1):
    try:
        # Load mesh using numpy-stl
        m = mesh.Mesh.from_file(input_path)
        vertices = m.vectors.reshape(-1, 3)
        
        min_v = vertices.min(axis=0)
        max_v = vertices.max(axis=0)
        bbox_size = max_v - min_v
        max_dim = np.max(bbox_size)
        
        if max_dim == 0:
            m.save(output_path)
            return True
            
        # Target roughly a resolution that gives us keep_ratio faces.
        # This is a heuristic approach to grid-based vertex clustering.
        # We start with a baseline resolution (e.g. 2% of max dimension) 
        # and adjust if needed.
        grid_resolution = max_dim * 0.02
        
        # Function to simplify given a grid size
        def do_simplify(g_size):
            v_idx = np.round((vertices - min_v) / g_size).astype(np.int32)
            # Find unique grid cells and map old vertices to them
            _, unique_idx, inv_idx = np.unique(v_idx, axis=0, return_index=True, return_inverse=True)
            new_verts = vertices[unique_idx]
            
            # Map faces to new vertices
            faces = inv_idx.reshape(-1, 3)
            
            # Remove degenerate faces (faces where at least two vertices resolve to the same cell)
            valid = (faces[:,0] != faces[:,1]) & (faces[:,1] != faces[:,2]) & (faces[:,0] != faces[:,2])
            valid_faces = faces[valid]
            
            return new_verts, valid_faces
            
        new_vertices, valid_faces = do_simplify(grid_resolution)
        
        # Build the simplified mesh
        new_m = mesh.Mesh(np.zeros(valid_faces.shape[0], dtype=mesh.Mesh.dtype))
        
        # Vectorized assignment
        new_m.vectors[:, 0, :] = new_vertices[valid_faces[:, 0]]
        new_m.vectors[:, 1, :] = new_vertices[valid_faces[:, 1]]
        new_m.vectors[:, 2, :] = new_vertices[valid_faces[:, 2]]
        
        # Calculate normals correctly
        new_m.update_normals()
        new_m.save(output_path)
        return True
        
    except Exception as e:
        print(f"Error simplifying STL: {e}")
        return False

if __name__ == "__main__":
    if len(sys.argv) > 2:
        simplify_stl(sys.argv[1], sys.argv[2])
