Code viewer for World: New World
var pgn = "";
var fen = "";
var pngUrl = "";
var APIKEY1 = "";
var APIKEY2 = "";
var solution = "";
var mateValue = 1;
var extractedMoves = {
    result1: null,
    result2: null
};
var tmp = 0.7;

function changeTemp(){//slider to change GPTs temperature
    tmp = jQuery("input#myRange").val();
    tmp = parseFloat(tmp);//change from string to float
}

function changeMateValue(){
    mateValue = jQuery("input#myRange2").val();
    mateValue = parseInt(mateValue);//change from string to float
}

function handleApiUpdate(event){
    event.preventDefault();
    document.getElementById("APIKEY1").value = sessionStorage.getItem("APIKEY1");
    document.getElementById("APIKEY2").value = sessionStorage.getItem("APIKEY2");
    const lockModal = bootstrap.Modal.getInstance(document.getElementById('lockModal'));
    lockModal.show();
    sessionStorage.setItem('modalClosed', false);
}
function handleApi(event){
    event.preventDefault();
    // close the modal
    sessionStorage.setItem("APIKEY1", event.target.elements["APIKEY1"].value);
    sessionStorage.setItem("APIKEY2", event.target.elements["APIKEY2"].value);
    APIKEY1=event.target.elements["APIKEY1"].value;
    APIKEY2=event.target.elements["APIKEY2"].value;
    const lockModal = bootstrap.Modal.getInstance(document.getElementById('lockModal'));
    lockModal.hide();
    sessionStorage.setItem('modalClosed', true);
    document.getElementById("updateApiBtn").style.display = "block";
}

function convert2FEN(pgn){
    
    const chess = new Chess();
    chess.load_pgn(pgn);
        
    const fen = chess.fen();
    return fen;
}
function initLockModal() {
    sessionStorage.clear();
    sessionStorage.setItem('modalClosed', false);
    const lockModal = new bootstrap.Modal(document.getElementById('lockModal'));
    // Check if sessionStorage has a flag
    if (sessionStorage.getItem('modalClosed')) {
        lockModal.show();// show modal on page load
    }
    // Close button behavior
    document.getElementById('closeModalBtn').addEventListener('click', () => {
      lockModal.hide();
      sessionStorage.setItem('modalClosed', true);
    });
}
async function fenToPngUrl(fen) {
    const encoded = encodeURIComponent(fen);
    return `https://fen2png.com/api/?fen=${encoded}&raw=true`;
}
async function getRandomPuzzlePGN() {
    const response = await fetch('https://lichess.org/api/puzzle/next?angle=mateIn1&difficulty=normal', {headers: {}}
    );

    if (!response.ok) {
        throw new Error("Invalid puzzle ID or Lichess error");
    }

    const data = await response.json();
    console.log(data)
    const pgn = data.game?.pgn || data.pgn;
    solution = data.puzzle.solution[0];
    
    if (!pgn || !solution) {
        throw new Error("Puzzle has no PGN or SOLUTION");
    }
    return pgn;
}

async function fetchPuzzleImage() {
    try {
        document.getElementById("result1").innerHTML = `<div class="d-flex justify-content-center"><div class="spinner-border text-warning" role="status"><span class="visually-hidden">Loading...</span></div></div>`;
        document.getElementById("result2").innerHTML = `<div class="d-flex justify-content-center"><div class="spinner-border text-warning" role="status"><span class="visually-hidden">Loading...</span></div></div>`;
        document.querySelector("main p:nth-of-type(2)").innerHTML = `<div class="spinner-grow" style="width: 3rem; height: 3rem;" role="status"><span class="visually-hidden">Loading...</span></div>`;

        // 1. Get puzzle PGN
        pgn = await getRandomPuzzlePGN();
        console.log("PGN:", pgn);

        // 2. Convert PGN → FEN
        fen = convert2FEN(pgn);
        console.log("FEN:", fen);

        // 3. Convert FEN → PNG URL
        pngUrl = await fenToPngUrl(fen);
        console.log("Image URL:", pngUrl);
        console.log("SOLUTION:", solution);

        // 4. Display puzzle image
        setTimeout(function() {
            document.querySelector("main p:nth-of-type(2)").innerHTML = `
                Mate in:<strong>${mateValue}</strong><br/>
                <img src="${pngUrl}" style="max-width:100%;border:1px solid #ccc;border-radius:8px;height:60%;width:60%;" /><br/>
                EXPECTED SOLUTION:<strong>${solution}</strong> 
            `;
        }, 2000);
        await send2APIs();

    } catch (err) {
        console.error(err);
        document.querySelector("main p:nth-of-type(2)").innerHTML = "Error: " + err.message;
    }
}
async function callAIAPI(apiOptions, targetElementId) {
  try {
    var url = "";
    if(targetElementId === "result1"){
        url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent";
        console.log(`Calling gemini with ${APIKEY2}`);
    }
    else{
        url = "https://api.groq.com/openai/v1/responses";
        console.log(`calling groq with ${APIKEY1}`);
    }
    const response = await fetch(url, apiOptions);
    const data = await response.json();
    
    var finalText = "";
    console.log(data);
    if(targetElementId === "result1"){
        finalText = data.candidates[0].content.parts[0].text;
    }else{
        finalText = data.output[1].content[0].text;
    }
    // Insert into the page
    document.getElementById(targetElementId).innerHTML = finalText;
    // Extract move inside brackets and store in global object
    const match = finalText.match(/\[([^\]]+)\]/);
    extractedMoves[targetElementId] = match ? match[1] : null;

  } catch (error) {
    console.error(error);
    document.getElementById(targetElementId).innerHTML = "Error fetching response";
  }
}
function showAlert(message, type = "success", duration = 10000) {
    const container = document.getElementById("alert-container");

    const alertId = "alert-" + Date.now();

    container.innerHTML = `
        <div class="alert alert-${type} alert-dismissible fade show mt-3" id="${alertId}" role="alert">
            <strong>${message}</strong>
            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
        </div>
    `;
    setTimeout(() => {
        const alertEl = document.getElementById(alertId);

        if (alertEl) {
            // Trigger Bootstrap fade-out
            alertEl.classList.remove("show");
            alertEl.classList.add("hide");

            // remove element after fade completes (150ms default Bootstrap fade)
            setTimeout(() => alertEl.remove(), 150);
        }
    }, duration);
}

async function send2APIs(){
    const input = `Mate in: ${mateValue}, give the solution for the given FEN string: ${fen},give the moves required for eg [f4d4] and give a brief 10 word explaination for your decision. Do not use markdown, just text will do.`;
    console.log(input);
    const API1Options = {//POST to API
        method: 'POST',
        headers: {
            'x-goog-api-key': `${APIKEY2}`,//checking API key
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({//what to send
            contents: [{
                parts: [{"text": input}],
            }],
            generationConfig: {
                temperature: tmp,
            },
        }),
    };
    const API2Options = {//POST to API
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${APIKEY1}`,//checking API key
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            model: "openai/gpt-oss-120b",
            temperature: tmp,
            input: input, 
        })
    };
    try{
        await callAIAPI(API1Options, "result1");
        await callAIAPI(API2Options, "result2");
        const move1 = extractedMoves.result1;
        const move2 = extractedMoves.result2;
        if (move1 === solution && move2 === solution) {
            showAlert("Both AIs are correct!", "success");
        } else if (move1 !== solution && move2 !== solution) {
            showAlert("Both AIs are incorrect!", "danger");
        } else if(move1 === solution) {
            showAlert("Gemini AI is correct!", "warning");
        } else if(move2 === solution) {
            showAlert("Groq AI is correct!", "warning");
        }
        // const response1 = await fetch("https://api.groq.com/openai/v1/responses", API1Options);
        // const data1 = await response1.json();
        // const finalTextApi1 = data1.output[1].content[0].text;
        // document.getElementById("result1").innerHTML = finalTextApi1;
        // const response2 = await fetch("https://api.groq.com/openai/v1/responses", API2Options);
        // const data2 = await response2.json();
        // const finalTextApi2 = data2.output[1].content[0].text;
        // //Insert into the div
        // document.getElementById("result2").innerHTML = finalTextApi2;
        // console.log(data1);
        // console.log(data2);
    }
    catch(err){
        console.error(err);
    }
}

// function start(){
    document.write(`
    <head>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
        <style>
            :root{
              --bg-100: #f5f7fb;
              --bg-200: #e8eefb;
              --accent-500: #1f6feb;
              --accent-600: #1258d6;
              --muted-500: #6b7280;
              --card-border: #e6eaf4;
              --success: #16a34a;
              --danger: #dc2626;
              --glass: rgba(255,255,255,0.65);
              --radius-lg: 12px;
            }
        
            html, body { height: 100%; }
            body {
                margin: 0;
                padding: 0.5rem;
                display: flex;
                flex-direction: column;
                font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
                color: #0f172a;
                background: linear-gradient(180deg, var(--bg-100) 0%, #eef6ff 100%);
                -webkit-font-smoothing:antialiased;
                line-height: 1.45;
            }
        
            /* A flex container that holds BOTH header and button */
            .top-bar {
                display: flex;
                align-items: center;
                flex-direction: row;
                justify-content: center;     /* centers the header block */
                position: relative;
                gap: 10px;
                background: linear-gradient(90deg, rgba(255,255,255,0.6), rgba(255,255,255,0.35));
                border-radius: var(--radius-lg);
                box-shadow: 0 6px 18px rgba(16,24,40,0.06);
            }
            
            /* Your original header styling */
            .page-header {
                // border: 2px solid #000;
                padding: 1rem;
                margin: 0;
                display: inline-block;
                color: #0b1220;
                letter-spacing: -0.2px;
                font-size: 1.25rem;
            }
            /* update API button (primary accent) */
            #updateApiBtn {
              padding: 0.5rem 0.9rem;
              border-radius: 10px;
              border: none;
              font-weight: 600;
            }
            #updateApiBtn:hover { transform: translateY(-1px); }
        
            .wrap {
              display: grid;
              grid-template-columns: 220px 1fr 220px; 
              gap: 1rem;
              padding: 1rem;
              flex: 1 1 auto;
              align-items: start;
            }
        
            aside {
              background: linear-gradient(180deg, var(--glass), rgba(255,255,255,0.85));
              border: 1px solid var(--card-border);
              padding: 1rem;
              border-radius: 8px;
              min-height: 200px;
              box-shadow: 0 8px 22px rgba(13, 20, 40, 0.04);
            }
            
            aside h2, main h2 {
              margin-top: 0;
              margin-bottom: 0.5rem;
              font-size: 0.95rem;
              color: var(--muted-500);
              text-align: center;
            }
            
            #result1, #result2 {
              min-height: 110px;
              padding: 0.75rem;
              border-radius: 10px;
              background: linear-gradient(180deg, rgba(255,255,255,0.6), rgba(250,251,255,0.4));
              border: 1px dashed rgba(20,35,65,0.04);
              color: #0b1220;
              font-size: 0.95rem;
              overflow-wrap: anywhere;
            }
    
            main {
              background: linear-gradient(180deg, var(--glass), rgba(255,255,255,0.85));
              border: 1px solid var(--card-border);
              padding: 1rem;
              border-radius: 8px;
              min-height: 200px;
              text-align: center;
              display: flex;
              flex-direction: column;
              align-items: center;
              box-shadow: 0 8px 22px rgba(13, 20, 40, 0.04);
            }
            
            /* main image & puzzle area */
            main p:nth-of-type(2) img {
              border-radius: 10px;
              box-shadow: 0 8px 18px rgba(16,24,40,0.06);
            }
            main p:nth-of-type(2) {
              min-height: 160px;
              display:flex;
              align-items:center;
              justify-content:center;
              flex-direction:column;
            }
        
            // .bottom-bar {
            //   position: sticky; 
            //   bottom: 0;
            //   background: #fff;
            //   border-top: 1px solid #e6e6e9;
            //   padding: 0.75rem 1rem;
            //   display: flex;
            //   gap: 0.5rem;
            //   align-items: center;
            //   justify-content: center;
            // }
            /* bottom bar */
            .bottom-bar {
              position: sticky;
              bottom: 0;
              left: 0;
              background: #ffffffcc;
              border-top: 1px solid var(--card-border);
              padding: 0.75rem 1rem;
              display: flex;
              gap: 0.5rem;
              align-items: center;
              justify-content: center;
              backdrop-filter: blur(6px);
            }
            
            /* send button */
            #sendMessage {
              background: linear-gradient(180deg,var(--accent-500),var(--accent-600));
              border: none;
              color: #fff;
              padding: 0.6rem 1rem;
              border-radius: 10px;
              font-weight: 700;
              cursor: pointer;
              box-shadow: 0 8px 18px rgba(31,111,235,0.14);
            }
            
            #sendMessage:hover { transform: translateY(-1px); }
            
            /* modal tweaks */
            .modal-content {
              border-radius: 12px;
              border: 1px solid var(--card-border);
              background: linear-gradient(180deg, #fff, #fbfdff);
            }
            
            /* spinner sizing */
            .spinner-border, .spinner-grow {
              color: var(--accent-500);
            }
            
            /* responsive */
            @media (max-width: 980px) {
              .wrap { grid-template-columns: 1fr; }
              .top-bar { flex-direction: column; gap: 0.5rem; }
              #updateApiBtn { width: 100%; }
              input[type="range"] { width: 160px; }
            }
        
            .bottom-bar-inner {
              width: 100%;
              max-width: 1100px;
              display: flex;
              gap: 0.5rem;
              align-items: center;
              justify-content: center;
            }
        
            .bottom-bar button {
              padding: 0.6rem 1rem;
              border-radius: 6px;
              border: 1px solid #1f6feb;
              background: #1f6feb;
              color: white;
              font-weight: 600;
              cursor: pointer;
              font-size: 1rem;
            }
        
            .bottom-bar button:active { transform: translateY(1px); }
        </style>
    </head>
    <body>
        <script  src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
        <script>
            window?.addEventListener('DOMContentLoaded', ${initLockModal});
        </script>
        <!-- Modal -->
        <div class="modal fade" id="lockModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
          <div class="modal-dialog modal-dialog-centered" style="width:fit-content;">
            <div class="modal-content">
              <div class="modal-header" style="display:flex; align-items: center; justify-content:center;">
                <h5 class="modal-title">Enter Api Keys</h5>
              </div>
              <div class="modal-body">
                <form onSubmit=handleApi(event) style="display:flex; flex-direction: column; align-items: center; gap: 1rem;">
                    <input type="text" id="APIKEY1" name="APIKEY1" placeholder="Groq Api Key" required=true onChange="APIKEY1=this.value;"/>
                    <input type="text" id="APIKEY2" name="APIKEY2" placeholder="Gemini Api Key" required=true onChange="APIKEY2=this.value;"/>
                    <button type="submit" class="btn btn-primary">Done</button>
                </form>
              </div>
            </div>
          </div>
        </div>
        <div class="top-bar">
            <header class="page-header">
                <h1>Chess Move Comparison</h1>
            </header>
            <button class="btn btn-warning" style="display:none;" id="updateApiBtn" onClick=handleApiUpdate(event)>Update Api Key</button>
        </div>
        <div id="alert-container"></div>
        <div class="wrap">
            <aside class="left" aria-label="Left column" style="max-height: 400px; overflow-y: auto;">
                <h2 style="font-size:0.95rem; margin-bottom:0.5rem; text-align:center;">Gemini AI</h2>
                <div id="result1"></div>
            </aside>
            <main role="main" aria-label="Main content">
                <h2 style="font-size:0.95rem; text-align:center; margin-bottom:0.5rem;">Chess Puzzle</h2>
                <p>
                <p style="margin-top:1rem;"></p>
                <!-- Add more content to demonstrate scrolling -->
            </main>
            <aside class="right" aria-label="Right column" style="max-height: 400px; overflow-y: auto;">
                <h2 style="font-size:0.95rem; margin-bottom:0.5rem; text-align:center;">Groq AI</h2>
                <div id="result2"></div>
            </aside>
        </div>
        <form class="bottom-bar">
            <div class="bottom-bar-inner">
                <p><b>Maybe change the <br>temperature to <br> see what happens :)</b></p>
                <input style="accent-color: #1f6feb;" type="range" min="0" max="1" value="0.7" step="0.1" class="slider" id="myRange" onChange="rangeValue.innerText = this.value; changeTemp();"> , <p id="rangeValue">0.7</p>
            </div>
            <div class="bottom-bar-inner">
                <button id="sendMessage" type="button" onClick=fetchPuzzleImage()>Send</button>
            </div>
            <div class="bottom-bar-inner">
                <p><b>Mate in N (Choose Between 1 and 5)</b></p>
            <input style="accent-color: #1f6feb;" type="range" min="1" max="5" value="1" step="1" class="slider" id="myRange2" onChange="rangeValue2.innerText = this.value; changeMateValue();"> , <p id="rangeValue2">1</p>
            </div>
        </form>
    </body>
    `);
// }