import gradio as gr
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from scratch3 import Project

# Load the LLaMa model and tokenizer
model_name = "TheBloke/LLaMa-7B-GGML"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")

# Define extended Scratch blocks
scratch_blocks = {
    "motion": [
        {"name": "move", "parameters": {"steps": "number"}},
        {"name": "turn", "parameters": {"angle": "number"}},
        {"name": "go to", "parameters": {"x": "number", "y": "number"}},
        {"name": "point in direction", "parameters": {"direction": "number"}}
    ],
    "sound": [
        {"name": "play sound", "parameters": {"sound": "string"}},
        {"name": "stop all sounds", "parameters": {}}
    ],
    "event": [
        {"name": "when green flag clicked", "parameters": {}},
        {"name": "when this sprite clicked", "parameters": {}}
    ],
    "control": [
        {"name": "wait", "parameters": {"seconds": "number"}},
        {"name": "repeat", "parameters": {"times": "number"}},
        {"name": "if then", "parameters": {"condition": "string"}},
    ],
    "variables": [
        {"name": "set variable", "parameters": {"variable": "string", "value": "number"}},
        {"name": "change variable", "parameters": {"variable": "string", "value": "number"}},
    ],
    "looks": [
        {"name": "say", "parameters": {"message": "string"}},
        {"name": "think", "parameters": {"message": "string"}},
        {"name": "show", "parameters": {}},
        {"name": "hide", "parameters": {}}
    ],
    "drawing": [
        {"name": "draw line", "parameters": {"x1": "number", "y1": "number", "x2": "number", "y2": "number"}},
        {"name": "draw circle", "parameters": {"x": "number", "y": "number", "radius": "number"}},
        {"name": "draw rectangle", "parameters": {"x": "number", "y": "number", "width": "number", "height": "number"}},
    ]
}

def generate_script(prompt):
    """
    Generate a Scratch script based on the provided prompt using the AI model.
    """
    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
    with torch.no_grad():
        outputs = model.generate(inputs["input_ids"], max_length=300, num_return_sequences=1)
    
    generated_script = tokenizer.decode(outputs[0], skip_special_tokens=True)
    return generated_script

def parse_generated_script(generated_script):
    """
    Parse the generated script into instructions.
    """
    instructions = []
    for line in generated_script.split('.'):
        line = line.strip()
        if line:
            instructions.append(line)
    return instructions

def create_scratch_project(title, sprites, sounds, custom_prompt):
    """
    Create a Scratch project based on user input and generated script.
    """
    # Combine title and custom prompt for better context
    prompt = f"Create a Scratch project titled '{title}' featuring sprites {sprites} with sounds {sounds}. " \
             f"Additional details: {custom_prompt}"
    generated_script = generate_script(prompt)

    # Parse the generated script into instructions
    instructions = parse_generated_script(generated_script)

    # Create a new Scratch project
    project = Project()

    # Add sprites
    sprite_list = [sprite.strip() for sprite in sprites.split(',')]
    for sprite in sprite_list:
        sprite_block = {
            "objName": sprite,
            "skin": 1,
            "x": 0,
            "y": 0,
            "direction": 90,
            "visible": True,
            "size": 100
        }
        project.sprites.append(sprite_block)

    # Add sounds if provided
    for sound in sounds.split(','):
        sound = sound.strip()
        if sound:
            sound_block = {
                "name": sound,
                "path": f"sounds/{sound}.wav"
            }
            project.sounds.append(sound_block)

    # Map parsed instructions to Scratch blocks
    for instruction in instructions:
        if "move" in instruction:
            steps = extract_number(instruction)
            project.add_motion_block({"name": "move", "parameters": {"steps": steps}})
        elif "turn" in instruction:
            angle = extract_number(instruction)
            project.add_motion_block({"name": "turn", "parameters": {"angle": angle}})
        elif "play sound" in instruction:
            project.add_sound_block({"name": "play sound", "parameters": {"sound": sounds.split(',')[0].strip()}})
        elif "when green flag clicked" in instruction:
            project.add_event_block({"name": "when green flag clicked"})
        elif "repeat" in instruction:
            times = extract_number(instruction)
            project.add_control_block({"name": "repeat", "parameters": {"times": times}})
        elif "say" in instruction:
            message = extract_message(instruction)
            project.add_looks_block({"name": "say", "parameters": {"message": message}})
        elif "draw line" in instruction:
            x1, y1, x2, y2 = extract_drawing_parameters(instruction)
            project.add_drawing_block({"name": "draw line", "parameters": {"x1": x1, "y1": y1, "x2": x2, "y2": y2}})
        elif "draw circle" in instruction:
            x, y, radius = extract_circle_parameters(instruction)
            project.add_drawing_block({"name": "draw circle", "parameters": {"x": x, "y": y, "radius": radius}})
        elif "draw rectangle" in instruction:
            x, y, width, height = extract_rectangle_parameters(instruction)
            project.add_drawing_block({"name": "draw rectangle", "parameters": {"x": x, "y": y, "width": width, "height": height}})
        elif "remove sprite" in instruction:
            sprite_name = extract_sprite_name(instruction)
            project.remove_sprite(sprite_name)
        elif "rename sprite" in instruction:
            old_name, new_name = extract_rename_sprite_parameters(instruction)
            project.rename_sprite(old_name, new_name)

    # Save the project as an .sb3 file
    sb3_file_path = f"{title}.sb3"
    with open(sb3_file_path, "wb") as f:
        f.write(project.export())
    
    return sb3_file_path

def extract_number(instruction):
    """
    Extract a number from the instruction string.
    """
    import re
    match = re.search(r'\d+', instruction)
    return int(match.group(0)) if match else 0

def extract_message(instruction):
    """
    Extract a message from the instruction string.
    """
    return instruction.split("say")[-1].strip().strip('"')

def extract_drawing_parameters(instruction):
    """
    Extract parameters for drawing a line from the instruction string.
    """
    numbers = list(map(int, re.findall(r'\d+', instruction)))
    return numbers

def extract_circle_parameters(instruction):
    """
    Extract parameters for drawing a circle from the instruction string.
    """
    numbers = list(map(int, re.findall(r'\d+', instruction)))
    return numbers

def extract_rectangle_parameters(instruction):
    """
    Extract parameters for drawing a rectangle from the instruction string.
    """
    numbers = list(map(int, re.findall(r'\d+', instruction)))
    return numbers

def extract_sprite_name(instruction):
    """
    Extract the sprite name from the instruction string for removal.
    """
    return instruction.split("remove sprite")[-1].strip()

def extract_rename_sprite_parameters(instruction):
    """
    Extract old and new names for renaming a sprite from the instruction string.
    """
    parts = instruction.split("rename sprite")[-1].strip().split("to")
    old_name = parts[0].strip()
    new_name = parts[1].strip() if len(parts) > 1 else None
    return old_name, new_name

# Create Gradio interface
iface = gr.Interface(
    fn=create_scratch_project,
    inputs=[
        gr.Textbox(label="Project Title"),
        gr.Textbox(label="Sprite Names (comma separated)"),
        gr.Textbox(label="Sound Names (comma separated)"),
        gr.Textbox(label="Custom Prompt (optional)"),
    ],
    outputs=gr.File(label="Download Your Scratch Project (.sb3)"),
    title="Scratch Project Generator",
    description="Create a Scratch project using AI. Provide a title, sprites, sounds, and a custom prompt."
)

# Launch the Gradio app
iface.launch()