use std::ops::{Neg, Not, SubAssign};
use std::ops::{DivAssign, MulAssign};
use std::ops::AddAssign;
use std::str::FromStr;

use indexmap::map::Iter;
use peginator::{ParseError, PegParser};

use crate::{CyberWaifuTags, parser::ast::PromptNode};

mod arithmetic;

impl CyberWaifuTags {
    pub fn push_tag(&mut self, tag: String, weight: f32) -> bool {
        match self.tags.get_mut(&tag) {
            Some(s) => {
                s.add_assign(weight);
                true
            }
            None => {
                self.tags.insert(tag.clone(), weight);
                false
            }
        }
    }
    pub fn remove_tag(&mut self, name: &str) -> Option<f32> {
        self.tags.remove(name)
    }
    pub fn get_tag(&self, name: &str) -> Option<f32> {
        self.tags.get(name).copied()
    }
    pub fn opposite(&mut self) {
        *self = self.clone().neg();
    }
    pub fn set_layout(&mut self, w: u32, h: u32) {
        self.ratio = w as f32 / h as f32
    }
    pub fn get_layout(&self) -> f32 {
        self.ratio
    }
    pub fn transpose(&mut self) {
        *self = self.clone().not()
    }
}

impl<'i> IntoIterator for &'i CyberWaifuTags {
    type Item = (&'i String, &'i f32);
    type IntoIter = Iter<'i, String, f32>;

    fn into_iter(self) -> Self::IntoIter {
        self.tags.iter()
    }
}

impl FromStr for CyberWaifuTags {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut cw = CyberWaifuTags::default();
        PromptNode::parse(s)?.visit(&mut cw);
        Ok(cw)
    }
}

impl Neg for CyberWaifuTags {
    type Output = Self;

    fn neg(self) -> Self::Output {
        Self {
            tags: self.tags.into_iter().map(|(k, v)| (k, -v)).collect(),
            ratio: self.ratio,
        }
    }
}

impl Not for CyberWaifuTags {
    type Output = Self;

    fn not(self) -> Self::Output {
        Self {
            tags: self.tags,
            ratio: self.ratio.recip(),
        }
    }
}

