# EFI-890R PFD by Benedikt Wolf (D-ECHO) based on A320 canvas avionics by Joshua Davidson

var EFI_890R_PFD_main = nil;
var EFI_890R_PFD_start = nil;
var EFI_890R_PFD_display = nil;

# Instrument Properties
var ias_kts		=	props.globals.getNode("instrumentation/airspeed-indicator/indicated-speed-kt", 1);
var no_wow		=	props.globals.getNode("fdm/jsbsim/fcs/gear-no-wow");
var gs		=	props.globals.getNode("velocities/groundspeed-kt");
var sat		=	props.globals.getNode("environment/temperature-degc");
var pitch		=	props.globals.getNode("orientation/pitch-deg");
var roll		=	props.globals.getNode("orientation/roll-deg");
var turn		=	props.globals.getNode("instrumentation/turn-indicator/indicated-turn-rate");
var radaralt	=	props.globals.getNode("instrumentation/radar-altimeter/radar-altitude-ft");
var heading		=	props.globals.getNode("orientation/heading-deg");
var vs_fpm		=	props.globals.getNode("instrumentation/vertical-speed-indicator/indicated-speed-fpm");
var vs_needle	=	base.getNode("vertical-speed-needle");
var alt		=	props.globals.getNode("instrumentation/altimeter/indicated-altitude-ft");
var qnh		=	props.globals.getNode("instrumentation/altimeter/setting-hpa");
var fd_pitch	=	props.globals.getNode("autopilot/fd/pitch-command-deg");
var fd_roll		=	props.globals.getNode("autopilot/fd/roll-command-deg");
var fd_active	=	props.globals.getNode("autopilot/internal/flight-director-active");

var minimums	=	props.globals.initNode("instrumentation/efi-890r[0]/minimums", 0, "INT"); # 0 = OFF, 1 = RA, 2 = DA
var ra_min		=	props.globals.initNode("instrumentation/efi-890r[0]/radio-alt-minimums", 200, "INT");
var decision_alt	=	props.globals.initNode("instrumentation/efi-890r[0]/decision-alt", 1000, "INT");
var alt_presel	=	props.globals.initNode("autopilot/settings/target-altitude-ft", 0.0, "DOUBLE");

var ap_on    = props.globals.getNode("autopilot/internal/autopilot-active");
var yd_on    = props.globals.getNode("autopilot/internal/yaw-damper-active");
aircraft.light.new( "/instrumentation/efi-890r/ap-fds/ap-flash", [0.2, 0.2] );
aircraft.light.new( "/instrumentation/efi-890r/ap-fds/yd-flash", [0.2, 0.2] );
var ap_flash = [
	props.globals.getNode("/instrumentation/efi-890r/ap-fds/ap-flash/state"),
	props.globals.getNode("/instrumentation/efi-890r/ap-fds/ap-flash/enabled"),
];
var yd_flash = [
	props.globals.getNode("/instrumentation/efi-890r/ap-fds/yd-flash/state"),
	props.globals.getNode("/instrumentation/efi-890r/ap-fds/yd-flash/enabled"),
];

var ap_status = [ 0, 0 ];	# 0 = off; 1 = flashing; 2 = on

var check_ap_status = func () {
	if( ap_on.getBoolValue() ){
		ap_status[0] = 2;
	} else if( ap_status[0] == 2 ){
		ap_status[0] = 1;
		settimer( func () { 
					ap_status[0] = 0;
					ap_flash[1].setBoolValue( 1 );
				}, 4 );
	} else if( ap_status[0] == 1 and !ap_flash[1].getBoolValue() ){
		ap_flash[1].setBoolValue( 1 );
	}
	if( yd_on.getBoolValue() ){
		ap_status[1] = 2;
	} else if( ap_status[1] == 2 ){
		ap_status[1] = 1;
		settimer( func () { 
					ap_status[1] = 0;
					yd_flash[1].setBoolValue( 1 );
				}, 4 );
	} else if( ap_status[1] == 1 and !yd_flash[1].getBoolValue() ){
		yd_flash[1].setBoolValue( 1 );
	}
}
		
var update_ap_status = maketimer( 0.05, check_ap_status );
update_ap_status.simulatedTime = 1;
update_ap_status.start();


var roundToNearest = func(n, m) {
	var x = int(n/m)*m;
	if((math.mod(n,m)) > (m/2) and n > 0)
			x = x + m;
	if((m - (math.mod(n,m))) > (m/2) and n < 0)
			x = x - m;
	return x;
}

var canvas_EFI_890R_PFD_base = {
	init: func(canvas_group, file) {
		var font_mapper = func(family, weight) {
			return "LiberationFonts/LiberationSans-Bold.ttf";
		};

		
		canvas.parsesvg(canvas_group, file, {'font-mapper': font_mapper});

		var svg_keys = me.getKeys();
		 
		foreach(var key; svg_keys) {
			me[key] = canvas_group.getElementById(key);
			var clip_el = canvas_group.getElementById(key ~ "_clip");
			if (clip_el != nil) {
				clip_el.setVisible(0);
				var tran_rect = clip_el.getTransformedBounds();
				var clip_rect = sprintf("rect(%d,%d, %d,%d)", 
				tran_rect[1], # 0 ys
				tran_rect[2], # 1 xe
				tran_rect[3], # 2 ye
				tran_rect[0]); #3 xs
				#   coordinates are top,right,bottom,left (ys, xe, ye, xs) ref: l621 of simgear/canvas/CanvasElement.cxx
				me[key].set("clip", clip_rect);
				me[key].set("clip-frame", canvas.Element.PARENT);
			}
		}

		if( me["horizon"] != nil and me["fd"] != nil ){
			me.h_trans = me["horizon"].createTransform();
			me.h_rot = me["horizon"].createTransform();
			me.fd_trans = me["fd"].createTransform();
			me.fd_rot = me["fd"].createTransform();
		}
		
		me.page = canvas_group;

		return me;
	},
	getKeys: func() {
		return [];
	},
	update: func() {
		if( du_status[0] == 2 ){
			EFI_890R_PFD_start.page.hide();
			EFI_890R_PFD_main.page.show();
		} else if ( du_status[0] == 1 ){
			EFI_890R_PFD_main.page.hide();
			EFI_890R_PFD_start.page.show();
		} else {
			EFI_890R_PFD_main.page.hide();
			EFI_890R_PFD_start.page.hide();
		}
	},
};
	
var vs_scale_shown = -2;	# -1 = low; 0 = normal; 1 = high

var canvas_EFI_890R_PFD_main = {
	new: func(canvas_group, file) {
		var m = { parents: [canvas_EFI_890R_PFD_main , canvas_EFI_890R_PFD_base] };
		m.init(canvas_group, file);

		return m;
	},
	getKeys: func() {
		return ["asi.digits.10", "asi.rollingdigits", "asi.tape", "asi.tape.lowspeed", "gs", "sat", "horizon", "roll.needle", "radaralt", "turn.needle", 
		"vs.scale.low", "vs.scale.low.arrow", "vs.scale.low.1000", "vs.scale.low.1",
		"vs.scale.high", "vs.scale.high.arrow", "vs.scale.high.1000", "vs.scale.high.1",
		"vs.scale.normal", "vs.scale.normal.arrow", "vs.scale.normal.1000", "vs.scale.normal.1",
		"alt_complete", "alt.tape", "alt.1", "alt.2", "alt.3", "alt.4", "alt.5", "alt.6", "alt.7",
		"alt_sel.100", "alt_sel.1", "alt_sel.pointer",
		"alt.digits.100", "alt.rollingdigits",
		"qnh.digits", "qnh.unit",
		"ap.annun", "yd.annun", "fd",
		"minimums.pointer", "minimums.pointer.text", "minimums.pointer.amber", "minimums.text", "alt.barber_pole",
		#	Slow update
		"compass", "compass.N", "compass.3", "compass.6", "compass.E", "compass.12", "compass.15", "compass.S", "compass.21", "compass.24", "compass.W", "compass.30", "compass.33",
		"heading.digits", "hdg_bug", "hdg_bug.digits", "wind.arrow", "wind.text",
		"nav1.text", "nav1.needle", "nav1.deflection_needle", "nav2.text", "nav2.needle", "nav2.deflection_needle",
		"brg1.src", "brg1.needle", "brg2.src", "brg2.needle",
		];
	},
	fast_update: func() {
		if( du_status[0] != 2 ) return;

		# Airspeed Indicator
		var airspeed = ias_kts.getDoubleValue();
		if( airspeed > 10 ) {
			me["asi.digits.10"].setText( sprintf("%2d", math.floor( airspeed / 10 , 1) ) );
			me["asi.digits.10"].show();
		} else {
			me["asi.digits.10"].hide();
		}
		me["asi.rollingdigits"].setTranslation( 0, math.mod( airspeed, 10 ) * 34.6 );
		me["asi.tape"].setTranslation( 0, 3.73878 * airspeed );
		if( no_wow.getValue() == 1 ) {
			me["asi.tape.lowspeed"].show();
		} else {
			me["asi.tape.lowspeed"].hide();
		}
		
		# Textual information
		me["gs"].setText( sprintf("%3d", math.round( gs.getDoubleValue() ) ) );
		me["sat"].setText( sprintf("%+2d", math.round( sat.getDoubleValue() ) ) );
		
		# Attitude Indicator
		var pitch_v = pitch.getDoubleValue();
		var roll_v = roll.getDoubleValue();
		me.h_trans.setTranslation( 0, pitch_v * 8.8819 );
		me.h_rot.setRotation( -roll_v * D2R , me["horizon"].getCenter() );
		me["roll.needle"].setRotation( -roll_v * D2R );
		# tiny turn indicator in the lower left corner of the AI
		me["turn.needle"].setRotation( turn.getDoubleValue() * 17.3 * D2R );
		
		# Flight Director
		if( fd_active.getBoolValue() ){
			me["fd"].show();
			me.fd_trans.setTranslation( 0, ( pitch_v + fd_pitch.getDoubleValue() ) * 8.8819 );
			me.fd_rot.setRotation( ( roll_v + fd_roll.getDoubleValue() ) * -D2R, me["fd"].getCenter() );
		} else {
			me["fd"].hide();
		}
		
		# Wind indicator
		var wind_from_v = wind_from.getDoubleValue();
		var wind_spd_v = wind_spd.getDoubleValue();
		me["wind.arrow"].setRotation( wind_from_v * D2R );
		me["wind.text"].setText( sprintf("%3d", math.round( wind_from_v ) ) ~ " / " ~ sprintf("%3d", math.round( wind_spd_v ) ) );
		
		# Vertical Speed
		var vs = vs_fpm.getDoubleValue();
		var vs_n = vs_needle.getDoubleValue();
		if( vs < -1500 ){
			if( vs_scale_shown != -1 ){
				vs_scale_shown = -1;
				me["vs.scale.high"].hide();
				me["vs.scale.normal"].hide();
				me["vs.scale.low"].show();
			}
			me["vs.scale.low.1000"].setText( sprintf( "%1d", math.floor( vs / 1000 ) + 1 ) );
			me["vs.scale.low.1"].setText( sprintf( "%03d", math.round( 1000 - math.mod( vs , 1000 ), 100 ) ) );
			me["vs.scale.low.arrow"].setRotation( -vs_n * D2R );
			me["vs.scale.low.arrow"].setTranslation( -vs_n / 90 * 44, 0 );
		} else if ( vs > 1500 ) {
			if( vs_scale_shown != 1 ){
				vs_scale_shown = 1;
				me["vs.scale.high"].show();
				me["vs.scale.normal"].hide();
				me["vs.scale.low"].hide();
			}
			me["vs.scale.high.1000"].setText( sprintf( "%1d", math.floor( vs / 1000 )  ) );
			me["vs.scale.high.1"].setText( sprintf( "%03d", math.round( math.mod( vs , 1000 ), 100 ) ) );
			me["vs.scale.high.arrow"].setRotation( -vs_n * D2R );
			me["vs.scale.high.arrow"].setTranslation( -vs_n / 90 * 44, 0 );
		} else {
			if( vs_scale_shown != 0 ){
				vs_scale_shown = 0;
				me["vs.scale.high"].hide();
				me["vs.scale.normal"].show();
				me["vs.scale.low"].hide();
			}
			me["vs.scale.normal.arrow"].setRotation( vs_n * D2R );
			if( vs >= 0 ){
				me["vs.scale.normal.1000"].setText( sprintf( "%1d", math.floor( vs / 1000 )  ) );
				me["vs.scale.normal.1"].setText( sprintf( "%03d", math.round( math.mod( vs , 1000 ), 100 ) ) );
			} else {
				me["vs.scale.normal.1000"].setText( sprintf( "%1d", math.floor( vs / 1000 ) + 1 ) );
				me["vs.scale.normal.1"].setText( sprintf( "%03d", math.round( 1000 - math.mod( vs , 1000 ), 100 ) ) );
			}
		}
		
		# Altimeter
		# spacing: 0.185969 per ft
		
		#Alt Tape
		var altitude = alt.getDoubleValue();
		me["alt.tape"].setTranslation(0,(altitude - roundToNearest(altitude, 1000))*0.185969);
		var alt_num = [ "0", "0", "0", "0", "0", "0", "0" ];
		if (roundToNearest(altitude, 1000) == 0) {
			alt_num = [ "-15", "-10", "-5", "0", "5", "10", "15" ];
		} elsif (roundToNearest(altitude, 1000) > 0) {
			var tmp = roundToNearest(altitude, 1000) / 100;
			alt_num = [ tmp-15, tmp-10, tmp-5, tmp, tmp+5, tmp+10, tmp+15 ];
		} elsif (roundToNearest(altitude, 1000) < 0) {
			var altNumLow = roundToNearest(altitude, 1000)/100+5;
			var altNumHigh = (roundToNearest(altitude, 1000)/1000 + 1)*10+5 ;
			var altNumCenter = altNumLow-5;
		}
		me["alt.1"].setText( sprintf( "%s", alt_num[0] ) );
		me["alt.2"].setText( sprintf( "%s", alt_num[1] ) );
		me["alt.3"].setText( sprintf( "%s", alt_num[2] ) );
		me["alt.4"].setText( sprintf( "%s", alt_num[3] ) );
		me["alt.5"].setText( sprintf( "%s", alt_num[4] ) );
		me["alt.6"].setText( sprintf( "%s", alt_num[5] ) );
		me["alt.7"].setText( sprintf( "%s", alt_num[6] ) );
		
		me["alt.digits.100"].setText( sprintf("%3d", math.floor( altitude / 100 ) ) );
		me["alt.rollingdigits"].setTranslation( 0, math.mod( altitude, 100 ) );
		
		me["qnh.digits"].setText( sprintf("%4d", qnh.getDoubleValue() ) );
		me["qnh.unit"].setText("hPa");

		var alt_sel = alt_presel.getDoubleValue();
		me["alt_sel.100"].setText( sprintf("%2d", math.round( math.floor( alt_sel / 100 ) ) ) );
		me["alt_sel.1"].setText( sprintf( "%02d", math.round( math.mod( alt_sel, 100 ) ) ) );
		me["alt_sel.pointer"].setTranslation( 0, altitude - alt_sel * 0.185969 );

		# Radio altimeter
		var ra = radaralt.getDoubleValue() or 0.0;
		if( ra <= 2500 ){
			me["radaralt"].show();
			if( ra >= 1000 ){
				me["radaralt"].setText(sprintf("%1d", math.round( ra, 50 )));
				me["alt.barber_pole"].hide();
			} else {
				me["radaralt"].setText(sprintf("%1d", math.round( ra, 10) ));
				me["alt.barber_pole"].show();
				me["alt.barber_pole"].setTranslation( 0, ra * 0.185969 );
			}
		} else {
			me["radaralt"].hide();
			me["alt.barber_pole"].hide();
		}

		# Minimums
		if( minimums.getIntValue() == 0 ){
			# OFF
			me["minimums.text"].hide();
			me["minimums.pointer"].hide();
		} elsif( minimums.getIntValue() == 1 ){
			# RA
			var ra_minimums = ra_min.getIntValue();
			me["minimums.text"].show();
			me["minimums.text"].setText( "RA   "~ sprintf("%4d", math.round( ra_minimums ) ) );
			if( ra <= ra_minimums and no_wow.getBoolValue() ){
				me["minimums.text"].setColorFill( 1, 0.75, 0 );
			} else {
				me["minimums.text"].setColorFill( 1, 1, 1 );
			}

			if( ra < 1000 ){
				me["minimums.pointer"].show();
				me["minimums.pointer.amber"].show();
				me["minimums.pointer.text"].setText( "RA" );
				me["minimums.pointer"].setTranslation( 0, ( ra - ra_minimums ) * 0.185969 );
			} else {
				me["minimums.pointer"].hide();
			}
		} elsif( minimums.getIntValue() == 2 ){
			# DA
			var da = decision_alt.getIntValue();
			var da_diff = altitude - da;

			me["minimums.text"].show();
			me["minimums.text"].setText( sprintf("%4d", math.round( da ) ) ~ "   DA" );

			if( da_diff <= 0 and no_wow.getBoolValue() ){
				me["minimums.text"].setColorFill( 1, 0.75, 0 );
			} else {
				me["minimums.text"].setColorFill( 1, 1, 1 );
			}

			me["minimums.pointer"].show();
			me["minimums.pointer.amber"].hide();
			me["minimums.pointer.text"].setText( "DA" );
			me["minimums.pointer"].setTranslation( 0, da_diff * 0.185969 );

		}

		
		# Autopilot Annunciations
		if( ap_status[0] == 2 or ( ap_status[0] * ap_flash[0].getBoolValue() ) == 1 ){
			me["ap.annun"].show();
		} else {
			me["ap.annun"].hide();
		}
		if( ap_status[1] == 2 or ( ap_status[1] * yd_flash[0].getBoolValue() ) == 1 ){
			me["yd.annun"].show();
		} else {
			me["yd.annun"].hide();
		}
	},
	slow_update: func{
		if( du_status[0] != 2 ) return;

		# HSI
		var heading_v = heading.getDoubleValue();
		me["compass"].setRotation( -heading_v * D2R );
		me["compass.N"].setRotation( heading_v * D2R );
		me["compass.3"].setRotation( heading_v * D2R );
		me["compass.6"].setRotation( heading_v * D2R );
		me["compass.E"].setRotation( heading_v * D2R );
		me["compass.12"].setRotation( heading_v * D2R );
		me["compass.15"].setRotation( heading_v * D2R );
		me["compass.S"].setRotation( heading_v * D2R );
		me["compass.21"].setRotation( heading_v * D2R );
		me["compass.24"].setRotation( heading_v * D2R );
		me["compass.W"].setRotation( heading_v * D2R );
		me["compass.30"].setRotation( heading_v * D2R );
		me["compass.33"].setRotation( heading_v * D2R );
		me["heading.digits"].setText( sprintf("%3d", math.round( heading_v ) ) );

		var hdg_bug_v = hdg_bug.getDoubleValue();
		me["hdg_bug"].setRotation( -hdg_bug_v * D2R );
		me["hdg_bug.digits"].setText( sprintf("%3d", math.round( hdg_bug_v ) ) );

		var nav1_src = math.min( nav_brg_src[0].getIntValue(), 1);
		me["nav1.text"].setText( nav_brg_src_txt[ nav1_src ] ~"\nZZZZ\nZZZZ\nZZZZ\nZZZZ" );
		me["nav1.needle"].setRotation( ( radial_deg[ nav1_src ].getDoubleValue() or 0.0 ) * D2R );
		me["nav1.deflection_needle"].setTranslation( -( deflection_norm[ nav1_src ].getDoubleValue() or 0.0 ) * 70.0, 0 );

		var nav2_src = math.min( nav_brg_src[1].getIntValue(), 1);
		me["nav2.text"].setText( nav_brg_src_txt[ nav2_src ] ~"\nZZZZ\nZZZZ\nZZZZ\nZZZZ" );
		me["nav2.needle"].setRotation( ( radial_deg[ nav2_src ].getDoubleValue() or 0.0 ) * D2R );
		me["nav2.deflection_needle"].setTranslation( -( deflection_norm[ nav2_src ].getDoubleValue() or 0.0 ) * 70.0, 0 );

		var brg1_src = nav_brg_src[2].getIntValue();
		me["brg1.src"].setText( nav_brg_src_txt[ brg1_src ] );
		me["brg1.needle"].setRotation( ( bearing_deg[ brg1_src ].getDoubleValue() or 0.0 ) * D2R );

		var brg2_src = nav_brg_src[3].getIntValue();
		me["brg2.src"].setText( nav_brg_src_txt[ brg2_src ] );
		me["brg2.needle"].setRotation( ( bearing_deg[ brg2_src ].getDoubleValue() or 0.0 ) * D2R );
	},
};


var canvas_EFI_890R_PFD_start = {
	new: func(canvas_group, file) {
		var m = { parents: [canvas_EFI_890R_PFD_start , canvas_EFI_890R_PFD_base] };
		m.init(canvas_group, file);

		return m;
	},
	getKeys: func() {
		return [];
	},
	update: func() {
	}
	
};



var efi_pfd_update = maketimer(0.02, func {
	canvas_EFI_890R_PFD_base.update();
});
efi_pfd_update.simulatedTime = 1;

var main_fast_update = maketimer( 0.02, func{ EFI_890R_PFD_main.fast_update(); } );
main_fast_update.simulatedTime = 1;

var main_slow_update = maketimer( 0.2, func{ EFI_890R_PFD_main.slow_update(); } );
main_slow_update.simulatedTime = 1;

var ls_pfd = setlistener("sim/signals/fdm-initialized", func {
	EFI_890R_PFD_display = canvas.new({
		"name": "EFI_890R_PFD",
		"size": [780, 780],
		"view": [780, 780],
		"mipmapping": 1
	});
	EFI_890R_PFD_display.addPlacement({"node": "efi_890r_du1"});
	EFI_890R_PFD_display.addPlacement({"node": "efi_890r_du4"});
	var groupMain = EFI_890R_PFD_display.createGroup();
	var groupStart = EFI_890R_PFD_display.createGroup();

	EFI_890R_PFD_main = canvas_EFI_890R_PFD_main.new(groupMain, instrument_path~"efi-890r-pfd.svg");
	EFI_890R_PFD_start = canvas_EFI_890R_PFD_start.new(groupStart, instrument_path~"efi-890r-start.svg");

	efi_pfd_update.start();
	main_fast_update.start();
	main_slow_update.start();
	
	removelistener(ls_pfd);
});
