Code viewer for World: AI TaleForge
let open_ai_response;

// Apply some modern styles to the body
document.body.style.fontFamily = "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif";
document.body.style.margin = "0";
document.body.style.padding = "20px";
document.body.style.backgroundColor = "#22092C !important";
// Create a futuristic-looking title for the page
const pageTitle = document.createElement("h1");
pageTitle.textContent = "AI TaleForge: ChatGPT & DALL-E Craft 4 Images, 1 Line, Endless Stories";
pageTitle.style.textAlign = "center";
pageTitle.style.color = "#4caf50"; // Green color
pageTitle.style.fontFamily = "'Orbitron', sans-serif"; // Futuristic font
pageTitle.style.fontSize = "36px";
pageTitle.style.marginBottom = "20px";
pageTitle.style.transition = "transform 0.3s ease-in-out"; // Add transition effect
pageTitle.style.borderBox = "border-box"; // Add box-sizing property

// Add a mouseover event to create a 3D effect on hover
pageTitle.addEventListener("mouseover", () => {
  pageTitle.style.transform = "rotateX(10deg)"; // Adjust the rotation angle as needed
});

// Add a mouseout event to reset the transform on mouse leave
pageTitle.addEventListener("mouseout", () => {
  pageTitle.style.transform = "rotateX(0)";
});

// Apply additional styles
pageTitle.style.backgroundColor = "#22092C"; // Background color
pageTitle.style.padding = "20px"; // Padding
pageTitle.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.1)"; // Box shadow

// Typing effect
const titleText = pageTitle.textContent;
pageTitle.textContent = ""; // Clear the initial text

// Function to simulate typing effect
function typeText(index) {
  if (index <= titleText.length) {
    pageTitle.textContent = titleText.slice(0, index);
    setTimeout(() => {
      typeText(index + 1);
    }, 100); // Adjust the typing speed (milliseconds)
  }
}

// Trigger the typing effect after a delay (e.g., 500 milliseconds)
setTimeout(() => {
  typeText(0);
}, 500);

// Append the title to the document
document.body.appendChild(pageTitle);


// Updated styles for the dropdown
const dropdownStyles = `
  .dropdown-container {
    position: relative;
    margin: 0 auto;
    width: 500px; /* Adjust the width as needed */
    text-align: center;
    margin-top: 20px;
  }

  .model-dropdown {
    width: 100%;
    padding: 8px;
    font-size: 24px; /* Adjust font size for better readability */
    border: 1px solid #4caf50; /* Green border */
    border-radius: 10px;
    background: #D5BDAF; /* Dark background color */
    color: #000000; /* Green text color */
    appearance: none; /* Remove default dropdown arrow */
    cursor: pointer;
    outline: none; /* Remove focus outline */
    transition: background 0.3s, border 0.3s;
    animation: fadeIn 0.5s ease-out; /* Add fadeIn animation */
    text-align: center;
    font-family: 'Roboto', sans-serif; /* Change the font to Roboto */
    font-weight: 500; /* Increase font weight for a bolder look */
    position: relative; /* Added position relative */
    overflow: hidden; /* Hide overflow for animation */
  }

  .model-dropdown option {
    background-color: #D5BDAF; /* Dark background color */
    color: #000000; /* Green text color */
    padding: 8px;
    font-size: 24px; /* Adjust font size for better readability */
    text-align: center;
    font-family: 'Roboto', sans-serif; /* Change the font to Roboto */
    font-weight: 500; /* Increase font weight for a bolder look */
    white-space: nowrap; /* Prevent text from wrapping */
    overflow: hidden; /* Hide overflow for animation */
    animation: typeWriter 2s steps(30, end); /* Reverse typing animation */
  }

  .model-dropdown:hover {
    background: #4caf50; /* Green background color on hover */
    border: 1px solid #fff; /* White border on hover */
  }

  .model-dropdown:focus {
    background: #4caf50; /* Green background color on focus */
    border: 1px solid #fff; /* White border on focus */
  }

  .model-dropdown::after {
    content: '';
    border-style: solid;
    border-width: 8px 8px 0;
    border-color: #000000 transparent transparent transparent;
    position: absolute;
    top: calc(50% - 4px);
    right: 15px;
    transition: transform 0.3s ease-in-out; /* Added transition for animation */
    transform: scaleY(1); /* Initial transform state */
  }

  .model-dropdown:hover::after {
    transform: scaleY(0); /* Final transform state on hover */
  }

  @keyframes fadeIn {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }

  @keyframes typeWriter {
    from {
      width: 0;
    }
    to {
      width: auto;
    }
  }
`;

// Create a style element and append it to the head
const dropdownStyleElement = document.createElement("style");
dropdownStyleElement.textContent = dropdownStyles;
document.head.appendChild(dropdownStyleElement);

// Create a container for the dropdown
const dropdownContainer = document.createElement("div");
dropdownContainer.classList.add("dropdown-container");

// Create a dropdown for selecting the model
const modelDropdown = document.createElement("select");
modelDropdown.classList.add("model-dropdown");

// Add options for DALL-E 2 and DALL-E 3
const dalle2Option = document.createElement("option");
dalle2Option.value = "dall-e-2";
dalle2Option.textContent = "DALL-E 2";
modelDropdown.appendChild(dalle2Option);

const dalle3Option = document.createElement("option");
dalle3Option.value = "dall-e-3";
dalle3Option.textContent = "DALL-E 3";
modelDropdown.appendChild(dalle3Option);

// Append the dropdown to the container
dropdownContainer.appendChild(modelDropdown);

// Append the container to the body
document.body.appendChild(dropdownContainer);






// Append the title to the body
document.body.appendChild(pageTitle);


// Create an input field for the user to enter the one-line story
const userInput = document.createElement("input");
userInput.type = "text";
userInput.placeholder = "Enter one-line story";
userInput.style.width = "100%";
userInput.style.padding = "10px";
userInput.style.margin = "20px auto";
userInput.style.fontSize = "16px";
userInput.style.border = "1px solid #ccc";
userInput.style.borderRadius = "5px";
document.body.appendChild(userInput);

const userAPIkey = document.createElement("input");
userAPIkey.type = "text";
userAPIkey.placeholder = "Enter OPEN AI API Key";
userAPIkey.style.width = "100%";
userAPIkey.style.padding = "10px";
userAPIkey.style.margin = "20px auto";
userAPIkey.style.fontSize = "16px";
userAPIkey.style.border = "1px solid #ccc";
userAPIkey.style.borderRadius = "5px";
document.body.appendChild(userAPIkey);



// Create a button to trigger the generation based on the user's input
const generateButton = document.createElement("button");
generateButton.textContent = "Generate Image";
generateButton.style.width = "100%";
generateButton.style.padding = "10px";
generateButton.style.margin = "10px auto";
generateButton.style.fontSize = "16px";
generateButton.style.cursor = "pointer";
generateButton.style.background = "#872341";
generateButton.style.color = "white";
generateButton.style.border = "none";
generateButton.style.borderRadius = "5px"; // Add margin to separate from the input
generateButton.addEventListener("click", (event) => {
  event.preventDefault(); // Prevent default form submission behavior
  const openAPIKey = userAPIkey.value;
  const oneLineStory = userInput.value;
  const model=modelDropdown.value;
  generatePrompts(oneLineStory,model,openAPIKey);
});


// Container to display generated prompts
const promptContainer = document.createElement("div");
promptContainer.style.width = "80%";
promptContainer.style.margin = "20px auto";
promptContainer.style.padding = "20px";
promptContainer.style.background = "#f8f8f8";
promptContainer.style.borderRadius = "5px";
promptContainer.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.1)";






document.body.appendChild(userInput);
document.body.appendChild(modelDropdown);
document.body.appendChild(generateButton);
document.body.appendChild(promptContainer);
// Function to get ChatGPT response

// Create a loading spinner
const loadingSpinner = document.createElement("div");
loadingSpinner.classList.add("loader");
document.body.appendChild(loadingSpinner);


async function getChatGPTResponse(prompt,openAPIKey) {
  const chatGPTApiKey = openAPIKey;
  const chatGPTUrl = "https://api.openai.com/v1/chat/completions";
  
  loadingSpinner.style.display = "block";
  
  const chatGPTData = JSON.stringify({
    model: "gpt-3.5-turbo",
    messages: [
      { role: "system", content: "You are story teller who follows below template and provide dalle 2 prompts, Make sure that you strictly follow below the template" },
      { role: "user", content: prompt },
    ],
  });

  const chatGPTResponse = await fetch(chatGPTUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${chatGPTApiKey}`,
    },
    body: chatGPTData,
  });


  

  const chatGPTResult = await chatGPTResponse.json();
  loadingSpinner.style.display = "none";

  const generatedPrompt = chatGPTResult.choices[0].message.content;

  // Display generated prompt in UI
  const promptElement = document.createElement("p");
  promptElement.textContent = `Using ChatGPT API Generated Dalle 2 Prompt: ${generatedPrompt}`;
  promptContainer.appendChild(promptElement);

  return generatedPrompt;
}


// Function to generate prompts for DALL-E 2
async function generatePrompts(oneLineStory,model,openAPIKey) {
  // Call ChatGPT to get a response
  const title = oneLineStory; // Use user's input as the title
  const promptTemplate = `
    Title: ${title}
Prompt:
[Line 1: Introduce the setting or context of Story Title . Describe the visual elements you want in the image (e.g., colors, shapes, objects, scenes) (max 250 characters)]
[Line 2: Introduce the main character and their goal or challenge .Specify any particular style or mood (e.g., surreal, vibrant, calm)(max 250 characters)]
[Line 3: Add a twist or complication to the story . Additional details to refine the visual concept (max 250 characters)]
[Line 4: Describe a critical moment or turning point . Any specific constraints or requirements for the image(max 250 characters)]
Description:
Create an image that captures the essence of [Title of the Visual Concept]. Emphasize [visual elements], ensuring a [specified style or mood]. Pay attention to [additional details] while adhering to [constraints or requirements]
  `;

  // Call ChatGPT to get a response
  const chatGPTResponse = await getChatGPTResponse(promptTemplate,openAPIKey);

  // Limit the length of the generated prompt
  const truncatedPrompt = chatGPTResponse;

  // Use regex to split the prompt into four lines
  const lineRegex = /\[Line (\d+): (.+?)\]/g;
  let match;
  const lines = [];
  while ((match = lineRegex.exec(truncatedPrompt)) !== null) {
    lines.push(match[2]);
  }
  
  // Check if the lines array is empty
  if (lines.length === 0) {
    // Recall generatePrompts with the same parameters
    await generatePrompts(oneLineStory, model, openAPIKey);
    return; // Exit the current invocation
  }
  console.log(lines)
  // Display the formatted prompts in the UI
  for (let i = 0; i < 4; i++) {
    const formattedPrompt = lines[i];

    // Display the formatted prompt in the UI
    const promptElement = document.createElement("p");
    loadingSpinner.style.display = "block";
    promptElement.textContent = `Generated Prompt ${i + 1}:\n${formattedPrompt}`;
    //promptContainer.appendChild(promptElement);
    // Call DALL-E 2 with the formatted prompt
    //try{
    await generateImageWithPrompt(formattedPrompt,model,openAPIKey);}
    //catch(error){console.log("Maximum retries reached. Unable to generate image.");}
    loadingSpinner.style.display = "none";
  //}
}



const maxRetries = 50;
let currentRetry = 0;

async function generateImageWithPrompt(prompt, model,openAPIKey) {
  // Show loading spinner while waiting for the response
  loadingSpinner.style.display = "block";
  console.log(model)
  const DALLE2ApiKey = openAPIKey;
  const DALLE2Url = "https://api.openai.com/v1/images/generations";
    
  const DALLE2Data = JSON.stringify({
    model: model,
    prompt: prompt,
  });

  try {
    const response = await fetch(DALLE2Url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${DALLE2ApiKey}`,
      },
      body: DALLE2Data,
    });

    // Check if the response indicates a rate limit error
    if (response.status === 429) {
      // Implement exponential backoff
      const delay = Math.pow(2, currentRetry) * 1000; // Backoff in milliseconds
      currentRetry++;

      // Wait for the specified delay
      await new Promise(resolve => setTimeout(resolve, delay));

      // Retry the request
      return generateImageWithPrompt(prompt, model,openAPIKey);
    }

    // Handle the response as needed
    const DALLE2Result = await response.json();

    // Log DALL-E 2 result for debugging
    console.log("DALL-E 2 Result:", DALLE2Result);

    // Display the generated image with a border
    const imageURL = DALLE2Result.data[0].url;
    const img = document.createElement("img");
    img.src = imageURL;
    img.style.border = "1px solid #ccc"; // Add a border to the image
    img.style.marginTop = "10px"; // Add margin to separate images
    document.body.appendChild(img);
  } catch (error) {
    // Handle other errors if needed

    if (currentRetry >= maxRetries) {
      console.log("Maximum retries reached. Unable to generate image.");
      // You can handle this situation as needed, e.g., display a message to the user.
    } else {
      console.log("Error generating image with prompt:", error.message);
      // Log the warning, but don't propagate the error
    }
  } finally {
    // Hide loading spinner once the response is received
    loadingSpinner.style.display = "none";
  }
}


// Add styles for the loading spinner
const spinnerStyles = `
  .loader {
    display: none;
    border: 8px solid #f3f3f3;
    border-top: 8px solid #3498db;
    border-radius: 50%;
    width: 50px;
    height: 50px;
    animation: spin 1s linear infinite;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1000; /* Ensure the spinner is on top of other elements */
  }

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

const spinnerStyleElement = document.createElement("style");
spinnerStyleElement.textContent = spinnerStyles;
document.head.appendChild(spinnerStyleElement);

// Apply some modern styles to the body
document.body.style.fontFamily = "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif";
document.body.style.margin = "0";
document.body.style.padding = "20px";
document.body.style.backgroundColor = "#22092C !important";