/// This module specifies the error types returned by the `Window` struct methods.
pub mod error;

use std::sync::mpsc::Receiver;

use self::error::WindowError;
use regex::Regex;

/// This enum specifies, within the `fowlhouse_bay::window::Config` struct, which channel is used to receive window messages.
///
/// # Variants
/// 
/// * `StandardInputChannel` - windows using this variant will receive messages from the sender end of the channel returned after calling `fowlhouse_bay::Term::new()`, multiple
/// windows can listen on this channel
/// * `Channel(Receiver<String>)` - while creating a new window you can pass a `std::sync::mpsc::Receiver<String>` to the `Channel` variant. The new window will display messages
/// send to the window through this channel. So far it is only possible for a single window to listen on a user passed channel.
pub enum Source {
    StandardInputChannel,
    Channel(Receiver<String>)
}

/// This enum is used to specify the tui window style within the `fowlhouse_bay::window::Config` struct.
/// There are three different window styles available: `Light`, `Heavy`, `DoubleLine`.
/// 
/// ```text
/// ╭Light:─────────────────────────╮
/// │                               │
/// ╰───────────────────────────────╯
/// 
/// ┏Heavy:━━━━━━━━━━━━━━━━━━━━━━━━━┓
/// ┃                               ┃
/// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
///
/// ╔DoubleLine:════════════════════╗
/// ║                               ║
/// ╚═══════════════════════════════╝
/// ```
#[derive(Copy, Clone)]
pub enum Style {
    Light,
    Heavy,
    DoubleLine
}

/// This struct is used to configure a new Window instance.
/// 
/// # Struct fields (members)
/// 
/// * `window_name` - the name used to identify the window, this string must match against this regex and the string must be unique
/// * `window_width` - the width of the window described in terminal cols
/// * `window_height` - the height of the window described in terminal rows
/// * `position_x` - the offset from the top left terminal corner (0,0) measured in cols
/// * `position_y` - the offset from the top left terminal corner (0,0) measured in rows
/// * `source` - specifies whether the window should print messages received over the 'StandardInputChannel' or a channel provided by you, please have a look
/// at `fowlhouse_bay::window::Source` for further information on the `Source`enum.
pub struct Config {
    pub window_name: String,
    pub window_width: usize,
    pub window_height: usize,
    pub position_x: usize,
    pub position_y: usize,
    pub source: Source,
    pub style: Style,
}

/// This struct represents a window on the `Term` created tui, a window must be named and the window name must be unique.
/// 
/// The window name is checked against the following regex `^[a-zA-z]{2, 8}$` if the window name does not match the regex, a `WindowError` will be returned.
/// 
/// For all window configuration options head over to the `fowlhouse_bay::window::Config` struct.
/// 
/// You can choose between different window styles, the following example ASCII art shows how the window could look like.
/// Have a look at the `fowlhouse_bay::window::Style` enum for further information.
/// 
///  ```text
/// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
/// ┃ PowerShell                                           _ □ x ┃
/// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
/// ┃╔DoubleLine:══════════════════╗╭Light:─────────────────────╮┃
/// ┃║                             ║│                           │┃
/// ┃║                             ║│                           │┃
/// ┃║                             ║│                           │┃
/// ┃╚═════════════════════════════╝╰───────────────────────────╯┃
/// ┃┏Heavy:━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃
/// ┃┃                                                          ┃┃
/// ┃┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛┃
/// ┃STDIN:~$                                                    ┃
/// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
/// ```
pub struct Window {
    window_name: String,
    window_width: usize,
    window_height: usize,
    position_x: usize,
    position_y: usize,
    source: Source,
    style: Style,
    messages: Vec<String>,
    changes: bool,
}

impl Window {

    /// This method creates and returns new `Window` instance.
    /// 
    /// # Error
    /// 
    /// Returns a `fowlhouse_bay::window::error::WindowError` if the `window_name` field within the
    /// `fowlhouse_bay::window::Config` does not match the following regex `^[a-zA-z]{2, 8}$`.
    pub fn new(config: Config) -> Result<Window, WindowError> {

        let regex = match Regex::new(r"^[a-zA-z]{2, 8}$") {
            Ok(r) => r,
            Err(_) => return Err(WindowError),
        };

        if !regex.is_match(config.window_name.as_str()) {
            return Err(WindowError);
        }

        let window = Window {
            window_name: config.window_name,
            window_width: config.window_width,
            window_height: config.window_height,
            position_x: config.position_x,
            position_y: config.position_y,
            source: config.source,
            style: config.style,
            messages: Vec::with_capacity(1024),
            changes: true,
        };

        Ok(window)
    }

    /// This getter returns the window name.
    pub fn window_name(&self) -> String {
        self.window_name.clone()
    }

    /// This getter returns the window width.
    pub fn window_width(&self) -> usize {
        self.window_width
    }

    /// This getter returns the window height.
    pub fn window_height(&self) -> usize {
        self.window_height
    }

    /// This getter returns the position on the x-axis or in other words, the offset in cols starting at zero in the top left corner
    /// of the terminal window.
    pub fn position_x(&self) -> usize {
        self.position_x
    }

    /// This getter returns the position on the y-axis or in other words, the offset in rows starting at zero in the top left corner
    /// of the terminal window.
    pub fn position_y(&self) -> usize {
        self.position_y
    }

    /// This method returns a reference of the vector holding the messages.
    pub fn messages(&self) -> &Vec<String> {
        &self.messages
    }

    /// This method returns `true` if the window was changed `false` otherwise.
    pub fn changes(&self) -> bool {
        self.changes
    }

    /// This method updates the message vector through receiving new messages from the provided channel
    /// If the window is set to use `StandardInputChannel` this function does nothing.
    pub fn update_messages(&mut self) {
        match &self.source {
            Source::Channel(recv) => {
                for m in recv.try_iter() {
                    if self.messages.len() >= self.messages.capacity() {
                        self.messages.remove(0);
                    }
                    self.messages.push(m);
                    self.changes = true;
                }
            },
            _ => {}
        }
    }

    /// The caller can use this method to reset the `changes` field from `true` to `false`.
    pub fn updated(&mut self) {
        self.changes = false;
    }

    /// This method is used to push messages to the window if the source is set to `StandardInputChannel`.
    pub fn push_message(&mut self, message: String) {
        match &self.source {
            Source::StandardInputChannel => {
                if self.messages.len() >= self.messages.capacity() {
                    self.messages.remove(0);
                }
                self.messages.push(message);
                self.changes = true;
            },
            _ => {}
        }
    }

    /// This method clears the vec holding the window messages.
    pub fn clear_messages(&mut self) {
        self.messages.clear();
        self.changes = true;
    }

    /// This getter is returns the selected window style.
    pub fn style(&self) -> Style {
        self.style
    }

}