use std::f32::consts::PI;
use crate::mode::Mode;
use nexrad2::Nexrad2Chunk;


use std::iter;
use chrono::Utc;
use itertools::Itertools;
use log::{debug, info};
use wasm_bindgen::JsCast;


use web_sys::{HtmlInputElement};
use wgpu::{BlendState, ColorTargetState, ColorWrites, DeviceDescriptor, Face, FragmentState, FrontFace, PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, RenderPipeline, RenderPipelineDescriptor, ShaderModuleDescriptor, ShaderSource, VertexState};
use wgpu::{
    Backends, Color, CommandEncoderDescriptor, Device, Features, Instance, InstanceDescriptor,
    Limits, LoadOp, MultisampleState, Operations, Queue, RenderPassColorAttachment,
    RenderPassDescriptor, StoreOp, Surface, SurfaceConfiguration, SurfaceError, TextureUsages,
    TextureViewDescriptor,
};
use wgpu::util::{DeviceExt, StagingBelt};
use wgpu_glyph::ab_glyph::FontArc;
use wgpu_glyph::{GlyphBrush, GlyphBrushBuilder, HorizontalAlign, Layout, Section, Text};
use winit::dpi::PhysicalSize;
use winit::event::WindowEvent;
use winit::window::Window;
use nexrad2::message31::MOMENT_DATA_BELOW_THRESHOLD;
use crate::mode::Mode::{RadarInoperative, Reflectivity};
use crate::rendering::colors::{color_scheme, rgb_to_srgb};
use crate::rendering::time::zulu;
use crate::rendering::vertex_index::{INDICES, Vertex, VERTICES};
use crate::utils::parse_date;
use crate::vcp::vcp_string;

pub struct ScopeState {
    pub ar2: Option<Nexrad2Chunk>,
    pub scope_mode: Mode,
    pub file_input: HtmlInputElement,
    pub lat: f64,
    pub long: f64,
    pub prefs: Preferences,
    pub command_buf: String,
    pub command_buf_response_mode: bool,
    pub new_data_available: bool,
    pub selected_elevation: usize,
    pub show_ui: bool,
    pub wgpu: WgpuState,
}

pub struct WgpuState {
    pub surface: Surface,
    pub device: Device,
    pub queue: Queue,
    pub surface_config: SurfaceConfiguration,
    pub size: PhysicalSize<u32>,
    pub window: Window,
    pub vertex_buffer: wgpu::Buffer,
    pub index_buffer: wgpu::Buffer,
    pub pipeline: RenderPipeline,
    pub staging_belt: StagingBelt,
    pub glyph_brush: GlyphBrush<()>,
    pub verticies: Vec<Vertex>,
    pub indicies: Vec<u16>,
    pub buffers_need_reinitialization: bool
}

impl ScopeState {
    pub async fn new(window: Window, size: PhysicalSize<u32>) -> Self {
        let instance = Instance::new(InstanceDescriptor {
            backends: Backends::all(),
            flags: Default::default(),
            dx12_shader_compiler: Default::default(),
            gles_minor_version: Default::default(),
        });
        let surface = unsafe { instance.create_surface(&window).unwrap() };

        let adapter = instance
            .request_adapter(&wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::default(),
                compatible_surface: Some(&surface),
                force_fallback_adapter: false,
            })
            .await
            .unwrap();

        let (device, queue) = adapter
            .request_device(
                &DeviceDescriptor {
                    features: Features::empty(),
                    limits: Limits::downlevel_webgl2_defaults(),
                    label: None,
                },
                None, // Trace path
            )
            .await
            .unwrap();

        let surface_capabilities = surface.get_capabilities(&adapter);

        let surface_format = surface_capabilities
            .formats
            .iter()
            .copied()
            .find(|f| f.is_srgb())
            .unwrap_or(surface_capabilities.formats[0]);

        let surface_config = SurfaceConfiguration {
            usage: TextureUsages::RENDER_ATTACHMENT,
            format: surface_format,
            width: size.width,
            height: size.height,
            present_mode: surface_capabilities.present_modes[0],
            alpha_mode: surface_capabilities.alpha_modes[0],
            view_formats: vec![],
        };

        surface.configure(&device, &surface_config);

        let vertex_buffer = device.create_buffer_init(
            &wgpu::util::BufferInitDescriptor {
                label: Some("Vertex Buffer"),
                contents: bytemuck::cast_slice(VERTICES),
                usage: wgpu::BufferUsages::VERTEX,
            }
        );

        let index_buffer = device.create_buffer_init(
            &wgpu::util::BufferInitDescriptor {
                label: Some("Index Buffer"),
                contents: bytemuck::cast_slice(INDICES),
                usage: wgpu::BufferUsages::INDEX,
            }
        );

        let shader = device.create_shader_module(ShaderModuleDescriptor {
            label: Some("Shader"),
            source: ShaderSource::Wgsl(include_str!("../shader.wgsl").into()),
        });

        let render_pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
            label: Some("Render Pipeline Layout"),
            bind_group_layouts: &[],
            push_constant_ranges: &[],
        });

        let render_pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
            label: Some("Render Pipeline"),
            layout: Some(&render_pipeline_layout),
            vertex: VertexState {
                module: &shader,
                entry_point: "vs_main",
                buffers: &[Vertex::descriptor()],
            },
            primitive: PrimitiveState {
                topology: PrimitiveTopology::TriangleList,
                strip_index_format: None,
                front_face: FrontFace::Ccw,
                cull_mode: Some(Face::Back),
                unclipped_depth: false,
                polygon_mode: PolygonMode::Fill,
                conservative: false,
            },
            depth_stencil: None,
            multisample: MultisampleState {
                count: 1,
                mask: !0,
                alpha_to_coverage_enabled: false,
            },
            fragment: Some(FragmentState {
                module: &shader,
                entry_point: "fs_main",
                targets: &[Some(ColorTargetState {
                    format: surface_config.format,
                    blend: Some(BlendState::REPLACE),
                    write_mask: ColorWrites::ALL,
                })],
            }),
            multiview: None,
        });

        let staging_belt = StagingBelt::new(1024);
        let font = FontArc::try_from_slice(include_bytes!("../fonts/Inconsolata-Regular.ttf")).unwrap();
        let mut glyph_brush = GlyphBrushBuilder::using_font(font).build(&device, surface_config.format);

        let wgpu = WgpuState {
            window,
            surface,
            device,
            queue,
            surface_config,
            size,
            vertex_buffer,
            pipeline: render_pipeline,
            staging_belt,
            glyph_brush,
            index_buffer,
            verticies: vec![],
            indicies: vec![],
            buffers_need_reinitialization: false
        };

        info!("initializing the scope");

        let document = web_sys::window()
            .expect("window should exist")
            .document()
            .expect("document should exist");

        info!("finding form input element");

        let file = document
            .get_element_by_id("file")
            .expect("file element should exist");
        let file: web_sys::HtmlInputElement = file
            .dyn_into::<web_sys::HtmlInputElement>()
            .map_err(|_| ())
            .expect("file input is not an input");

        let scope_state = ScopeState {
            ar2: None,
            scope_mode: Mode::RadarInoperative,
            file_input: file,
            lat: 30.48500,   // jacksonville
            long: -81.70200, // jacksonville
            prefs: Preferences { fcs: 24.0, range: 50.0 },
            command_buf: String::new(),
            command_buf_response_mode: false,
            new_data_available: false,
            selected_elevation: 1,
            show_ui: false,
            wgpu,
        };

        scope_state
    }

    pub fn window(&self) -> &Window {
        &self.wgpu.window
    }

    pub fn reconfigure(&mut self, new_size: PhysicalSize<u32>) {
        if new_size.width > 0 && new_size.height > 0 {
            self.wgpu.size = new_size;
            self.wgpu.surface_config.width = new_size.width;
            self.wgpu.surface_config.height = new_size.height;
            self.wgpu.surface.configure(&self.wgpu.device, &self.wgpu.surface_config);
        }
    }

    pub fn input(&mut self, _event: &WindowEvent) -> bool {
        false
    }

    pub fn update(&mut self) {}

    pub fn recalculate_scope_display(&mut self) {
        self.wgpu.buffers_need_reinitialization = true;
        self.wgpu.verticies = vec![];
        self.wgpu.indicies = vec![];
        let ar2 = self.ar2.as_ref().unwrap();
        let px_per_km = 1.0 / (self.prefs.range * 2.0);

        let radials = ar2.elevations.get(&self.selected_elevation).unwrap();

        let first_gate_px = radials[0].available_data.get(self.scope_mode.rname()).unwrap().gdm.data_moment_range as f32 / 1000.0 * px_per_km;
        let gate_interval_km = radials[0].available_data.get(self.scope_mode.rname()).unwrap().gdm.data_moment_range_sample_interval as f32 / 1000.0;
        let gate_width_px = gate_interval_km * px_per_km;

        for radial in radials {
            // radial rounding
            let mut azimuth_angle = radial.header.azimuth_angle - 90.0;
            if azimuth_angle < 0.0 {
                azimuth_angle = 360.0 + azimuth_angle;
            }
            let azimuth_spacing = match radial.header.azimuth_resolution_spacing {
                1 => 0.5,
                _ => 1.0
            };
            let mut azimuth = azimuth_angle.floor();
            if (azimuth_angle + azimuth_spacing).floor() > azimuth {
                azimuth += azimuth_spacing;
            }
            let start_angle = azimuth * (PI / 180.0);
            let end_angle = (azimuth + azimuth_spacing) * (PI / 180.0);

            let mut distance = first_gate_px;

            let gates = radial.available_data.get(self.scope_mode.rname()).unwrap().scaled_data();

            for (gate_num, gate) in gates.iter().enumerate() {
                if *gate != MOMENT_DATA_BELOW_THRESHOLD {
                    let a = [(distance * start_angle.cos()) as f32, (distance * start_angle.sin()) as f32, 0.0];
                    let b = [(distance * end_angle.cos()) as f32, (distance * end_angle.sin()) as f32, 0.0];
                    let c = [(distance + gate_width_px * start_angle.cos()) as f32, (distance + gate_width_px * start_angle.sin()) as f32, 0.0];
                    let d = [(distance + gate_width_px * end_angle.cos()) as f32, (distance + gate_width_px * end_angle.sin()) as f32, 0.0];
                    let vertex_a = Vertex { position: a, color: color_scheme(self.scope_mode, *gate) };
                    let vertex_b = Vertex { position: b, color: color_scheme(self.scope_mode, *gate) };
                    let vertex_c = Vertex { position: c, color: color_scheme(self.scope_mode, *gate) };
                    let vertex_d = Vertex { position: d, color: color_scheme(self.scope_mode, *gate) };

                    let vindex_a = self.wgpu.verticies.len();
                    self.wgpu.verticies.push(vertex_a);
                    let vindex_b = self.wgpu.verticies.len();
                    self.wgpu.verticies.push(vertex_b);
                    let vindex_c = self.wgpu.verticies.len();
                    self.wgpu.verticies.push(vertex_c);
                    let vindex_d = self.wgpu.verticies.len();
                    self.wgpu.verticies.push(vertex_d);

                    // ABC <-> BCD

                    self.wgpu.indicies.extend_from_slice(&[vindex_a as u16, vindex_b as u16, vindex_c as u16]);
                    self.wgpu.indicies.extend_from_slice(&[vindex_b as u16, vindex_c as u16, vindex_d as u16]);

                }
                distance += gate_width_px;
                azimuth += azimuth_spacing;
            }
        }
        info!("Tesselated, {}/{}", self.wgpu.verticies.len(), self.wgpu.indicies.len());
        info!("{:?} {:?} {:?} {:?}", self.wgpu.verticies[0], self.wgpu.verticies[1], self.wgpu.verticies[2], self.wgpu.verticies[3]);
    }

/*
    pub fn recalculate_scope_display(&mut self) {
        self.wgpu.buffers_need_reinitialization = true;
        self.wgpu.verticies = vec![];
        self.wgpu.indicies = vec![];
        let ar2 = self.ar2.as_ref().unwrap();
        let px_per_km = 1.0 / (self.prefs.range * 2.0);

        let radials = ar2.elevations.get(&self.selected_elevation).unwrap();

        info!("Tesselating {} radials", radials.len());

        let distance_firstgate = ((radials[0].available_data.get(self.scope_mode.rname()).expect("selected mode is missing!").gdm.data_moment_range as f64) / 1000.0) * px_per_km;
        let distance_gatespacing = ((radials[0].available_data.get(self.scope_mode.rname()).expect("selected mode is missing!").gdm.data_moment_range_sample_interval as f64) / 1000.0) * px_per_km;

        let mut distance_gate = distance_firstgate;

        for radial in radials {
            let mut azimuth = radial.header.azimuth_angle as f64;

            let azimuth_spacing = match radial.header.azimuth_resolution_spacing {
                1 => 0.5,
                _ => 1.0,
            };

            // line width
            // line cap

            let gates = radial
                .available_data
                .get(self.scope_mode.rname())
                .expect("selected unavailable product")
                .scaled_data();

            let num_gates = gates.len();

            for (num, value) in gates.iter().enumerate() {
                if *value != MOMENT_DATA_BELOW_THRESHOLD {
                    let az = azimuth * (PI / 180.0);
                    let bz = (azimuth + azimuth_spacing) * (PI / 180.0);

                    let d_gateend = distance_gate + distance_gatespacing;

                    let a = [(distance_gate * az.cos()) as f32, (distance_gate * az.sin()) as f32, 0.0];
                    let b = [(distance_gate * bz.cos()) as f32, (distance_gate * bz.sin()) as f32, 0.0];
                    let c = [(d_gateend * az.cos()) as f32, (d_gateend * az.sin()) as f32, 0.0];
                    let d = [(d_gateend * bz.cos()) as f32, (d_gateend * bz.sin()) as f32, 0.0];

                    // C--D
                    // |  |
                    // A--B
                    /*
                    (x0, y1) -- (x1, y1)
                        |           |
                    (x0, y0) -- (x1, y0)

                     */

                    let vertex_a = Vertex { position: a, color: color_scheme(self.scope_mode, *value) };
                    let vertex_b = Vertex { position: b, color: color_scheme(self.scope_mode, *value) };
                    let vertex_c = Vertex { position: c, color: color_scheme(self.scope_mode, *value) };
                    let vertex_d = Vertex { position: d, color: color_scheme(self.scope_mode, *value) };

                    let vindex_a = self.wgpu.verticies.len();
                    self.wgpu.verticies.push(vertex_a);
                    let vindex_b = self.wgpu.verticies.len();
                    self.wgpu.verticies.push(vertex_b);
                    let vindex_c = self.wgpu.verticies.len();
                    self.wgpu.verticies.push(vertex_c);
                    let vindex_d = self.wgpu.verticies.len();
                    self.wgpu.verticies.push(vertex_d);

                    // ABC <-> BCD

                    self.wgpu.indicies.extend_from_slice(&[vindex_a as u16, vindex_b as u16, vindex_c as u16]);
                    //self.wgpu.indicies.extend_from_slice(&[vindex_a as u16, vindex_d as u16, vindex_b as u16]);
                }
                azimuth += azimuth_spacing;
            }
            distance_gate += distance_gatespacing;
        }
        info!("Radar data triangled - {} verticies {} indicies", self.wgpu.verticies.len(), self.wgpu.indicies.len());
    }


 */
    pub fn render(&mut self) -> Result<(), SurfaceError> {
        let output = self.wgpu.surface.get_current_texture()?;
        let view = output
            .texture
            .create_view(&TextureViewDescriptor::default());
        let mut encoder = self
            .wgpu.device
            .create_command_encoder(&CommandEncoderDescriptor {
                label: Some("Render Encoder"),
            });

        if self.wgpu.buffers_need_reinitialization {
            self.wgpu.buffers_need_reinitialization = false;
            self.wgpu.vertex_buffer.destroy();
            let new_vertex_buffer = self.wgpu.device.create_buffer_init(
                &wgpu::util::BufferInitDescriptor {
                    label: Some("Vertex Buffer"),
                    contents: bytemuck::cast_slice(self.wgpu.verticies.as_slice()),
                    usage: wgpu::BufferUsages::VERTEX,
                }
            );
            self.wgpu.vertex_buffer = new_vertex_buffer;

            self.wgpu.index_buffer.destroy();
            let new_index_buffer = self.wgpu.device.create_buffer_init(
                &wgpu::util::BufferInitDescriptor {
                    label: Some("Index Buffer"),
                    contents: bytemuck::cast_slice(self.wgpu.indicies.as_slice()),
                    usage: wgpu::BufferUsages::INDEX,
                }
            );
            self.wgpu.index_buffer = new_index_buffer;
        }


        // LOCK BLOCK
        {
            let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
                label: Some("Render Pass"),
                color_attachments: &[Some(RenderPassColorAttachment {
                    view: &view,
                    resolve_target: None,
                    ops: Operations {
                        load: LoadOp::Clear(Color {
                            r: 0.0,
                            g: 0.0,
                            b: 0.0,
                            a: 1.0,
                        }),
                        store: StoreOp::Store,
                    },
                })],
                depth_stencil_attachment: None,
                timestamp_writes: None,
                occlusion_query_set: None,
            });

            render_pass.set_pipeline(&self.wgpu.pipeline);
            render_pass.set_vertex_buffer(0, self.wgpu.vertex_buffer.slice(..));
            render_pass.set_index_buffer(self.wgpu.index_buffer.slice(..), wgpu::IndexFormat::Uint16); // 1.
            render_pass.draw_indexed(0..(self.wgpu.indicies.len() as u32), 0, 0..1);

        }

        // TEXT RENDERING //
        let time = Utc::now();
        let line_height = self.prefs.fcs - 1.0;

        if self.show_ui {
            self.wgpu.glyph_brush.queue(Section {
                screen_position: (10.0, 8.0),
                bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                text: vec![Text::new(&format!(
                    "NEXRAD {} {}",
                    self
                        .ar2
                        .as_ref()
                        .map(|u| u.volume_header_record.icao.as_str())
                        .unwrap_or("INOP"),
                    zulu(time)
                ))
                    .with_color(rgb_to_srgb(0x32cd32, 1.0))
                    .with_scale(self.prefs.fcs)],
                ..Section::default()
            });

            if self.ar2.is_none() {
                self.wgpu.glyph_brush.queue(Section {
                    screen_position: (10.0, 8.0 + line_height),
                    bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                    text: vec![Text::new("RADAR INOPERATIVE NO DATA LOADED")
                        .with_color(rgb_to_srgb(0xff0000, 1.0))
                        .with_scale(self.prefs.fcs)],
                    ..Section::default()
                });
            }
            if self.new_data_available {
                self.wgpu.glyph_brush.queue(Section {
                    screen_position: (10.0, 8.0 + (line_height * 2.0)),
                    bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                    text: vec![Text::new("NEW DATA AVAIL RLD RQD")
                        .with_color(rgb_to_srgb(0xff0000, 1.0))
                        .with_scale(self.prefs.fcs)],
                    ..Section::default()
                });
            }
        }

        let one_third = (self.wgpu.surface_config.height / 3) as f32;

        for (line_no, line) in self.command_buf.split('\n').enumerate() {
            self.wgpu.glyph_brush.queue(Section {
                screen_position: (10.0, one_third + (line_height * line_no as f32)),
                bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                text: vec![Text::new(line)
                    .with_color(rgb_to_srgb(0x32cd32, 1.0))
                    .with_scale(self.prefs.fcs)],
                ..Section::default()
            });
        }

        if self.show_ui {
            self.wgpu.glyph_brush.queue(Section {
                screen_position: (self.wgpu.surface_config.width as f32 - 10.0, 8.0),
                bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                text: vec![Text::new("RADAR SITE")
                    .with_color(rgb_to_srgb(0x32cd32, 1.0))
                    .with_scale(self.prefs.fcs)],
                ..Section::default()
            }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));

            if let Some(ar2) = &self.ar2 {
                self.wgpu.glyph_brush.queue(Section {
                    screen_position: (self.wgpu.surface_config.width as f32 - 10.0, 8.0 + line_height),
                    bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                    text: vec![Text::new(&format!("{} VCP {} {}", ar2.volume_header_record.icao,
                                                  ar2.meta_rda_status_data.vcp,
                                                  vcp_string(ar2.meta_rda_status_data.vcp)))
                        .with_color(rgb_to_srgb(0x32cd32, 1.0))
                        .with_scale(self.prefs.fcs)],
                    ..Section::default()
                }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));

                let recorded = parse_date(ar2.volume_header_record.date, ar2.volume_header_record.time);
                let delay = time - recorded;

                let seconds = delay.num_seconds() % 60;
                let minutes = (delay.num_seconds() / 60) % 60;
                let hours = delay.num_seconds() / 3600;

                self.wgpu.glyph_brush.queue(Section {
                    screen_position: (self.wgpu.surface_config.width as f32 - 10.0, 8.0 + (line_height*2.0)),
                    bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                    text: vec![Text::new(&format!("SITE DELAY {:0>2}:{:0>2}:{:0>2}", hours, minutes, seconds))
                        .with_color(rgb_to_srgb(0x32cd32, 1.0))
                        .with_scale(self.prefs.fcs)],
                    ..Section::default()
                }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));
            } else {
                self.wgpu.glyph_brush.queue(Section {
                    screen_position: (self.wgpu.surface_config.width as f32 - 10.0, 8.0 + line_height),
                    bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                    text: vec![Text::new("SITE INFORMATION UNAVAILABLE")
                        .with_color(rgb_to_srgb(0x32cd32, 1.0))
                        .with_scale(self.prefs.fcs)],
                    ..Section::default()
                }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));
                self.wgpu.glyph_brush.queue(Section {
                    screen_position: (self.wgpu.surface_config.width as f32 - 10.0, 8.0 + (line_height * 2.0)),
                    bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                    text: vec![Text::new("DELAY INFORMATION UNAVAILABLE")
                        .with_color(rgb_to_srgb(0x32cd32, 1.0))
                        .with_scale(self.prefs.fcs)],
                    ..Section::default()
                }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));
            }

            // mode info
            self.wgpu.glyph_brush.queue(Section {
                screen_position: (self.wgpu.surface_config.width as f32 - 10.0, one_third),
                bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                text: vec![Text::new("MODE")
                    .with_color(rgb_to_srgb(0x32cd32, 1.0))
                    .with_scale(self.prefs.fcs)],
                ..Section::default()
            }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));

            let modelines = [
                [Mode::Reflectivity, Mode::Velocity, Mode::SpectrumWidth].as_slice(),
                [
                    Mode::DifferentialReflectivity,
                    Mode::DifferentialPhase,
                    Mode::CorrelationCoefficient,
                ]
                    .as_slice(),
                [Mode::ClutterFilterPowerRemoved, Mode::RadarInoperative].as_slice(),
            ];

            let mut available_modes = vec![];

            if let Some(ar2) = &self.ar2 {
                let types = ar2.elevations.get(&self.selected_elevation).unwrap();
                available_modes = types[0].available_data.keys().collect();
            }

            for (line_no, line) in modelines.iter().enumerate() {
                let x = (self.wgpu.surface_config.width as f32 - 10.0);
                let y = one_third + line_height * (line_no as f32 + 1.0);
                for (item_no, item) in line.iter().enumerate() {
                    let pad_start = item_no;
                    let pad_end = 2 - item_no;
                    let pad_start = " ".repeat(pad_start * 6);
                    let pad_end = " ".repeat(pad_end * 6);
                    let middle = if self.scope_mode == *item {
                        item.selected()
                    } else {
                        item.unselected()
                    };

                    if self.scope_mode == *item {
                        // display selected color
                        if *item == RadarInoperative {
                            self.wgpu.glyph_brush.queue(Section {
                                screen_position: (x, y),
                                bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                                text: vec![Text::new(">RADR INOP<")
                                    .with_color(rgb_to_srgb(0xff0000, 1.0))
                                    .with_scale(self.prefs.fcs)],
                                ..Section::default()
                            }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));
                            continue;
                        }
                    } else if *item == RadarInoperative {
                        continue;
                    }

                    if available_modes.contains(&&item.rname().to_string()) {
                        self.wgpu.glyph_brush.queue(Section {
                            screen_position: (x, y),
                            bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                            text: vec![Text::new(&format!(" {}{}{}", pad_start, middle, pad_end))
                                .with_color(rgb_to_srgb(if self.scope_mode == *item { 0xdedede } else { 0x32cd32 }, 1.0))
                                .with_scale(self.prefs.fcs)],
                            ..Section::default()
                        }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));
                    }
                }
            }

            let four_sevenths = ((self.wgpu.surface_config.height / 7) * 4) as f32;

            self.wgpu.glyph_brush.queue(Section {
                screen_position: (self.wgpu.surface_config.width as f32 - 10.0, four_sevenths),
                bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                text: vec![Text::new("ELEVATIONS")
                    .with_color(rgb_to_srgb(0x32cd32, 1.0))
                    .with_scale(self.prefs.fcs)],
                ..Section::default()
            }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));

            if let Some(ar2) = &self.ar2 {
                let mut line_no = 0;

                for chunk in &ar2
                    .elevations
                    .iter()
                    .sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
                    .enumerate()
                    .chunks(4)
                {
                    for (no_in_line, (elevation_no, scans)) in chunk {
                        if self.selected_elevation != *elevation_no {
                            let s = &format!(
                                " {:0>3} {}",
                                elevation_no,
                                " ".repeat(5 * (3 - (no_in_line % 4)))
                            );

                            self.wgpu.glyph_brush.queue(Section {
                                screen_position: (self.wgpu.surface_config.width as f32 - 10.0, four_sevenths + line_height * (line_no as f32 + 1.0)),
                                bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                                text: vec![Text::new(s)
                                    .with_color(rgb_to_srgb(0x32cd32, 1.0))
                                    .with_scale(self.prefs.fcs)],
                                ..Section::default()
                            }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));
                        } else {
                            let s = &format!(
                                ">{:0>3}<{}",
                                elevation_no,
                                " ".repeat(5 * (3 - (no_in_line % 4)))
                            );

                            self.wgpu.glyph_brush.queue(Section {
                                screen_position: (self.wgpu.surface_config.width as f32 - 10.0, four_sevenths + line_height * (line_no as f32 + 1.0)),
                                bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                                text: vec![Text::new(s)
                                    .with_color(rgb_to_srgb(0xdedede, 1.0))
                                    .with_scale(self.prefs.fcs)],
                                ..Section::default()
                            }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));
                        }
                    }
                    line_no += 1;
                }
            } else {
                self.wgpu.glyph_brush.queue(Section {
                    screen_position: (self.wgpu.surface_config.width as f32 - 10.0, four_sevenths + line_height),
                    bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32),
                    text: vec![Text::new("ELEVATION INFO UNAVAILABLE")
                        .with_color(rgb_to_srgb(0x32cd32, 1.0))
                        .with_scale(self.prefs.fcs)],
                    ..Section::default()
                }.with_layout(Layout::default().h_align(HorizontalAlign::Right)));
            }
        }

        self.wgpu.glyph_brush
            .draw_queued(
                &self.wgpu.device,
                &mut self.wgpu.staging_belt,
                &mut encoder,
                &view,
                self.wgpu.surface_config.width,
                self.wgpu.surface_config.height,
            )
            .expect("Draw queued");

        self.wgpu.staging_belt.finish();
        self.wgpu.queue.submit(iter::once(encoder.finish()));
        output.present();

        self.wgpu.staging_belt.recall();

        Ok(())
    }
}

pub struct Preferences {
    pub fcs: f32,
    pub range: f32
}
