
use godot::prelude::*;
use godot::classes::{Label, Engine};

#[derive(GodotClass)]
#[class(base=Label)]
struct ScramblingText {
    #[export(multiline)]
    chars: GString,
    #[export]
    scrambling: bool,
    #[export]
    speed: f64,
    #[export]
    duration: f64,
    #[export]
    infinite: bool,
    forbidden_chars: Option<Vec<u8>>,
    original_text: Option<String>,
    time_since_scramble: f64, //[DOC] This is the variable used for measuring time since the letters have been scrambled
    time_since_step: f64, //[DOC] This is the variable used for measuring time since the amount of static letters was changed
    step_iterator: i32,
    base: Base<Label>
}


use godot::classes::ILabel;
use rand::Rng;

#[godot_api]
impl ILabel for ScramblingText {
    fn init(base: Base<Label>) -> Self {

        let editor_hint = Engine::singleton().is_editor_hint();

        Self {
            chars: GString::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ124567890".to_string()),
            forbidden_chars: Some(vec![0x20, 0x0A]),
            scrambling: false,
            speed: 15.0,
            duration: 1.0,
            infinite: editor_hint,
            original_text: None,
            step_iterator: 0,     //[DOC] This is the iterator used for changing which letters to scramble
            time_since_scramble: 0.0,
            time_since_step: 0.0,
            base
        }

    }

    fn set_property(&mut self, property: StringName, value: Variant) -> bool {
         if property == StringName::from("scrambling") {
            let boolean_value = value.booleanize();
            let original_text = self.original_text.clone();
            if !boolean_value {
                Self::set_text(self, GString::from(original_text.unwrap_or("Hello World!".to_string())));
                self.step_iterator = 0;
            }
            self.scrambling = boolean_value;
            return false
         }
         else if property == StringName::from("infinite") {
            self.step_iterator = 0;
            return false
         }

         else if property == StringName::from("text") {
            self.original_text = Some(value.to_string());
            return false
         }

         else {
            return false
         }
        
    }

    fn enter_tree(&mut self) {
        let text = Self::get_text(self);
        self.original_text = Some(text.to_string());
    }

    fn process(&mut self, delta: f64) {
        if self.scrambling {
            let original_text = self.original_text.clone().unwrap();
            let duration = self.duration;
            let speed = self.speed;

            self.time_since_scramble += delta;
            self.time_since_step += delta;
            
            if !self.infinite && self.time_since_step >= duration {
                self.time_since_step = 0.0;
                self.step_iterator += 1;
            }

            if self.time_since_scramble >= duration / speed {
                self.time_since_scramble = 0.0;
                let chars = self.chars.to_string();
                let forbidden_chars = self.forbidden_chars.clone().unwrap();
                let left = Self::slice_left(original_text.clone(), self.step_iterator);
                let right = Self::slice_right(original_text.clone(), self.step_iterator);
                self.base_mut().set_text(GString::from(String::from(left + &Self::get_glitch_text(right, chars, forbidden_chars))));

            }

            if original_text.len() as i32 <= self.step_iterator {
                self.time_since_scramble = 0.0;
                self.time_since_step = 0.0;
                self.step_iterator = 0;
                self.scrambling = false;
                self.base_mut().set_text(GString::from(original_text));
            }
            
        }
    }
}

#[godot_api]
impl ScramblingText {

    #[func]
    fn start(&mut self) {
        self.scrambling = true;
    }

    fn get_glitch_text(text: String, chars: String, forbidden_characters: Vec<u8>) -> String {

        let mut rng = rand::thread_rng();
        
        let collect: Vec<u8> = text.bytes()
        .map(|b| if !forbidden_characters.contains(&b) { chars.to_string().as_bytes()[rng.gen_range(0..chars.len())] } else { b })
        .collect();
        return String::from_utf8(collect).unwrap()

    }
    #[func]
    fn set_text(&mut self, text: GString) {
        self.base_mut().set_text(text)
    }

    #[func]
    fn get_text(&mut self) -> GString {
        return self.base_mut().get_text()
    }

    fn slice_left(text: String, amount: i32) -> String {
        // let len: i32 = text.chars().count() as i32;
        let mut res: String = String::new();
        for i in 0..amount {
            res.push(text.chars().nth(i as usize).unwrap());
        }
        res
    }
    

    fn slice_right(text: String, amount: i32) -> String {
        let res = text.clone().split_off(amount as usize);
        return res
    } 


}