mod editor;
mod value;
pub mod cursor;
pub use cursor::Cursor;
pub use value::Value;
use editor::Editor;
use crate::{
    keyboard, layout,
    mouse::{self, click},
    text, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle,
    Size, Widget,
};
use std::u32;
#[allow(missing_debug_implementations)]
pub struct TextInput<'a, Message, Renderer: self::Renderer> {
    state: &'a mut State,
    placeholder: String,
    value: Value,
    is_secure: bool,
    font: Renderer::Font,
    width: Length,
    max_width: u32,
    padding: u16,
    size: Option<u16>,
    on_change: Box<dyn Fn(String) -> Message>,
    on_submit: Option<Message>,
    style: Renderer::Style,
}
impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> {
    
    
    
    
    
    
    
    
    
    
    pub fn new<F>(
        state: &'a mut State,
        placeholder: &str,
        value: &str,
        on_change: F,
    ) -> Self
    where
        F: 'static + Fn(String) -> Message,
    {
        TextInput {
            state,
            placeholder: String::from(placeholder),
            value: Value::new(value),
            is_secure: false,
            font: Default::default(),
            width: Length::Fill,
            max_width: u32::MAX,
            padding: 0,
            size: None,
            on_change: Box::new(on_change),
            on_submit: None,
            style: Renderer::Style::default(),
        }
    }
    
    
    
    pub fn password(mut self) -> Self {
        self.is_secure = true;
        self
    }
    
    
    
    
    pub fn font(mut self, font: Renderer::Font) -> Self {
        self.font = font;
        self
    }
    
    
    
    pub fn width(mut self, width: Length) -> Self {
        self.width = width;
        self
    }
    
    
    
    pub fn max_width(mut self, max_width: u32) -> Self {
        self.max_width = max_width;
        self
    }
    
    
    
    pub fn padding(mut self, units: u16) -> Self {
        self.padding = units;
        self
    }
    
    
    
    pub fn size(mut self, size: u16) -> Self {
        self.size = Some(size);
        self
    }
    
    
    
    
    pub fn on_submit(mut self, message: Message) -> Self {
        self.on_submit = Some(message);
        self
    }
    
    
    
    pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
        self.style = style.into();
        self
    }
    
    
    
    pub fn state(&self) -> &State {
        self.state
    }
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
    for TextInput<'a, Message, Renderer>
where
    Renderer: self::Renderer,
    Message: Clone,
{
    fn width(&self) -> Length {
        self.width
    }
    fn height(&self) -> Length {
        Length::Shrink
    }
    fn layout(
        &self,
        renderer: &Renderer,
        limits: &layout::Limits,
    ) -> layout::Node {
        let padding = self.padding as f32;
        let text_size = self.size.unwrap_or(renderer.default_size());
        let limits = limits
            .pad(padding)
            .width(self.width)
            .max_width(self.max_width)
            .height(Length::Units(text_size));
        let mut text = layout::Node::new(limits.resolve(Size::ZERO));
        text.move_to(Point::new(padding, padding));
        layout::Node::with_children(text.size().pad(padding), vec![text])
    }
    fn on_event(
        &mut self,
        event: Event,
        layout: Layout<'_>,
        cursor_position: Point,
        messages: &mut Vec<Message>,
        renderer: &Renderer,
        clipboard: Option<&dyn Clipboard>,
    ) {
        match event {
            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
                let is_clicked = layout.bounds().contains(cursor_position);
                if is_clicked {
                    let text_layout = layout.children().next().unwrap();
                    let target = cursor_position.x - text_layout.bounds().x;
                    let click = mouse::Click::new(
                        cursor_position,
                        self.state.last_click,
                    );
                    match click.kind() {
                        click::Kind::Single => {
                            if target > 0.0 {
                                let value = if self.is_secure {
                                    self.value.secure()
                                } else {
                                    self.value.clone()
                                };
                                let position = renderer.find_cursor_position(
                                    text_layout.bounds(),
                                    self.font,
                                    self.size,
                                    &value,
                                    &self.state,
                                    target,
                                );
                                self.state.cursor.move_to(position);
                            } else {
                                self.state.cursor.move_to(0);
                            }
                        }
                        click::Kind::Double => {
                            if self.is_secure {
                                self.state.cursor.select_all(&self.value);
                            } else {
                                let position = renderer.find_cursor_position(
                                    text_layout.bounds(),
                                    self.font,
                                    self.size,
                                    &self.value,
                                    &self.state,
                                    target,
                                );
                                self.state.cursor.select_range(
                                    self.value.previous_start_of_word(position),
                                    self.value.next_end_of_word(position),
                                );
                            }
                        }
                        click::Kind::Triple => {
                            self.state.cursor.select_all(&self.value);
                        }
                    }
                    self.state.last_click = Some(click);
                }
                self.state.is_dragging = is_clicked;
                self.state.is_focused = is_clicked;
            }
            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => {
                self.state.is_dragging = false;
            }
            Event::Mouse(mouse::Event::CursorMoved { x, .. }) => {
                if self.state.is_dragging {
                    let text_layout = layout.children().next().unwrap();
                    let target = x - text_layout.bounds().x;
                    if target > 0.0 {
                        let value = if self.is_secure {
                            self.value.secure()
                        } else {
                            self.value.clone()
                        };
                        let position = renderer.find_cursor_position(
                            text_layout.bounds(),
                            self.font,
                            self.size,
                            &value,
                            &self.state,
                            target,
                        );
                        self.state.cursor.select_range(
                            self.state.cursor.start(&value),
                            position,
                        );
                    }
                }
            }
            Event::Keyboard(keyboard::Event::CharacterReceived(c))
                if self.state.is_focused
                    && self.state.is_pasting.is_none()
                    && !c.is_control() =>
            {
                let mut editor =
                    Editor::new(&mut self.value, &mut self.state.cursor);
                editor.insert(c);
                let message = (self.on_change)(editor.contents());
                messages.push(message);
            }
            Event::Keyboard(keyboard::Event::KeyPressed {
                key_code,
                modifiers,
            }) if self.state.is_focused => match key_code {
                keyboard::KeyCode::Enter => {
                    if let Some(on_submit) = self.on_submit.clone() {
                        messages.push(on_submit);
                    }
                }
                keyboard::KeyCode::Backspace => {
                    if platform::is_jump_modifier_pressed(modifiers)
                        && self.state.cursor.selection(&self.value).is_none()
                    {
                        if self.is_secure {
                            let cursor_pos = self.state.cursor.end(&self.value);
                            self.state.cursor.select_range(0, cursor_pos);
                        } else {
                            self.state.cursor.select_left_by_words(&self.value);
                        }
                    }
                    let mut editor =
                        Editor::new(&mut self.value, &mut self.state.cursor);
                    editor.backspace();
                    let message = (self.on_change)(editor.contents());
                    messages.push(message);
                }
                keyboard::KeyCode::Delete => {
                    if platform::is_jump_modifier_pressed(modifiers)
                        && self.state.cursor.selection(&self.value).is_none()
                    {
                        if self.is_secure {
                            let cursor_pos = self.state.cursor.end(&self.value);
                            self.state
                                .cursor
                                .select_range(cursor_pos, self.value.len());
                        } else {
                            self.state
                                .cursor
                                .select_right_by_words(&self.value);
                        }
                    }
                    let mut editor =
                        Editor::new(&mut self.value, &mut self.state.cursor);
                    editor.delete();
                    let message = (self.on_change)(editor.contents());
                    messages.push(message);
                }
                keyboard::KeyCode::Left => {
                    if platform::is_jump_modifier_pressed(modifiers)
                        && !self.is_secure
                    {
                        if modifiers.shift {
                            self.state.cursor.select_left_by_words(&self.value);
                        } else {
                            self.state.cursor.move_left_by_words(&self.value);
                        }
                    } else if modifiers.shift {
                        self.state.cursor.select_left(&self.value)
                    } else {
                        self.state.cursor.move_left(&self.value);
                    }
                }
                keyboard::KeyCode::Right => {
                    if platform::is_jump_modifier_pressed(modifiers)
                        && !self.is_secure
                    {
                        if modifiers.shift {
                            self.state
                                .cursor
                                .select_right_by_words(&self.value);
                        } else {
                            self.state.cursor.move_right_by_words(&self.value);
                        }
                    } else if modifiers.shift {
                        self.state.cursor.select_right(&self.value)
                    } else {
                        self.state.cursor.move_right(&self.value);
                    }
                }
                keyboard::KeyCode::Home => {
                    if modifiers.shift {
                        self.state.cursor.select_range(
                            self.state.cursor.start(&self.value),
                            0,
                        );
                    } else {
                        self.state.cursor.move_to(0);
                    }
                }
                keyboard::KeyCode::End => {
                    if modifiers.shift {
                        self.state.cursor.select_range(
                            self.state.cursor.start(&self.value),
                            self.value.len(),
                        );
                    } else {
                        self.state.cursor.move_to(self.value.len());
                    }
                }
                keyboard::KeyCode::V => {
                    if platform::is_copy_paste_modifier_pressed(modifiers) {
                        if let Some(clipboard) = clipboard {
                            let content = match self.state.is_pasting.take() {
                                Some(content) => content,
                                None => {
                                    let content: String = clipboard
                                        .content()
                                        .unwrap_or(String::new())
                                        .chars()
                                        .filter(|c| !c.is_control())
                                        .collect();
                                    Value::new(&content)
                                }
                            };
                            let mut editor = Editor::new(
                                &mut self.value,
                                &mut self.state.cursor,
                            );
                            editor.paste(content.clone());
                            let message = (self.on_change)(editor.contents());
                            messages.push(message);
                            self.state.is_pasting = Some(content);
                        }
                    } else {
                        self.state.is_pasting = None;
                    }
                }
                keyboard::KeyCode::A => {
                    if platform::is_copy_paste_modifier_pressed(modifiers) {
                        self.state.cursor.select_all(&self.value);
                    }
                }
                keyboard::KeyCode::Escape => {
                    self.state.is_focused = false;
                    self.state.is_dragging = false;
                    self.state.is_pasting = None;
                }
                _ => {}
            },
            Event::Keyboard(keyboard::Event::KeyReleased {
                key_code, ..
            }) => match key_code {
                keyboard::KeyCode::V => {
                    self.state.is_pasting = None;
                }
                _ => {}
            },
            _ => {}
        }
    }
    fn draw(
        &self,
        renderer: &mut Renderer,
        _defaults: &Renderer::Defaults,
        layout: Layout<'_>,
        cursor_position: Point,
    ) -> Renderer::Output {
        let bounds = layout.bounds();
        let text_bounds = layout.children().next().unwrap().bounds();
        if self.is_secure {
            self::Renderer::draw(
                renderer,
                bounds,
                text_bounds,
                cursor_position,
                self.font,
                self.size.unwrap_or(renderer.default_size()),
                &self.placeholder,
                &self.value.secure(),
                &self.state,
                &self.style,
            )
        } else {
            self::Renderer::draw(
                renderer,
                bounds,
                text_bounds,
                cursor_position,
                self.font,
                self.size.unwrap_or(renderer.default_size()),
                &self.placeholder,
                &self.value,
                &self.state,
                &self.style,
            )
        }
    }
    fn hash_layout(&self, state: &mut Hasher) {
        use std::{any::TypeId, hash::Hash};
        struct Marker;
        TypeId::of::<Marker>().hash(state);
        self.width.hash(state);
        self.max_width.hash(state);
        self.padding.hash(state);
        self.size.hash(state);
    }
}
pub trait Renderer: text::Renderer + Sized {
    
    type Style: Default;
    
    
    
    fn measure_value(&self, value: &str, size: u16, font: Self::Font) -> f32;
    
    
    
    
    
    
    
    
    fn offset(
        &self,
        text_bounds: Rectangle,
        font: Self::Font,
        size: u16,
        value: &Value,
        state: &State,
    ) -> f32;
    
    
    
    
    
    
    
    
    
    
    
    
    
    fn draw(
        &mut self,
        bounds: Rectangle,
        text_bounds: Rectangle,
        cursor_position: Point,
        font: Self::Font,
        size: u16,
        placeholder: &str,
        value: &Value,
        state: &State,
        style: &Self::Style,
    ) -> Self::Output;
    
    
    
    
    fn find_cursor_position(
        &self,
        text_bounds: Rectangle,
        font: Self::Font,
        size: Option<u16>,
        value: &Value,
        state: &State,
        x: f32,
    ) -> usize {
        let size = size.unwrap_or(self.default_size());
        let offset = self.offset(text_bounds, font, size, &value, &state);
        find_cursor_position(
            self,
            &value,
            font,
            size,
            x + offset,
            0,
            value.len(),
        )
    }
}
impl<'a, Message, Renderer> From<TextInput<'a, Message, Renderer>>
    for Element<'a, Message, Renderer>
where
    Renderer: 'a + self::Renderer,
    Message: 'a + Clone,
{
    fn from(
        text_input: TextInput<'a, Message, Renderer>,
    ) -> Element<'a, Message, Renderer> {
        Element::new(text_input)
    }
}
#[derive(Debug, Default, Clone)]
pub struct State {
    is_focused: bool,
    is_dragging: bool,
    is_pasting: Option<Value>,
    last_click: Option<mouse::Click>,
    cursor: Cursor,
    
}
impl State {
    
    
    
    pub fn new() -> Self {
        Self::default()
    }
    
    
    
    pub fn focused() -> Self {
        Self {
            is_focused: true,
            is_dragging: false,
            is_pasting: None,
            last_click: None,
            cursor: Cursor::default(),
        }
    }
    
    
    
    pub fn is_focused(&self) -> bool {
        self.is_focused
    }
    
    
    
    
    pub fn cursor(&self) -> Cursor {
        self.cursor
    }
    
    
    
    
    pub fn move_cursor_to_front(&mut self) {
        self.cursor.move_to(0);
    }
    
    
    
    
    pub fn move_cursor_to_end(&mut self) {
        self.cursor.move_to(usize::MAX);
    }
    
    
    
    
    pub fn move_cursor_to(&mut self, position: usize) {
        self.cursor.move_to(position);
    }
}
fn find_cursor_position<Renderer: self::Renderer>(
    renderer: &Renderer,
    value: &Value,
    font: Renderer::Font,
    size: u16,
    target: f32,
    start: usize,
    end: usize,
) -> usize {
    if start >= end {
        if start == 0 {
            return 0;
        }
        let prev = value.until(start - 1);
        let next = value.until(start);
        let prev_width = renderer.measure_value(&prev.to_string(), size, font);
        let next_width = renderer.measure_value(&next.to_string(), size, font);
        if next_width - target > target - prev_width {
            return start - 1;
        } else {
            return start;
        }
    }
    let index = (end - start) / 2;
    let subvalue = value.until(start + index);
    let width = renderer.measure_value(&subvalue.to_string(), size, font);
    if width > target {
        find_cursor_position(
            renderer,
            value,
            font,
            size,
            target,
            start,
            start + index,
        )
    } else {
        find_cursor_position(
            renderer,
            value,
            font,
            size,
            target,
            start + index + 1,
            end,
        )
    }
}
mod platform {
    use crate::keyboard;
    pub fn is_jump_modifier_pressed(
        modifiers: keyboard::ModifiersState,
    ) -> bool {
        if cfg!(target_os = "macos") {
            modifiers.alt
        } else {
            modifiers.control
        }
    }
    pub fn is_copy_paste_modifier_pressed(
        modifiers: keyboard::ModifiersState,
    ) -> bool {
        if cfg!(target_os = "macos") {
            modifiers.logo
        } else {
            modifiers.control
        }
    }
}