use crate::error::{self, ExpectedKind, Received, io_eof};

use std::borrow::Cow;
use std::io;
use std::marker::PhantomData;

use super::CompoundClass;
use super::DomainDecode;
use super::DomainParse;
use super::Double;
use super::Float;
use super::IOValue;
use super::IOValueDomainCodec;
use super::NestedValue;
use super::ViaCodec;
use super::boundary as B;
use super::signed_integer::SignedInteger;

pub type ReaderResult<T> = std::result::Result<T, error::Error>;

pub enum Token<N: NestedValue> {
    Embedded(N::Embedded),
    Atom(N),
    Compound(CompoundClass),
    End,
}

pub trait Reader<'de, N: NestedValue> {
    fn next(&mut self, read_annotations: bool) -> io::Result<Option<N>>;
    fn open_record(&mut self, arity: Option<usize>) -> ReaderResult<B::Type>;
    fn open_sequence_or_set(&mut self) -> ReaderResult<B::Item>;
    fn open_sequence(&mut self) -> ReaderResult<()>;
    fn open_set(&mut self) -> ReaderResult<()>;
    fn open_dictionary(&mut self) -> ReaderResult<()>;
    fn boundary(&mut self, b: &B::Type) -> ReaderResult<()>;

    // close_compound implies a b.shift(...) and a self.boundary(b).
    fn close_compound(&mut self, b: &mut B::Type, i: &B::Item) -> ReaderResult<bool>;

    fn open_embedded(&mut self) -> ReaderResult<()>;
    fn close_embedded(&mut self) -> ReaderResult<()>;

    type Mark;
    fn mark(&mut self) -> io::Result<Self::Mark>;
    fn restore(&mut self, mark: &Self::Mark) -> io::Result<()>;

    fn next_token(&mut self, read_embedded_annotations: bool) -> io::Result<Token<N>>;
    fn next_annotations_and_token(&mut self) -> io::Result<(Vec<N>, Token<N>)>;

    //---------------------------------------------------------------------------

    fn skip_value(&mut self) -> io::Result<()> {
        // TODO efficient skipping in specific impls of this trait
        let _ = self.demand_next(false)?;
        Ok(())
    }

    fn demand_next(&mut self, read_annotations: bool) -> io::Result<N> {
        self.next(read_annotations)?.ok_or_else(io_eof)
    }

    fn next_boolean(&mut self) -> ReaderResult<bool> {
        self.demand_next(false)?.value().to_boolean()
    }

    fn next_float(&mut self) -> ReaderResult<Float> {
        Ok(self.demand_next(false)?.value().to_float()?.to_owned())
    }

    fn next_double(&mut self) -> ReaderResult<Double> {
        Ok(self.demand_next(false)?.value().to_double()?.to_owned())
    }

    fn next_signedinteger(&mut self) -> ReaderResult<SignedInteger> {
        Ok(self.demand_next(false)?.value().to_signedinteger()?.to_owned())
    }

    fn next_i8(&mut self) -> ReaderResult<i8> { self.demand_next(false)?.value().to_i8() }
    fn next_u8(&mut self) -> ReaderResult<u8> { self.demand_next(false)?.value().to_u8() }
    fn next_i16(&mut self) -> ReaderResult<i16> { self.demand_next(false)?.value().to_i16() }
    fn next_u16(&mut self) -> ReaderResult<u16> { self.demand_next(false)?.value().to_u16() }
    fn next_i32(&mut self) -> ReaderResult<i32> { self.demand_next(false)?.value().to_i32() }
    fn next_u32(&mut self) -> ReaderResult<u32> { self.demand_next(false)?.value().to_u32() }
    fn next_i64(&mut self) -> ReaderResult<i64> { self.demand_next(false)?.value().to_i64() }
    fn next_u64(&mut self) -> ReaderResult<u64> { self.demand_next(false)?.value().to_u64() }
    fn next_i128(&mut self) -> ReaderResult<i128> { self.demand_next(false)?.value().to_i128() }
    fn next_u128(&mut self) -> ReaderResult<u128> { self.demand_next(false)?.value().to_u128() }
    fn next_f32(&mut self) -> ReaderResult<f32> { self.demand_next(false)?.value().to_f32() }
    fn next_f64(&mut self) -> ReaderResult<f64> { self.demand_next(false)?.value().to_f64() }
    fn next_char(&mut self) -> ReaderResult<char> { self.demand_next(false)?.value().to_char() }

    fn next_str(&mut self) -> ReaderResult<Cow<'de, str>> {
        Ok(Cow::Owned(self.demand_next(false)?.value().to_string()?.to_owned()))
    }

    fn next_bytestring(&mut self) -> ReaderResult<Cow<'de, [u8]>> {
        Ok(Cow::Owned(self.demand_next(false)?.value().to_bytestring()?.to_owned()))
    }

    fn next_symbol(&mut self) -> ReaderResult<Cow<'de, str>> {
        Ok(Cow::Owned(self.demand_next(false)?.value().to_symbol()?.to_owned()))
    }

    fn open_option(&mut self) -> ReaderResult<Option<B::Type>>
    {
        let b = self.open_record(None)?;
        let label: &str = &self.next_symbol()?;
        match label {
            "None" => {
                self.ensure_complete(b, &B::Item::RecordField)?;
                Ok(None)
            }
            "Some" =>
                Ok(Some(b)),
            _ =>
                Err(error::Error::Expected(ExpectedKind::Option,
                                           Received::ReceivedRecordWithLabel(label.to_owned()))),
        }
    }

    fn open_simple_record(&mut self, name: &str, arity: Option<usize>) -> ReaderResult<B::Type>
    {
        let b = self.open_record(arity)?;
        let label: &str = &self.next_symbol()?;
        if label == name {
            Ok(b)
        } else {
            Err(error::Error::Expected(ExpectedKind::SimpleRecord(name.to_owned(), arity),
                                       Received::ReceivedRecordWithLabel(label.to_owned())))
        }
    }

    fn configured(self, read_annotations: bool) -> ConfiguredReader<'de, N, Self>
    where
        Self: std::marker::Sized
    {
        ConfiguredReader {
            reader: self,
            read_annotations,
            phantom: PhantomData,
        }
    }

    fn ensure_more_expected(&mut self, b: &mut B::Type, i: &B::Item) -> ReaderResult<()> {
        if !self.close_compound(b, i)? {
            Ok(())
        } else {
            Err(error::Error::MissingItem)
        }
    }

    fn ensure_complete(&mut self, mut b: B::Type, i: &B::Item) -> ReaderResult<()> {
        if !self.close_compound(&mut b, i)? {
            Err(error::Error::MissingCloseDelimiter)
        } else {
            Ok(())
        }
    }
}

impl<'r, 'de, N: NestedValue, R: Reader<'de, N>> Reader<'de, N> for &'r mut R {
    fn next(&mut self, read_annotations: bool) -> io::Result<Option<N>> {
        (*self).next(read_annotations)
    }

    fn open_record(&mut self, arity: Option<usize>) -> ReaderResult<B::Type> {
        (*self).open_record(arity)
    }

    fn open_sequence_or_set(&mut self) -> ReaderResult<B::Item> {
        (*self).open_sequence_or_set()
    }

    fn open_sequence(&mut self) -> ReaderResult<()> {
        (*self).open_sequence()
    }

    fn open_set(&mut self) -> ReaderResult<()> {
        (*self).open_set()
    }

    fn open_dictionary(&mut self) -> ReaderResult<()> {
        (*self).open_dictionary()
    }

    fn boundary(&mut self, b: &B::Type) -> ReaderResult<()> {
        (*self).boundary(b)
    }

    fn close_compound(&mut self, b: &mut B::Type, i: &B::Item) -> ReaderResult<bool> {
        (*self).close_compound(b, i)
    }

    fn open_embedded(&mut self) -> ReaderResult<()> {
        (*self).open_embedded()
    }

    fn close_embedded(&mut self) -> ReaderResult<()> {
        (*self).close_embedded()
    }

    type Mark = R::Mark;

    fn mark(&mut self) -> io::Result<Self::Mark> {
        (*self).mark()
    }

    fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
        (*self).restore(mark)
    }

    fn next_token(&mut self, read_embedded_annotations: bool) -> io::Result<Token<N>> {
        (*self).next_token(read_embedded_annotations)
    }

    fn next_annotations_and_token(&mut self) -> io::Result<(Vec<N>, Token<N>)> {
        (*self).next_annotations_and_token()
    }
}


pub trait BinarySource<'de>: Sized {
    type Mark;
    fn mark(&mut self) -> io::Result<Self::Mark>;
    fn restore(&mut self, mark: &Self::Mark) -> io::Result<()>;

    fn skip(&mut self) -> io::Result<()>;
    fn peek(&mut self) -> io::Result<u8>;
    fn readbytes(&mut self, count: usize) -> io::Result<Cow<'de, [u8]>>;
    fn readbytes_into(&mut self, bs: &mut [u8]) -> io::Result<()>;

    fn packed<N: NestedValue, Dec: DomainDecode<N::Embedded>>(
        &mut self,
        decode_embedded: Dec,
    ) -> super::PackedReader<'de, '_, N, Dec, Self> {
        super::PackedReader::new(self, decode_embedded)
    }

    fn packed_iovalues(&mut self) ->
        super::PackedReader<'de, '_, IOValue, IOValueDomainCodec, Self>
    {
        self.packed(IOValueDomainCodec)
    }

    fn text<N: NestedValue, Dec: DomainParse<N::Embedded>>(
        &mut self,
        decode_embedded: Dec,
    ) -> super::TextReader<'de, '_, N::Embedded, Dec, Self> {
        super::TextReader::new(self, decode_embedded)
    }

    fn text_iovalues(&mut self) ->
        super::TextReader<'de, '_, IOValue, ViaCodec<IOValueDomainCodec>, Self>
    {
        self.text::<IOValue, _>(ViaCodec::new(IOValueDomainCodec))
    }
}

pub struct IOBinarySource<R: io::Read + io::Seek> {
    pub read: R,
    pub buf: Option<u8>,
}

impl<R: io::Read + io::Seek> IOBinarySource<R> {
    #[inline(always)]
    pub fn new(read: R) -> Self {
        IOBinarySource { read, buf: None }
    }
}

impl<'de, R: io::Read + io::Seek> BinarySource<'de> for IOBinarySource<R> {
    type Mark = u64;

    #[inline(always)]
    fn mark(&mut self) -> io::Result<Self::Mark> {
        Ok(self.read.stream_position()? - (if self.buf.is_some() { 1 } else { 0 }))
    }

    #[inline(always)]
    fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
        self.read.seek(io::SeekFrom::Start(*mark))?;
        self.buf = None;
        Ok(())
    }

    #[inline(always)]
    fn skip(&mut self) -> io::Result<()> {
        if self.buf.is_none() { unreachable!(); }
        self.buf = None;
        Ok(())
    }

    #[inline(always)]
    fn peek(&mut self) -> io::Result<u8> {
        match self.buf {
            Some(b) => Ok(b),
            None => {
                let b = &mut [0];
                match self.read.read(b)? {
                    0 => Err(io_eof()),
                    1 => {
                        self.buf = Some(b[0]);
                        Ok(b[0])
                    }
                    _ => unreachable!(),
                }
            }
        }
    }

    #[inline(always)]
    fn readbytes(&mut self, count: usize) -> io::Result<Cow<'de, [u8]>> {
        if self.buf.is_some() { unreachable!(); }
        let mut bs = vec![0; count];
        self.read.read_exact(&mut bs)?;
        Ok(Cow::Owned(bs))
    }

    #[inline(always)]
    fn readbytes_into(&mut self, bs: &mut [u8]) -> io::Result<()> {
        if self.buf.is_some() { unreachable!(); }
        self.read.read_exact(bs)
    }
}

pub struct BytesBinarySource<'de> {
    pub bytes: &'de [u8],
    pub index: usize,
}

impl<'de> BytesBinarySource<'de> {
    #[inline(always)]
    pub fn new(bytes: &'de [u8]) -> Self {
        BytesBinarySource { bytes, index: 0 }
    }
}

impl<'de> BinarySource<'de> for BytesBinarySource<'de> {
    type Mark = usize;

    #[inline(always)]
    fn mark(&mut self) -> io::Result<Self::Mark> {
        Ok(self.index)
    }

    #[inline(always)]
    fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> {
        self.index = *mark;
        Ok(())
    }

    #[inline(always)]
    fn skip(&mut self) -> io::Result<()> {
        if self.index >= self.bytes.len() { unreachable!(); }
        self.index += 1;
        Ok(())
    }

    #[inline(always)]
    fn peek(&mut self) -> io::Result<u8> {
        if self.index >= self.bytes.len() {
            Err(io_eof())
        } else {
            Ok(self.bytes[self.index])
        }
    }

    #[inline(always)]
    fn readbytes(&mut self, count: usize) -> io::Result<Cow<'de, [u8]>> {
        if self.index + count > self.bytes.len() {
            Err(io_eof())
        } else {
            let bs = &self.bytes[self.index..self.index+count];
            self.index += count;
            Ok(Cow::Borrowed(bs))
        }
    }

    #[inline(always)]
    fn readbytes_into(&mut self, bs: &mut [u8]) -> io::Result<()> {
        let count = bs.len();
        if self.index + count > self.bytes.len() {
            Err(io_eof())
        } else {
            bs.copy_from_slice(&self.bytes[self.index..self.index+count]);
            self.index += count;
            Ok(())
        }
    }
}

pub struct ConfiguredReader<'de, N: NestedValue, R: Reader<'de, N>> {
    pub reader: R,
    pub read_annotations: bool,
    phantom: PhantomData<&'de N>,
}

impl<'de, N: NestedValue, R: Reader<'de, N>> ConfiguredReader<'de, N, R> {
    pub fn new(reader: R) -> Self {
        reader.configured(true)
    }

    pub fn set_read_annotations(&mut self, read_annotations: bool) {
        self.read_annotations = read_annotations
    }

    pub fn demand_next(&mut self) -> io::Result<N> {
        self.reader.demand_next(self.read_annotations)
    }
}

impl<'de, N: NestedValue, R: Reader<'de, N>> std::iter::Iterator for ConfiguredReader<'de, N, R> {
    type Item = io::Result<N>;
    fn next(&mut self) -> Option<Self::Item> {
        match self.reader.next(self.read_annotations) {
            Err(e) => Some(Err(e)),
            Ok(None) => None,
            Ok(Some(v)) => Some(Ok(v)),
        }
    }
}
