//! This module implements the no_std version of [zstr], which are
//! zero-terminated strings of fixed maximum lengths.  
//! Type zstr\<N\> can store strings consisting of up to N-1 bytes
//! Also, it is assumed that the zstr may carray non-textul data and therefore
//! implements some of the traits differently.
//!
//! **`zstr<N>`** also implements the [core::ops::IndexMut] trait for usize.
//! This allows destructive changes to single bytes, such as
//! ```ignore
//!   let mut s = <zstr<8>>::from("abcd");
//!   s[0] = b'A';
//!   assert_eq!(&s[0..3],"Abc");
//! ```
//! The consequence of IndexMut is that the buffer may not represent an
//! utf8 string.  In fact, a [zstr::from_raw] method also exists.
//! In constrast, fstr and the alias types str4-str256 do not implement
//! IndexMut.  This distinction of zstr means also means that
//! the [core::ops::Index] traits are separately implemented for the
//! Range types.


#![allow(unused_variables)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(unused_parens)]
#![allow(unused_assignments)]
#![allow(unused_mut)]
#![allow(dead_code)]
use crate::{tstr};
use core::cmp::{min, Ordering};
//use core::ops::{Range,RangeFull,RangeFrom,RangeTo};
//use core::ops::{RangeInclusive,RangeToInclusive};

/// `zstr<N>`: zero-terminated utf8 strings of size up to N bytes.  Note that
/// zstr supports unicode, so that the length of string in characters may
/// be less than N.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct zstr<const N: usize> {
    chrs: [u8; N],
} //zstr
impl<const N: usize> zstr<N> {
    /// creates a new `zstr<N>` with given &str.  If the length of s exceeds
    /// N, the extra characters are ignored and a warning sent to stderr.
    /// This function is also called by
    /// several others including [zstr::from].  This function can now handle
    /// utf8 strings properly.
    pub fn make(s: &str) -> zstr<N> {
        let mut chars = [0u8; N];
        let bytes = s.as_bytes(); // &[u8]
        if (bytes.len() >= N) {
            panic!("!Fixedstr Warning in zstr::make: length of string literal \"{}\" exceeds the capacity of type zstr<{}>; string truncated",s,N);
        }
        let mut i = 0;
        let limit = min(N - 1, bytes.len());
        chars[..limit].clone_from_slice(&bytes[..limit]);
        /*
        for i in 0..bytes.len()
        {
          if i<N-1 {chars[i] = bytes[i];} else {break;}
        }
        */
        zstr { chrs: chars }
    } //make

    /// Version of make that does not print warning to stderr.  If the
    /// capacity limit is exceeded, the extra characters are ignored.
    pub fn create(s: &str) -> zstr<N> {
        let mut chars = [0u8; N];
        let bytes = s.as_bytes(); // &[u8]
        let mut i = 0;
        let limit = min(N - 1, bytes.len());
        chars[..limit].clone_from_slice(&bytes[..limit]);
        zstr { chrs: chars }
    } //create

    /// version of make that does not truncate
    pub fn try_make(s: &str) -> Result<zstr<N>, &str> {
        if s.len() > N - 1 {
            Err(s)
        } else {
            Ok(zstr::make(s))
        }
    }

    /// creates an empty string, equivalent to zstr::default()
    pub fn new() -> zstr<N> {
        zstr::make("")
    }


  /// creates a new `zstr<N>` with given u8 slice.  If the length of s exceeds
  /// N, the extra characters are ignored.  The last byte of the array is
  /// is set to 0 to ensure that the string is zero-terminated.  This
  /// operation does not check if the u8 slice is an utf8 source.
  pub fn from_raw(s:&[u8]) -> zstr<N>
  {
     let mut s2 = s;
     if s.len()>N { s2 = &s[..N]; }
     let mut z = zstr {
       chrs: [0;N],
     };
     z.chrs[0..s2.len()].copy_from_slice(s2);
     if (z.chrs.len()>0) {z.chrs[z.chrs.len()-1]=0;}
     z
  }//from_raw



    /// length of the string in bytes (consistent with [str::len]).
    pub fn len(&self) -> usize {
        let mut i = 0;
        while self.chrs[i] != 0 {
            i += 1;
        }
        return i;
        //return core::str::from_utf8(&self.chrs[0..i]).unwrap().len();
    }

    /// returns maximum capacity in bytes
    pub fn capacity(&self) -> usize {
        N - 1
    }

    // returns the byte length of the string, which will be less than N
    fn blen(&self) -> usize {
        let mut i = 0;
        while self.chrs[i] != 0 {
            i += 1;
        }
        return i;
    }

    /// returns slice of u8 array underneath the zstr, including terminating 0
    pub fn as_bytes(&self) -> &[u8] {
        &self.chrs[..self.blen()+1]
    }

    /// converts zstr to &str using [core::str::from_utf8_unchecked].
    /// Since zstr can only be constructed from valid utf8 sources,
    /// this conversion is safe.
    pub fn to_str(&self) -> &str {
        unsafe { core::str::from_utf8_unchecked(&self.chrs[0..self.blen()]) }
    }
    /// checked version of [zstr::to_str], may panic
    pub fn as_str(&self) -> &str {
        core::str::from_utf8(&self.chrs[0..self.blen()]).unwrap()
    }

    /// changes a character at character position i to c.  This function
    /// requires that c is in the same character class (ascii or unicode)
    /// as the char being replaced.  It never shuffles the bytes underneath.
    /// The function returns true if the change was successful.
    pub fn set(&mut self, i: usize, c: char) -> bool {
        let ref mut cbuf = [0u8; 4];
        c.encode_utf8(cbuf);
        let clen = c.len_utf8();
        if let Some((bi, rc)) = self.as_str().char_indices().nth(i) {
            if clen == rc.len_utf8() {
                self.chrs[bi..bi + clen].clone_from_slice(&cbuf[..clen]);
                //for k in 0..clen {self.chrs[bi+k] = cbuf[k];}
                return true;
            }
        }
        return false;
    } //set
    /// adds chars to end of current string up to maximum size N of `zstr<N>`,
    /// returns the portion of the push string that was NOT pushed due to
    /// capacity, so
    /// if "" is returned then all characters were pushed successfully.
    pub fn push<'t>(&mut self, s: &'t str) -> &'t str {
        if s.len() < 1 {
            return s;
        }
        let mut buf = [0u8; 4];
        let mut i = self.blen();
        let mut sci = 0; // indexes characters in s
        for c in s.chars() {
            let clen = c.len_utf8();
            c.encode_utf8(&mut buf);
            if i <= N - clen - 1 {
                self.chrs[i..i + clen].clone_from_slice(&buf[..clen]);
                /*
                for k in 0..clen
                {
                  self.chrs[i+k] = buf[k];
                }
                */
                i += clen;
            } else {
                self.chrs[i] = 0;
                return &s[sci..];
            }
            sci += clen;
        }
        if i < N {
            self.chrs[i] = 0;
        } // zero-terminate
        &s[sci..]
    } //push

    /// alias for [zstr::push]
    pub fn push_str<'t>(&mut self, s: &'t str) -> &'t str {
      self.push(s)
    }

    /// returns the number of characters in the string regardless of
    /// character class
    pub fn charlen(&self) -> usize {
        self.as_str().chars().count()
    }

    /// returns the nth char of the zstr
    pub fn nth(&self, n: usize) -> Option<char> {
        self.as_str().chars().nth(n)
        //if n<self.len() {Some(self.chrs[n] as char)} else {None}
    }

    /// returns the nth byte of the string as a char.  This
    /// function should only be called on ascii strings.  It
    /// is designed to be quicker than [zstr::nth], and does not check array bounds or
    /// check n against the length of the string. Nor does it check
    /// if the value returned is within the ascii range.
    pub fn nth_ascii(&self, n: usize) -> char {
        self.chrs[n] as char
    }

    /// determines if string is an ascii string
    pub fn is_ascii(&self) -> bool {
        self.as_str().is_ascii()
    }

    /// shortens the zstr in-place. Note that n indicates
    /// a *character* position to truncate up to, not the byte position.
    /// If n is greater than the
    /// current character length of the string, this operation will have no effect.
    pub fn truncate(&mut self, n: usize) // n is char position, not binary position
    {
        if let Some((bi, c)) = self.as_str().char_indices().nth(n) {
            self.chrs[bi] = 0;
        }
    }
    
    /// truncates string up to *byte* position n.  **Panics** if n is
    /// not on a character boundary, similar to truncate on owned strings
    pub fn truncate_bytes(&mut self, n: usize) {
         if n<N {
           assert!(self.is_char_boundary(n));
    	   self.chrs[n] = 0;
	 }
    }
    
    /// resets string to empty string
    pub fn clear(&mut self) {
      self.chrs[0]=0;
    }
    
    /*
    /// mimics same function on str
    pub fn chars(&self) -> core::str::Chars<'_> {
        self.as_str().chars()
    }
    /// mimics same function on str
    pub fn char_indices(&self) -> core::str::CharIndices<'_> {
        self.as_str().char_indices()
    }
    */
    
    /// in-place modification of ascii characters to lower-case
    pub fn make_ascii_lowercase(&mut self) {
      for b in &mut self.chrs {
        if *b==0 {break;}
        else if *b>=65 && *b<=90 { *b += 32; }
      }
    }//make_ascii_lowercase

    /// in-place modification of ascii characters to upper-case
    pub fn make_ascii_uppercase(&mut self) {
      for b in &mut self.chrs {
        if *b==0 {break;}
        else if *b>=97 && *b<=122 { *b -= 32; }
      }      
    }

    /// Constructs a clone of this fstr but with only upper-case ascii
    /// characters. 
    pub fn to_ascii_upper(&self) -> Self
    {
      let mut cp = self.clone();
      cp.make_ascii_uppercase();
      cp
    }

    /// Constructs a clone of this fstr but with only lower-case ascii
    /// characters. 
    pub fn to_ascii_lower(&self) -> Self
    {
      let mut cp = *self;
      cp.make_ascii_lowercase();
      cp
    }

} //impl zstr<N>

impl<const N:usize> core::ops::Deref for zstr<N>
{
    type Target = str;
    fn deref(&self) -> &Self::Target {
      self.to_str()
    }
}

impl<const N: usize> core::convert::AsRef<str> for zstr<N> {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl<const N: usize> core::convert::AsMut<str> for zstr<N> {
    fn as_mut(&mut self) -> &mut str {
        let blen = self.blen();
        unsafe { core::str::from_utf8_unchecked_mut(&mut self.chrs[0..blen]) }
    }
}

impl<T: AsRef<str> + ?Sized, const N: usize> core::convert::From<&T> for zstr<N> {
    fn from(s: &T) -> zstr<N> {
        zstr::create(s.as_ref())
    }
}
impl<T: AsMut<str> + ?Sized, const N: usize> core::convert::From<&mut T> for zstr<N> {
    fn from(s: &mut T) -> zstr<N> {
        zstr::create(s.as_mut())
    }
}

impl<const N: usize, const M: usize> core::convert::From<tstr<M>> for zstr<N> {
    fn from(s: tstr<M>) -> zstr<N> {
        zstr::<N>::create(s.to_str())
    }
}

impl<const N: usize> core::cmp::PartialOrd for zstr<N> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        //Some(self.chrs[0..self.blen()].cmp(other.chrs[0..other.blen()]))
        Some(self.cmp(other))
    }
}

impl<const N: usize> core::cmp::Ord for zstr<N> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.chrs[0..self.blen()].cmp(&other.chrs[0..other.blen()])
    }
}

impl<const M: usize> zstr<M> {
    /// converts an zstr\<M\> to an zstr\<N\>. If the length of the string being
    /// converted is greater than N, the extra characters are ignored.
    /// This operation produces a copy.
    /// Example:
    ///```ignore
    ///  let s1:zstr<8> = zstr::from("abcdefg");
    ///  let s2:zstr<16> = s1.resize();
    ///```
    pub fn resize<const N: usize>(&self) -> zstr<N> {
        let slen = self.blen();
        let length = if (slen < N - 1) { slen } else { N - 1 };
        let mut chars = [0u8; N];
        chars[..length].clone_from_slice(&self.chrs[..length]);
        //for i in 0..length {chars[i] = self.chrs[i];}
        zstr { chrs: chars }
    } //resize

    /// version of resize that does not allow string truncation due to length
    pub fn reallocate<const N: usize>(&self) -> Option<zstr<N>> {
        if self.len() < N {
            Some(self.resize())
        } else {
            None
        }
    }
} //impl zstr<M>

impl<const N: usize> core::fmt::Display for zstr<N> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl<const N: usize> PartialEq<&str> for zstr<N> {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other // see below
    } //eq
}
impl<const N: usize> PartialEq<&str> for &zstr<N> {
    fn eq(&self, other: &&str) -> bool {
        &self.as_str() == other
        /*
          let obytes = other.as_bytes();
          let olen = obytes.len();
          let blen = self.blen();
          if olen!=blen {return false;}
          for i in 0..olen
          {
             if obytes[i] != self.chrs[i] {return false;}
          }
          return true;
        */
    } //eq
}
impl<'t, const N: usize> PartialEq<zstr<N>> for &'t str {
    fn eq(&self, other: &zstr<N>) -> bool {
        &other.as_str() == self
    }
}
impl<'t, const N: usize> PartialEq<&zstr<N>> for &'t str {
    fn eq(&self, other: &&zstr<N>) -> bool {
        &other.as_str() == self
    }
}

/// defaults to empty string
impl<const N: usize> Default for zstr<N> {
    fn default() -> Self {
        zstr::<N>::make("")
    }
}

impl<const N: usize> core::fmt::Debug for zstr<N> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.pad(&self.to_str())
    }
} // Debug impl

impl<const N: usize> zstr<N> {
    /// returns a copy of the portion of the string, string could be truncated
    /// if indices are out of range. Similar to slice [start..end]
    pub fn substr(&self, start: usize, end: usize) -> zstr<N> {
        let mut chars = [0u8; N];
        let mut inds = self.char_indices();
        let len = self.len();
        let blen = self.blen();
        if start >= len || end <= start {
            return zstr { chrs: chars };
        }
        let (si, _) = inds.nth(start).unwrap();
        let last = if (end >= len) {
            blen
        } else {
            match inds.nth(end - start - 1) {
                Some((ei, _)) => ei,
                None => blen,
            } //match
        }; //let last =...
        chars[..last - si].clone_from_slice(&self.chrs[si..last]);
        /*
        for i in si..last
        {
          chars[i-si] = self.chrs[i];
        }
        */
        zstr { chrs: chars }
    } //substr
}

/// types for small strings
pub type ztr8 = zstr<8>;
pub type ztr16 = zstr<16>;
pub type ztr32 = zstr<32>;
pub type ztr64 = zstr<64>;

////////////// core::fmt::Write trait
/// Usage:
/// ```ignore
///   use core::fmt::Write;
///   let mut s = zstr::<32>::new();
///   let result = write!(&mut s,"hello {}, {}, {}",1,2,3);
///   /* or */
///   let s2 = str_format!(zstr<16>,"abx{}{}{}",1,2,3);
/// ```
impl<const N: usize> core::fmt::Write for zstr<N> {
    fn write_str(&mut self, s: &str) -> core::fmt::Result //Result<(),core::fmt::Error>
    {
        if s.len() + self.len() > N - 1 {
            return Err(core::fmt::Error::default());
        }
        self.push(s);
        Ok(())
    } //write_str
} //core::fmt::Write trait



/*
///Convert zstr to &str
impl<IndexType, const N: usize> core::ops::Index<IndexType> for zstr<N>
where
    IndexType: core::slice::SliceIndex<str>,
{
    type Output = IndexType::Output;
    fn index(&self, index: IndexType) -> &Self::Output {
        &self.as_str()[index]
    }
} //impl Index
*/


///The implementation of `Index<usize>` for types `zstr<N>` is different
///from that of `fstr<N>` and `tstr<N>`, to allow `IndexMut` on a single
///byte.  The type returned by this trait is &u8, not &str.
impl<const N:usize> core::ops::Index<usize> for zstr<N>
{
  type Output = u8;
  fn index(&self, index:usize)-> &Self::Output
  {
     &self.chrs[index]
  }
}//impl Index
impl<const N:usize> core::ops::IndexMut<usize> for zstr<N>
{
  fn index_mut(&mut self, index:usize)-> &mut Self::Output
  {
     &mut self.chrs[index]
  }
}//impl Index

/*
impl<const N:usize> core::ops::Index<Range<usize>> for zstr<N> {
  type Output = str;
  fn index(&self, index:Range<usize>)-> &Self::Output  {
     &self.as_str()[index]
  }
}//impl Index
impl<const N:usize> core::ops::Index<RangeTo<usize>> for zstr<N> {
  type Output = str;
  fn index(&self, index:RangeTo<usize>)-> &Self::Output  {
     &self.as_str()[index]
  }
}//impl Index
impl<const N:usize> core::ops::Index<RangeFrom<usize>> for zstr<N> {
  type Output = str;
  fn index(&self, index:RangeFrom<usize>)-> &Self::Output  {
     &self.as_str()[index]
  }
}//impl Index
impl<const N:usize> core::ops::Index<RangeInclusive<usize>> for zstr<N> {
  type Output = str;
  fn index(&self, index:RangeInclusive<usize>)-> &Self::Output  {
     &self.as_str()[index]
  }
}//impl Index
impl<const N:usize> core::ops::Index<RangeToInclusive<usize>> for zstr<N> {
  type Output = str;
  fn index(&self, index:RangeToInclusive<usize>)-> &Self::Output  {
     &self.as_str()[index]
  }
}//impl Index
impl<const N:usize> core::ops::Index<RangeFull> for zstr<N> {
  type Output = str;
  fn index(&self, index:RangeFull)-> &Self::Output  {
     &self.as_str()[index]
  }
}//impl Index
*/
