'use strict';
angular.module('beamng.apps')
.directive('sgrUc10', ['$timeout', function ($timeout) {
  return {
    template: `
      <div style="width:100%; height:100%; position:relative;">
        <object style="width:100%; height:100%;" type="image/svg+xml" data="/ui/modules/apps/sgruc10/sgruc10.svg"></object>
      </div>
    `,
    replace: true,
    restrict: 'EA',
    scope: true,
    link: function (scope, element) {
      let svg;
      const updateInterval = 50;
      let lastUpdate = { rpm:0, mph:0, volts:0, fuel:0, boost:0, water:0, oil:0, oilp:0, afr:0, transTemp:0 };
      let prevIgn = 0;
      let loadingTimeout = null;
      let maxRPM = 10200;
      const gaugeMax = 10200;
      const speedoMax = 240;
      const streams = ['engineInfo','electrics','engineThermalData','sensors'];
      StreamsManager.add(streams);
      let inSplash = false, sweeping = false, sweepDone = false;
      let splashTimeouts = [];
      let currentIgn = 0;

      function clearSplashTimeouts() {
        splashTimeouts.forEach(t => $timeout.cancel(t));
        splashTimeouts = [];
      }

      let current = { oilPressure: 65, voltage: 14.7 };

      function smooth(val, cur, f) { return cur + (val - cur) * f; }

      function updateSimulatedVolts(rpm, ignitionLevel) {
        let targetVoltage;
        if (ignitionLevel === 3) {
          targetVoltage = 11.9;
        } else {
          if (rpm < 0) rpm = 0;
          if (rpm < 1000) {
            const factor = rpm / 1000;
            targetVoltage = 13.2 + (14.7 - 13.2) * factor;
          } else if (rpm < 5000) {
            const factor = (rpm - 1000) / 4000;
            targetVoltage = 14.7 + (15.0 - 14.7) * factor;
            if (targetVoltage > 15.0) targetVoltage = 15.0;
          } else {
            targetVoltage = 15.0;
          }
        }
        current.voltage = smooth(targetVoltage, current.voltage, 0.05);
        return current.voltage;
      }

      function calculateSimulatedOilPressure(tempC, rpm) {
        let targetPressure;
        const oilMass = 5, gForce = 1, starvingXY = 0, starvingZ = 0, maxSafeG = 9999;
        if (oilMass < 1.0 || rpm <= 0 || starvingXY > 0 || starvingZ > 0 || gForce > maxSafeG) {
          targetPressure = 0;
        } else {
          const tempLow = 15.6;
          const tempHigh = 165.6;
          let basePressure;
          if (tempC <= tempLow) {
            basePressure = 60;
          } else if (tempC >= tempHigh) {
            basePressure = 0;
          } else {
            basePressure = 60 * (1 - (tempC - tempLow) / (tempHigh - tempLow));
          }
          let rpmFactor = Math.min(rpm / 3000, 1);
          let pressure = basePressure + (25 * rpmFactor);
          let gForceReduction = Math.min(gForce / 2, 15);
          pressure -= gForceReduction;
          targetPressure = Math.max(0, Math.min(pressure, 200));
        }
        current.oilPressure = smooth(targetPressure, current.oilPressure, 0.02);
        return current.oilPressure;
      }

      function calculateSimulatedTransTemp(oilF) {
        if (oilF <= 32) return 32;
        if (oilF >= 250) return oilF;
        if (oilF <= 190) {
          return 32 + ((oilF - 32) / (190 - 32)) * (120 - 32);
        } else {
          return 120 + ((oilF - 190) / (250 - 190)) * (oilF - 120);
        }
      }

      function injectFont() {
        const defs = svg.querySelector('defs') || document.createElementNS(svg.namespaceURI,'defs');
        const styleEl = document.createElement('style');
        styleEl.textContent = `
          @font-face {
            font-family: 'DS-Digital';
            src: url('/ui/common/Arian LT Heavy.ttf') format('truetype');
          }
          text { font-family: 'DS-Digital', sans-serif; }
        `;
        defs.appendChild(styleEl);
        const root = svg.querySelector('svg') || svg;
        if (!svg.querySelector('defs')) root.insertBefore(defs, root.firstChild);
      }

      function setBar(group, warning) {
        for (let i=0; i<10; i++) {
          const bar = svg.getElementById(group+'Bar'+i);
          if (bar) bar.setAttribute('fill', warning ? '#ff0000' : '#FFDE21');
        }
      }
      function setTxt(id, warning) {
        const t = svg.getElementById(id);
        if (t) t.setAttribute('fill', warning ? '#ff0000' : '#FFDE21');
      }

      function getRandomAFR(base, variation) {
        return (base + (Math.random() * variation - variation / 2)).toFixed(2);
      }
      function updateAFR(tps, rpm, speed) {
        const left = svg.getElementById('afrlValue');
        const right = svg.getElementById('afrrValue');
        if (!left || !right) return;
        let afrL, afrR;
        if (rpm < 200) {
          afrL = afrR = '0';
        } else if (tps >= 70) {
          afrL = getRandomAFR(12.8,0.1);
          afrR = getRandomAFR(12.8,0.1);
        } else if (tps < 70 && speed >= 10) {
          afrL = getRandomAFR(11.2,0.1);
          afrR = getRandomAFR(11.2,0.1);
        } else {
          afrL = getRandomAFR(14.5,0.1);
          afrR = getRandomAFR(14.5,0.1);
        }
        left.textContent = afrL;
        right.textContent = afrR;
        setBar('afrl', afrL > 16.5); setBar('afrr', afrR > 16.5);
        setTxt('afrlValue', afrL > 16.5); setTxt('afrrValue', afrR > 16.5);
      }

      function updateBars(group, level) {
        for (let i=0; i<10; i++) {
          const bar = svg.getElementById(group+'Bar'+(9-i));
          if (bar) bar.style.opacity = i<level?1:0;
        }
      }

      function updateRedSweep() {
        const start = 98.5, span = 278, R = 420;
        const toRad = d => (d - 90) * Math.PI / 180;
        const pt = (r, d) => ({
          x: 925 + r * Math.cos(toRad(d)),
          y: 660 + r * Math.sin(toRad(d))
        });
        const frac = Math.max(0, (gaugeMax - maxRPM) / gaugeMax);
        const endAng = start - span * frac;
        const p1 = pt(R, start), p2 = pt(R, endAng);
        const largeArc = span * frac > 180 ? 1 : 0;
        const sweepPath = svg.getElementById('rpmSweep');
        if (sweepPath) {
          sweepPath.setAttribute(
            'd',
            `M ${p1.x} ${p1.y} A ${R} ${R} 0 ${largeArc} 0 ${p2.x} ${p2.y}`
          );
        }
      }

      function updateNeedle(rpm) {
        const start = 98.5, span = 278, NEED = 224;
        const frac = Math.max(0, Math.min(1, rpm / gaugeMax));
        svg.getElementById('rpmNeedle').setAttribute(
          'transform',
          `rotate(${start + span * frac - NEED} 925 660)`
        );
      }

      function updateSpeedoNeedle(mph) {
        const speedStart = 9.5, speedSpan = 274, speedNeed = 135;
        const speedFrac = Math.max(0, Math.min(1, mph / speedoMax));
        svg.getElementById('mphNeedle').setAttribute(
          'transform',
          `rotate(${(speedStart + speedSpan * speedFrac) - speedNeed} 2250 660)`
        );
      }

      function updateGear(gear) {
        const gearElement = svg.getElementById('gearValue');
        if (gearElement) {
          if (gear === -1) {
            gearElement.textContent = 'R';
          } else if (gear === 0 || gear === 'N') {
            gearElement.textContent = 'N';
          } else {
            gearElement.textContent = gear.toString();
          }
        }
      }

      function fadeElem(elem, toOpacity, duration = 1000) {
        if (!elem) return;
        elem.style.transition = `opacity ${duration}ms`;
        elem.style.opacity = toOpacity;
      }

      function doSplashAndSweep() {
        if (!svg) return;
        if (inSplash || sweeping) return;
        inSplash = true;
        sweepDone = false;
        fadeElem(svg.getElementById('blackoutRect'), 1, 1);
        fadeElem(svg.getElementById('splashLogo'), 1, 1);
        splashTimeouts.push($timeout(() => {
          fadeElem(svg.getElementById('splashLogo'), 0, 1000);
        }, 1000));
        splashTimeouts.push($timeout(() => {
          fadeElem(svg.getElementById('blackoutRect'), 0, 1000);
          startSweep();
        }, 2000));
        splashTimeouts.push($timeout(() => {
          inSplash = false;
          sweeping = false;
          sweepDone = true;
        }, 6000));
      }

      function startSweep() {
        if (!svg) return;
        sweeping = true;
        const total = 4000, half = 2000, t0 = performance.now();
        function animateSweep(ts) {
          if (!sweeping) return;
          const e = ts - t0;
          if (e >= total) {
            sweeping = false;
            sweepDone = true;
            return;
          }
          let f;
          if (e < half) f = e / half;
          else f = 1 - (e - half) / half;
          updateSweepGauges(f);
          requestAnimationFrame(animateSweep);
        }
        requestAnimationFrame(animateSweep);
      }

      function updateSweepGauges(f) {
        let lvl = Math.floor(f * 10);
        let groups = ['afrl','trans','water','oil','afrr','fuel','volts','oilp'];
        groups.forEach(g => {
          for (let i = 0; i < 10; i++) {
            const b = svg.getElementById(`${g}Bar${9 - i}`);
            if (!b) continue;
            b.style.opacity = i < lvl ? 1 : 0;
            b.setAttribute('fill', '#FFDE21');
          }
        });
        const rpm = f * gaugeMax;
        const frac = Math.min(1, rpm / gaugeMax);
        updateNeedle(rpm);
        updateSpeedoNeedle(f * speedoMax);
        const START = 98.5, SPAN = 278, R = 420, NEED_ORIG = 224;
        const toRad = d => (d - 90) * Math.PI / 180;
        const pt = (r, d) => ({ x: 925 + r * Math.cos(toRad(d)), y: 660 + r * Math.sin(toRad(d)) });
        let ringFrac = 1 - f;
        const e = START - SPAN * ringFrac, p1 = pt(R, START), p2 = pt(R, e), lg = SPAN * ringFrac > 180 ? 1 : 0;
        const sweepPath = svg.getElementById('rpmSweep');
        if (sweepPath) {
          sweepPath.setAttribute('d', `M ${p1.x} ${p1.y} A ${R} ${R} 0 ${lg} 0 ${p2.x} ${p2.y}`);
        }
      }

      function handleIgn(ign) {
        if (!svg) return;
        const blackout = svg.getElementById('blackoutRect');
        const splash = svg.getElementById('splashLogo');
        if (ign === 0) {
          clearSplashTimeouts();
          inSplash = sweeping = false;
          sweepDone = false;
          fadeElem(blackout, 1, 200);
          fadeElem(splash, 0, 200);
        } else if (prevIgn === 0 && (ign === 1 || ign === 3)) {
          clearSplashTimeouts();
          doSplashAndSweep();
        }
        prevIgn = ign;
      }

      function setIcon(id, show) {
        if (!svg) return;
        const icon = svg.getElementById(id);
        if (icon) icon.style.opacity = show ? 1 : 0;
      }

      element[0].querySelector('object').addEventListener('load', ()=>{
        $timeout(()=>{
          svg = element[0].querySelector('object').contentDocument;
          if (!svg) return;
          injectFont();
          const blackout = svg.getElementById('blackoutRect');
          const splash = svg.getElementById('splashLogo');
          let lastIgn = parseInt(localStorage.getItem('sgrUc10_lastIgn') || '0', 10);
          if (!isNaN(lastIgn)) {
            if (lastIgn === 0) {
              inSplash = sweeping = false;
              sweepDone = false;
              prevIgn = 0;
              if (blackout) { blackout.style.opacity = 1; blackout.style.transition = 'opacity 0s'; }
              if (splash) { splash.style.opacity = 0; splash.style.transition = 'opacity 0s'; }
            } else if (lastIgn === 1 || lastIgn === 2 || lastIgn === 3) {
              inSplash = false;
              sweeping = false;
              sweepDone = true;
              prevIgn = lastIgn;
              if (blackout) { blackout.style.opacity = 0; blackout.style.transition = 'opacity 0s'; }
              if (splash) { splash.style.opacity = 0; splash.style.transition = 'opacity 0s'; }
            }
          }
        },500);
      });

      scope.$on('streamsUpdate',(evt,data)=>{
        if(!svg||!data.engineInfo||!data.electrics) return;
        const now=Date.now();
        if (data.electrics.maxrpm) {
          maxRPM = data.electrics.maxrpm;
        } else {
          maxRPM = 8000;
        }
        updateRedSweep();
        const ign = data.electrics.ignitionLevel || 0;
        currentIgn = ign;
        localStorage.setItem('sgrUc10_lastIgn', ign);
        if (svg && (typeof prevIgn === "undefined" || prevIgn === null)) {
          prevIgn = ign;
        }
        handleIgn(ign);
        if (inSplash || sweeping) return;

        const thermal = data.engineThermalData || {};
        const coolantTempC = (typeof thermal.coolantTemperature === "number") ? thermal.coolantTemperature : 90;
        const oilTempC = (typeof thermal.oilTemperature === "number") ? thermal.oilTemperature : 90;

        if(now-lastUpdate.rpm>=updateInterval) {
          lastUpdate.rpm=now;
          const rpm = data.engineInfo[4]||0;
          updateNeedle(rpm);
          svg.getElementById('rpmValue').textContent=Math.round(rpm);
        }
        if (now - lastUpdate.mph >= updateInterval) {
          lastUpdate.mph = now;
          const wheelspeed = data.electrics.wheelspeed || 0;
          const mphRaw = wheelspeed * 2.23694;
          const mph = Math.round(mphRaw);
          updateSpeedoNeedle(mphRaw);
          svg.getElementById('mphValue').textContent = mph;
        }
        const gear = (data.electrics.gear !== undefined) ? data.electrics.gear : 'N';
        updateGear(gear);
        if(now-lastUpdate.volts>=updateInterval) {
          lastUpdate.volts=now;
          const rpm = data.engineInfo[4]||0;
          const volts = updateSimulatedVolts(rpm, ign);
          svg.getElementById('voltsValue').textContent = volts.toFixed(1);
          const vLvl = Math.floor((Math.max(12,Math.min(18,volts))-12)/((18-12)/10));
          updateBars('volts', vLvl);
          setBar('volts', volts < 13);
          setTxt('voltsValue', volts < 13);
        }
        if(now-lastUpdate.fuel>=updateInterval) {
          lastUpdate.fuel=now;
          const fuelPct = Math.round((data.engineInfo[11]/data.engineInfo[12]||0)*100);
          svg.getElementById('fuelValue').textContent=fuelPct;
          const fLvl = Math.floor(Math.max(0,Math.min(100,fuelPct))/(100/10));
          updateBars('fuel', fLvl);
          setBar('fuel', fuelPct < 20);
          setTxt('fuelValue', fuelPct < 20);
        }
        if(now-lastUpdate.boost>=updateInterval) {
          lastUpdate.boost=now;
          svg.getElementById('psiValue').textContent=Math.round(data.electrics.boost||0);
        }
        if(now-lastUpdate.water>=updateInterval) {
          lastUpdate.water=now;
          const wF = Math.round((coolantTempC*9/5+32)||0);
          svg.getElementById('waterValue').textContent=wF;
          const wLvl = Math.floor((Math.max(100,Math.min(266,wF))-100)/((266-100)/10));
          updateBars('water', wLvl);
          setBar('water', wF > 240);
          setTxt('waterValue', wF > 240);
        }
        let oilF = 0;
        if(now-lastUpdate.oil>=updateInterval) {
          lastUpdate.oil=now;
          oilF = oilTempC * 9/5 + 32;
          const oF = Math.round(oilF);
          svg.getElementById('oiltempValue').textContent=oF;
          const oLvl = Math.floor((Math.max(100,Math.min(266,oF))-100)/((266-100)/10));
          updateBars('oil', oLvl);
          setBar('oil', oF > 240);
          setTxt('oiltempValue', oF > 240);
        } else {
          oilF = oilTempC * 9/5 + 32;
        }
        if(now-lastUpdate.oilp>=updateInterval) {
          lastUpdate.oilp=now;
          const rpm = data.engineInfo[4] || 0;
          const op = Math.round(calculateSimulatedOilPressure(oilTempC, rpm));
          svg.getElementById('oilpressValue').textContent = op;
          const pLvl = Math.floor(Math.max(0,Math.min(100,op))/(100/10));
          updateBars('oilp', pLvl);
          setBar('oilp', op < 20);
          setTxt('oilpressValue', op < 20);
        }
        if(now-lastUpdate.afr>=updateInterval) {
          lastUpdate.afr=now;
          const tps = (data.electrics.throttle||0)*100;
          const spd = data.electrics.wheelspeed||0;
          updateAFR(tps, data.engineInfo[4]||0, spd);
          let afrL = parseFloat(svg.getElementById('afrlValue').textContent)||0;
          let afrR = parseFloat(svg.getElementById('afrrValue').textContent)||0;
          afrL = Math.max(10, Math.min(16, afrL));
          afrR = Math.max(10, Math.min(16, afrR));
          let aLvlL = Math.floor(((afrL - 10) / (16 - 10)) * 10);
          let aLvlR = Math.floor(((afrR - 10) / (16 - 10)) * 10);
          updateBars('afrl', aLvlL);
          updateBars('afrr', aLvlR);
        }
        if(now-lastUpdate.transTemp>=updateInterval) {
          lastUpdate.transTemp=now;
          const oilF2 = oilTempC * 9/5 + 32;
          const transF = Math.round(calculateSimulatedTransTemp(oilF2));
          svg.getElementById('transValue').textContent = transF;
          const tLvl = Math.floor((Math.max(100, Math.min(266, transF)) - 100) / ((266 - 100) / 10));
          updateBars('trans', tLvl);
          setBar('trans', transF > 240);
          setTxt('transValue', transF > 240);
        }

        setIcon('icon-left',  (data.electrics.signal_L || 0) > 0);
        setIcon('icon-right', (data.electrics.signal_R || 0) > 0);

        if (ign >= 1) {
          setIcon('icon-park',    (data.electrics.parkingbrake || 0) > 0);
          setIcon('icon-light',   (data.electrics.highbeam || 0) > 0);
          const oilPressure = parseFloat(svg.getElementById('oilpressValue')?.textContent) || 0;
          setIcon('icon-lowoil', oilPressure < 20);
          const volts = parseFloat(svg.getElementById('voltsValue')?.textContent) || 0;
          setIcon('icon-lowbat', volts < 13);
          const coolantF = parseFloat(svg.getElementById('waterValue')?.textContent) || 0;
          setIcon('icon-cel', coolantF > 240);
        } else {
          setIcon('icon-park', false);
          setIcon('icon-light', false);
          setIcon('icon-lowoil', false);
          setIcon('icon-lowbat', false);
          setIcon('icon-cel', false);
        }
      });

      scope.$on('$destroy',()=>{
        StreamsManager.remove(streams);
        if(loadingTimeout) $timeout.cancel(loadingTimeout);
        clearSplashTimeouts();
      });
    }
  };
}]);
