use std::cell::RefCell;
use crate::map::sources::baselayers::*;
use crate::map::sources::datalayers::*;
use crate::map::sources::{ActiveLayer, LayerSource, Tile};
use eframe::emath::{pos2, Rect};
use futures::channel::mpsc::TrySendError;
use image::{ColorType, DynamicImage};
use lru::LruCache;
use poll_promise::Promise;
use serde::{Deserialize, Serialize};
use std::cmp::PartialEq;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use tracing::{debug, error, trace};
use walkers::sources::{Attribution, TileSource};
use walkers::{HttpTiles, Texture, TextureWithUv, TileId, Tiles};

// type aliases to make the tile map less hellish
pub type ZoomLevel = usize;
pub type XCoord = usize;
pub type YCoord = usize;
pub type SourceId = u64;

#[derive(Default, Serialize, Deserialize, Debug)]
#[serde(default)]
pub struct LayerManager {
    #[serde(skip)]
    pub ctx: Option<egui::Context>,
    pub registered_sources: HashMap<SourceId, LayerSource>,
    pub active_layers: Vec<ActiveLayer>,
    #[serde(skip)]
    pub individual_sources: HashMap<SourceId, WHttpTiles>,
}

pub struct WHttpTiles(pub Rc<RefCell<HttpTiles>>);
impl Debug for WHttpTiles {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "<WHttpTiles>")
    }
}

impl LayerManager {
    pub fn register_layer(&mut self, layer: &LayerSource) {
        self.registered_sources
            .insert(layer.source_id, layer.clone());
    }
    pub fn unregister_layer(&mut self, layer_id: &SourceId) {
        self.registered_sources.remove(layer_id);
        self.active_layers.retain(|u| u.source_id != *layer_id);
    }
    pub fn register_builtin_layers(&mut self) {
        let layers = [
            // -- Base layers --
            &*BASELAYER_OSM, // OpenStreetMap
            // -- NOAA MRMS - Merged Composite Reflectivity - QCd
            &*NOAA_MRMS_MERGED_CREF_QC_CONUS, // Continental United States
            &*NOAA_MRMS_MERGED_CREF_QC_HAWAII, // Hawaii
            &*NOAA_MRMS_MERGED_CREF_QC_ALASKA, // Alaska FSS
            &*NOAA_MRMS_MERGED_CREF_QC_CARIB, // Caribbean
            &*NOAA_MRMS_MERGED_CREF_QC_GUAM,  // Guam
            // -- NOAA MRMS - Merged RhoHV (Differential Reflectivity) @ 3km
            &*NOAA_MRMS_MERGED_RHOHV_3KM_CONUS,
        ];

        for layer in layers {
            self.register_layer(layer);
        }
    }

    pub fn remove_unregistered_layers(&mut self) {
        // remove any
        self.active_layers
            .retain(|u| self.registered_sources.contains_key(&u.source_id));
    }
}

impl TileSource for LayerSource {
    fn tile_url(&self, tile_id: TileId) -> String {
        self.tile_url
            .replace("{z}", &tile_id.zoom.to_string())
            .replace("{x}", &tile_id.x.to_string())
            .replace("{y}", &tile_id.y.to_string())
    }

    fn attribution(&self) -> Attribution {
        Attribution {
            text: self.source.clone().leak(),
            url: self.source_link.clone().leak(),
            logo_light: None,
            logo_dark: None,
        }
    }

    fn tile_size(&self) -> u32 {
        self.tile_size
    }

    fn max_zoom(&self) -> u8 {
        self.max_zoom
    }
}

impl Tiles for LayerManager {
    fn at(&mut self, tile_id: TileId) -> Vec<TextureWithUv> {
        let mut layers = vec![];
        for layer in self.active_layers.iter().rev().filter(|u| u.visible) {
            let layer = self.individual_sources.entry(layer.source_id)
                .or_insert_with(|| {
                        WHttpTiles(Rc::new(RefCell::new(HttpTiles::new(
                            self.registered_sources
                                .get(&layer.source_id)
                                .unwrap()
                                .clone(),
                            self.ctx.as_ref().unwrap().clone(),
                        ))))
                });
            let tiles_here = layer.0.borrow_mut().at(tile_id);
            layers.extend(tiles_here);
        }
        layers
    }

    fn attribution(&self) -> Attribution {
        Attribution {
            text: "our sources",
            url: "https://weather.ax/credits",
            logo_light: None,
            logo_dark: None,
        }
    }

    fn tile_size(&self) -> u32 {
        256
    }
}