use crate::interpreter::{data::Data, pointer::Pointer};
use std::{collections::HashMap, fmt::Debug};

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SimpleAddress(usize);

impl SimpleAddress {
    pub fn as_pointer(self) -> Pointer {
        Pointer::Address(self)
    }
    pub fn as_data(self) -> Data {
        Data::Pointer(self.as_pointer())
    }

    pub const fn zero() -> Self {
        SimpleAddress(0)
    }
}

pub trait DataStore: Debug {
    fn len(&self) -> usize;
    fn push(&mut self, data: Data) -> SimpleAddress;
    fn access(&self, addr: SimpleAddress) -> &Data;
    fn set(&mut self, addr: SimpleAddress, data: Data);
    fn peel_layer(self: Box<Self>) -> (Box<dyn DataStore>, MemoryLayer);
    fn add_layer(self: Box<Self>) -> Box<dyn DataStore>;
    fn has_layer(&self) -> bool;
}

impl DataStore for Vec<Data> {
    fn len(&self) -> usize {
        self.len()
    }
    fn push(&mut self, data: Data) -> SimpleAddress {
        let n = self.len();
        self.push(data);
        SimpleAddress(n)
    }
    fn access(&self, addr: SimpleAddress) -> &Data {
        &self[addr.0]
    }
    fn set(&mut self, addr: SimpleAddress, data: Data) {
        self[addr.0] = data;
    }

    fn peel_layer(self: Box<Self>) -> (Box<dyn DataStore>, MemoryLayer) {
        panic!("DataStore::peel_layer called on a non-peelable layer")
    }
    fn add_layer(self: Box<Self>) -> Box<dyn DataStore> {
        Box::new(LayeredDataStore::new(self))
    }

    fn has_layer(&self) -> bool {
        false
    }
}

pub type MemoryLayer = HashMap<SimpleAddress, Data>;

#[derive(Debug)]
struct LayeredDataStore {
    layer_len: usize,
    under: Box<dyn DataStore>,
    layer: MemoryLayer,
}

impl DataStore for LayeredDataStore {
    fn len(&self) -> usize {
        self.under.len()
    }
    fn push(&mut self, data: Data) -> SimpleAddress {
        self.under.push(data)
    }
    fn access(&self, addr: SimpleAddress) -> &Data {
        self.layer
            .get(&addr)
            .unwrap_or_else(|| self.under.access(addr))
    }
    fn set(&mut self, addr: SimpleAddress, data: Data) {
        if addr.0 < self.layer_len {
            self.layer.insert(addr, data);
        } else {
            self.under.set(addr, data);
        }
    }

    fn peel_layer(self: Box<Self>) -> (Box<dyn DataStore>, MemoryLayer) {
        self.destructure()
    }
    fn add_layer(self: Box<Self>) -> Box<dyn DataStore> {
        Box::new(LayeredDataStore::new(self))
    }

    fn has_layer(&self) -> bool {
        true
    }
}

impl LayeredDataStore {
    fn new(under: Box<dyn DataStore>) -> Self {
        let layer_len = under.len();
        let layer = HashMap::new();
        LayeredDataStore {
            layer_len,
            under,
            layer,
        }
    }
    fn destructure(self) -> (Box<dyn DataStore>, MemoryLayer) {
        (self.under, self.layer)
    }
}
