This is my app.py code along with the flowchart.html code...please modify the code such that the generated flowchart/mindmap i.e in animation option should display the beggining of the node to ending of the node in a loop which are being controlled by the buttons "play,pause ,forward ,backward' and please improve the ui elements more beautiful and better 
"

from flask import Flask, render_template, request, jsonify, send_file
import os
from werkzeug.utils import secure_filename
from PIL import Image
import io
import logging
import requests
from dotenv import load_dotenv
import google.generativeai as genai
from google.generativeai.types import GenerationConfig, HarmCategory, HarmBlockThreshold

from mailjet_rest import Client
from pdf2image import convert_from_bytes
from docx import Document
from docx.shared import Inches, Pt, RGBColor
from docx.enum.style import WD_STYLE_TYPE
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
import fitz  # PyMuPDF
from bs4 import BeautifulSoup, NavigableString
import firebase_admin
from firebase_admin import credentials, firestore, storage

import base64
from threading import Lock
from functools import wraps

import re
import json





mailjet = Client(auth=(mail_API_KEY, mail_API_SECRET), version='v3.1')

app = Flask(__name__)
# Load environment variables
load_dotenv()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Configure the Google Generative AI API
genai.configure(api_key=api_key)
#session key

user_data = {}  # Dictionary to store user data




app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024  # 10MB
# Generation configurations
generation_config = GenerationConfig(
    temperature=0.9,
    top_p=1,
    top_k=1,
    max_output_tokens=2048,
    candidate_count=1  # Explicitly set to 1 as per documentation
)
generation_config_health = GenerationConfig(
    temperature=0.7,
    top_p=1,
    top_k=1,
    max_output_tokens=2048,
    candidate_count=1  # Explicitly set to 1 as per documentation
)

# Safety Settings
safety_settings = {
    HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
    HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
    HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
    HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
    # ... add other harm categories as needed with BLOCK_NONE
}
logging.basicConfig(level=logging.INFO)
# Create model instances (using the same config for now)
persona = """You are Krishna, a 21-year-old boy from Vizag, India. You are currently pursuing a Master's degree in Computer Science 
at GVP College of Engineering, where you excel in your studies and are known for your quick wit and insightful contributions 
to class discussions.

Beyond academics, you are an avid cricketer and enjoy playing with your friends on weekends. You are also a music enthusiast 
with a diverse taste, ranging from classical Indian music to modern rock. Traveling and exploring new cultures are other passions 
of yours, and you have a knack for discovering hidden gems wherever you go.

You are known for your friendly and approachable demeanor and have a wide circle of friends who appreciate your humor and 
willingness to lend a helping hand. While you are serious about your studies and future career, you also maintain a healthy 
work-life balance and believe in enjoying the present moment.

You are a highly talented individual with a strong command of various programming languages and a natural aptitude for 
problem-solving. You are proficient in Python, Java, C++, and have dabbled in web development as well. You are confident 
in your abilities but also humble and always eager to learn from others and expand your knowledge.

Remember to:
1. Never prefix your responses with "Bot:" or any similar identifier
2. Always maintain your character as Krishna
3. Be natural and conversational
4. Use appropriate emojis occasionally to make conversations more engaging"""

chat_model = genai.GenerativeModel(
    model_name="gemini-1.5-flash",
    generation_config=generation_config,
    system_instruction=persona
)
chef_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
story_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
psychology_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
code_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
algorithm_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
model_vision = genai.GenerativeModel('gemini-1.5-flash-8b',generation_config=generation_config_health)
model_text = genai.GenerativeModel('gemini-pro',generation_config=generation_config_health)
model = genai.GenerativeModel('gemini-1.5-flash')  # Model for flowchart generation

def format_response(response_text):
    """Formats the response text for display."""
    lines = [line.strip() for line in response_text.split('\n') if line.strip()]
    formatted_text = '<br>'.join(lines)
    return formatted_text


@app.route('/')
def index():
    return render_template('index.html')

@app.route('/contributors',methods=['GET', 'POST'])
def contributions ():
    return render_template('contributors.html')


@app.route('/api/weather')
def get_weather():
    user_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
    ip_api_url = f"http://ip-api.com/json/{user_ip}"
    ip_api_response = requests.get(ip_api_url)

    if ip_api_response.status_code == 200:
        ip_api_data = ip_api_response.json()
        city = ip_api_data.get('city')
        if not city:
            return jsonify({'error': 'City not found based on IP'}), 404
    else:
        return jsonify({'error': 'Failed to get location from IP'}), 404

    url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}&units=metric"
    response = requests.get(url)

    if response.status_code == 200:
        data = response.json()
        weather = {
            'city': data['name'],
            'temperature': data['main']['temp'],
            'description': data['weather'][0]['description'] if 'weather' in data and len(data['weather']) > 0 else 'N/A',
            'icon': f"http://openweathermap.org/img/wn/{data['weather'][0]['icon']}@2x.png" if 'weather' in data and len(data['weather']) > 0 else 'N/A'
        }
        return jsonify(weather)
    else:
        return jsonify({'error': 'City not found or API request failed'}), 404


@app.route('/fetch_image')
def fetch_image():
    genre = request.args.get('genre', 'recipe')
    url = f"https://api.unsplash.com/photos/random?query={genre}&client_id={unsplash_api_key}&w=1920&h=1080"
    response = requests.get(url)
    if response.status_code == 200:
        data = response.json()
        image_url = data['urls']['regular']
        return jsonify({'image_url': image_url})
    else:
        return jsonify({'error': 'Failed to fetch image'}), 500


@app.route('/chat', methods=['GET', 'POST'])
def chat():
    if request.method == 'POST':
        user_message = request.json['message']
        user_id = request.remote_addr  # Using IP address as a simple user identifier

        if user_id not in user_data:
            user_data[user_id] = {'chat_history': []}

        # Add user message to chat history
        user_data[user_id]['chat_history'].append({
            "role": "user", 
            "message": user_message
        })

        # Create conversation history for context
        conversation = []
        for msg in user_data[user_id]['chat_history']:
            if msg['role'] == 'user':
                conversation.append(f"User: {msg['message']}")
            else:
                conversation.append(msg['message'])

        # Generate response
        response = chat_model.generate_content("\n".join(conversation))
        reply = response.text.strip()

        # Add response to chat history without "Bot:" prefix
        user_data[user_id]['chat_history'].append({
            "role": "assistant", 
            "message": reply
        })

        return jsonify({
            "reply": reply,
            "chat_history": user_data[user_id]['chat_history']
        })

    return render_template('chat.html')
import PIL

@app.route('/chef', methods=['GET', 'POST'])
def chef():
    if request.method == 'POST':
        if 'image' in request.files:
            image = request.files['image']
            if image.filename != '':
                try:
                    img = Image.open(BytesIO(image.read()))
                    prompt = ["Generate a recipe based on the vegetables in the image and explain the steps to cook it in a stepwise manner and formatted manner. Also explain who can eat and who shouldn't eat.", img]
                    response = model_vision.generate_content(prompt, safety_settings=safety_settings, stream=True)
                    response.resolve()
                    response_text = format_response(response.text)
                    return jsonify({'response': response_text})

                except PIL.UnidentifiedImageError:
                    return jsonify({'error': "Image format not recognized"}), 400
                except Exception as e:
                    logging.error(f"Error processing image: {e}")
                    return jsonify({'error': "Image processing failed"}), 500

        user_ingredients = request.form['user_ingredients']
        prompt = f"Generate a recipe based on the following ingredients {user_ingredients} and explain the steps to cook it in a stepwise manner and formatted manner. Also explain who can eat and who shouldn't eat."
        response = chef_model.generate_content([prompt], safety_settings=safety_settings)  # Use chef_model here
        response_text = format_response(response.text)
        return jsonify({'response': response_text})

    return render_template('chef.html')


@app.route('/story_generator', methods=['GET', 'POST'])
def story_generator():
    if request.method == 'POST':
        user_input_words = request.form['keywords']
        genre = request.form['genre']
        prompt = f"""Generate an engaging short story based on the following words: {user_input_words}. The genre should be {genre}.
        
        Requirements:
        1. Create a compelling narrative with well-developed characters and an interesting plot. Also use simple english.
        2. Use vivid descriptions and sensory details to bring the story to life.
        3. Include at least 3 advanced vocabulary words that fit naturally within the story.
        4. End the story with a clear moral or lesson.
        5. After the story, provide definitions for the advanced vocabulary words used.

        Format the response as JSON with the following fields:
        - 'title': The title of the story
        - 'story': The main body of the story
        - 'moral': The moral or lesson of the story
        - 'vocabulary': A list of dictionaries, each containing 'word' and 'definition' fields for the advanced vocabulary used

        Ensure that the JSON is properly formatted and can be parsed."""

        try:
            response = story_model.generate_content([prompt], safety_settings=safety_settings)
            if response.candidates and response.candidates[0].content.parts:
                response_text = response.candidates[0].content.parts[0].text
                return jsonify({'response': response_text})
            else:
                return jsonify({'response': '```json {"title": "Error", "story": "Sorry, I couldn\'t generate a story with the provided input.", "moral": "", "vocabulary": []} ```'})
        except Exception as e:
            logging.error(f"Error generating story: {e}")
            return jsonify({'response': '```json {"title": "Error", "story": "An error occurred while generating the story. Please try again.", "moral": "", "vocabulary": []} ```'}), 500
    return render_template('story_generator.html')

@app.route('/psychology_prediction', methods=['GET', 'POST'])
def psychology_prediction():
    if request.method == 'POST':
        name = request.form['name']
        age = request.form['age']
        gender = request.form['gender']
        occupation = request.form['occupation']
        keywords = request.form['keywords']
        
        prompt = f"""As an expert psychological profiler, provide an insightful and engaging analysis for {name}, a {age}-year-old {gender} working as {occupation} who describes themselves as: {keywords}.

Generate a captivating and well-structured response using the following format:

<h2>1. First Impression & Key Traits</h2>
<p>[Start with 2-3 sentences about their immediate personality indicators]</p>
<ul>
<li>[Key trait 1]</li>
<li>[Key trait 2]</li>
<li>[Key trait 3]</li>
</ul>

<h2>2. Cognitive Style & Decision Making</h2>
<p>[2-3 sentences about their thought processes]</p>
<ul>
<li><strong>Thinking style:</strong> [description]</li>
<li><strong>Problem-solving approach:</strong> [description]</li>
<li><strong>Learning preference:</strong> [description]</li>
</ul>

<h2>3. Emotional Landscape</h2>
<p>[2-3 sentences about emotional intelligence]</p>
<ul>
<li><strong>Emotional awareness:</strong> [description]</li>
<li><strong>Relationship handling:</strong> [description]</li>
<li><strong>Stress response:</strong> [description]</li>
</ul>

<h2>4. Motivations & Aspirations</h2>
<p>[2-3 sentences about what drives them]</p>
<ul>
<li><strong>Core values:</strong> [description]</li>
<li><strong>Career motivations:</strong> [description]</li>
<li><strong>Personal goals:</strong> [description]</li>
</ul>

<h2>5. Interpersonal Dynamics</h2>
<p>[2-3 sentences about social interactions]</p>
<ul>
<li><strong>Communication style:</strong> [description]</li>
<li><strong>Social preferences:</strong> [description]</li>
<li><strong>Leadership tendencies:</strong> [description]</li>
</ul>

<h2>Concluding Insights</h2>
<p>[3-4 sentences summarizing key strengths and potential areas for growth]</p>

<p><em>Note: This analysis is an interpretation based on limited information and should be taken as exploratory rather than definitive.</em></p>

Important formatting rules:
- Use appropriate HTML tags for headings, paragraphs, and lists as shown.
- Ensure that the final response is valid HTML and can be rendered directly on a web page.
- Do not include any extra text outside the HTML structure.
"""

        try:
            response = psychology_model.generate_content([prompt], safety_settings=safety_settings)
            response_text = response.text.strip()
            return jsonify({'response': response_text})
        except Exception as e:
            logging.error(f"Error generating psychology prediction: {e}")
            return jsonify({'error': "An error occurred while generating the prediction. Please try again."}), 500

    return render_template('psychology_prediction.html')

@app.route('/code_generation', methods=['GET', 'POST'])
def code_generation():
    if request.method == 'POST':
        code_type = request.form['codeType']
        language = request.form['language']
        prompt = f"Write a {language} code to implement {code_type}."
        response = code_model.generate_content([prompt], safety_settings=safety_settings)  # Use code_model here
        if response.candidates and response.candidates[0].content.parts:
            response_text = response.candidates[0].content.parts[0].text
        else:
            response_text = "No valid response found."
        return jsonify({'response': response_text})
    return render_template('code_generation.html')


@app.route('/algorithm_generation', methods=['GET', 'POST'])
def algorithm_generation():
    if request.method == 'POST':
        algo = request.form['algorithm']
        prompt = f"""
        Write a function to implement the {algo} algorithm. Follow these guidelines:
        1. Ensure the function is well-structured and follows best practices for readability and efficiency.
        2. Include clear comments explaining the logic and any complex steps.
        3. Use type hints for function parameters and return values.
        4. Include a brief docstring explaining the purpose of the function and its parameters.
        5. If applicable, include a simple example of how to use the function.
        6. If the algorithm is complex, consider breaking it down into smaller helper functions.
        """
        
        try:
            response = algorithm_model.generate_content([prompt], safety_settings=safety_settings)
            if response.candidates and response.candidates[0].content.parts:
                response_text = response.candidates[0].content.parts[0].text
                # Format the response for better display
                formatted_response = response_text.replace('```python', '<pre><code class="language-python">').replace('```', '</code></pre>')
                return jsonify({'response': formatted_response})
            else:
                return jsonify({'error': "No valid response generated. Please try again."}), 500
        except Exception as e:
            logging.error(f"Error generating algorithm: {e}")
            return jsonify({'error': f"An error occurred: {str(e)}"}), 500

    return render_template('algorithm_generation.html')



from io import BytesIO

@app.route('/analyze', methods=['GET', 'POST'])
def analyze():
    if request.method == 'POST':
        try:
            gender = request.form.get('gender')
            symptoms = request.form.get('symptoms')
            body_part = request.form.get('body-part')
            layer = request.form.get('layer')
            image = request.files.get('image')

            prompt = f"""As an AI medical assistant, analyze the following information about a patient:

Gender: {gender}
Symptoms: {symptoms}
Affected Body Part: {body_part}
Layer Affected: {layer}

Based on this information, provide a detailed analysis considering the following:

1. Possible conditions: List and briefly describe potential conditions that match the symptoms and affected area.
2. Risk factors: Discuss any risk factors associated with the gender or affected body part.
3. Recommended next steps: Suggest appropriate medical tests or examinations that could help diagnose the condition.
4. General advice: Offer some general health advice related to the symptoms or affected area.

Important: This is not a diagnosis. Advise the patient to consult with a healthcare professional for an accurate diagnosis and treatment plan.

Format the response using the following structure:
<section>
<h2>Section Title</h2>
<p>Paragraph text</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
</section>

Use <strong> for emphasis on important points.
"""

            if image:
                img = Image.open(BytesIO(image.read()))
                img_byte_arr = BytesIO()
                img.save(img_byte_arr, format='PNG')
                img_byte_arr = img_byte_arr.getvalue()
                image_base64 = base64.b64encode(img_byte_arr).decode('utf-8')

                prompt += f"""
<section>
<h2>Image Analysis</h2>
<p>Analyze the provided image in relation to the patient's symptoms and affected body part. Consider:</p>
<ul>
<li>Any visible symptoms or abnormalities</li>
<li>Correlation between the image and the reported symptoms</li>
<li>Additional insights the image might provide about the patient's condition</li>
</ul>
</section>

Image data: data:image/png;base64,{image_base64}
"""

                response = model_vision.generate_content([prompt, Image.open(BytesIO(base64.b64decode(image_base64)))], safety_settings=safety_settings)
            else:
                response = model_text.generate_content([prompt], safety_settings=safety_settings)

            analysis_text = response.text if hasattr(response, 'text') else response.parts[0].text

            # Wrap the entire response in a div for styling
            formatted_analysis = f'<div class="analysis-content">{analysis_text}</div>'

            return jsonify({'analysis': formatted_analysis})
        except Exception as e:
            logging.error(f"Error in /analyze route: {e}")
            return jsonify({'error': "Internal Server Error"}), 500
    return render_template('analyze.html')


# Flowchart Generation Routes
@app.route('/flowchart', methods=['GET', 'POST'])
def flowchart():
    return render_template('flowchart.html')



def generate_flowchart(topic, chart_type, animation, detail_level):
    prompt = f"""
    Generate a {'mind map' if chart_type == 'mind_map' else 'flowchart'} for the topic/algorithm: "{topic}".
    
    The {chart_type.replace('_', ' ')} should be {'animated' if animation == 'animated' else 'static'} and {'simple' if detail_level == 'simple' else 'normal' if detail_level == 'normal' else 'detailed'}.

    The output should be in JSON format with the following structure:
    
    {{
        "nodes": [
            {{"id": 1, "label": "Start", "level": 0, "shape": "ellipse"}},
            {{"id": 2, "label": "Step 1", "level": 1, "shape": "box"}}
        ],
        "edges": [
            {{"from": 1, "to": 2}}
        ]
    }}
    
    **Important Guidelines:**
    
    1. **Unique IDs:** Ensure each node has a unique integer `id`.
    2. **Descriptive Labels:** Provide clear and concise labels for each node (`"label"`).
    3. **Hierarchical Levels:** Use `level` to indicate the hierarchy (0 for the top level, 1 for the next level, etc.).
    4. **Node Shapes:** Choose appropriate shapes using the `shape` field:
        - "ellipse": For start/end nodes
        - "box": For process steps
        - "diamond": For decision nodes
        - "hexagon": For preparation steps
        - "circle": For connectors (if needed)
    5. **Edges:** Specify connections using the `from` and `to` fields in the `edges` array.
    6. **Flow:** Ensure a logical and easy-to-follow flow.
    7. **Comprehensiveness:** Include all major steps or concepts.
    8. **Spacing:** Use a minimum horizontal spacing of 200 and vertical spacing of 150 between nodes to prevent overlapping. 
    9. **No Isolated Nodes:** All nodes should be connected in a coherent structure.
    10. **Clear Visualization:** The flowchart/mind map should be visually clear and easily understandable. Avoid overly complex visualizations. 
    11. **Avoid Overlapping:** Make sure nodes don't overlap with each other (at any level) due to their size or placement.
    12. **Spacing Considerations:** Adjust the spacing between nodes based on the node size and content to ensure adequate readability.
    
    **Detail Level:**
    - **Simple:** Include only the main concepts with minimal details.
    - **Normal:** Provide a balanced amount of detail for each step.
    - **Detailed:** Offer comprehensive information and sub-steps for each concept.
    
    **Animation Guidelines (for Animated Charts):**
    - Sequentially highlight nodes and edges to visualize the flow.
    - Avoid abrupt transitions; use smooth animations.
    - Ensure that the animation loops smoothly without glitches.
    
    **Output Format:**
    - Output only the JSON structure, no additional text or explanations.
    - Ensure that the output is correctly formatted and adheres to the provided JSON structure.
    
    **Example (Simple Algorithm):**
    
    {{
        "nodes": [
            {{"id": 1, "label": "Start", "level": 0, "shape": "ellipse"}},
            {{"id": 2, "label": "Get input", "level": 1, "shape": "box"}},
            {{"id": 3, "label": "Process input", "level": 1, "shape": "box"}},
            {{"id": 4, "label": "Output results", "level": 1, "shape": "box"}},
            {{"id": 5, "label": "End", "level": 0, "shape": "ellipse"}}
        ],
        "edges": [
            {{"from": 1, "to": 2}},
            {{"from": 2, "to": 3}},
            {{"from": 3, "to": 4}},
            {{"from": 4, "to": 5}}
        ]
    }}
    """  # Closing triple-quoted string correctly

    try:
        response = model.generate_content(prompt)
        logger.info("Raw API response: %s", response.text)  # For debugging

        # Try to extract a JSON object from the response
        json_match = re.search(r'\{.*\}', response.text, re.DOTALL)
        if json_match:
            try:
                flowchart_data = json.loads(json_match.group())
                return flowchart_data
            except json.JSONDecodeError:
                return {"error": "Invalid JSON structure", "raw_response": response.text}
        else:
            return {"error": "No JSON object found in the response", "raw_response": response.text}
    except Exception as e:
        logger.error(f"Error generating flowchart: {e}")
        return {"error": f"Error generating flowchart: {str(e)}"}

@app.route('/get_flowchart_data', methods=['POST'])
def get_flowchart_data():
    data = request.json
    topic = data.get('topic')
    chart_type = data.get('type', 'flowchart')  # Default to 'flowchart' if not provided
    animation = data.get('animation', 'static')  # Default to 'static'
    detail_level = data.get('detail_level', 'normal')  # Default to 'normal'
    
    if not topic:
        return jsonify({"error": "No topic provided."}), 400
    
    flowchart_data = generate_flowchart(topic, chart_type, animation, detail_level)
    
    if 'error' in flowchart_data:
        return jsonify({"error": flowchart_data['error'], "raw_response": flowchart_data.get("raw_response", "")}), 500
    
    # Prepare the data for vis-network
    nodes = [{"id": node["id"], "label": node["label"], "shape": node.get("shape", "box")} for node in flowchart_data.get('nodes', [])]
    edges = [{"from": edge["from"], "to": edge["to"]} for edge in flowchart_data.get('edges', [])]
    
    # Include animation parameter in the response to handle differently on frontend if needed
    return jsonify({
        "nodes": nodes,
        "edges": edges,
        "animation": animation
    })

@app.route('/send-email', methods=['POST'])
def send_email():
    data = request.json
    name = data.get('name')
    email = data.get('email')
    message = data.get('message')

    mail_data = {
        'Messages': [
            {
                "From": {
                    "Email": "21131A05C6@gvpce.ac.in",  # Replace with your email
                    "Name": "Kv Nexus"
                },
                "To": [
                    {
                        "Email": "21131A05C6@gvpce.ac.in",  # Replace with your email
                        "Name": "Kv Nexus"
                    }
                ],
                "Subject": f"New Contact Form Submission from {name}",
                "TextPart": f"Name: {name}\nEmail: {email}\nMessage: {message}",
                "HTMLPart": f"<h3>New Contact Form Submission</h3><p><strong>Name:</strong> {name}</p><p><strong>Email:</strong> {email}</p><p><strong>Message:</strong> {message}</p>"
            }
        ]
    }

    result = mailjet.send.create(data=mail_data)
    
    if result.status_code == 200:
        return jsonify({"message": "Email sent successfully!"}), 200
    else:
        return jsonify({"message": "Failed to send email."}), 500



if __name__ == '__main__':
    app.run(debug=True)
"
"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flowchart & Mind Map Generator</title>
    <link href="https://fonts.googleapis.com/css2?family=Raleway:wght@400;600&family=Roboto:wght@400;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/vis-network@9.1.2/dist/vis-network.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.waves.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            height: 100%;
            font-family: 'Roboto', sans-serif;
            background-color: #1A1A2E;
            color: #ffffff;
            overflow: hidden;
        }

        #vanta-background {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: -1;
        }

        .container {
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            height: 100vh;
            padding: 20px;
            box-sizing: border-box;
            backdrop-filter: blur(10px);
        }

        h1 {
            font-size: 3rem;
            color: #FF006E; 
            margin-bottom: 20px;
            text-align: center;
            text-shadow: 3px 3px 6px rgba(0,0,0,0.5);
        }

        .input-group {
            display: flex;
            flex-direction: column;
            align-items: center;
            width: 80%;
            max-width: 600px;
            gap: 15px;
            margin-bottom: 20px;
        }

        .input-group label {
            align-self: flex-start;
            margin-left: 10%;
            font-size: 1rem;
            color: #ffffff;
            font-weight: 500;
        }

        .input-group input[type="text"], .input-group select {
            width: 100%;
            padding: 12px 20px;
            border: none;
            border-radius: 25px;
            background-color: rgba(255, 255, 255, 0.1);
            color: #ffffff;
            font-size: 1rem;
            transition: all 0.3s ease;
            appearance: none;
            background-image: url('data:image/svg+xml;charset=US-ASCII,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="%23ffffff"><path d="M7 10l5 5 5-5z"/></svg>');
            background-repeat: no-repeat;
            background-position: right 15px center;
            background-size: 1em;
        }

        .input-group input[type="text"]:focus, .input-group select:focus {
            outline: none;
            box-shadow: 0 0 10px #FF006E;
            transform: scale(1.02);
        }

        #generate-btn {
            background: linear-gradient(45deg, #FF006E, #8338EC);
            color: #ffffff;
            padding: 15px 40px;
            border: none;
            border-radius: 30px;
            cursor: pointer;
            font-size: 1.2rem;
            font-weight: 700;
            transition: transform 0.3s ease, box-shadow 0.3s ease;
            box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
        }

        #generate-btn:hover {
            transform: translateY(-5px) scale(1.05);
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
        }

        #flowchart-container {
            width: 90%;
            height: 60vh;
            background-color: rgba(0, 0, 0, 0.5);
            border-radius: 20px;
            margin-top: 30px;
            overflow: hidden;
            box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
            display: none;
            position: relative; 
        }

        #flowchart {
            width: 100%;
            height: 100%;
        }

        #loading-animation {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            display: none;
        }

        /* Loader Styles */
        .loader {
            border: 12px solid rgba(255, 255, 255, 0.1);
            border-top: 12px solid #FF006E;
            border-radius: 50%;
            width: 100px;
            height: 100px;
            animation: spin 2s linear infinite;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        #error-message {
            color: #FF006E;
            text-align: center;
            margin-top: 20px;
            display: none;
            font-weight: 600;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
        }

        /* Control Buttons Styles */
        .control-buttons {
            position: absolute;
            bottom: 15px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 15px;
        }

        .control-buttons button {
            background-color: #FF006E;
            color: #ffffff;
            border: none;
            border-radius: 10px;
            padding: 10px 15px;
            cursor: pointer;
            font-size: 16px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            transition: background-color 0.3s ease, transform 0.2s ease;
        }

        .control-buttons button:hover {
            background-color: #E60053;
            transform: scale(1.05);
        }

        /* Fullscreen Button Styles */
        #fullscreen-btn {
            position: absolute;
            top: 15px;
            right: 15px;
            background-color: #FF006E;
            color: #ffffff;
            border: none;
            border-radius: 10px;
            padding: 10px 15px;
            cursor: pointer;
            font-size: 16px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            transition: background-color 0.3s ease;
        }

        #fullscreen-btn:hover {
            background-color: #E60053;
        }

        /* Zoom Controls Styles */
        .zoom-btn {
            position: absolute;
            top: 15px;
            left: 15px;
            background-color: #FF006E;
            color: #ffffff;
            border: none;
            border-radius: 10px;
            padding: 8px 12px;
            cursor: pointer;
            font-size: 18px;
            margin-right: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            transition: background-color 0.3s ease;
        }

        .zoom-btn:hover {
            background-color: #E60053;
        }

        @media (max-width: 768px) {
            h1 {
                font-size: 2.2rem;
            }

            .input-group {
                width: 90%;
            }

            #flowchart-container {
                height: 50vh;
            }

            .loader {
                width: 80px;
                height: 80px;
                border: 10px solid rgba(255, 255, 255, 0.1);
                border-top: 10px solid #FF006E;
            }

            .control-buttons {
                bottom: 10px;
                gap: 10px;
            }

            .control-buttons button {
                padding: 8px 12px;
                font-size: 14px;
            }
        }
    </style>
</head>
<body>
    <div id="vanta-background"></div>
    <div class="container">
        <h1>Flowchart & Mind Map Generator</h1>
        <div class="input-group">
            <label for="topic-input">Enter Topic or Algorithm Name</label>
            <input type="text" id="topic-input" placeholder="e.g., Machine Learning Algorithm" required>
        </div>
        <div class="input-group">
            <label for="type-select">Select Type</label>
            <select id="type-select">
                <option value="flowchart">Flowchart</option>
                <option value="mind_map">Mind Map</option>
            </select>
        </div>
        <div class="input-group">
            <label for="animation-select">Choose Animation</label>
            <select id="animation-select">
                <option value="static">Static</option>
                <option value="animated">Animated</option>
            </select>
        </div>
        <div class="input-group">
            <label for="detail-select">Detail Level</label>
            <select id="detail-select">
                <option value="simple">Simple</option>
                <option value="normal">Normal</option>
                <option value="detailed">Detailed</option>
            </select>
        </div>
        <button id="generate-btn">Generate</button>
        <div id="flowchart-container">
            <div id="flowchart"></div>
            <div id="loading-animation">
                <div class="loader"></div>
            </div>
            <button class="zoom-btn" id="zoom-in-btn">+</button>
            <button class="zoom-btn" id="zoom-out-btn">−</button>
            <button id="fullscreen-btn">Fullscreen</button>
            <!-- Animation Control Buttons -->
            <div class="control-buttons">
                <button id="play-btn">Play</button>
                <button id="pause-btn">Pause</button>
                <button id="back-btn">← Back</button>
                <button id="forward-btn">Forward →</button>
            </div>
        </div>
        <div id="error-message"></div>
    </div>
    <script>
        // Initialize Vanta Background
        VANTA.WAVES({
            el: "#vanta-background",
            mouseControls: true,
            touchControls: true,
            gyroControls: false,
            minHeight: 200.00,
            minWidth: 200.00,
            scale: 1.00,
            scaleMobile: 1.00,
            color: 0x1a1a2e,
            shininess: 60.00,
            waveHeight: 20.00,
            waveSpeed: 1.50,
            zoom: 0.65
        })

        // Animate elements on load using GSAP
        window.addEventListener('load', () => {
            gsap.from('h1', {duration: 1, y: -50, opacity: 0, ease: 'power3.out'});
            gsap.from('.input-group', {duration: 1, y: 50, opacity: 0, ease: 'power3.out', stagger: 0.2, delay: 0.3});
            gsap.from('#generate-btn', {duration: 1, scale: 0.8, opacity: 0, ease: 'back.out(1.7)', delay: 1.2});
        });

        // Animation control variables
        let animationInterval = null;
        let currentAnimationStep = 0;
        let totalAnimationSteps = 0;
        let animationData = {};

        document.getElementById('generate-btn').addEventListener('click', function () {
            var topic = document.getElementById('topic-input').value.trim();
            var chartType = document.getElementById('type-select').value;
            var animation = document.getElementById('animation-select').value;
            var detailLevel = document.getElementById('detail-select').value;

            if (!topic) {
                gsap.to('#error-message', {duration: 0.5, opacity: 1, display: 'block', text: "Please enter a topic.", ease: 'power2.inOut'});
                return;
            }

            gsap.to('#flowchart-container', {duration: 0.5, opacity: 1, display: 'block', ease: 'power2.inOut'});
            gsap.to('#loading-animation', {duration: 0.3, opacity: 1, display: 'block', ease: 'power2.inOut'});
            gsap.to('#error-message', {duration: 0.3, opacity: 0, display: 'none', ease: 'power2.inOut'});

            fetch('/get_flowchart_data', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ 
                    topic: topic,
                    type: chartType,
                    animation: animation,
                    detail_level: detailLevel
                })
            })
                .then(response => response.json())
                .then(data => {
                    gsap.to('#loading-animation', {duration: 0.3, opacity: 0, display: 'none', ease: 'power2.inOut'});
                    if (data.error) {
                        gsap.to('#error-message', {duration: 0.5, opacity: 1, display: 'block', text: "Error: " + data.error, ease: 'power2.inOut'});
                    } else {
                        animationData = data; // Store for animation controls
                        gsap.to('#error-message', {duration: 0.3, opacity: 0, display: 'none', ease: 'power2.inOut'});
                        drawFlowchart(data);
                    }
                })
                .catch(error => {
                    gsap.to('#loading-animation', {duration: 0.3, opacity: 0, display: 'none', ease: 'power2.inOut'});
                    gsap.to('#error-message', {duration: 0.5, opacity: 1, display: 'block', text: "An unexpected error occurred.", ease: 'power2.inOut'});
                    console.error('Error:', error);
                });
        });

        function drawFlowchart(data) {
            var container = document.getElementById('flowchart');

            // Destroy any existing network instance to avoid duplicates
            if (window.network) {
                window.network.destroy();
            }

            var nodes = new vis.DataSet(data.nodes);
            var edges = new vis.DataSet(data.edges);

            var options = {
                layout: {
                    hierarchical: {
                        direction: 'UD',
                        sortMethod: 'directed',
                        nodeSpacing: 200,
                        levelSeparation: 200,
                    }
                },
                nodes: {
                    shape: 'box',
                    font: {
                        size: 16,
                        face: 'Roboto',
                        color: '#ffffff',
                    },
                    color: {
                        background: '#8338EC',
                        border: '#FF006E',
                        highlight: {
                            background: '#FF006E',
                            border: '#8338EC',
                        },
                    },
                    borderWidth: 3,
                    shadow: {
                        enabled: true,
                        color: '#000000',
                        size: 10,
                        x: 5,
                        y: 5
                    },
                },
                edges: {
                    smooth: {
                        type: 'cubicBezier',
                        forceDirection: 'vertical',
                        roundness: 0.4,
                    },
                    arrows: {
                        to: { enabled: true, scaleFactor: 1.2 },
                    },
                    color: '#FF006E',
                    width: 2,
                    shadow: {
                        enabled: true,
                        color: '#000000',
                        size: 10,
                        x: 5,
                        y: 5
                    },
                },
                interaction: {
                    dragNodes: true,
                    dragView: true,
                    zoomView: true,
                    hover: true,
                    tooltipDelay: 100,
                    hideEdgesOnDrag: false,
                },
                physics: false,
            };

            var network = new vis.Network(container, { nodes: nodes, edges: edges }, options);
            window.network = network;

            // Reset animation variables
            clearInterval(animationInterval);
            currentAnimationStep = 0;

            if (data.animation === 'animated') {
                initializeAnimation(data);
            }
        }

        function initializeAnimation(data) {
            const nodes = data.nodes;
            const edges = data.edges;
            totalAnimationSteps = Math.max(nodes.length, edges.length);
            animationData = { nodes, edges, animation: data.animation };
        }

        // Animation Control Functions
        function playAnimation() {
            if (animationInterval) return; // Prevent multiple intervals
            animationInterval = setInterval(() => {
                if (currentAnimationStep >= totalAnimationSteps) {
                    clearInterval(animationInterval);
                    animationInterval = null;
                    return;
                }
                animateStep(currentAnimationStep);
                currentAnimationStep++;
            }, 1000); // Adjust delay as needed
        }

        function pauseAnimation() {
            if (animationInterval) {
                clearInterval(animationInterval);
                animationInterval = null;
            }
        }

        function forwardAnimation() {
            if (currentAnimationStep < totalAnimationSteps) {
                animateStep(currentAnimationStep);
                currentAnimationStep++;
            }
        }

        function backwardAnimation() {
            if (currentAnimationStep > 0) {
                currentAnimationStep--;
                revertStep(currentAnimationStep);
            }
        }

        function animateStep(step) {
            if (step < animationData.nodes.length) {
                var node = animationData.nodes[step];
                window.network.body.data.nodes.update({ id: node.id, color: { background: '#00FF00' } });
            }
            if (step < animationData.edges.length) {
                var edge = animationData.edges[step];
                // Highlight the edge by changing its color
                window.network.body.data.edges.update({ id: `${edge.from}-${edge.to}`, color: '#00FF00' });
            }
        }

        function revertStep(step) {
            if (step < animationData.nodes.length) {
                var node = animationData.nodes[step];
                window.network.body.data.nodes.update({ id: node.id, color: { background: '#8338EC' } });
            }
            if (step < animationData.edges.length) {
                var edge = animationData.edges[step];
                window.network.body.data.edges.update({ id: `${edge.from}-${edge.to}`, color: '#FF006E' });
            }
        }

        // Event Listeners for Control Buttons
        document.getElementById('play-btn').addEventListener('click', playAnimation);
        document.getElementById('pause-btn').addEventListener('click', pauseAnimation);
        document.getElementById('forward-btn').addEventListener('click', forwardAnimation);
        document.getElementById('back-btn').addEventListener('click', backwardAnimation);

        // Zoom controls
        document.getElementById('zoom-in-btn').addEventListener('click', () => {
            window.network.moveTo({scale: window.network.getScale() * 1.2});
        });

        document.getElementById('zoom-out-btn').addEventListener('click', () => {
            window.network.moveTo({scale: window.network.getScale() * 0.8});
        });

        // Fullscreen functionality
        const flowchartContainer = document.getElementById('flowchart-container');
        const fullscreenButton = document.getElementById('fullscreen-btn');

        fullscreenButton.addEventListener('click', () => {
            if (document.fullscreenElement) {
                document.exitFullscreen();
            } else {
                flowchartContainer.requestFullscreen();
            }
        });
    </script>
</body>
</html>
"