/*
 * Copyright 2019 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::slice;

use dupe::Clone_;

use crate::iter::def_double_ended_iter;
use crate::iter::def_iter;
use crate::vec2;
use crate::Hashed;
use crate::StarlarkHashValue;

#[derive(Clone_)]
pub(crate) struct Keys<'a, K: 'a, V: 'a> {
    pub(crate) iter: Iter<'a, K, V>,
}

impl<'a, K: 'a, V: 'a> Keys<'a, K, V> {
    #[inline]
    fn map((k, _v): (&'a K, &'a V)) -> <Self as Iterator>::Item {
        k
    }
}

impl<'a, K: 'a, V: 'a> Iterator for Keys<'a, K, V> {
    type Item = &'a K;

    def_iter!();
}

impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Keys<'a, K, V> {
    def_double_ended_iter!();
}

impl<'a, K: 'a, V: 'a> ExactSizeIterator for Keys<'a, K, V> {
    #[inline]
    fn len(&self) -> usize {
        self.iter.len()
    }
}

#[derive(Clone_)]
pub(crate) struct Values<'a, K: 'a, V: 'a> {
    pub(crate) iter: Iter<'a, K, V>,
}

impl<'a, K: 'a, V: 'a> Values<'a, K, V> {
    #[inline]
    fn map((_k, v): (&'a K, &'a V)) -> <Self as Iterator>::Item {
        v
    }
}

impl<'a, K: 'a, V: 'a> Iterator for Values<'a, K, V> {
    type Item = &'a V;

    def_iter!();
}

impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Values<'a, K, V> {
    def_double_ended_iter!();
}

impl<'a, K: 'a, V: 'a> ExactSizeIterator for Values<'a, K, V> {
    #[inline]
    fn len(&self) -> usize {
        self.iter.len()
    }
}

pub(crate) struct ValuesMut<'a, K: 'a, V: 'a> {
    pub(crate) iter: IterMut<'a, K, V>,
}

impl<'a, K: 'a, V: 'a> ValuesMut<'a, K, V> {
    #[inline]
    fn map((_k, v): (&'a K, &'a mut V)) -> <Self as Iterator>::Item {
        v
    }
}

impl<'a, K: 'a, V: 'a> Iterator for ValuesMut<'a, K, V> {
    type Item = &'a mut V;

    def_iter!();
}

impl<'a, K: 'a, V: 'a> DoubleEndedIterator for ValuesMut<'a, K, V> {
    def_double_ended_iter!();
}

impl<'a, K: 'a, V: 'a> ExactSizeIterator for ValuesMut<'a, K, V> {
    #[inline]
    fn len(&self) -> usize {
        self.iter.len()
    }
}

#[derive(Clone_)]
pub(crate) struct Iter<'a, K: 'a, V: 'a> {
    pub(crate) iter: slice::Iter<'a, (K, V)>,
}

impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> {
    type Item = (&'a K, &'a V);

    def_iter!();
}

impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> {
    def_double_ended_iter!();
}

impl<'a, K: 'a, V: 'a> ExactSizeIterator for Iter<'a, K, V> {}

impl<'a, K: 'a, V: 'a> Iter<'a, K, V> {
    #[inline]
    fn map((k, v): &'a (K, V)) -> (&'a K, &'a V) {
        (k, v)
    }
}

#[derive(Clone_)]
pub(crate) struct IterHashed<'a, K: 'a, V: 'a> {
    pub(crate) iter: vec2::Iter<'a, (K, V), StarlarkHashValue>,
}

impl<'a, K: 'a, V: 'a> IterHashed<'a, K, V> {
    #[inline]
    fn map(((k, v), hash): (&'a (K, V), &'a StarlarkHashValue)) -> (Hashed<&'a K>, &'a V) {
        (Hashed::new_unchecked(*hash, k), v)
    }
}

impl<'a, K: 'a, V: 'a> Iterator for IterHashed<'a, K, V> {
    type Item = (Hashed<&'a K>, &'a V);

    def_iter!();
}

impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterHashed<'a, K, V> {
    def_double_ended_iter!();
}

impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterHashed<'a, K, V> {
    #[inline]
    fn len(&self) -> usize {
        self.iter.len()
    }
}

pub(crate) struct IterMut<'a, K: 'a, V: 'a> {
    pub(crate) iter: slice::IterMut<'a, (K, V)>,
}

pub(crate) struct IterMutUnchecked<'a, K: 'a, V: 'a> {
    pub(crate) iter: slice::IterMut<'a, (K, V)>,
}

impl<'a, K: 'a, V: 'a> IterMut<'a, K, V> {
    #[inline]
    fn map((k, v): &mut (K, V)) -> (&K, &mut V) {
        (k, v)
    }
}

impl<'a, K: 'a, V: 'a> IterMutUnchecked<'a, K, V> {
    #[inline]
    fn map((k, v): &mut (K, V)) -> (&mut K, &mut V) {
        (k, v)
    }
}

impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> {
    type Item = (&'a K, &'a mut V);

    def_iter!();
}

impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMut<'a, K, V> {
    def_double_ended_iter!();
}

impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMut<'a, K, V> {
    #[inline]
    fn len(&self) -> usize {
        self.iter.len()
    }
}

impl<'a, K: 'a, V: 'a> Iterator for IterMutUnchecked<'a, K, V> {
    type Item = (&'a mut K, &'a mut V);

    def_iter!();
}

impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMutUnchecked<'a, K, V> {
    def_double_ended_iter!();
}

impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMutUnchecked<'a, K, V> {
    #[inline]
    fn len(&self) -> usize {
        self.iter.len()
    }
}

pub(crate) struct IntoIterHashed<K, V> {
    pub(crate) iter: vec2::IntoIter<(K, V), StarlarkHashValue>,
}

impl<K, V> IntoIterHashed<K, V> {
    #[inline]
    fn map(((k, v), hash): ((K, V), StarlarkHashValue)) -> (Hashed<K>, V) {
        (Hashed::new_unchecked(hash, k), v)
    }
}

impl<K, V> Iterator for IntoIterHashed<K, V> {
    type Item = (Hashed<K>, V);

    def_iter!();
}

impl<K, V> ExactSizeIterator for IntoIterHashed<K, V> {
    #[inline]
    fn len(&self) -> usize {
        self.iter.len()
    }
}

impl<K, V> DoubleEndedIterator for IntoIterHashed<K, V> {
    def_double_ended_iter!();
}

pub(crate) struct IntoIter<K, V> {
    pub(crate) iter: IntoIterHashed<K, V>,
}

impl<K, V> IntoIter<K, V> {
    #[inline]
    fn map((k, v): (Hashed<K>, V)) -> (K, V) {
        (k.into_key(), v)
    }
}

impl<'a, K: 'a, V: 'a> Iterator for IntoIter<K, V> {
    type Item = (K, V);

    def_iter!();
}

impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IntoIter<K, V> {
    def_double_ended_iter!();
}

impl<'a, K: 'a, V: 'a> ExactSizeIterator for IntoIter<K, V> {
    #[inline]
    fn len(&self) -> usize {
        self.iter.len()
    }
}


/*
 * Copyright 2019 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

mod hint;
mod iter;
mod simd;

use std::hash::Hash;
use std::hash::Hasher;
use std::mem;

use allocative::Allocative;

use crate::equivalent::Equivalent;
use crate::hash_value::StarlarkHashValue;
use crate::hashed::Hashed;
pub(crate) use crate::vec2::Vec2;
use crate::vec_map::hint::likely;
pub(crate) use crate::vec_map::iter::IntoIter;
pub(crate) use crate::vec_map::iter::IntoIterHashed;
pub(crate) use crate::vec_map::iter::Iter;
pub(crate) use crate::vec_map::iter::IterHashed;
pub(crate) use crate::vec_map::iter::IterMut;
pub(crate) use crate::vec_map::iter::IterMutUnchecked;
pub(crate) use crate::vec_map::iter::Keys;
pub(crate) use crate::vec_map::iter::Values;
pub(crate) use crate::vec_map::iter::ValuesMut;
use crate::vec_map::simd::find_hash_in_array;

/// Bucket in [`VecMap`].
#[derive(Debug, Clone, Eq, PartialEq, Allocative)]
pub(crate) struct Bucket<K, V> {
    hash: StarlarkHashValue,
    key: K,
    value: V,
}

#[allow(clippy::derived_hash_with_manual_eq)]
impl<K: Hash, V: Hash> Hash for Bucket<K, V> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.hash.hash(state);
        // Ignore the key, because `hash` is already the hash of the key,
        // although maybe not as good hash as what is requested.
        self.value.hash(state);
    }
}

#[derive(Debug, Clone, Allocative)]
pub(crate) struct VecMap<K, V> {
    buckets: Vec2<(K, V), StarlarkHashValue>,
}

impl<K, V> Default for VecMap<K, V> {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl<K, V> VecMap<K, V> {
    #[inline]
    pub(crate) const fn new() -> Self {
        VecMap {
            buckets: Vec2::new(),
        }
    }

    #[inline]
    pub(crate) fn with_capacity(n: usize) -> Self {
        VecMap {
            buckets: Vec2::with_capacity(n),
        }
    }

    pub(crate) fn reserve(&mut self, additional: usize) {
        self.buckets.reserve(additional);
    }

    #[inline]
    pub(crate) fn capacity(&self) -> usize {
        self.buckets.capacity()
    }

    #[inline]
    pub(crate) fn get_index_of_hashed_raw(
        &self,
        hash: StarlarkHashValue,
        mut eq: impl FnMut(&K) -> bool,
    ) -> Option<usize> {
        const _: () = assert!(mem::size_of::<StarlarkHashValue>() == mem::size_of::<u32>());
        let hashes_ints =
            unsafe { &*(self.buckets.bbb() as *const [StarlarkHashValue] as *const [u32]) };
        let mut i = 0;
        while i < hashes_ints.len() {
            i += find_hash_in_array(&hashes_ints[i..], hash.get())?;
            let k = unsafe { &self.buckets.aaa().get_unchecked(i).0 };
            if likely(eq(k)) {
                return Some(i);
            }
            i += 1;
        }
        debug_assert!(i == hashes_ints.len());
        None
    }

    #[inline]
    pub(crate) fn get_index_of_hashed<Q>(&self, key: Hashed<&Q>) -> Option<usize>
    where
        Q: ?Sized + Equivalent<K>,
    {
        self.get_index_of_hashed_raw(key.hash(), |k| key.key().equivalent(k))
    }

    #[inline]
    pub(crate) fn get_index(&self, index: usize) -> Option<(&K, &V)> {
        let ((k, v), _hash) = self.buckets.get(index)?;
        Some((k, v))
    }

    #[inline]
    pub(crate) unsafe fn get_unchecked(&self, index: usize) -> (Hashed<&K>, &V) {
        debug_assert!(index < self.buckets.len());
        let ((key, value), hash) = self.buckets.get_unchecked(index);
        (Hashed::new_unchecked(*hash, key), value)
    }

    #[inline]
    pub(crate) unsafe fn get_unchecked_mut(&mut self, index: usize) -> (Hashed<&K>, &mut V) {
        debug_assert!(index < self.buckets.len());
        let ((key, value), hash) = self.buckets.get_unchecked_mut(index);
        (Hashed::new_unchecked(*hash, key), value)
    }

    #[inline]
    pub(crate) fn insert_hashed_unique_unchecked(&mut self, key: Hashed<K>, value: V) {
        let hash = key.hash();
        self.buckets.push((key.into_key(), value), hash);
    }

    pub(crate) fn remove_hashed_entry<Q>(&mut self, key: Hashed<&Q>) -> Option<(K, V)>
    where
        Q: ?Sized + Equivalent<K>,
    {
        if let Some(index) = self.get_index_of_hashed(key) {
            let (k, v) = self.remove(index);
            Some((k.into_key(), v))
        } else {
            None
        }
    }

    #[inline]
    pub(crate) fn remove(&mut self, index: usize) -> (Hashed<K>, V) {
        let ((key, value), hash) = self.buckets.remove(index);
        (Hashed::new_unchecked(hash, key), value)
    }

    #[inline]
    pub(crate) fn pop(&mut self) -> Option<(Hashed<K>, V)> {
        let ((key, value), hash) = self.buckets.pop()?;
        Some((Hashed::new_unchecked(hash, key), value))
    }

    #[inline]
    pub(crate) fn len(&self) -> usize {
        self.buckets.len()
    }

    #[inline]
    pub(crate) fn is_empty(&self) -> bool {
        self.buckets.is_empty()
    }

    pub(crate) fn clear(&mut self) {
        self.buckets.clear();
    }

    #[inline]
    pub(crate) fn values(&self) -> Values<K, V> {
        Values { iter: self.iter() }
    }

    #[inline]
    pub(crate) fn values_mut(&mut self) -> ValuesMut<K, V> {
        ValuesMut {
            iter: self.iter_mut(),
        }
    }

    #[inline]
    pub(crate) fn keys(&self) -> Keys<K, V> {
        Keys { iter: self.iter() }
    }

    #[inline]
    pub(crate) fn into_iter(self) -> IntoIter<K, V> {
        IntoIter {
            iter: self.into_iter_hashed(),
        }
    }

    #[inline]
    pub(crate) fn iter(&self) -> Iter<K, V> {
        Iter {
            iter: self.buckets.aaa().iter(),
        }
    }

    #[inline]
    pub(crate) fn iter_hashed(&self) -> IterHashed<K, V> {
        IterHashed {
            // Values go first since they terminate first and we can short-circuit
            iter: self.buckets.iter(),
        }
    }

    #[inline]
    pub(crate) fn into_iter_hashed(self) -> IntoIterHashed<K, V> {
        // See the comments on VMIntoIterHash for why this one looks different
        IntoIterHashed {
            iter: self.buckets.into_iter(),
        }
    }

    #[inline]
    pub(crate) fn iter_mut(&mut self) -> IterMut<K, V> {
        IterMut {
            iter: self.buckets.aaa_mut().iter_mut(),
        }
    }

    #[inline]
    pub(crate) fn iter_mut_unchecked(&mut self) -> IterMutUnchecked<K, V> {
        IterMutUnchecked {
            iter: self.buckets.aaa_mut().iter_mut(),
        }
    }

    pub(crate) fn sort_keys(&mut self)
    where
        K: Ord,
    {
        self.buckets.sort_by(|(a, _ah), (b, _bh)| a.0.cmp(&b.0));
    }

    pub(crate) fn is_sorted_by_key(&self) -> bool
    where
        K: Ord,
    {
        self.buckets.aaa().windows(2).all(|w| w[0].0 <= w[1].0)
    }

    /// Equal if entries are equal in the iterator order.
    pub(crate) fn eq_ordered(&self, other: &Self) -> bool
    where
        K: PartialEq,
        V: PartialEq,
    {
        // We compare hashes before comparing keys and values because it is faster
        // (fewer branches, and no comparison of the rest it at lest one hash is different).
        self.buckets.bbb() == other.buckets.bbb() && self.buckets.aaa() == other.buckets.aaa()
    }

    /// Hash entries in the iterator order.
    ///
    /// Note, keys are not hashed, but previously computed hashes are hashed instead.
    pub(crate) fn hash_ordered<H: Hasher>(&self, state: &mut H)
    where
        K: Hash,
        V: Hash,
    {
        for e in self.iter_hashed() {
            e.hash(state);
        }
    }
}


/*
 * Copyright 2019 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#[inline(always)]
pub(crate) fn find_hash_in_array_without_simd(array: &[u32], hash: u32) -> Option<usize> {
    let mut i = 0;
    while i < array.len() {
        if array[i] == hash {
            return Some(i);
        }
        i += 1;
    }
    None
}

#[inline]
pub(crate) fn find_hash_in_array(array: &[u32], hash: u32) -> Option<usize> {
    #[cfg(rust_nightly)]
    unsafe {
        use std::simd::*;

        // 128-bit SIMD is available on x86_64 and aarch64.
        // Also shorter SIMD works better for shorter arrays.
        type T = Simd<u32, 4>;

        if array.len() < T::LANES {
            find_hash_in_array_without_simd(array, hash)
        } else {
            let mut i = 0;
            let hash = T::splat(hash);

            // Process 4 elements at a time except last <= 4 elements.
            while i + T::LANES < array.len() {
                let next_hashes = T::from_slice(array.get_unchecked(i..i + T::LANES));
                let eq = next_hashes.simd_eq(hash);
                if eq.any() {
                    return Some(i + eq.to_bitmask().trailing_zeros() as usize);
                }
                i += T::LANES;
            }

            // Process last <= 4 elements.
            debug_assert!(i >= array.len() - T::LANES);
            debug_assert!(i < array.len());
            let next_hashes = T::from_slice(array.get_unchecked(array.len() - T::LANES..));
            let eq = next_hashes.simd_eq(hash);
            if eq.any() {
                Some(array.len() - T::LANES + eq.to_bitmask().trailing_zeros() as usize)
            } else {
                None
            }
        }
    }
    #[cfg(not(rust_nightly))]
    {
        find_hash_in_array_without_simd(array, hash)
    }
}

#[cfg(test)]
mod tests {
    use crate::vec_map::simd::find_hash_in_array;

    #[test]
    fn test_find_hash_in_array() {
        assert_eq!(None, find_hash_in_array(&[], 77));

        for len in 1..20 {
            let array: Vec<u32> = (0..len).map(|_| 88).collect();
            assert_eq!(None, find_hash_in_array(&array, 77));

            for i in 0..len {
                let array: Vec<u32> = (0..len).map(|j| if j == i { 77 } else { 88 }).collect();
                assert_eq!(Some(i as usize), find_hash_in_array(&array, 77));
            }

            for i in 0..len {
                // Test first index is returned if there are multiple matches.
                let array: Vec<u32> = (0..len).map(|j| if j < i { 88 } else { 77 }).collect();
                assert_eq!(Some(i as usize), find_hash_in_array(&array, 77));
            }
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::thread::sleep;
use std::time::Duration;

use superconsole::style::Stylize;
use superconsole::Component;
use superconsole::Line;
use superconsole::Lines;
use superconsole::Span;
use superconsole::SuperConsole;

const CRATES: &str = include_str!("cargo/crates.txt");
const WIDTH: usize = "=======>                  ".len() - 1;

#[derive(Debug)]
struct LoadingBar<'a> {
    crates: &'a [&'a str],
    iteration: usize,
}

impl<'a> Component for LoadingBar<'a> {
    fn draw_unchecked(
        &self,

        _dimensions: superconsole::Dimensions,
        mode: superconsole::DrawMode,
    ) -> anyhow::Result<superconsole::Lines> {
        let res = match mode {
            superconsole::DrawMode::Normal => {
                const BUILDING: &str = "   Building ";
                let iteration = self.iteration;
                let percentage = iteration as f64 / self.crates.len() as f64;
                let amount = (percentage * WIDTH as f64).ceil() as usize;

                let building = Span::new_styled(BUILDING.to_owned().cyan().bold())?;
                let loading_bar = format!(
                    "[{test:=>bar_amt$}{test2:padding_amt$}] {}/{}: ...",
                    iteration,
                    self.crates.len(),
                    test = ">",
                    test2 = "",
                    bar_amt = amount,
                    padding_amt = WIDTH - amount,
                );
                let loading = Span::new_unstyled(loading_bar)?;
                Line::from_iter([building, loading])
            }
            superconsole::DrawMode::Final => {
                const FINISHED: &str = "   Finished ";
                let finished = Span::new_styled(FINISHED.to_owned().green().bold())?;
                const COMPLETION: &str = "dev [unoptimized + debuginfo] target(s) in 14.45s";
                Line::from_iter([finished, Span::new_unstyled(COMPLETION)?])
            }
        };

        Ok(Lines(vec![res]))
    }
}

fn main() {
    let crates: Vec<_> = CRATES
        .lines()
        .map(|line| line.trim().split_once(char::is_whitespace).unwrap().1)
        .collect();
    let count = crates.len();

    let mut superconsole = SuperConsole::new().unwrap();

    for (i, c) in crates.iter().enumerate() {
        let building = Span::new_styled("  Compiling ".to_owned().green().bold()).unwrap();
        superconsole.emit(Lines(vec![Line::from_iter([
            building,
            Span::new_unstyled(c).unwrap(),
        ])]));
        superconsole
            .render(&LoadingBar {
                crates: &crates,
                iteration: i,
            })
            .unwrap();
        sleep(Duration::from_secs_f64(0.2));
    }

    superconsole
        .finalize(&LoadingBar {
            crates: &crates,
            iteration: count,
        })
        .unwrap();
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Example that demonstrates finalization.

use std::time::Duration;

use derive_more::Display;
use superconsole::components::Component;
use superconsole::components::DrawMode;
use superconsole::Dimensions;
use superconsole::Lines;
use superconsole::SuperConsole;
use tokio::time;

/// A component representing a store greeter.
struct Greeter<'a> {
    name: &'a str,
    store_name: &'a StoreName,
    customers: &'a [CustomerName],
    correct_num: usize,
}

#[derive(Display)]
struct StoreName(String);
#[derive(Display)]
struct CustomerName(String);

impl<'a> Component for Greeter<'a> {
    fn draw_unchecked(&self, _dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        Ok(match mode {
            DrawMode::Normal => {
                // Prints a greeting to the current customer.
                let store_name = self.store_name;
                let customers = self.customers;
                let identification = vec![format!("Hello my name is {}!", self.name)]
                    .try_into()
                    .unwrap();
                let mut messages = vec![identification];
                for customer_name in customers {
                    let greeting = vec![format!("Welcome to {}, {}!", store_name, customer_name)]
                        .try_into()
                        .unwrap();
                    messages.push(greeting);
                }
                Lines(messages)
            }
            DrawMode::Final => {
                // Prints a message about the employee when he or she leaves for the day.
                let store_name = self.store_name;
                let total_customers = self.correct_num;

                let farewell = vec![format!("{} is leaving {}", self.name, store_name)]
                    .try_into()
                    .unwrap();
                let exit_stats =
                    format!("{} greeted {} customers today", self.name, total_customers);

                Lines(vec![farewell, vec![exit_stats].try_into().unwrap()])
            }
        })
    }
}

#[tokio::main]
async fn main() {
    let name = "Alex";
    let mut console = SuperConsole::new().unwrap();

    let people = [
        "Joseph", "Janet", "Bob", "Christie", "Raj", "Sasha", "Rayna", "Veronika", "Russel",
        "David",
    ];
    let store_names = [
        "Target",
        "Target",
        "Target",
        "TJ",
        "TJ",
        "Walmart",
        "Wendys",
        "Wendys",
        "Uwajimaya",
        "DSW",
    ];

    let mut timer = time::interval(Duration::from_secs_f32(0.5));
    let mut last = None;
    for (i, store_name) in store_names.iter().enumerate() {
        console.emit(Lines(vec![vec![i.to_string()].try_into().unwrap()]));
        let customers = (i..std::cmp::min(10, i + 2))
            .map(|x| CustomerName(people[x].to_owned()))
            .collect::<Vec<_>>();
        let store_name = StoreName((*store_name).to_owned());
        let correct_num = i + 1;
        console
            .render(&Greeter {
                name,
                store_name: &store_name,
                customers: &customers,
                correct_num,
            })
            .unwrap();

        last = Some((store_name, customers, correct_num));

        timer.tick().await;
    }

    let (store_name, customers, correct_num) = last.unwrap();
    console
        .finalize(&Greeter {
            name,
            store_name: &store_name,
            customers: &customers,
            correct_num,
        })
        .unwrap();
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! A basic example of using components and emitting content.

use std::time::Duration;
use std::time::Instant;

use superconsole::components::Component;
use superconsole::components::DrawMode;
use superconsole::Dimensions;
use superconsole::Line;
use superconsole::Lines;
use superconsole::SuperConsole;
use tokio::select;
use tokio::time;

/// Prints the seconds elapsed since it was created at each render loop
#[derive(Debug)]
struct Foo {
    created: Instant,
    now: Instant,
}

impl Component for Foo {
    /// Draws the number of seconds that have elapsed since the component was created.
    /// On a second line, draws the string "Hello world!".
    fn draw_unchecked(&self, _dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        Ok(match mode {
            DrawMode::Final => Lines::new(),
            DrawMode::Normal => {
                let elapsed = self.now.duration_since(self.created);
                let line1 = vec![elapsed.as_secs().to_string()].try_into().unwrap();
                let line2 = vec!["Hello world!".to_owned()].try_into().unwrap();
                Lines(vec![line1, line2])
            }
        })
    }
}

/// helper method to generate lines to emit from an arbitrary word:
/// - Line 1: the string.
/// - Line 2: the string, but reversed.
/// - Line 3: "Some stuff to do with strings"
fn process_word(word: String) -> Vec<Line> {
    let echoed = word.clone();
    let reversed: String = word.chars().rev().collect();
    let line1 = vec![echoed, reversed].try_into().unwrap();
    let line2 = vec!["Some stuff to do with strings".to_owned()]
        .try_into()
        .unwrap();
    vec![line1, line2]
}

/// generates a random word using lorum ipsum at half second intervals
async fn task_that_takes_some_time() -> String {
    time::sleep(time::Duration::from_secs_f32(0.5)).await;
    "hello world".to_owned()
}

#[tokio::main]
async fn main() {
    // set up state to be used for rendering
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;

    // set up future to periodically cause re-render
    let delay = Duration::from_secs(1);
    let mut interval = time::interval(delay);

    let created = Instant::now();
    let mut renderer = SuperConsole::new().unwrap();

    // alternate between re-rendering and updating state
    loop {
        select! {
            _ = interval.tick() => {
                renderer.render(&Foo {
                    created,
                    now: Instant::now(),
                }).unwrap();
            }
            word = task_that_takes_some_time() => {
                renderer.emit(Lines(process_word(word)));
            }
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

// If this code needs fixing, make sure you fix the README.md too!

use std::convert::TryInto;

use superconsole::components::bordering::Bordered;
use superconsole::components::bordering::BorderedSpec;
use superconsole::Component;
use superconsole::Dimensions;
use superconsole::DrawMode;
use superconsole::Lines;
use superconsole::SuperConsole;

#[derive(Debug)]
struct HelloWorld;

impl Component for HelloWorld {
    fn draw_unchecked(&self, _dimensions: Dimensions, _mode: DrawMode) -> anyhow::Result<Lines> {
        Ok(Lines(vec![
            vec!["Hello world!".to_owned()].try_into().unwrap(),
        ]))
    }
}

pub fn main() -> anyhow::Result<()> {
    let bordering = BorderedSpec::default();
    let mut superconsole = SuperConsole::new().ok_or_else(|| anyhow::anyhow!("Not a TTY"))?;
    let component = Bordered::new(HelloWorld, bordering);
    superconsole.render(&component)?;
    superconsole.finalize(&component)?;
    Ok(())
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Example that demonstrates stylization.

use std::time::Duration;

use derive_more::Display;
use superconsole::components::Blank;
use superconsole::components::Component;
use superconsole::components::DrawMode;
use superconsole::style::style;
use superconsole::style::Color;
use superconsole::style::Stylize;
use superconsole::Dimensions;
use superconsole::Line;
use superconsole::Lines;
use superconsole::Span;
use superconsole::SuperConsole;
use tokio::time;

/// A component representing a store greeter.
struct Greeter<'a> {
    name: &'a str,
    store_name: &'a StoreName,
    customers: &'a [CustomerName],
}

#[derive(Display)]
struct StoreName(String);
#[derive(Display)]
struct CustomerName(String);

impl<'a> Component for Greeter<'a> {
    /// Prints a greeting to the current customer.
    fn draw_unchecked(&self, _dimensions: Dimensions, _mode: DrawMode) -> anyhow::Result<Lines> {
        let store_name = self.store_name;
        let customers = self.customers;
        let identification = Line::from_iter([
            "Hello my name is ".to_owned().italic().try_into()?,
            style(self.name.to_owned()).bold().try_into()?,
        ]);
        let mut messages = vec![identification];
        for customer_name in customers {
            let greeting = Line::from_iter([Span::new_styled(style(format!(
                "Welcome to {}, {}!",
                store_name, customer_name
            )))?]);
            messages.push(greeting);
        }
        Ok(Lines(messages))
    }
}

#[tokio::main]
async fn main() {
    let name = "Alex";
    let mut console = SuperConsole::new().unwrap();

    let people = [
        "Joseph", "Janet", "Bob", "Christie", "Raj", "Sasha", "Rayna", "Veronika", "Russel",
        "David",
    ];
    let store_names = [
        "Target",
        "Target",
        "Target",
        "TJ",
        "TJ",
        "Walmart",
        "Wendys",
        "Wendys",
        "Uwajimaya",
        "DSW",
    ];

    let mut timer = time::interval(Duration::from_secs_f32(0.5));
    for (i, store_name) in store_names.iter().enumerate() {
        let styled = i.to_string().with(Color::Green).on(Color::Black);
        console.emit(Lines::from_iter([Line::from_iter([styled
            .try_into()
            .unwrap()])]));
        let customers = (i..std::cmp::min(10, i + 2))
            .map(|x| CustomerName(people[x].to_owned()))
            .collect::<Vec<_>>();
        let store_name = StoreName((*store_name).to_owned());

        console
            .render(&Greeter {
                name,
                store_name: &store_name,
                customers: &customers,
            })
            .unwrap();

        timer.tick().await;
    }

    // view the output before it's collapsed
    tokio::time::sleep(Duration::from_secs(1)).await;
    console.finalize(&Blank).unwrap();
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#[cfg(not(windows))]
pub(crate) fn enable_ansi_support() -> anyhow::Result<()> {
    Ok(())
}

#[cfg(windows)]
pub(crate) fn enable_ansi_support() -> anyhow::Result<()> {
    #[derive(Debug, thiserror::Error)]
    #[error("Terminal does not support ANSI escapes")]
    struct AnsiIsNotSupported;

    // This functions has side effect: it enables ANSI support in terminals on Windows.
    // https://github.com/crossterm-rs/crossterm/blob/769b18151c6c177d43c752cc964d2b3d058bcf14/src/ansi_support.rs#L39
    // Note if this function returns false, we cannot buffer ANSI escape sequences.
    if !crossterm::ansi_support::supports_ansi() {
        return Err(AnsiIsNotSupported.into());
    }

    Ok(())
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::io;
use std::io::Write;

use crate::output::BlockingSuperConsoleOutput;
use crate::output::NonBlockingSuperConsoleOutput;
use crate::output::SuperConsoleOutput;
use crate::Dimensions;
use crate::SuperConsole;

/// A builder to create SuperConsole, with more options.
pub struct Builder {
    non_blocking: bool,
    stream: Box<dyn Write + Send + 'static + Sync>,
}

impl Default for Builder {
    fn default() -> Self {
        Builder::new()
    }
}

impl Builder {
    pub fn new() -> Self {
        Self {
            non_blocking: false,
            stream: Box::new(io::stderr()),
        }
    }

    /// Enable non-blocking I/O.
    pub fn non_blocking(&mut self) -> &mut Self {
        self.non_blocking = true;
        self
    }

    /// Write to a different I/O
    pub fn write_to(&mut self, stream: Box<dyn Write + Send + 'static + Sync>) -> &mut Self {
        self.stream = stream;
        self
    }

    /// Build a new SuperConsole if stderr is a TTY.
    pub fn build(self) -> anyhow::Result<Option<SuperConsole>> {
        if !SuperConsole::compatible() {
            return Ok(None);
        }
        Some(self.build_inner(None)).transpose()
    }

    /// Build a new SuperConsole regardless of whether stderr is a TTY.
    pub fn build_forced(self, fallback_size: Dimensions) -> anyhow::Result<SuperConsole> {
        self.build_inner(Some(fallback_size))
    }

    fn build_inner(self, fallback_size: Option<Dimensions>) -> anyhow::Result<SuperConsole> {
        Ok(SuperConsole::new_internal(fallback_size, self.output()?))
    }

    fn output(self) -> anyhow::Result<Box<dyn SuperConsoleOutput>> {
        if self.non_blocking {
            Ok(Box::new(NonBlockingSuperConsoleOutput::new(self.stream)?))
        } else {
            Ok(Box::new(BlockingSuperConsoleOutput::new(self.stream)))
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Getting started truly from scratch is excessive for every user of superconsole.
//! A small set of starter components are provided, exposed below.

use std::fmt::Debug;

pub use alignment::Aligned;
pub use blank::Blank;
pub use bordering::Bordered;
pub use bounding::Bounded;
pub(crate) use canvas::Canvas;
pub use padding::Padded;
pub use splitting::Split;

pub use crate::components::draw_horizontal::DrawHorizontal;
pub use crate::components::draw_vertical::DrawVertical;
use crate::Dimensions;
use crate::Lines;

pub mod alignment;
mod blank;
pub mod bordering;
mod bounding;
mod canvas;
mod draw_horizontal;
mod draw_vertical;
pub(crate) mod echo;
pub mod padding;
pub mod splitting;

/// Used to mark whether a draw is final.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum DrawMode {
    Normal,
    Final,
}

/// Components are pluggable drawers that output lines of formatted text.
/// They are composable (eventually) and re-render in place at each render.
pub trait Component {
    /// This method is to be implemented for components to provide the `draw` method.
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines>;

    /// Interprets the current caller state to create its drawing.
    /// Dimensions refers to the maximum (width, height) this component may use.
    /// The mode refers to if this is the final time the component will be drawn.
    /// If a child component is too large to fit in the dimensions, it is truncated.
    fn draw(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        let mut res = self.draw_unchecked(dimensions, mode)?;
        res.shrink_lines_to_dimensions(dimensions);
        Ok(res)
    }
}

impl Component for Box<dyn Component> {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        (**self).draw_unchecked(dimensions, mode)
    }
}

impl Component for Box<dyn Component + Send> {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        (**self).draw_unchecked(dimensions, mode)
    }
}

// TODO(nga): this is not really needed.
impl<C: Component> Component for Box<C> {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        (**self).draw_unchecked(dimensions, mode)
    }
}

impl<'a> Component for &'a dyn Component {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        (**self).draw_unchecked(dimensions, mode)
    }
}

impl<'a> Component for &'a (dyn Component + Send) {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        (**self).draw_unchecked(dimensions, mode)
    }
}

impl<'a, C: Component> Component for &'a C {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        (**self).draw_unchecked(dimensions, mode)
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Provides a variety of utilities for working with [`Line`s](Line).

pub use line::Line;
pub use lines::Lines;
pub use span::Span;

mod line;
mod lines;
mod span;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::cmp;

use crate::Lines;

/// Denotes a rectangular area.
///
/// Passed to [`Component`](crate::Component)s to give valid drawing area.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct Dimensions {
    pub width: usize,
    pub height: usize,
}

/// Denotes a particular axis (x or y).  Used for several Components which are generalized over direction.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
    Horizontal,
    Vertical,
}

impl From<(u16, u16)> for Dimensions {
    fn from((x, y): (u16, u16)) -> Self {
        Self {
            width: x.into(),
            height: y.into(),
        }
    }
}

impl From<(usize, usize)> for Dimensions {
    fn from((width, height): (usize, usize)) -> Self {
        Self { width, height }
    }
}

impl Dimensions {
    pub fn new(width: usize, height: usize) -> Self {
        Self { width, height }
    }

    pub fn dimension(self, direction: Direction) -> usize {
        match direction {
            Direction::Horizontal => self.width,
            Direction::Vertical => self.height,
        }
    }

    /// Best effort conversion - it will truncate to the nearest valid dimension.
    pub fn multiply(self, multiplicand: f64, direction: Direction) -> Self {
        fn mul(lhs: usize, rhs: f64) -> usize {
            let lhs = lhs as f64; // this might truncate the size if too large
            let result = lhs * rhs;
            result as usize // this might not be an exact dimension.
        }

        let Self { width, height } = self;
        match direction {
            Direction::Horizontal => Self {
                width: mul(width, multiplicand),
                height,
            },
            Direction::Vertical => Self {
                width,
                height: mul(height, multiplicand),
            },
        }
    }

    pub fn saturating_sub(self, subtractor: usize, direction: Direction) -> Self {
        let Self { width, height } = self;
        match direction {
            Direction::Horizontal => Self {
                width: width.saturating_sub(subtractor),
                height,
            },
            Direction::Vertical => Self {
                width,
                height: height.saturating_sub(subtractor),
            },
        }
    }

    /// Finds the size of a [`Component`](crate::Component)'s output in a given dimension.
    /// Truncates the size to at most `u16::MAX`.
    // ptr args allowed because line trait only implemented on `Vec<line>`
    #[allow(clippy::ptr_arg)]
    pub fn dimension_from_output_truncated(output: &Lines, direction: Direction) -> usize {
        match direction {
            Direction::Horizontal => output.max_line_length(),
            Direction::Vertical => output.len(),
        }
    }

    /// Returns the smallest bounding box that fits in both dimensions.
    pub fn intersect(self, Self { width, height }: Self) -> Self {
        Self {
            width: cmp::min(self.width, width),
            height: cmp::min(self.height, height),
        }
    }

    /// Returns the smallest bounding box in which both Dimensions fit.
    pub fn union(self, Self { width, height }: Self) -> Self {
        Self {
            width: cmp::max(self.width, width),
            height: cmp::max(self.height, height),
        }
    }

    /// Returns true iff the passed dimension is smaller than or equal to in both dimensions.
    pub fn contains(self, Self { width, height }: Self) -> bool {
        width <= self.width && height <= self.height
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_intersect() {
        let lhs = Dimensions {
            width: 5,
            height: 10,
        };
        let rhs = Dimensions {
            width: 8,
            height: 3,
        };
        let intersect = lhs.intersect(rhs);
        assert_eq!(
            intersect,
            Dimensions {
                width: 5,
                height: 3
            }
        );
    }

    #[test]
    fn test_union() {
        let lhs = Dimensions {
            width: 5,
            height: 10,
        };
        let rhs = Dimensions {
            width: 8,
            height: 3,
        };
        let union = lhs.union(rhs);
        assert_eq!(
            union,
            Dimensions {
                width: 8,
                height: 10
            }
        );
    }

    #[test]
    fn test_contains() {
        let lhs = Dimensions {
            width: 8,
            height: 10,
        };
        let rhs = Dimensions {
            width: 5,
            height: 3,
        };
        assert!(lhs.contains(rhs));
        assert!(!rhs.contains(lhs));
        assert!(lhs.contains(lhs));
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! The superconsole crate provides a handler and building blocks for powerful, yet minimally intrusive TUIs.
//! Built on-top of [`crossterm`](crossterm), it cross-compiles on Windows 7+, Linux, and MacOS.
//!
//! Rendering is handled by [`SuperConsole`](SuperConsole), which draws to [`stdout`](std::io::stdout).
//! The caller is responsible for re-rendering whenever necessary.
//! User input will cause aberrations in output; similarly, one should also not produce output from other sources while superconsole is active.
//!
//! The rendering can be divided into two principle components:
//! * In the *scratch* area, the previous content is overwritten at each render.
//! * In the *emitted* area, lines scroll away above the scratch with various diagnostic output.
//! Components live in the scratch area.
//!
//! A set of pre-baked composition and testing oriented components are provided in the [`components`](components) module.

pub use components::Component;
pub use components::DrawMode;
pub use content::Line;
pub use content::Lines;
pub use content::Span;
pub use dimensions::Dimensions;
pub use dimensions::Direction;

pub use crate::builder::Builder;
pub use crate::superconsole::SuperConsole;

pub(crate) mod ansi_support;
pub mod builder;
pub mod components;
pub mod content;
mod dimensions;
pub mod output;
pub mod style;
mod superconsole;
pub mod testing;
pub(crate) mod vec_as_fmt_write;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::any::Any;
use std::io;
use std::io::Write;
use std::thread::JoinHandle;

use anyhow::Context as _;
use crossbeam_channel::bounded;
use crossbeam_channel::unbounded;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;

use crate::Dimensions;

pub trait SuperConsoleOutput: Send + Sync + 'static {
    /// Called before rendering will occur. This has a chance to prevent rendering by returning
    /// false.
    fn should_render(&mut self) -> bool;

    /// Called to produce output. This may be called without should_render if we are finalizing or
    /// clearing. This should flush if possible.
    fn output(&mut self, buffer: Vec<u8>) -> anyhow::Result<()>;

    /// How big is the terminal to write to.
    fn terminal_size(&self) -> anyhow::Result<Dimensions> {
        Ok(crossterm::terminal::size()?.into())
    }

    /// Called when the console has finalized. This must block if necessary. No further output will
    /// be emitted.
    fn finalize(self: Box<Self>) -> anyhow::Result<()>;

    /// Get this Output as an Any. This is used for testing.
    fn as_any(&self) -> &dyn Any;

    /// Get this Output as a mutable Any. This is used for testing.
    fn as_any_mut(&mut self) -> &mut dyn Any;
}

pub struct BlockingSuperConsoleOutput {
    /// Stream to write to.
    stream: Box<dyn Write + Send + 'static + Sync>,
}

impl BlockingSuperConsoleOutput {
    pub fn new(stream: Box<dyn Write + Send + 'static + Sync>) -> Self {
        Self { stream }
    }
}

impl SuperConsoleOutput for BlockingSuperConsoleOutput {
    fn should_render(&mut self) -> bool {
        true
    }

    fn output(&mut self, buffer: Vec<u8>) -> anyhow::Result<()> {
        self.stream.write_all(&buffer)?;
        self.stream.flush()?;

        Ok(())
    }

    fn finalize(self: Box<Self>) -> anyhow::Result<()> {
        Ok(())
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}

/// A non-blocking output for the SuperConsole. This makes a few guarantees:
///
/// - Calls to output() after should_render() returned true will not block.
/// - When finalize() returns, the last frame passed to output() is shown.
/// - When an error occurs, the next fallible call will return it.
pub(crate) struct NonBlockingSuperConsoleOutput {
    /// A channel to send frames for writing.
    sender: Sender<Vec<u8>>,
    /// A channel back for errors encountered by the thread doing the writing.
    errors: Receiver<io::Error>,
    /// The thread doing the writing. It owns the other end of the aforementioned channels and will
    /// exit when the data sender is closed.
    handle: JoinHandle<()>,
}

impl NonBlockingSuperConsoleOutput {
    pub fn new(stream: Box<dyn Write + Send + 'static + Sync>) -> anyhow::Result<Self> {
        Self::new_for_writer(stream)
    }

    fn new_for_writer(mut stream: Box<dyn Write + Send + 'static + Sync>) -> anyhow::Result<Self> {
        let (sender, receiver) = bounded::<Vec<u8>>(1);
        let (error_sender, errors) = unbounded::<io::Error>();

        let handle = std::thread::Builder::new()
            .name("superconsole-io".to_owned())
            .spawn(move || {
                for frame in receiver.into_iter() {
                    match stream.write_all(&frame).and_then(|()| stream.flush()) {
                        Ok(()) => {}
                        Err(e) => {
                            // This can only fail if the sender disconnected, in which case they'll
                            // stop sending us data momentarily, so ignore the failure.
                            let _ignored = error_sender.try_send(e);
                        }
                    }
                }
            })
            .context("Error spawning Superconsole I/O thread")?;

        Ok(Self {
            sender,
            errors,
            handle,
        })
    }
}

impl SuperConsoleOutput for NonBlockingSuperConsoleOutput {
    /// Check if we have free capacity in our channel. Note that if the channel is full, that means
    /// our writer thread already has 2 buffered frames (one in the channel, one it's currently
    /// writing out). In this case, refuse to produce further output.
    fn should_render(&mut self) -> bool {
        !self.errors.is_empty() || !self.sender.is_full()
    }

    /// Attempt to send out a frame. If we called should_render, this won't block. If we didn't,
    /// then it may block.
    fn output(&mut self, buffer: Vec<u8>) -> anyhow::Result<()> {
        if let Ok(err) = self.errors.try_recv() {
            return Err(anyhow::Error::from(err).context("Superconsole I/O thread errored"));
        }

        self.sender
            .send(buffer)
            .context("Superconsole I/O thread has crashed")?;

        Ok(())
    }

    /// Notify our writer thread that no further writes are expected. Wait for it to flush.
    fn finalize(self: Box<Self>) -> anyhow::Result<()> {
        let Self {
            sender,
            errors,
            handle,
        } = *self;
        drop(sender);

        let res = match errors.into_iter().next() {
            Some(err) => Err(anyhow::Error::from(err).context("Superconsole I/O thread errored")),
            None => Ok(()),
        };

        match handle.join() {
            Ok(()) => {}
            Err(panic) => std::panic::resume_unwind(panic),
        }

        res
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}

#[cfg(test)]
mod test {
    use crossbeam_channel::Receiver;

    use super::*;

    /// A test writer that just sends into a channel. Lets us block / unblock the output to test
    /// for race conditions.
    #[derive(Clone)]
    struct TestWriter {
        sender: Sender<()>,
    }

    impl TestWriter {
        pub fn new() -> (Self, Receiver<()>) {
            let (sender, receiver) = bounded(0);
            (Self { sender }, receiver)
        }
    }

    impl Write for TestWriter {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.sender
                .send(())
                .map_err(|_| io::Error::new(io::ErrorKind::Other, "not writable"))?;

            Ok(buf.len())
        }

        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    fn msg() -> Vec<u8> {
        // Not an empty buffer, to ensure write() gets called.
        vec![1]
    }

    #[test]
    fn test_non_blocking_output_errors_on_next_output() -> anyhow::Result<()> {
        let (writer, drain) = TestWriter::new();

        let mut output = NonBlockingSuperConsoleOutput::new_for_writer(Box::new(writer))?;

        // Send a first message, this will go into write()
        assert!(output.should_render());
        output.output(msg())?;

        // Send a second message, this will stay in the channel.
        output.output(msg())?;

        // Now, kill the output
        assert!(!output.should_render());
        drop(drain);

        // We expect that should_render() will eventually return true.
        while !output.should_render() {
            std::thread::yield_now();
            continue;
        }

        // Likewise, we expect that sending output and finalizing wold fail.
        assert!(output.output(vec![]).is_err());
        assert!(Box::new(output).finalize().is_err());

        Ok(())
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Provides stylization for Strings.
//! - Create a styled string using `style`.
//! - Set the foreground or background color of the string using the `Color` enum.
//! - Set the attribute (bold, italic, underlined, etc) using the `Attribute` enum.

pub use crossterm::style::style;
pub use crossterm::style::Attribute;
pub use crossterm::style::Color;
pub use crossterm::style::ContentStyle;
pub use crossterm::style::StyledContent;
pub use crossterm::style::Stylize;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::cmp;
use std::env;
use std::io;

use crossterm::terminal::Clear;
use crossterm::terminal::ClearType;
use crossterm::tty::IsTty;
use crossterm::QueueableCommand;

use crate::ansi_support::enable_ansi_support;
use crate::components::Canvas;
use crate::components::Component;
use crate::components::DrawMode;
use crate::content::Line;
use crate::output::BlockingSuperConsoleOutput;
use crate::output::SuperConsoleOutput;
use crate::Dimensions;
use crate::Direction;
use crate::Lines;

const MINIMUM_EMIT: usize = 5;
const MAX_GRAPHEME_BUFFER: usize = 1000000;

/// Handles rendering the console using the user-defined [Component](Component)s and emitted messages.
/// A Canvas area at the bottom of the terminal is re-rendered in place at each tick for the components,
/// while a log area of emitted messages is produced above.
/// Producing output from sources other than SuperConsole while break the TUI.
pub struct SuperConsole {
    root: Canvas,
    to_emit: Lines,
    // A default screen size to use if the size cannot be fetched
    // from the terminal. This generally is only used for testing
    // situations.
    fallback_size: Option<Dimensions>,
    pub(crate) output: Box<dyn SuperConsoleOutput>,
}

impl SuperConsole {
    /// Build a new SuperConsole with a root component.
    pub fn new() -> Option<Self> {
        Self::compatible().then(|| {
            Self::new_internal(
                None,
                Box::new(BlockingSuperConsoleOutput::new(Box::new(io::stderr()))),
            )
        })
    }

    /// Force a new SuperConsole to be built with a root component, regardless of
    /// whether the tty is compatible
    pub fn forced_new(fallback_size: Dimensions) -> Self {
        Self::new_internal(
            Some(fallback_size),
            Box::new(BlockingSuperConsoleOutput::new(Box::new(io::stderr()))),
        )
    }

    pub(crate) fn new_internal(
        fallback_size: Option<Dimensions>,
        output: Box<dyn SuperConsoleOutput>,
    ) -> Self {
        Self {
            root: Canvas::new(),
            to_emit: Lines::new(),
            fallback_size,
            output,
        }
    }

    pub fn compatible() -> bool {
        // Superconsole only renders on the stderr, so we can display the superconsole
        // even if someone does `command > out.txt`.
        io::stderr().is_tty() && !Self::is_term_dumb() && enable_ansi_support().is_ok()
    }

    fn is_term_dumb() -> bool {
        // Some terminals (e.g. Emacs' eshell) are very limited in functionality and don't support
        // the control codes required for superconsole to work.
        matches!(env::var("TERM"), Ok(kind) if kind == "dumb")
    }

    /// Render at a given tick.  Draws all components and drains the emitted events buffer.
    /// This will produce any pending emitting events above the Canvas and will re-render the drawing area.
    pub fn render(&mut self, root: &dyn Component) -> anyhow::Result<()> {
        // `render_general` refuses to drain more than a single frame, so repeat until done.
        // or until the rendered frame is too large to print anything.
        let mut anything_emitted = true;
        let mut has_rendered = false;
        while !has_rendered || (anything_emitted && !self.to_emit.is_empty()) {
            if !self.output.should_render() {
                break;
            }

            let last_len = self.to_emit.len();
            self.render_with_mode(root, DrawMode::Normal)?;
            anything_emitted = last_len == self.to_emit.len();
            has_rendered = true;
        }

        Ok(())
    }

    /// Perform a final render with [`DrawMode::Final`].
    /// Each component will have a chance to finalize themselves before the terminal is disposed of.
    pub fn finalize(self, root: &dyn Component) -> anyhow::Result<()> {
        self.finalize_with_mode(root, DrawMode::Final)
    }

    /// Perform a final render, using a specified [`DrawMode`].
    /// Each component will have a chance to finalize themselves before the terminal is disposed of.
    pub fn finalize_with_mode(
        mut self,
        root: &dyn Component,

        mode: DrawMode,
    ) -> anyhow::Result<()> {
        self.render_with_mode(root, mode)?;
        self.output.finalize()
    }

    /// Convenience method:
    /// - Calls queue_emit to add the lines.
    /// - Next, re-renders the `superconsole`.
    ///
    /// Because this re-renders the console, it requires passed state.
    /// Overuse of this method can cause `superconsole` to use significant CPU.
    pub fn emit_now(&mut self, lines: Lines, root: &dyn Component) -> anyhow::Result<()> {
        self.emit(lines);
        self.render(root)
    }

    /// Queues the passed lines to be drawn on the next render.
    /// The lines *will not* appear until the next render is called.
    pub fn emit(&mut self, mut lines: Lines) {
        self.to_emit.0.append(&mut lines.0);
    }

    fn size(&self) -> anyhow::Result<Dimensions> {
        // We want to get the size, but if that fails or is empty use the fallback_size if available.
        match (self.output.terminal_size(), self.fallback_size) {
            (Ok(size), Some(fallback)) if size.width == 0 || size.height == 0 => Ok(fallback),
            (Ok(size), _) => Ok(size),
            (Err(_), Some(fallback)) => Ok(fallback),
            (Err(e), None) => Err(e),
        }
    }

    /// Clears the canvas portion of the superconsole.
    pub fn clear(&mut self) -> anyhow::Result<()> {
        let mut buffer = vec![];
        self.root.clear(&mut buffer)?;
        self.output.output(buffer)
    }

    /// Helper method to share render + finalize behavior by specifying mode.
    fn render_with_mode(&mut self, root: &dyn Component, mode: DrawMode) -> anyhow::Result<()> {
        // TODO(cjhopman): We may need to try to keep each write call to be under the pipe buffer
        // size so it can be completed in a single syscall otherwise we might see a partially
        // rendered frame.

        // We remove the last line as we always have a blank final line in our output.
        let size = self.size()?.saturating_sub(1, Direction::Vertical);
        let mut buffer = Vec::new();

        self.render_general(&mut buffer, root, mode, size)?;
        self.output.output(buffer)
    }

    /// Helper method that makes rendering highly configurable.
    fn render_general(
        &mut self,
        buffer: &mut Vec<u8>,
        root: &dyn Component,

        mode: DrawMode,
        size: Dimensions,
    ) -> anyhow::Result<()> {
        /// Heuristic to determine if a buffer is too large to buffer.
        /// Can be tuned, but is currently set to 1000000 graphemes.
        #[allow(clippy::ptr_arg)]
        fn is_big(buf: &Lines) -> bool {
            let len: usize = buf.iter().map(Line::len).sum();
            len > MAX_GRAPHEME_BUFFER
        }

        // Go the beginning of the canvas.
        self.root.move_up(buffer)?;

        // Pre-draw the frame *and then* start rendering emitted messages.
        let mut frame = self.root.draw(root, size, mode)?;
        // Render at most a single frame if this not the last render.
        // Does not buffer if there is a ridiculous amount of data.
        let limit = match mode {
            DrawMode::Normal if !is_big(&self.to_emit) => {
                let limit = size.height.saturating_sub(frame.len());
                // arbitrary value picked so we don't starve `emit` on small terminal sizes.
                Some(cmp::max(limit, MINIMUM_EMIT))
            }
            _ => None,
        };
        self.to_emit.render(buffer, limit)?;
        frame.render(buffer, None)?;

        // clear any residue from the previous render.
        buffer.queue(Clear(ClearType::FromCursorDown))?;

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use anyhow::Context as _;
    use derive_more::AsRef;

    use super::*;
    use crate::components::echo::Echo;
    use crate::testing::frame_contains;
    use crate::testing::test_console;
    use crate::testing::SuperConsoleTestingExt;
    use crate::Lines;

    #[derive(AsRef, Debug)]
    struct Msg(Lines);

    #[test]
    fn test_small_buffer() -> anyhow::Result<()> {
        let mut console = test_console();
        let msg_count = MINIMUM_EMIT + 5;
        console.emit(Lines(vec![vec!["line 1"].try_into()?; msg_count]));
        let msg = Lines(vec![vec!["line"].try_into()?; msg_count]);
        let root = Echo(msg);
        let mut buffer = Vec::new();

        // even though the canvas is larger than the tty
        console.render_general(
            &mut buffer,
            &root,
            DrawMode::Normal,
            Dimensions::new(100, 2),
        )?;

        // we should still drain a minimum of 5 messages.
        assert_eq!(console.to_emit.len(), msg_count - MINIMUM_EMIT);

        Ok(())
    }

    #[test]
    fn test_huge_buffer() -> anyhow::Result<()> {
        let mut console = test_console();
        console.emit(Lines(vec![
            vec!["line 1"].try_into()?;
            MAX_GRAPHEME_BUFFER * 2
        ]));
        let root = Echo(Lines(vec![vec!["line"].try_into()?; 1]));
        let mut buffer = Vec::new();

        // Even though we have more messages than fit on the screen in the `to_emit` buffer
        console.render_general(
            &mut buffer,
            &root,
            DrawMode::Normal,
            Dimensions::new(100, 20),
        )?;

        // We have so many that we should just drain them all.
        assert!(console.to_emit.is_empty());

        Ok(())
    }

    /// Check that no frames are produced when should_render returns false.
    #[test]
    fn test_block_render() -> anyhow::Result<()> {
        let mut console = test_console();

        let root = Echo(Lines(vec![vec!["state"].try_into()?; 1]));

        console.render(&root)?;
        assert_eq!(console.test_output()?.frames.len(), 1);

        console.test_output_mut()?.should_render = false;
        console.render(&root)?;
        assert_eq!(console.test_output()?.frames.len(), 1);

        console.emit(Lines(vec![vec!["line 1"].try_into()?]));
        console.render(&root)?;
        assert_eq!(console.test_output()?.frames.len(), 1);

        Ok(())
    }

    /// Check that lines are deferred when should_render returns false, and emitted once the output
    /// is unblocked.
    #[test]
    fn test_block_lines() -> anyhow::Result<()> {
        let mut console = test_console();

        let root = Echo(Lines(vec![vec!["state"].try_into()?; 1]));

        console.test_output_mut()?.should_render = false;
        console.emit(Lines(vec![vec!["line 1"].try_into()?]));
        console.render(&root)?;
        assert_eq!(console.test_output()?.frames.len(), 0);

        console.test_output_mut()?.should_render = true;
        console.emit(Lines(vec![vec!["line 2"].try_into()?]));
        console.render(&root)?;

        let frame = console
            .test_output_mut()?
            .frames
            .pop()
            .context("No frame was emitted")?;

        assert!(frame_contains(&frame, "state"));
        assert!(frame_contains(&frame, "line 1"));
        assert!(frame_contains(&frame, "line 2"));

        Ok(())
    }

    /// Check that render_with_mode does not respect should_render.
    #[test]
    fn test_block_finalize() -> anyhow::Result<()> {
        let mut console = test_console();

        let root = Echo(Lines(vec![vec!["state"].try_into()?; 1]));

        console.test_output_mut()?.should_render = false;
        console.emit(Lines(vec![vec!["line 1"].try_into()?]));
        console.emit(Lines(vec![vec!["line 2"].try_into()?]));
        console.render_with_mode(&root, DrawMode::Final)?;

        let frame = console
            .test_output_mut()?
            .frames
            .pop()
            .context("No frame was emitted")?;

        assert!(frame_contains(&frame, "state"));
        assert!(frame_contains(&frame, "line 1"));
        assert!(frame_contains(&frame, "line 2"));

        Ok(())
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Testing utilities for Superconsole.
use std::any::Any;

use anyhow::Context as _;

use crate::output::SuperConsoleOutput;
use crate::superconsole::SuperConsole;
use crate::Dimensions;

/// An output for testing that doesn't do real I/O.
pub struct TestOutput {
    /// Callers can modify this to indicate whether the output is blocked.
    pub should_render: bool,
    /// The size to report the terminal
    pub terminal_size: Dimensions,
    /// The frames that were written to this output.
    pub frames: Vec<Vec<u8>>,
}

impl SuperConsoleOutput for TestOutput {
    fn should_render(&mut self) -> bool {
        self.should_render
    }

    fn output(&mut self, buffer: Vec<u8>) -> anyhow::Result<()> {
        self.frames.push(buffer);
        Ok(())
    }

    fn terminal_size(&self) -> anyhow::Result<Dimensions> {
        Ok(self.terminal_size)
    }

    fn finalize(self: Box<Self>) -> anyhow::Result<()> {
        Ok(())
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}

pub trait SuperConsoleTestingExt {
    fn test_output(&self) -> anyhow::Result<&TestOutput>;
    fn test_output_mut(&mut self) -> anyhow::Result<&mut TestOutput>;
}

impl SuperConsoleTestingExt for SuperConsole {
    fn test_output(&self) -> anyhow::Result<&TestOutput> {
        self.output
            .as_any()
            .downcast_ref()
            .context("Downcast failed")
    }

    fn test_output_mut(&mut self) -> anyhow::Result<&mut TestOutput> {
        self.output
            .as_any_mut()
            .downcast_mut()
            .context("Downcast failed")
    }
}

pub fn test_console() -> SuperConsole {
    let size = Dimensions {
        width: 80,
        height: 80,
    };
    SuperConsole::new_internal(
        Some(size),
        Box::new(TestOutput {
            should_render: true,
            terminal_size: size,
            frames: Vec::new(),
        }),
    )
}

pub fn frame_contains(frame: &[u8], needle: impl AsRef<[u8]>) -> bool {
    let needle = needle.as_ref();
    for w in frame.windows(needle.len()) {
        if w == needle {
            return true;
        }
    }
    false
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::fmt;

pub(crate) struct VecAsFmtWrite<'a>(pub(crate) &'a mut Vec<u8>);

impl<'a> fmt::Write for VecAsFmtWrite<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.0.extend_from_slice(s.as_bytes());
        Ok(())
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Oftentimes it is useful to align text inside the bounding box.
//! This component offers a composiion based approach to do so.
//! One may align text left, center, or right, and top, middle, or bottom.
//! Additionally, horizontally left aligned text may be optionally justified.

use crate::components::Blank;
use crate::Component;
use crate::Dimensions;
use crate::DrawMode;
use crate::Lines;

/// Select the alignment of the vertical content
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum VerticalAlignmentKind {
    /// Content appears at the top.
    Top,
    /// Content appears approximately equidistant between top and bottom
    Center,
    /// Content appears at the bottom.
    Bottom,
}

/// Select the alignment of the horizontal content
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum HorizontalAlignmentKind {
    /// Content appears at the left.
    /// The argument determines whether the text is justified.
    Left(bool),
    /// Content appears in the middle.
    Center,
    /// Content appears to the right.
    Right,
}

/// The [`Aligned`](Aligned) [`Component`](Component) can be used to specify in which part of the view the content should live.
/// The [`HorizontalAlignmentKind`](HorizontalAlignmentKind) enum specifies the location relative to the x-axis.
/// The [`VerticalAlignmentKind`](VerticalAlignmentKind) enum specified the location relative to the y-axis.
#[derive(Debug)]
pub struct Aligned<C: Component = Box<dyn Component>> {
    pub child: C,
    pub horizontal: HorizontalAlignmentKind,
    pub vertical: VerticalAlignmentKind,
}

impl<C: Component> Aligned<C> {
    /// Creates a new `Alignment` component with the given alignments.
    pub fn new(
        child: C,
        horizontal: HorizontalAlignmentKind,
        vertical: VerticalAlignmentKind,
    ) -> Self {
        Self {
            child,
            horizontal,
            vertical,
        }
    }
}

impl Default for Aligned {
    fn default() -> Self {
        Self {
            child: Box::new(Blank),
            horizontal: HorizontalAlignmentKind::Left(false),
            vertical: VerticalAlignmentKind::Top,
        }
    }
}

impl<C: Component> Component for Aligned<C> {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        let Dimensions { width, height } = dimensions;
        let mut output = self.child.draw(dimensions, mode)?;

        let number_of_lines = output.len();
        let padding_needed = height.saturating_sub(number_of_lines);
        match self.vertical {
            VerticalAlignmentKind::Top => {}
            VerticalAlignmentKind::Center => {
                let top_pad = padding_needed / 2;
                output.pad_lines_top(top_pad);
                output.pad_lines_bottom(padding_needed - top_pad);
            }
            VerticalAlignmentKind::Bottom => {
                output.pad_lines_top(padding_needed);
            }
        }

        match self.horizontal {
            HorizontalAlignmentKind::Left(justified) => {
                if justified {
                    output.justify();
                }
            }
            HorizontalAlignmentKind::Center => {
                for line in output.iter_mut() {
                    let output_len = line.len();
                    let padding_needed = width.saturating_sub(output_len);
                    let left_pad = padding_needed / 2;
                    line.pad_left(left_pad);
                    // handles any rounding issues
                    line.pad_right(padding_needed - left_pad);
                }
            }
            HorizontalAlignmentKind::Right => {
                for line in output.iter_mut() {
                    line.pad_left(width.saturating_sub(line.len()));
                }
            }
        }
        Ok(output)
    }
}

#[cfg(test)]
mod tests {
    use derive_more::AsRef;

    use crate::components::alignment::HorizontalAlignmentKind;
    use crate::components::alignment::VerticalAlignmentKind;
    use crate::components::echo::Echo;
    use crate::components::Aligned;
    use crate::components::DrawMode;
    use crate::Component;
    use crate::Dimensions;
    use crate::Line;
    use crate::Lines;

    #[derive(AsRef, Debug)]
    struct Msg(Lines);

    #[test]
    fn test_align_left_unjustified() {
        let original = Lines(vec![
            vec!["hello world"].try_into().unwrap(),
            vec!["pretty normal test"].try_into().unwrap(),
        ]);
        let component = Aligned::new(
            Echo(original.clone()),
            HorizontalAlignmentKind::Left(false),
            VerticalAlignmentKind::Top,
        );
        let dimensions = Dimensions::new(20, 20);
        let actual = component.draw(dimensions, DrawMode::Normal).unwrap();

        assert_eq!(actual, original);
    }

    #[test]
    fn test_align_left_justified() {
        let original = Lines(vec![
            vec!["hello world"].try_into().unwrap(),        // 11 chars
            vec!["pretty normal test"].try_into().unwrap(), // 18 chars
            vec!["short"].try_into().unwrap(),              // 5 chars
        ]);
        let component = Aligned::new(
            Echo(original),
            HorizontalAlignmentKind::Left(true),
            VerticalAlignmentKind::Top,
        );
        let dimensions = Dimensions::new(20, 20);
        let actual = component.draw(dimensions, DrawMode::Normal).unwrap();
        let expected = Lines(vec![
            vec!["hello world", &" ".repeat(18 - 11)]
                .try_into()
                .unwrap(),
            vec!["pretty normal test"].try_into().unwrap(),
            vec!["short", &" ".repeat(18 - 5)].try_into().unwrap(),
        ]);

        assert_eq!(actual, expected,);
    }

    #[test]
    fn test_align_col_center() {
        let original = Lines(vec![
            vec!["hello world"].try_into().unwrap(),          // 11 chars
            vec!["pretty normal testss"].try_into().unwrap(), // 20 chars
            vec!["shorts"].try_into().unwrap(),               // 6 chars
        ]);
        let component = Aligned::new(
            Echo(original),
            HorizontalAlignmentKind::Center,
            VerticalAlignmentKind::Top,
        );
        let dimensions = Dimensions::new(20, 20);
        let actual = component.draw(dimensions, DrawMode::Normal).unwrap();
        let expected = Lines(vec![
            vec![" ".repeat(4).as_ref(), "hello world", &" ".repeat(5)]
                .try_into()
                .unwrap(),
            vec!["pretty normal testss"].try_into().unwrap(),
            vec![" ".repeat(7).as_ref(), "shorts", &" ".repeat(7)]
                .try_into()
                .unwrap(),
        ]);

        assert_eq!(actual, expected);
    }

    #[test]
    fn test_align_right() {
        let original = Lines(vec![
            vec!["hello world"].try_into().unwrap(),           // 11 chars
            vec!["pretty normal testsss"].try_into().unwrap(), // 21 chars
            vec!["shorts"].try_into().unwrap(),                // 6 chars
        ]);
        let component = Aligned::new(
            Echo(original),
            HorizontalAlignmentKind::Right,
            VerticalAlignmentKind::Top,
        );
        let dimensions = Dimensions::new(20, 20);
        let actual = component.draw(dimensions, DrawMode::Normal).unwrap();
        let expected = Lines(vec![
            vec![" ".repeat(9).as_ref(), "hello world"]
                .try_into()
                .unwrap(),
            vec!["pretty normal testss"].try_into().unwrap(),
            vec![" ".repeat(14).as_ref(), "shorts"].try_into().unwrap(),
        ]);

        assert_eq!(actual, expected);
    }

    #[test]
    fn test_align_top() {
        let original = Lines(vec![
            vec!["hello world"].try_into().unwrap(),           // 11 chars
            vec!["pretty normal testsss"].try_into().unwrap(), // 21 chars
            vec!["shorts"].try_into().unwrap(),                // 6 chars
        ]);
        let component = Aligned::new(
            Echo(original),
            HorizontalAlignmentKind::Left(false),
            VerticalAlignmentKind::Top,
        );
        let dimensions = Dimensions::new(20, 20);
        let actual = component.draw(dimensions, DrawMode::Normal).unwrap();
        let expected = Lines(vec![
            vec!["hello world"].try_into().unwrap(),
            vec!["pretty normal testss"].try_into().unwrap(),
            vec!["shorts"].try_into().unwrap(),
        ]);

        assert_eq!(actual, expected);
    }

    #[test]
    fn test_align_row_center() {
        let original = Lines(vec![
            vec!["hello world"].try_into().unwrap(),           // 11 chars
            vec!["pretty normal testsss"].try_into().unwrap(), // 21 chars
            vec!["shorts"].try_into().unwrap(),                // 6 chars
        ]);
        let component = Aligned::new(
            Echo(original),
            HorizontalAlignmentKind::Left(false),
            VerticalAlignmentKind::Center,
        );
        let dimensions = Dimensions::new(20, 10);
        let actual = component.draw(dimensions, DrawMode::Normal).unwrap();
        let expected = Lines(vec![
            Line::default(),
            Line::default(),
            Line::default(),
            vec!["hello world"].try_into().unwrap(),
            vec!["pretty normal testss"].try_into().unwrap(),
            vec!["shorts"].try_into().unwrap(),
            Line::default(),
            Line::default(),
            Line::default(),
            Line::default(),
        ]);

        assert_eq!(actual, expected);
    }

    #[test]
    fn test_align_bottom() {
        let original = Lines(vec![
            vec!["hello world"].try_into().unwrap(),           // 11 chars
            vec!["pretty normal testsss"].try_into().unwrap(), // 21 chars
            vec!["shorts"].try_into().unwrap(),                // 6 chars
        ]);
        let component = Aligned::new(
            Echo(original),
            HorizontalAlignmentKind::Left(false),
            VerticalAlignmentKind::Bottom,
        );
        let dimensions = Dimensions::new(20, 10);
        let actual = component.draw(dimensions, DrawMode::Normal).unwrap();
        let expected = Lines(vec![
            Line::default(),
            Line::default(),
            Line::default(),
            Line::default(),
            Line::default(),
            Line::default(),
            Line::default(),
            vec!["hello world"].try_into().unwrap(),
            vec!["pretty normal testss"].try_into().unwrap(),
            vec!["shorts"].try_into().unwrap(),
        ]);

        assert_eq!(actual, expected);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use crate::components::Dimensions;
use crate::components::DrawMode;
use crate::Component;
use crate::Lines;

/// The `Blank` component is a dead-end component that emits nothing.
/// It can be used for testing purposes or to make a portion of a dividing component empty.
/// By default, the [`SuperConsole`](crate::SuperConsole) is created with a [`Blank`](Blank) component.
#[derive(Debug)]
pub struct Blank;

impl Component for Blank {
    /// Returns the empty vector
    fn draw_unchecked(&self, _dimensions: Dimensions, _mode: DrawMode) -> anyhow::Result<Lines> {
        Ok(Lines::new())
    }
}

#[cfg(test)]
mod tests {
    use derive_more::AsRef;

    use crate::components::echo::Echo;
    use crate::components::DrawMode;
    use crate::Component;
    use crate::Dimensions;
    use crate::Lines;

    #[derive(AsRef, Debug)]
    struct EchoMsg(Lines);

    #[test]
    fn test_echo_empty() {
        let output = Echo(Lines::new())
            .draw(Dimensions::new(10, 10), DrawMode::Normal)
            .unwrap();
        assert_eq!(output, Lines::new());
    }

    #[test]
    fn test_echo() {
        let output = Lines(vec![
            vec!["Line 1"].try_into().unwrap(),
            vec!["Line 2"].try_into().unwrap(),
        ]);

        let test_output = Echo(output.clone())
            .draw(Dimensions::new(10, 10), DrawMode::Final)
            .unwrap();
        assert_eq!(output, test_output);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::borrow::Cow;

use crate::components::alignment::HorizontalAlignmentKind;
use crate::components::alignment::VerticalAlignmentKind;
use crate::components::Aligned;
use crate::Component;
use crate::Dimensions;
use crate::DrawMode;
use crate::Line;
use crate::Lines;
use crate::Span;

/// The `Bordered` component can be used to put borders on all sides of the output of its child.
/// This is useful for delimiting the boundaries of a component for reading and aesthetic purposes.
///
/// # About borders
/// Borders may be any `Span`, and can thus be more than a single character long.
///
/// They may consist of any valid sequence of utf-8 characters.
/// However, `superconsole` has a `unicode-segmentation` dependency, which is tied to a specific version of unicode.
/// Differing unicode versions have different `unicode-segmentation` dependencies, so if a newer (or older) version of unicode is used,
/// then some graphemes may cause superconsole to panic.  This is only relevant in the top and bottom borders, which iterate over the graphemes passed.
///
/// Horizontal borders (i.e. top and bottom) are transposed.  For example, if `top = Word::new_unstyled("@@")`,
/// then the resulting output would look something like this:
///
/// ```console
/// @@@@@@@@@@@@@@@@@@@@@
/// @@@@@@@@@@@@@@@@@@@@@
/// // rest of the output
/// ```
#[derive(Debug)]
pub struct Bordered<C: Component = Box<dyn Component>> {
    child: Aligned<C>,
    pub border: BorderedSpec,
}

/// The `BorderedSpec` allows the callee to specify the borders (or lack thereof) of each side.
/// The implementation of [`Default`] allows the user to leave some boundaries unspecified.
/// Unspecified boundaries default to:
/// * '|' if `left` or `right`
/// * '-' if `top` or `bottom`
#[derive(Debug)]
pub struct BorderedSpec {
    pub left: Option<Span>,
    pub right: Option<Span>,
    pub top: Option<Span>,
    pub bottom: Option<Span>,
}

impl Default for BorderedSpec {
    fn default() -> Self {
        let vertical = Some(Span::new_unstyled("|").unwrap());
        let horizontal = Some(Span::new_unstyled("-").unwrap());
        Self {
            left: vertical.clone(),
            right: vertical,
            top: horizontal.clone(),
            bottom: horizontal,
        }
    }
}

impl<C: Component> Bordered<C> {
    pub fn new(child: C, border: BorderedSpec) -> Self {
        Self {
            child: Aligned {
                child,
                horizontal: HorizontalAlignmentKind::Left(true),
                vertical: VerticalAlignmentKind::Top,
            },
            border,
        }
    }
}

/// helper method to transpose horizontal padding.
fn construct_vertical_padding(padding: Span, width: usize) -> Vec<Line> {
    padding
        // iterating over the padding here allows us to retain the styling on each duplicate.
        .iter()
        .map(|mut span| {
            // iterator is a single character here, so fill to width.
            // it's possible that a word could be more than a single column, so the number of repetitions must reflect that.
            span.content = Cow::Owned(span.content.repeat(width / span.len()));
            Line::from_iter([span])
        })
        .collect()
}

impl<C: Component> Component for Bordered<C> {
    fn draw_unchecked(
        &self,

        Dimensions { width, height }: Dimensions,
        mode: DrawMode,
    ) -> anyhow::Result<Lines> {
        // Reserve enough draw space for the walls.
        let opt_len = |opt_word: &Option<Span>| match opt_word {
            Some(word) => word.len(),
            None => 0,
        };
        let new_dims = Dimensions {
            width: width.saturating_sub(opt_len(&self.border.left) + opt_len(&self.border.right)),
            height: height.saturating_sub(opt_len(&self.border.top) + opt_len(&self.border.bottom)),
        };

        // The [`Aligned`] box ensures that the child is justified and bounded.
        let mut output = self.child.draw(new_dims, mode)?;

        for line in output.iter_mut() {
            if let Some(left) = &self.border.left {
                line.push_front(left.clone());
            }
            if let Some(right) = &self.border.right {
                line.push(right.clone());
            }
        }
        if let Some(top) = &self.border.top {
            let lines = construct_vertical_padding(top.clone(), output.max_line_length());
            output.0.splice(0..0, lines.into_iter());
        }
        if let Some(bottom) = &self.border.bottom {
            let lines = construct_vertical_padding(bottom.clone(), output.max_line_length());
            output.0.extend(lines.into_iter());
        }

        Ok(output)
    }
}

#[cfg(test)]
mod tests {
    use derive_more::AsRef;

    use super::*;
    use crate::components::echo::Echo;

    #[derive(AsRef, Debug)]
    struct Msg(Lines);

    #[test]
    fn test_basic() -> anyhow::Result<()> {
        let msg = Lines(vec![
            vec!["Test"].try_into()?,              // 4 chars
            vec!["Longer"].try_into()?,            // 6 chars
            vec!["Even Longer", "ok"].try_into()?, // 13 chars
            Line::default(),
        ]);

        let component = Bordered::new(Echo(msg), BorderedSpec::default());

        let output = component.draw(Dimensions::new(14, 5), DrawMode::Normal)?;

        // A single character on the right side of the message gets truncated to make way for side padding
        let expected = Lines(vec![
            vec!["-".repeat(14)].try_into()?,
            vec!["|", "Test", &" ".repeat(12 - 4), "|"].try_into()?,
            vec!["|", "Longer", &" ".repeat(12 - 6), "|"].try_into()?,
            vec!["|", "Even Longer", "o", "|"].try_into()?,
            vec!["-".repeat(14)].try_into()?,
        ]);

        assert_eq!(output, expected);

        Ok(())
    }

    #[test]
    fn test_complex() -> anyhow::Result<()> {
        let msg = Lines(vec![
            vec!["Test"].try_into()?,              // 4 chars
            vec!["Longer"].try_into()?,            // 6 chars
            vec!["Even Longer", "ok"].try_into()?, // 13 chars
            Line::default(),
        ]);

        let component = Bordered::new(
            Echo(msg),
            BorderedSpec {
                top: Some("@@@".try_into()?),
                left: None,
                bottom: Some("@".try_into()?),
                ..Default::default()
            },
        );

        let output = component.draw(Dimensions::new(13, 7), DrawMode::Normal)?;

        // A single character on the right side of the message gets truncated to make way for side padding
        let expected = Lines(vec![
            vec!["@".repeat(13)].try_into()?,
            vec!["@".repeat(13)].try_into()?,
            vec!["@".repeat(13)].try_into()?,
            vec!["Test", &" ".repeat(12 - 4), "|"].try_into()?,
            vec!["Longer", &" ".repeat(12 - 6), "|"].try_into()?,
            vec!["Even Longer", "o", "|"].try_into()?,
            vec!["@".repeat(13)].try_into()?,
        ]);

        assert_eq!(output, expected);

        Ok(())
    }

    #[test]
    fn test_multi_width_unicode() -> anyhow::Result<()> {
        let multi_width = "ðŸ¦¶";

        let msg = Lines(vec![vec!["Tested"].try_into()?]);

        let component = Bordered::new(
            Echo(msg),
            BorderedSpec {
                top: Some(multi_width.try_into()?),
                left: None,
                right: None,
                bottom: None,
            },
        );

        let output = component.draw(Dimensions::new(13, 7), DrawMode::Normal)?;
        let expected = Lines(vec![vec!["ðŸ¦¶ðŸ¦¶ðŸ¦¶"].try_into()?, vec!["Tested"].try_into()?]);

        assert_eq!(output, expected);
        Ok(())
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use crate::Component;
use crate::Dimensions;
use crate::DrawMode;
use crate::Lines;

/// Component that ensures its child component has at most `max_size` render space.
#[derive(Debug)]
pub struct Bounded<C: Component = Box<dyn Component>> {
    child: C,
    max_size: Dimensions,
}

impl<C: Component> Bounded<C> {
    pub fn new(child: C, max_x: Option<usize>, max_y: Option<usize>) -> Self {
        Self {
            child,
            max_size: Dimensions {
                width: max_x.unwrap_or(usize::MAX),
                height: max_y.unwrap_or(usize::MAX),
            },
        }
    }
}

impl<C: Component> Component for Bounded<C> {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        let output = self.child.draw(dimensions.intersect(self.max_size), mode)?;
        Ok(output)
    }
}

#[cfg(test)]
mod tests {
    use derive_more::AsRef;

    use super::*;
    use crate::components::echo::Echo;
    use crate::Line;
    use crate::Span;

    #[derive(AsRef, Debug)]
    struct Msg(Lines);

    #[test]
    fn test_no_bounding() -> anyhow::Result<()> {
        let msg = Lines(vec![Line::from_iter([Span::new_unstyled("hello world")?])]);
        let test = Bounded::new(Echo(msg.clone()), Some(40), Some(40));
        let output = test.draw(
            Dimensions {
                width: 50,
                height: 50,
            },
            DrawMode::Normal,
        )?;

        assert_eq!(output, msg);

        Ok(())
    }

    #[test]
    fn test_bounding() -> anyhow::Result<()> {
        let msg = Lines(vec![
            Line::from_iter([Span::new_unstyled("hello world")?]),
            Line::from_iter([Span::new_unstyled("hello world")?]),
        ]);
        let test = Bounded::new(Echo(msg), Some(2), Some(1));
        let output = test.draw(
            Dimensions {
                width: 50,
                height: 50,
            },
            DrawMode::Normal,
        )?;
        let expected = Lines(vec![Line::from_iter([Span::new_unstyled("he")?])]);

        assert_eq!(output, expected);

        Ok(())
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! The root component which manages all other components.
//! This is a fake component since it 1) manages state and 2) cannot be made by users and 3) is heavily coupled with [`superconsole`](crate::SuperConsole).
//! Do not make this somewhere down the component hierarchy unless you have a good reason for it.

use std::cell::Cell;

use crossterm::cursor::MoveToColumn;
use crossterm::cursor::MoveUp;
use crossterm::terminal::Clear;
use crossterm::terminal::ClearType;
use crossterm::QueueableCommand;

use crate::components::Dimensions;
use crate::components::DrawMode;
use crate::Component;
use crate::Lines;

/// The root components which manages all other components.
#[derive(Debug, Default)]
pub(crate) struct Canvas {
    // used to overwrite previous canvas buffer
    last_lines: Cell<u16>,
}

impl Canvas {
    /// A passthrough method that resizes the Canvas to reflect the size of the root.
    /// Allows dynamic resizing.
    /// Cuts off any lines that are too for long a single row
    pub(crate) fn draw(
        &self,
        root: &dyn Component,

        dimensions: Dimensions,
        mode: DrawMode,
    ) -> anyhow::Result<Lines> {
        let mut output = root.draw(dimensions, mode)?;
        // We don't trust the child to not truncate the result.
        output.shrink_lines_to_dimensions(dimensions);
        self.last_lines.set(output.len().try_into()?);
        Ok(output)
    }
}

impl Canvas {
    /// Canvas only has a single child.
    /// It essentially functions as a passthrough - an invisible window which handles sizing and re-drawing correctly.
    pub(crate) fn new() -> Self {
        Self {
            ..Default::default()
        }
    }

    /// The first half of drawing.  It moves the buffer up to be overwritten and sets the length to 0.
    /// This is used to clear the scratch area so that any possibly emitted messages can write over it.
    pub(crate) fn move_up(&self, writer: &mut Vec<u8>) -> anyhow::Result<()> {
        let len = self.last_lines.take();
        writer.queue(MoveUp(len))?;
        writer.queue(MoveToColumn(0))?;

        Ok(())
    }

    /// Clears the canvas.
    pub fn clear(&self, writer: &mut Vec<u8>) -> anyhow::Result<()> {
        self.move_up(writer)?;
        writer.queue(Clear(ClearType::FromCursorDown))?;

        Ok(())
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use crate::Component;
use crate::Dimensions;
use crate::DrawMode;
use crate::Lines;

/// Draw components horizontally, each next component is stacked on the right.
pub struct DrawHorizontal {
    /// Original dimensions.
    dim: Dimensions,
    /// Remaining dimensions.
    rem_width: usize,
    /// What we have drawn so far.
    blocks: Vec<Lines>,
}

impl DrawHorizontal {
    /// Construct rendered with given dimensions.
    pub fn new(dimensions: Dimensions) -> DrawHorizontal {
        DrawHorizontal {
            dim: dimensions,
            rem_width: dimensions.width,
            blocks: Vec::new(),
        }
    }

    /// Add another component.
    /// New component `draw` is called with remaining dimensions.
    pub fn draw(&mut self, component: &dyn Component, mode: DrawMode) -> anyhow::Result<()> {
        // We call `draw` even if no space is left, but maybe we should not.
        let output = component.draw(
            Dimensions {
                width: self.rem_width,
                height: self.dim.height,
            },
            mode,
        )?;
        self.rem_width = self.rem_width.saturating_sub(output.max_line_length());
        self.blocks.push(output);
        Ok(())
    }

    /// Finish rendering, return the result.
    pub fn finish(self) -> Lines {
        let mut lines = Lines::join_horizontally(self.blocks);
        // This should be no-op, but just in case.
        lines.shrink_lines_to_dimensions(self.dim);
        lines
    }
}

#[cfg(test)]
mod tests {

    use crate::components::draw_horizontal::DrawHorizontal;
    use crate::Component;
    use crate::Dimensions;
    use crate::DrawMode;
    use crate::Line;
    use crate::Lines;

    #[test]
    fn test_draw_horizontal() {
        #[derive(Debug)]
        struct C0;
        #[derive(Debug)]
        struct C1;

        impl Component for C0 {
            fn draw_unchecked(
                &self,

                dimensions: Dimensions,
                _mode: DrawMode,
            ) -> anyhow::Result<Lines> {
                assert_eq!(
                    Dimensions {
                        width: 50,
                        height: 10,
                    },
                    dimensions
                );
                Ok(Lines(vec![
                    Line::sanitized("quick"),
                    Line::sanitized("fox"),
                    Line::sanitized("over"),
                ]))
            }
        }

        impl Component for C1 {
            fn draw_unchecked(
                &self,

                dimensions: Dimensions,
                _mode: DrawMode,
            ) -> anyhow::Result<Lines> {
                assert_eq!(
                    Dimensions {
                        width: 45,
                        height: 10,
                    },
                    dimensions
                );
                Ok(Lines(vec![
                    Line::sanitized("brown"),
                    Line::sanitized("jumped"),
                ]))
            }
        }

        let mut draw = DrawHorizontal::new(Dimensions {
            width: 50,
            height: 10,
        });
        draw.draw(&C0, DrawMode::Normal).unwrap();
        draw.draw(&C1, DrawMode::Normal).unwrap();
        assert_eq!(
            Lines(vec![
                Line::sanitized("quickbrown "),
                Line::sanitized("fox  jumped"),
                Line::sanitized("over       "),
            ]),
            draw.finish(),
        );
    }
}