use core::{hint::unreachable_unchecked, slice::from_raw_parts};

use crate::{Parser, config::Config, misc::*, source::*, value::builder::*};

impl<'a, S: Source, C: Config> Parser<'a, S, C> {
    #[inline]
    pub(super) unsafe fn value_unchecked<V: ValueBuilder<'a, S>>(&mut self) -> V {
        if S::Volatility::IS_VOLATILE {
            let tmp = self.idx().wrapping_add(1);
            self.src.trim(tmp);
        }

        unsafe {
            match self.skip_whitespace() {
                b'"' => self.string_unchecked::<_, V::String, V::Error>(),
                b'{' => self.object_unchecked(),
                b'[' => self.array_unchecked(),
                _ => self.literal_unchecked(),
            }
        }
    }

    pub(super) unsafe fn object_unchecked<V: ValueBuilder<'a, S>>(&mut self) -> V {
        #[cfg(feature = "prealloc")]
        let mut obj = V::Object::with_capacity(self.prealloc);
        #[cfg(not(feature = "prealloc"))]
        let mut obj = V::Object::new();
        #[cfg(feature = "span")]
        let start = self.idx();

        loop {
            match self.skip_whitespace() {
                b'"' => {
                    obj.on_value(self.string_unchecked::<V::String, V::String, V::Error>(), {
                        self.skip_whitespace();
                        self.value_unchecked()
                    });

                    continue;
                }
                b',' => continue,
                b'}' => {
                    obj.on_complete();

                    #[cfg(feature = "prealloc")]
                    (self.prealloc = obj.len());
                    #[allow(unused_mut)]
                    let mut tmp = obj.into();

                    #[cfg(feature = "span")]
                    tmp.apply_span(start, self.idx());
                    return tmp;
                }
                _ => unreachable_unchecked(),
            };
        }
    }

    #[allow(unused_mut)]
    pub(super) unsafe fn array_unchecked<V: ValueBuilder<'a, S>>(&mut self) -> V {
        #[cfg(feature = "span")]
        let start = self.idx();
        let mut arr = V::Array::new();

        loop {
            match self.skip_whitespace() {
                b',' => continue,
                b']' => {
                    arr.on_complete();
                    let mut tmp = arr.into();

                    #[cfg(feature = "span")]
                    tmp.apply_span(start, self.idx());
                    return tmp;
                }
                _ => {
                    self.dec();
                    arr.on_value(self.value_unchecked());
                }
            }
        }
    }

    pub(super) unsafe fn string_unchecked<T, V, E>(&mut self) -> T
    where
        V: StringBuilder<'a, S, E> + Into<T>,
        E: ErrorBuilder,
    {
        let start = self.idx();
        let mut offset = start + 1;
        let mut buf = V::new();
        let end = loop {
            if self.simd_str_unchecked() {
                continue;
            }

            self.inc(1);
            match self.cur() {
                b'"' => break self.idx(),
                b'\\' => {
                    buf.on_chunk(from_raw_parts(self.src.ptr(offset), self.idx() - offset));

                    self.inc(1);
                    offset = self.idx() + 1;

                    let tmp = self.cur();
                    let esc = ESC_LUT[tmp as usize];

                    if esc != 0 {
                        buf.on_escape(&[esc]);
                        continue;
                    }

                    let tmp = &mut [0; 4];
                    let esc = self.unicode_escape(tmp).unwrap_unchecked();

                    offset = self.idx() + 1;
                    buf.on_escape(esc);

                    continue;
                }
                _ => continue,
            }
        };

        buf.on_final_chunk(from_raw_parts(self.src.ptr(offset), end - offset));
        buf.on_complete(from_raw_parts(self.src.ptr(start + 1), end - start - 1))
            .unwrap_unchecked();
        #[cfg(feature = "span")]
        buf.apply_span(start, end);

        buf.into()
    }

    #[inline]
    #[allow(unused_mut)]
    pub(super) unsafe fn literal_unchecked<V: ValueBuilder<'a, S>>(&mut self) -> V {
        if V::CUSTOM_LITERAL {
            let start = self.idx();
            let end = loop {
                if !S::NULL_PADDED && self.idx() + 1 >= self.src.len()
                    || NON_LIT_LUT[self.cur() as usize]
                {
                    break self.idx();
                }

                self.inc(1);
                if self.simd_lit() {
                    break self.idx();
                }
            };
            let len = end - start;

            self.dec();
            return V::literal(from_raw_parts(self.src.ptr(start), len)).unwrap_unchecked();
        }

        #[cfg(feature = "span")]
        let stamp = self.idx();
        let tmp = self.cur();

        if NUM_LUT[tmp as usize] {
            let neg = tmp == b'-';
            if neg {
                self.inc(1)
            }

            let start = self.idx();
            let (val, is_int) = self.parse_u64();

            'int: {
                if is_int {
                    self.dec();
                    let mut tmp = if neg {
                        if val > 9223372036854775808 {
                            break 'int;
                        }

                        V::integer(val.wrapping_neg(), true)
                    } else {
                        V::integer(val, false)
                    };

                    #[cfg(feature = "span")]
                    tmp.apply_span(stamp, self.idx());
                    return tmp;
                }
            }

            let mut tmp = V::float(self.parse_f64(val, neg, start).unwrap_unchecked());
            #[cfg(feature = "span")]
            tmp.apply_span(stamp, self.idx());
            return tmp;
        }

        let mut tmp = match self.cur() {
            b'n' => V::null(),
            c => {
                let val = c == b't';
                self.inc(!val as _);
                V::bool(val)
            }
        };

        self.inc(3);
        #[cfg(feature = "span")]
        tmp.apply_span(stamp, self.idx());
        tmp
    }
}
