/* From Dev 04/14/25 */

"use strict";

let lastPerformanceCheck = performance.now();
let frameCount = 0;

function MakeTimeStr() {
    var tmpSec = CaseTimeInSec;
    var tmpHH = Math.floor(tmpSec / 3600);
    var tmpMM = Math.floor((tmpSec - tmpHH * 3600) / 60);
    var tmpSS = tmpSec - tmpHH * 3600 - tmpMM * 60;
    var tmpStr = "";
    if (tmpHH > 9) {
        tmpStr += tmpHH + ":"
    } else {
        tmpStr += "0" + tmpHH + ":"
    }
    if (tmpMM > 9) {
        tmpStr += tmpMM + ":"
    } else {
        tmpStr += "0" + tmpMM + ":"
    }
    if (tmpSS > 9) {
        tmpStr += tmpSS
    } else {
        tmpStr += "0" + tmpSS
    }
    return (tmpStr)
}

function InitializeHemoWaveforms() {
    ZapHt = [29, 29, 28, 28, 27, 27, 26, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 12, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    CprHt = [0, 10, 20, 30, 40, 46, 47, 48, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 48, 44, 40, 36, 32, 28, 20, 15, 10, 5, 0, -4, -8, -12, -16, -20, -24, -27, -30, -33, -34, -35, -35, -35, -35, -35, -35, -35, -30, -27, -24, -21, -18, -15, -12, -9, -6, -4, -2, 0, 0];
    CoHt = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -2, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -17, -18, -18, -19, -19, -20, -20, -20, -21, -21, -21, -22, -22, -22, -23, -23, -23, -23, -23, -24, -24, -24, -24, -24, -24, -24, -25, -25, -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -24, -24, -24, -24, -24, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -21, -20, -20, -20, -19, -19, -19, -19, -18, -18, -18, -18, -17, -17, -16, -16, -16, -16, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
}

function SetNextHemoWave() {
    var tmpHemoRate;
    if (PddHr > 159) {
        tmpHemoRate = "160"
    } else if (PddHr > 139) {
        tmpHemoRate = "140"
    } else if (PddHr > 119) {
        tmpHemoRate = "120"
    } else if (PddHr > 99) {
        tmpHemoRate = "100"
    } else if (PddHr > 89) {
        tmpHemoRate = "90"
    } else if (PddHr > 79) {
        tmpHemoRate = "80"
    } else if (PddHr > 69) {
        tmpHemoRate = "70"
    } else if (PddHr > 59) {
        tmpHemoRate = "60"
    } else if (PddHr > 49) {
        tmpHemoRate = "50"
    } else if (PddHr > 39) {
        tmpHemoRate = "40"
    } else if (PddHr > 29) {
        tmpHemoRate = "30"
    } else {
        tmpHemoRate = "20"
    }
    switch (CurrentState.rhythm) {
        case "Afib6":
            NextHemoWave = afib6[160];
            break;
        case "Asystole1":
            NextHemoWave = asyst1[20];
            break;
        case "Sinus1":
            NextHemoWave = sinus1[tmpHemoRate];
            break;
        case "Sinus2":
            NextHemoWave = sinus2[tmpHemoRate];
            break;
        case "Sinus3":
            NextHemoWave = sinus3[tmpHemoRate];
            break;
        case "Sinus4":
            NextHemoWave = sinus4[tmpHemoRate];
            break;
        case "Sinus5":
            NextHemoWave = sinus5[tmpHemoRate];
            break;
        case "Sinus6":
            if (tmpHemoRate < 70) {
                NextHemoWave = sinus6[50]
            } else {
                NextHemoWave = sinus6[80]
            }
            break;
        case "St1":
            NextHemoWave = st1[tmpHemoRate];
            break;
        case "St2":
            NextHemoWave = st2[tmpHemoRate];
            break;
        case "St3":
            NextHemoWave = st3[tmpHemoRate];
            break;
        case "Vfib2":
            NextHemoWave = vfib2[20];
            break;
        case "Vtach3":
            NextHemoWave = vtach3[140];
            break;
        default:
            alert("rhythm: " + CurrentState.rhythm + " is undefined. ");
            throw "invalid rhythm: " + CurrentState.rhythm;
    }
}

function SetNextRespWave() {
    var tmpRespRate;
    if (PrpMonRr > 23) {
        tmpRespRate = "30"
    } else if (PrpMonRr > 18) {
        tmpRespRate = "20"
    } else if (PrpMonRr > 13) {
        tmpRespRate = "15"
    } else if (PrpMonRr > 11) {
        tmpRespRate = "12"
    } else if (PrpMonRr > 9) {
        tmpRespRate = "10"
    } else if (PrpMonRr > 7) {
        tmpRespRate = "8"
    } else if (PrpMonRr > 4) {
        tmpRespRate = "6"
    } else {
        tmpRespRate = "4"
    }
    switch (PrpRespWaveStr) {
        case "nl":
            NextRespWave = nl[tmpRespRate];
            break;
        case "hump":
            NextRespWave = hump[tmpRespRate];
            break;
        case "slant":
            NextRespWave = slant[tmpRespRate];
            break;
        default:
            throw "invalid resp waveform: " + PrpRespWaveStr;
    }
}

function UpdateHemoDisplay() {
    var tmpP1Flag;      
    var scaleAbp;
    var scalePap;
    var scaleCo;

    for (var i = 0; i <= 1; i++) {
        HemoCurrentPoint++;
        if (HemoCurrentPoint > CurrentHemoWave.HemoNpoints) {
            CurrentHemoWave = NextHemoWave;
            HemoCurrentPoint = 1;
        }
        HemoSweepPosition++;
        if (HemoSweepPosition > SweepWidth) {
            HemoSweepPosition = 1;
            CoSweep++;
            HemoContext.clearRect(HemoX0 - 2, 1, 4, SpO2Limit2+2);  // x,y,w,h 
			
// My old method--------------------------------------------------------- 
		//	console.log("SweepTime: " + (CaseTimeInSec - SweepStartTime).toFixed(2) + " sec");  
			SweepStartTime = CaseTimeInSec;
// ------------------------------------------------------------------ 

/* James' method -----------------------------------------------------
            const now = performance.now();
            if (window.lastSweepTime) {
                const sweepDuration = (now - window.lastSweepTime) / 1000;
                if (sweepDuration > 1) { // Only log if greater than 1 second
                    console.log("SweepTime: " + sweepDuration.toFixed(2) + " sec");
                }
            }
            window.lastSweepTime = now;
            SweepStartTime = CaseTimeInSec;
--------------------------------------------------------------- */
        }
        scaleAbp = (CurrentState.baseAbpS - CurrentState.baseAbpD) / 40;
        scalePap = (CurrentState.papS - CurrentState.papD) / 10;
        scaleCo = 8 / PddCo; 
        HemoX1 = HemoX0 + HemoSweepPosition - 1;
        HemoX2 = HemoX0 + HemoSweepPosition;
        EcgIIY1 = EcgIIY2;
        EcgV5Y1 = EcgV5Y2;
        var zHt = 0;
        if (HemoZapCount > 0) {
            HemoCprCount = 0;
            if (HemoZapCount < 100) {
                zHt = 0
            } else if (HemoZapCount == 100) {
                zHt = 0
            } else if (HemoZapCount == 101) {
                zHt = -30
            } else if (HemoZapCount < 150) {
                zHt = -30
            } else if (HemoZapCount < 241) {
                zHt = -ZapHt[HemoZapCount - 149]
            }
            HemoZapCount += 1;
            if (HemoZapCount == 241) {
                zHt = 0;
                HemoZapCount = 0;
                HemoCurrentPoint = 1
            }
        }
        var cHt = 0;
        if (HemoCprCount > 0) {
            if (HemoCprCount < 60) {
                cHt = -CprHt[HemoCprCount]
            } else if (HemoCprCount < 120) {
                cHt = -CprHt[HemoCprCount - 59]
            } else if (HemoCprCount < 180) {
                cHt = -CprHt[HemoCprCount - 119]
            } else if (HemoCprCount < 240) {
                cHt = -CprHt[HemoCprCount - 179]
            }
            HemoCprCount += 1;
            if (HemoCprCount == 240) {
                HemoCprCount = 1
            }
        }
        if (HemoZapCount > 100) {
            EcgIIY2 = EcgIIBaseline + zHt;
            EcgV5Y2 = EcgV5Baseline + zHt
        } else {
            EcgIIY2 = EcgIIBaseline + (CurrentHemoWave.hemoPoints[HemoCurrentPoint - 1].EcgIIHt) + cHt + zHt;
            EcgV5Y2 = EcgV5Baseline + (CurrentHemoWave.hemoPoints[HemoCurrentPoint - 1].EcgV5Ht) + cHt + zHt;
        }
        SpO2Y1 = SpO2Y2;
        SpO2Y2 = SpO2Baseline + CurrentState.spSignal * 0.33 * (CurrentHemoWave.hemoPoints[HemoCurrentPoint - 1].SpHt + cHt);

// --- pulse ox beep ----------------------------------------------------------------
        if (CurrentHemoWave.hemoPoints[HemoCurrentPoint - 1].EcgTag >= 2) {  
			// console.log("UserSoundFlag: " + UserSoundFlag);
			if (UserSoundFlag == true) {
				var audio = document.createElement("audio");
				audio.setAttribute("autoplay", "autoplay");
				var source = document.createElement("source");
				source.src = "app/sounds/po" + Math.floor(PrpSpO2) + ".wav";   
				source.type = "audio/wav";
				audio.appendChild(source)
			}
        }  
// ----------------------------------- 

        if (UserMonitorDisplayStr == "nbp") {
            tmpP1Flag = false;                 
            P1Y1 = P1Y2;
            P1Y2 = P1Baseline;
            $("txtP1").innerHTML = "NBP";
            $("divP1single").hide();
            $("divP1sdm").show();
            $("btnGo").show();
            $("txtMin").show();
            $("txtM").show();
            $("pMean").hide();

        } else if (UserMonitorDisplayStr == "abp") {
            tmpP1Flag = true;
            P1Y1 = P1Y2;
            P1Y2 = P1Baseline + scaleAbp * (CurrentHemoWave.hemoPoints[HemoCurrentPoint - 1].AbpHt + cHt);
            $("txtP1").innerHTML = "ABP";
            $("divP1single").hide();
            $("divP1sdm").show();
            $("btnGo").hide();
            $("txtMin").hide();
            $("txtS-D").innerHTML = PddAbpS.toFixed(0) + "/" + PddAbpD.toFixed(0);
            $("pMean").innerHTML = PddAbpM.toFixed(0);
            $("txtM").hide();
            $("pMean").show();

        } else if (UserMonitorDisplayStr == "cvp") {
            tmpP1Flag = true;
            P1Y1 = P1Y2;
            P1Y2 = P1Baseline + (CurrentHemoWave.hemoPoints[HemoCurrentPoint - 1].CvpHt + cHt);
            $("txtP1").innerHTML = "CVP";
            $("divP1sdm").hide();
            $("divP1single").show();
            $("txtP1single").innerHTML = PddCvp.toFixed(0);

        } else if (UserMonitorDisplayStr == "pap") {
            tmpP1Flag = true;
            P1Y1 = P1Y2;
            P1Y2 = P1Baseline + scalePap * (CurrentHemoWave.hemoPoints[HemoCurrentPoint - 1].PapHt + cHt);
            $("txtP1").innerHTML = "PAP";
            $("divP1single").hide();
            $("divP1sdm").show();
            $("btnGo").hide();
            $("txtMin").hide();
            $("txtS-D").innerHTML = PddPapS.toFixed(0) + "/" + PddPapD.toFixed(0);
            $("pMean").innerHTML = PddPapM.toFixed(0);
            $("txtM").hide();
            $("pMean").show();

        } else if (UserMonitorDisplayStr == "pawp") {
            tmpP1Flag = true;
            P1Y1 = P1Y2;
            P1Y2 = P1Baseline + (CurrentHemoWave.hemoPoints[HemoCurrentPoint - 1].CvpHt + cHt);
            $("txtP1").innerHTML = "PAWP";
            $("divP1sdm").hide();
            $("divP1single").show();
            $("txtP1single").innerHTML = PddPapW.toFixed(0);

        } else if (UserMonitorDisplayStr == "co") {
            $("txtP1").innerHTML = "Cardiac Out";
            $("divP1sdm").hide();
            $("divP1single").show();

            if (CoSweep > 1) {
                tmpP1Flag = true;
                CoCurrentPoint++;
                P1Y1 = P1Y2;
                P1Y2 = P1Baseline + 20 + scaleCo * CoHt[CoCurrentPoint]; 
                if (CoCurrentPoint > SweepWidth) {
                    CoCurrentPoint = 0
                }
            } else {
                tmpP1Flag = false;
            }
            if (CoSweep > 2) {
                $("txtP1single").innerHTML = PddCo.toFixed(1);
            }
        }

        if (EcgIIY2 < EcgIILimit1) {
            EcgIIY2 = EcgIILimit1
        } else if (EcgIIY2 > EcgIILimit2) {
            EcgIIY2 = EcgIILimit2
        }
        if (EcgV5Y2 < EcgV5Limit1) {
            EcgV5Y2 = EcgV5Limit1
        } else if (EcgV5Y2 > EcgV5Limit2) {
            EcgV5Y2 = EcgV5Limit2
        }
        if (P1Y2 < P1Limit1) {
            P1Y2 = P1Limit1
        } else if (P1Y2 > P1Limit2) {
            P1Y2 = P1Limit2
        }
        if (SpO2Y2 < SpO2Limit1) {
            SpO2Y2 = SpO2Limit1
        } else if (SpO2Y2 > SpO2Limit2) {
            SpO2Y2 = SpO2Limit2
        }

        // Batch drawing for better performance
        HemoContext.clearRect(HemoX1, 1, 3, SpO2Limit2+2);  
        
        // Draw ECG lines
        HemoContext.strokeStyle = "#91e197";
        HemoContext.beginPath();
        HemoContext.moveTo(HemoX1, EcgIIY1);
        HemoContext.lineTo(HemoX2, EcgIIY2);
        HemoContext.stroke();
        
        HemoContext.beginPath();
        HemoContext.moveTo(HemoX1, EcgV5Y1);
        HemoContext.lineTo(HemoX2, EcgV5Y2);
        HemoContext.stroke();

        // Draw P1 line if needed
        if (tmpP1Flag) {
            if (UserMonitorDisplayStr == "abp") {
                HemoContext.strokeStyle = "#FF3333";
            } else {
                HemoContext.strokeStyle = "#91e197";
            }
            HemoContext.beginPath();
            HemoContext.moveTo(HemoX1, P1Y1);
            HemoContext.lineTo(HemoX2, P1Y2);
            HemoContext.stroke();
        }
        
        // Draw SpO2 line
        HemoContext.strokeStyle = "#33AAEE";
        HemoContext.beginPath();
        HemoContext.moveTo(HemoX1, SpO2Y1);
        HemoContext.lineTo(HemoX2, SpO2Y2);
        HemoContext.stroke();
    }
}

function UpdateHemoNumberDisplay() {
    $('btnTime').textContent = MakeTimeStr();
    $('txtScore').textContent = "Score: " + Score + " / " + CurrentCase.possiblePoints;
    $("txtHr").innerHTML = PddHr.toFixed(0);
    $("txtII").innerHTML = "II " + CurrentState.stSegII.toFixed(1);
    $("txtV5").innerHTML = "V5 " + CurrentState.stSegV5.toFixed(1);

    /* $("btnPlaySound").show();  was removed */

    if (UserMonitorDisplayStr == "nbp") {
        if (NbpTimer < 2) {
            NbpTimer = UserNbpInterval * 60;
            UserNbpModeStr = "inflating";
            $("txtMin").innerHTML = "";
            NbpM = 0
        } else {
            NbpTimer = NbpTimer - 1;
            if (NbpTimer > 60) {
                $("txtMin").innerHTML = Math.round(NbpTimer / 60) + " min";
            } else {
                $("txtMin").innerHTML = NbpTimer + " sec";
            }
        }
        if (UserNbpModeStr == "inflating") {
            $("txtMin").innerHTML = "";
            NbpM = NbpM - 16;
            $("txtMin").innerHTML = "";
            $("txtM").innerHTML = "Cuff  " + -NbpM;
            if (-NbpM > Math.max(100, PddAbpS + 20)) {
                UserNbpModeStr = "deflating"
            }
        } else if (UserNbpModeStr == "deflating") {
            $("txtMin").innerHTML = "";
            NbpM = NbpM + 8;
            if (-NbpM < Math.max(24, PddAbpD - 20)) {
                UserNbpModeStr = "waiting";
                NbpS = PddAbpS + parseInt((8 * Math.random() - 4));
                NbpD = PddAbpD + parseInt((4 * Math.random() - 2));
                NbpM = PddAbpM + parseInt((4 * Math.random() - 2));
                if (NbpS < 30) {
                    $("txtS-D").innerHTML = "====";
                    $("txtM").innerHTML = ""
                } else {
                    $("txtS-D").innerHTML = NbpS.toFixed(0) + "/" + NbpD.toFixed(0);  
                    $("txtM").innerHTML = NbpM.toFixed(0)
                }
            } else {
                $("txtM").innerHTML = "Cuff  " + -NbpM
            }
        }
    } 
	
    if (CurrentState.spSignal > 0) {
        $("txtSpo2Sat").innerHTML = PrpSpO2.toFixed(0);
        $("txtSpo2Hr").innerHTML = "Rate " + PddSpRate.toFixed(0);
        if (CurrentState.spSignal == 3) {
            $("txtSpo2signal").innerHTML = "***"
        } else if (CurrentState.spSignal == 2) {
            $("txtSpo2signal").innerHTML = "**"
        } else if (CurrentState.spSignal == 1) {
            $("txtSpo2signal").innerHTML = "*"
        }
    } else {
        $("txtSpo2Sat").innerHTML = "---";
        $("txtSpo2Hr").innerHTML = "Rate ---";
        $("txtSpo2signal").innerHTML = "---"
    }
    $("txtTemperature").show();
    $("txtTemperature").innerHTML = CurrentState.temperature.toFixed(1)
}

function UpdateRespNumberDisplay() {
    $("tableAgm").show();
    $("txtEtCO2").innerHTML = PrpEtCO2.toFixed(0);
    $("txtI-O2").innerHTML = PrpFiO2.toFixed(0);
    $("txtI-Vap").innerHTML = PrpFiAgt.toFixed(1);
    $("txtE-O2").innerHTML = PrpFeO2.toFixed(0);
    $("txtE-Vap").innerHTML = PrpFeAgt.toFixed(1);
    $("lblPip").show();
    $("lblPeep").show();
    $("txtPip").innerHTML = PrpMonPip.toFixed(0);
    $("txtPeep").innerHTML = PrpMonPeep.toFixed(0);
    $("lblTv").show();
    $("lblRr").show();
    $("txtTv").innerHTML = PrpMonTv.toFixed(0);
    $("txtRr").innerHTML = PrpMonRr.toFixed(0)
}

function UpdateRespDisplayAndImage() {
    var scaleCO2;
    var scaleAp;
    for (var i = 0; i <= 1; i++) {
        if (UserConnectedThisCaseFlag) {
            RespCurrentPoint++
        }
        if (RespCurrentPoint > CurrentRespWave.RespNpoints) {
            CurrentRespWave = NextRespWave;
            RespCurrentPoint = 1
        }
        RespSweepPosition++;
        if (RespSweepPosition > SweepWidth) {
            RespSweepPosition = 1;
            HemoContext.clearRect(HemoX0 - 2, CO2Limit1-2, 4, (APLimit2 - CO2Limit1 + 4));  
        }
        scaleCO2 = 1.2 * (PrpEtCO2 / 30);   /* was 1.3 */
        RespX1 = HemoX0 + RespSweepPosition - 1;
        RespX2 = HemoX0 + RespSweepPosition;
        CO2Y1 = CO2Y2;
        CO2Y2 = CO2Baseline + scaleCO2 * (CurrentRespWave.respPoints[RespCurrentPoint - 1].Co2Ht);
        APY1 = APY2;
        if ((UserAirwayStr == "none") || (UserConnectedFlag == false)) {
            RespCurrentPoint = 1;
            APY2 = APBaseline;
            CO2Y2 = CO2Baseline;
            $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_spontFlat.jpg"
        } else if (UserBreathModeStr == "spontaneous") {
            scaleAp = PrpMonPip / 2;
            if ((PrpMonRr == 0) || (PrpMonTv == 0)) {
                CO2Y2 = CO2Baseline;
                APY2 = APBaseline - scaleAp * PrpMonPeep;
                $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_spontExp.jpg"
            } else {
                APY2 = APBaseline + scaleAp * (CurrentRespWave.respPoints[RespCurrentPoint - 1].PspontHt - PrpMonPeep);
                if ((CurrentRespWave.respPoints[RespCurrentPoint - 1].RespTag == 1) || (CurrentRespWave.respPoints[RespCurrentPoint - 1].RespTag == 3)) {
                    $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_spontInsp.jpg"
                } else {
                    $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_spontExp.jpg"
                }
            }
        } else if (UserBreathModeStr == "bagControl") {
            scaleAp = PrpMonPip / 28;
            if ((PrpMonRr == 0) || (PrpMonTv == 0)) {
                CO2Y2 = CO2Baseline;
                APY2 = APBaseline - scaleAp * PrpMonPeep;
                $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_bagExp.jpg"
            } else {
                APY2 = APBaseline + scaleAp * (CurrentRespWave.respPoints[RespCurrentPoint - 1].PbagHt - PrpMonPeep);
                if ((CurrentRespWave.respPoints[RespCurrentPoint - 1].RespTag == 2) || (CurrentRespWave.respPoints[RespCurrentPoint - 1].RespTag == 3)) {
                    $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_bagInsp.jpg"
                } else {
                    $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_bagExp.jpg"
                }
            }
        } else if (UserBreathModeStr == "ventilator") {
            scaleAp = PrpMonPip / 28;
            if ((PrpMonRr == 0) || (PrpMonTv == 0)) {
                CO2Y2 = CO2Baseline;
                APY2 = APBaseline - scaleAp * PrpMonPeep;
                $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_vent0.jpg"
            } else {
                APY2 = APBaseline + scaleAp * (CurrentRespWave.respPoints[RespCurrentPoint - 1].PventHt - PrpMonPeep);
                if ((CurrentRespWave.respPoints[RespCurrentPoint - 1].RespTag == 2) || (CurrentRespWave.respPoints[RespCurrentPoint - 1].RespTag == 3)) {
                    $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_vent2.jpg"
                } else {
                    $("imgBagVent").src = "app/images/" + UserImageStr + "/circle_vent0.jpg"
                }
            }
        }
        if (CO2Y2 < CO2Limit1) {
            CO2Y2 = CO2Limit1
        } else if (CO2Y2 > CO2Limit2) {
            CO2Y2 = CO2Limit2
        }
        if (APY2 < APLimit1) {
            APY2 = APLimit1
        } else if (APY2 > APLimit2) {
            APY2 = APLimit2
        }
        HemoContext.strokeStyle = "#91e197";
        HemoContext.clearRect(RespX1, CO2Limit1-2, 3, (CO2Limit2 - CO2Limit1 + 4));  
        HemoContext.beginPath();
        HemoContext.moveTo(RespX1, CO2Y1);
        HemoContext.lineTo(RespX2, CO2Y2);
        HemoContext.stroke();
        HemoContext.clearRect(RespX1, APLimit1-2, 3, (APLimit2 - APLimit1 + 4));    
        HemoContext.beginPath();                               
        HemoContext.moveTo(RespX1, APY1);                      
        HemoContext.lineTo(RespX2, APY2);                      
        HemoContext.stroke();                                  
    }
}

/* James method ------------------------------------------------
function clamp(value, min, max) {
    return Math.max(min, Math.min(value, max));
}

// Performance monitoring function
function monitorPerformance() {
    frameCount++;
    const now = performance.now();
    const elapsed = now - lastPerformanceCheck;
    
    if (elapsed >= 1000) {
        const fps = (frameCount * 1000) / elapsed;
        if (fps < 30) {
            console.log('Performance warning: ' + fps.toFixed(1) + ' FPS');
// Note: console log never shows 'Performance warning' 			
        }
        frameCount = 0;
        lastPerformanceCheck = now;
    }
    
    requestAnimationFrame(monitorPerformance);
}

// Memory cleanup function
function cleanupWaveformMemory() {
	console.log("In cleanUpWaveformMemory");
// Note: console log never shows 'In cleanUpWaveformMemory' 			
    if (HemoContext) {
        HemoContext.clearRect(0, 0, HemoContext.canvas.width, HemoContext.canvas.height);
		console.log("cleanedUp");
// Note: console log never shows 'cleanedUp' 			
    }
    
    // Reset all counters and positions
    HemoCurrentPoint = 1;
    HemoSweepPosition = 1;
    CoSweep = 0;
    frameCount = 0;
    
    // Clear timeouts
    if (typeof MyTimeout !== 'undefined') {
        clearTimeout(MyTimeout);
    }
}

// Start performance monitoring
monitorPerformance();

// Add cleanup event listeners
window.addEventListener('beforeunload', cleanupWaveformMemory);
---------------------------------------------------------------- */