import m from 'mithril';
import { NumericValue } from 'components/widgets/numeric-value';
import { Pipe } from 'utilities/pipe';

require('./gauge.css');

export class Gauge extends NumericValue {
	protected bars: SVGGeometryElement[] = [];
	
	private displayedValue = 0.0;
	private targetValue = 0.0;
	private valueChangeRate = 0.0;
	private litBars = 0;
	
	private animating = false;
	private shuttingDown = false;
	
	private lastGaugeUpdate = 0.0;
	private lastAnimationTick = 0.0;
	
	constructor(vnode: any) {
		super(vnode);
	}
	
	onbeforeremove(vnode: m.Vnode<{}, {}>) {
		this.shuttingDown = true;
		super.onbeforeremove(vnode);
	}
	
	protected mainClassName() { return 'gauge'; }
	
	protected graphicalRepresentation(vnode: m.Vnode<{}, {}>): m.Children {
		return <svg></svg>;
	}
	
	protected onValueChange(value: number) {
		let now = Date.now() / 1000.0;
		
		super.onValueChange(value);
		
		let ratio = value / (this.maxValue || 1.0);
		let numBars = this.bars.length;
		let threshold = 0.33 / numBars;
		
		if(ratio > this.targetValue + threshold || ratio < this.targetValue - threshold) {
			let animationTime = this.lastGaugeUpdate == 0.0 ? 0.0001 : Math.max(0.0001, Math.min(0.75, now - this.lastGaugeUpdate));
			
			this.valueChangeRate = (ratio - this.displayedValue) / animationTime;
			this.targetValue = ratio;
			
			this.enableAnimation();
		}
		
		this.lastGaugeUpdate = now;
	}
	
	private enableAnimation() {
		if(this.animating) return;
		this.animating = true;
		this.lastAnimationTick = Date.now() / 1000.0 - 0.016;
		requestAnimationFrame(() => this.animate());
	}
	
	private animate() {
		if(this.shuttingDown) return;
		
		if(this.valueChangeRate == 0.0) {
			this.animating = false;
			return;
		}
		
		requestAnimationFrame(() => this.animate());
		
		let now = Date.now() / 1000.0;
		if(now > this.lastAnimationTick + 0.75) {
			this.displayedValue = this.targetValue;
			this.valueChangeRate = 0.0;
		}
		else {
			let dt = now - this.lastAnimationTick;
			if(dt < 0.04) return; // limit framerate to save battery
			
			this.displayedValue += this.valueChangeRate * dt;
			if((this.displayedValue > this.targetValue) == (this.valueChangeRate > 0.0)) {
				this.displayedValue = this.targetValue;
				this.valueChangeRate = 0.0;
			}
		}
		
		this.lastAnimationTick = now;
		
		let numBars = this.bars.length;
		let litBars = Math.round(this.displayedValue * numBars);
		if(litBars == this.litBars)
			return;
		this.litBars = litBars;
		
		for(let barIdx = 0; barIdx < numBars; ++barIdx) {
			let bar = this.bars[barIdx];
			let isLit = barIdx < litBars;
			if(bar.classList.contains('lit') != isLit)
				bar.classList.toggle('lit', isLit);
		}
	}
}
