use crate::parser::KVPair;
use crate::parser::{Filter, RValue};
use crate::renderer::context_element::CompareContextElement;
use crate::renderer::context_element::ContextElement;
use crate::renderer::walking::owned_walk_path;
use crate::renderer::Loopable;
use crate::renderer::RenderError;
use crate::renderer::Renderable;
use crate::renderer::WalkError;
use crate::renderer::Walkable;
use std::{cmp::Ordering, collections::HashMap};

/// Copy the data from an RValue to an Owned struct
///
/// In order to get comparisons to work for our `ContextElement` trait
/// objects, we need to be able to use `std::any::Any`. Unfortunately,
/// `Any` requires that the structs do not have a lifetime (so they
/// will have a `'static` lifetime. This means that we cannot have a
/// `<'a>` appended to the struct type, so the struct cannot contain
/// any borrows. Rather than impose the copy cost in the parser, we
/// are imposing the cost of copying the data in the renderer because
/// the parser has no reason to not be able to reference data from the
/// input string.
#[derive(Clone, Debug, PartialEq)]
pub enum OwnedRValue {
    RVPath(OwnedPath),
    RVString(String),
    RVPositiveInteger(u64),
}

#[derive(Clone, Debug, PartialEq)]
pub struct OwnedPath {
    pub keys: Vec<String>,
}

impl From<&RValue<'_>> for OwnedRValue {
    fn from(original: &RValue<'_>) -> Self {
        match original {
            RValue::RVString(text) => OwnedRValue::RVString(text.to_owned()),
            RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath {
                keys: path.keys.iter().map(|k| k.to_string()).collect(),
            }),
            RValue::RVPositiveInteger(num) => OwnedRValue::RVPositiveInteger(*num),
        }
    }
}

#[derive(Debug)]
pub struct ParametersContext {
    params: HashMap<String, OwnedRValue>,
    breadcrumbs: Vec<Box<dyn ContextElement>>,
}

impl ParametersContext {
    pub fn new(breadcrumbs: &Vec<&dyn ContextElement>, params: &Vec<KVPair>) -> ParametersContext {
        let owned_params: HashMap<String, OwnedRValue> = params
            .iter()
            .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value)))
            .collect();
        let owned_breadcrumbs: Vec<Box<dyn ContextElement>> =
            breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect();

        ParametersContext {
            params: owned_params,
            breadcrumbs: owned_breadcrumbs,
        }
    }
}

impl ContextElement for ParametersContext {}

impl Renderable for ParametersContext {
    fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
        // TODO: Would this even ever be called? Won't matter, but I'd
        // like to know. Since it is injected 1 above the current
        // context, we wouldn't be able to access it with `{.}`.
        Ok("[object Object]".to_owned())
    }
}

impl Loopable for ParametersContext {
    fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
        // TODO: Would this even ever be called? Won't matter, but I'd
        // like to know. Since it is injected 1 above the current
        // context, we wouldn't be able to access it with `{.}`.
        vec![self]
    }
}

impl Walkable for ParametersContext {
    fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
        let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?;
        match rval {
            OwnedRValue::RVPath(path) => owned_walk_path(&self.breadcrumbs, &path.keys),
            OwnedRValue::RVString(text) => Ok(text),
            OwnedRValue::RVPositiveInteger(num) => Ok(num),
        }
    }
}

impl Clone for ParametersContext {
    fn clone(&self) -> Self {
        let new_params: HashMap<String, OwnedRValue> = self
            .params
            .iter()
            .map(|(k, v)| (k.clone(), v.clone()))
            .collect();
        let new_breadcrumbs: Vec<Box<dyn ContextElement>> = self
            .breadcrumbs
            .iter()
            .map(|bread| bread.clone_to_box())
            .collect();
        ParametersContext {
            params: new_params,
            breadcrumbs: new_breadcrumbs,
        }
    }
}

impl CompareContextElement for ParametersContext {
    fn equals(&self, other: &dyn ContextElement) -> bool {
        // TODO: Does this ever happen? perhaps I should have a panic here.
        false
    }

    fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
        // TODO: Does this ever happen? perhaps I should have a panic here.
        None
    }
}

impl ContextElement for String {}

impl Renderable for String {
    fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
        Ok(self.clone())
    }
}

impl Loopable for String {
    fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
        if self.is_empty() {
            Vec::new()
        } else {
            vec![self]
        }
    }
}

impl Walkable for String {
    fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
        Err(WalkError::CantWalk)
    }
}

impl CompareContextElement for String {
    fn equals(&self, other: &dyn ContextElement) -> bool {
        // If its a String then compare them directly, otherwise defer
        // to the other type's implementation of CompareContextElement
        // since the end user could add any type.
        match other.to_any().downcast_ref::<Self>() {
            None => other.equals(self),
            Some(other_string) => self == other_string,
        }
    }

    fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
        // If its a string then compare them directly, otherwise defer
        // to the other type's implementation of CompareContextElement
        // since the end user could add any type.
        match other.to_any().downcast_ref::<Self>() {
            None => match other.partial_compare(self) {
                None => None,
                Some(ord) => match ord {
                    Ordering::Equal => Some(Ordering::Equal),
                    Ordering::Greater => Some(Ordering::Less),
                    Ordering::Less => Some(Ordering::Greater),
                },
            },
            Some(other_string) => self.partial_cmp(other_string),
        }
    }
}

impl ContextElement for u64 {}

impl Renderable for u64 {
    fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
        Ok(self.to_string())
    }
}

impl Loopable for u64 {
    fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
        vec![self]
    }
}

impl Walkable for u64 {
    fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
        Err(WalkError::CantWalk)
    }
}

impl CompareContextElement for u64 {
    fn equals(&self, other: &dyn ContextElement) -> bool {
        // If its a u64 then compare them directly, otherwise defer
        // to the other type's implementation of CompareContextElement
        // since the end user could add any type.
        match other.to_any().downcast_ref::<Self>() {
            None => other.equals(self),
            Some(other_num) => self == other_num,
        }
    }

    fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
        // If its a u64 then compare them directly, otherwise defer
        // to the other type's implementation of CompareContextElement
        // since the end user could add any type.
        match other.to_any().downcast_ref::<Self>() {
            None => match other.partial_compare(self) {
                None => None,
                Some(ord) => match ord {
                    Ordering::Equal => Some(Ordering::Equal),
                    Ordering::Greater => Some(Ordering::Less),
                    Ordering::Less => Some(Ordering::Greater),
                },
            },
            Some(other_num) => self.partial_cmp(other_num),
        }
    }
}