import random
from relatively_constant_variables import player_engagement_items, story_events, all_idea_lists, existing_game_inspirations, multiplayer_features, list_names
import json
import gradio as gr
import re
import os


def pick_random_items(items, n):
    return random.sample(items, n)

def generate_timeline(events, label):
    timeline = []
    for event in events:
        timeline.append((random.randint(1, 100), label, event))
    return timeline

def create_story(timeline):
    story = []
    for entry in timeline:
        if entry[1] == "Story":
            story.append(f"The hero {entry[2].replace('engageBattle', 'engaged in a fierce battle').replace('solveRiddle', 'solved a complex riddle').replace('exploreLocation', 'explored a mysterious location')}.")
        else:
            story.append(f"The player interacted with {entry[2]}.")
    return " ".join(story)

def generate_story_and_timeline(no_story_timeline_points=10, no_ui_timeline_points=10, num_lists=1, items_per_list=1, include_existing_games=False, include_multiplayer=False): # , no_media_timeline_points=5, include_media=True):
    # Pick 10 random UI items
    random_ui_items = pick_random_items(player_engagement_items, no_ui_timeline_points)
    random_story_items = pick_random_items(story_events, no_story_timeline_points)

    # Generate UI and story timelines
    ui_timeline = generate_timeline(random_ui_items, "UI")
    story_timeline = generate_timeline(random_story_items, "Story")

    # Initialize merged timeline with UI and story timelines
    merged_timeline = ui_timeline + story_timeline
    #no_media_merged_timeline = ui_timeline + story_timeline
    #print(merged_timeline)
    #print(no_media_merged_timeline)

    # Include media-related items if specified
    # if include_media:
    #     media_files = generate_media_file_list(no_media_timeline_points)
    #     #rendered_media = render_media_with_dropdowns(media_files)
    #     media_timeline = generate_timeline(media_files, "Media")
    #     merged_timeline += media_timeline

    # print(merged_timeline)

    # Sort the merged timeline based on the random numbers
    merged_timeline.sort(key=lambda x: x[0])
    # no_media_merged_timeline.sort(key=lambda x: x[0])

    # Create the story
    story = create_story(merged_timeline)

    # Format the timeline for display
    formatted_timeline = "\n".join([f"{entry[0]}: {entry[1]} - {entry[2]}" for entry in merged_timeline])
    # no_media_formatted_timeline = "\n".join([f"{entry[0]}: {entry[1]} - {entry[2]}" for entry in no_media_merged_timeline])

    # game_structure_with_media = generate_game_structures(formatted_timeline) #, game_structure_without_media = generate_game_structures(formatted_timeline, no_media_formatted_timeline)
    game_structure_with_media =  convert_timeline_to_game_structure(formatted_timeline)

    print("simulplay debug - good to here 4")

    suggestions, selected_list_names = timeline_get_random_suggestions(num_lists, items_per_list, include_existing_games, include_multiplayer)

    print("simulplay debug - good to here 4")

    return formatted_timeline, story, json.dumps(game_structure_with_media, indent=2), suggestions, selected_list_names #no_media_formatted_timeline, json.dumps(game_structure_without_media, indent=2) #, game_structure_with_media

media_file_types = ["image", "video", "audio"]

def generate_media_file_list(n):
    return [random.choice(media_file_types) for _ in range(n)]


def show_elements(text):
    # Parse the input text
    pattern = r'(\d+): (UI|Story|Media) - (.+)'
    blocks = re.findall(pattern, text)
    
    # Sort blocks by their timestamp
    blocks.sort(key=lambda x: int(x[0]))
    
    outputs = []
    
    for timestamp, block_type, content in blocks:
        if block_type == 'UI':
            # Create HTML for UI elements
            ui_html = f'<div class="ui-element">{content}</div>'
            outputs.append(gr.HTML(ui_html))
        elif block_type == 'Story':
            # Display story elements as Markdown
            outputs.append(gr.Markdown(f"**{content}**"))
        elif block_type == 'Media':
            if content.lower() == 'audio':
                # Placeholder for audio element
                outputs.append(gr.Audio(label=f"Audio at {timestamp} in the order"))
            elif content.lower() == 'video':
                # Placeholder for video element
                outputs.append(gr.Video(label=f"Video at {timestamp} in the order"))
            elif content.lower() == 'image':
                # Placeholder for image element
                outputs.append(gr.Image(label=f"Image at {timestamp} in the order"))
    
    return outputs

def show_elements_json_input(json_input):
    data = json.loads(json_input)
    masterlocation1 = data['masterlocation1']
    
    outputs = []
    
    for location, details in masterlocation1.items():
        if location == 'end':
            continue
        
        with gr.Accordion(f"Location: {location} - Previous description {details['description']}", open=False):
            description = gr.Textbox(label="Description", value=details['description'], interactive=True)
            outputs.append(description)
            
            events = gr.Textbox(label="Events", value=json.dumps(details['events']), interactive=True)
            outputs.append(events)
            
            choices = gr.Textbox(label="Choices", value=json.dumps(details['choices']), interactive=True)
            outputs.append(choices)
            
            transitions = gr.Textbox(label="Transitions", value=json.dumps(details['transitions']), interactive=True)
            outputs.append(transitions)
            
            # New media field
            media = gr.Textbox(label="Media", value=json.dumps(details['media']), interactive=True)
            outputs.append(media)

            # New developernotes field
            developernotes = gr.Textbox(label="developernotes", value=json.dumps(details['developernotes']), interactive=True)
            outputs.append(developernotes)

    #adding/removing a field means incrementing/decreasing the i+n to match the fields
    num_current_unique_fields = 6
    
    def update_json(*current_values):
        updated_data = {"masterlocation1": {}}
        locations = [loc for loc in masterlocation1.keys() if loc != 'end']
        for i, location in enumerate(locations):
            updated_data["masterlocation1"][location] = {
                "description": current_values[i*num_current_unique_fields],
                "events": json.loads(current_values[i*num_current_unique_fields + 1]),
                "choices": json.loads(current_values[i*num_current_unique_fields + 2]),
                "transitions": json.loads(current_values[i*num_current_unique_fields + 3]),
                "media": json.loads(current_values[i*num_current_unique_fields + 4]),  # New media field
                "developernotes": json.loads(current_values[i*num_current_unique_fields + 5])
            }
        updated_data["masterlocation1"]["end"] = masterlocation1["end"]
        return json.dumps(updated_data, indent=2) #json.dumps(updated_data, default=lambda o: o.__dict__, indent=2)
    
    update_button = gr.Button("Update JSON - Still need to copy to correct textbox to load")
    json_output = gr.Textbox(label="Updated JSON - Still need to copy to correct textbox to load", lines=10)
    #json_output = gr.Code(label="Updated JSON", lines=10) #Locks whole UI so use textbox

    update_button.click(update_json, inputs=outputs, outputs=json_output)
    
    return outputs + [update_button, json_output] #, json_output_code]

def show_elements_json_input_play_and_edit_version(json_input):
    data = json.loads(json_input)
    outputs = []

    for location_name, location_data in data.items():
        if location_name == "end":
            continue

        for sub_location, details in location_data.items():
            with gr.Accordion(f"Location: {location_name} - {sub_location}", open=False):
                description = gr.Textbox(label="Description", value=details.get('description', ''), interactive=True)
                outputs.append(description)

                choices = gr.Textbox(label="Choices", value=json.dumps(details.get('choices', [])), interactive=True)
                outputs.append(choices)

                transitions = gr.Textbox(label="Transitions", value=json.dumps(details.get('transitions', {})), interactive=True)
                outputs.append(transitions)

                consequences = gr.Textbox(label="Consequences", value=json.dumps(details.get('consequences', {})), interactive=True)
                outputs.append(consequences)

                media = gr.Textbox(label="Media", value=json.dumps(details.get('media', [])), interactive=True)
                outputs.append(media)

                # Add developernotes field if it exists in the config
                if 'developernotes' in details:
                    developernotes = gr.Textbox(label="Developer Notes", value=details.get('developernotes', ''), interactive=True)
                    outputs.append(developernotes)

    # Determine the number of fields dynamically
    num_current_unique_fields = 5 if 'developernotes' not in next(iter(next(iter(data.values())).values())) else 6

    def update_json(*current_values):
        updated_data = {}
        location_names = list(data.keys())
        location_names.remove("end") if "end" in location_names else None

        value_index = 0
        for location_name in location_names:
            updated_data[location_name] = {}
            sub_locations = list(data[location_name].keys())
            
            for sub_location in sub_locations:
                updated_data[location_name][sub_location] = {
                    "description": current_values[value_index],
                    "choices": json.loads(current_values[value_index + 1]),
                    "transitions": json.loads(current_values[value_index + 2]),
                    "consequences": json.loads(current_values[value_index + 3]),
                    "media": json.loads(current_values[value_index + 4])
                }
                if num_current_unique_fields == 6:
                    updated_data[location_name][sub_location]["developernotes"] = current_values[value_index + 5]
                value_index += num_current_unique_fields

        if "end" in data:
            updated_data["end"] = data["end"]

        return json.dumps(updated_data, indent=2)

    update_button = gr.Button("Update JSON")
    json_output = gr.Textbox(label="Updated JSON", lines=10)

    update_button.click(update_json, inputs=outputs, outputs=json_output)

    return outputs + [update_button, json_output]

def create_media_component(file_path):
    print(file_path)
    _, extension = os.path.splitext(file_path)
    extension = extension.lower()[1:]  # Remove the dot and convert to lowercase

    if extension in ['jpg', 'jpeg', 'png', 'gif', 'webp']:
        return gr.Image(value=file_path, label="Image Input")
    elif extension in ['mp4', 'avi', 'mov']:
        return gr.Video(value=file_path, label="Video Input")
    elif extension in ['mp3', 'wav', 'ogg']:
        return gr.Audio(value=file_path, label="Audio Input")
    else:
        return gr.Textbox(value=file_path, label=f"File: {os.path.basename(file_path)}")

def convert_timeline_to_game_structure(timeline):
    lines = timeline.split('\n')
    game_structure = {}
    current_location = 0
    sub_location = 0

    for i, line in enumerate(lines):
        if line.strip() == "":
            continue
        
        if line[0].isdigit():  # New location starts
            current_location += 1
            sub_location = 0
            location_key = f"location{current_location}"
            game_structure[location_key] = {
                "description": "",
                "events": [],
                "choices": ["continue"],
                "transitions": {},
                "media": [],
                "developernotes": []
            }
        else:  # Continue with sub-locations or media entries
            sub_location += 1
            location_key = f"location{current_location}_{sub_location}"
        
        # Extract the event description
        parts = line.split(': ', 1)
        if len(parts) == 2:
            prefix, rest = parts
            event_parts = rest.split(' - ', 1)
            if len(event_parts) == 2:
                event_type, event_description = event_parts
            else:
                event_type, event_description = "Unknown", rest
        else:
            event_type, event_description = "Unknown", line
        
        description = rest.strip() if event_type in ["Media", "UI"] else f"{event_type}: {event_description}"
        
        if sub_location == 0:
            game_structure[f"location{current_location}"]["description"] = description
        else:
            game_structure[f"location{current_location}"]["events"].append({
                "description": description,
                "type": event_type
            })
        
        # Set the transition to the next location or to the end
        if i < len(lines) - 1:
            next_line = lines[i + 1].strip()
            if next_line and next_line[0].isdigit():  # New location starts
                game_structure[f"location{current_location}"]["transitions"]["continue"] = f"masterlocation1_location{current_location + 1}"
            else:
                #game_structure[f"location{current_location}"]["transitions"]["continue"] = f"location_{current_location}_{sub_location + 1}"
                game_structure[f"location{current_location}"]["transitions"]["continue"] = "end"
        else:
            game_structure[f"location{current_location}"]["transitions"]["continue"] = "end"
    
    # Add an end location
    game_structure["end"] = {
        "description": "The adventure ends here.",
#         "choices": [],
#         "transitions": {}
        "choices": ["restart"],
        "transitions": {"restart": "location1"}  # Assuming location_1 is the start

    }
    
    # Wrap the game structure in master_location1
    wrapped_structure = {"masterlocation1": game_structure}
    
    return wrapped_structure
    
# def generate_game_structures(timeline_with_media): #, timeline_without_media):
    
#     game_structure_with_media = convert_timeline_to_game_structure(timeline_with_media)
#     #game_structure_without_media = convert_timeline_to_game_structure(timeline_without_media)
    
#     return game_structure_with_media #, game_structure_without_media


# def timeline_get_random_suggestions(num_lists, items_per_list):
#     """
#     Generate random suggestions from a specified number of lists.
    
#     :param num_lists: Number of lists to consider
#     :param items_per_list: Number of items to select from each list
#     :return: A list of randomly selected suggestions
#     """
#     selected_lists = random.sample(all_idea_lists, min(num_lists, len(all_idea_lists)))
#     suggestions = []
    
#     for lst in selected_lists:
#         suggestions.extend(random.sample(lst, min(items_per_list, len(lst))))
    
#     return suggestions

def timeline_get_random_suggestions(num_lists, items_per_list, include_existing_games, include_multiplayer):
    """
    Generate random suggestions from a specified number of lists.
    
    :param num_lists: Number of lists to consider
    :param items_per_list: Number of items to select from each list
    :param include_existing_games: Whether to include existing game inspiration lists
    :param include_multiplayer: Whether to include multiplayer features list
    :return: A tuple containing the list of randomly selected suggestions and the names of selected lists
    """
    available_lists = all_idea_lists.copy()
    if not include_existing_games:
        available_lists = [lst for lst in available_lists if lst not in existing_game_inspirations]
    if not include_multiplayer:
        available_lists = [lst for lst in available_lists if lst != multiplayer_features]
    
    selected_lists = random.sample(available_lists, min(num_lists, len(available_lists)))
    suggestions = []
    selected_list_names = []
    
    for lst in selected_lists:
        suggestions.extend(random.sample(lst, min(items_per_list, len(lst))))
        selected_list_names.append(list_names[all_idea_lists.index(lst)])
    
    return suggestions, selected_list_names