use crate::prelude::*;

use async_utf8_decoder::DecodeError;
use futures::StreamExt;
use iref::IriRefBuf;
use tracing::debug;
use winnow::ascii::digit0;
use winnow::ascii::digit1;
use winnow::ascii::line_ending;
use winnow::ascii::multispace0;
use winnow::ascii::space0;
use winnow::ascii::space1;
use winnow::ascii::till_line_ending;
use winnow::ascii::Caseless;
use winnow::combinator::alt;
use winnow::combinator::delimited;
use winnow::combinator::eof;
use winnow::combinator::opt;
use winnow::combinator::preceded;
use winnow::combinator::repeat;
use winnow::combinator::trace as p_trace;
use winnow::error::ContextError;
use winnow::error::ErrMode;
use winnow::stream::Offset as _;
use winnow::stream::Stream as _;
use winnow::stream::StreamIsPartial;
use winnow::token::none_of;
use winnow::token::one_of;
use winnow::token::take;
use winnow::token::take_while;
use winnow::Parser;
use winnow::Partial;

use core::str;
use std::collections::VecDeque;
use std::io::BufRead;
use std::sync::Arc;
use std::sync::Mutex;

use tokio::io::AsyncRead;
use tokio_util::compat::{Compat, TokioAsyncReadCompatExt};

use async_utf8_decoder::Utf8Decoder;

use std::fmt;

use crate::terms::*;
use crate::triples::*;

use winnow::stream::Stateful;
use winnow::ModalResult;

use futures::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};

use tracing::{error, trace};

use thiserror::Error;

#[derive(Debug, Error, PartialEq)]
pub enum TurtleParserError {
    #[error("Illegal state transition")]
    IllegalTransition,
}

#[derive(Clone, Debug, PartialEq)]
enum Phase {
    Outer,
    InTripleSubject {
        restore: Box<Phase>,
        cur_subject: Arc<Term>,
        is_blank_node_property_list: bool,
        is_first: bool,
    },
    InTriplePredicate {
        restore: Box<Phase>,
        cur_subject: Arc<Term>,
        cur_predicate: Arc<Term>,
    },
    InTripleObject {
        restore: Box<Phase>,
        cur_subject: Arc<Term>,
        cur_predicate: Arc<Term>,
    },
    InBlankNodePropertyList {
        restore: Box<Phase>,
        cur_subject: Arc<Term>,
        is_first: bool,
    },
    InCollection {
        restore: Box<Phase>,
        cur_subject: Arc<Term>,
        is_first: bool,
    },
}

impl Phase {
    fn to_triple_subject(
        &mut self,
        cur_subject: Arc<Term>,
        is_blank_node_property_list: bool,
        is_first: bool,
    ) -> Result<(), TurtleParserError> {
        let mut new = match self {
            Self::Outer => Self::InTripleSubject {
                restore: Box::new(self.clone()),
                cur_subject,
                is_blank_node_property_list,
                is_first,
            },
            _ => return Err(TurtleParserError::IllegalTransition),
        };

        std::mem::swap(self, &mut new);

        Ok(())
    }

    fn to_not_first(&mut self) -> Result<(), TurtleParserError> {
        let mut new = match self {
            Self::InTripleSubject {
                restore,
                cur_subject,
                is_blank_node_property_list,
                ..
            } => &mut Self::InTripleSubject {
                restore: restore.clone(),
                cur_subject: cur_subject.clone(),
                is_blank_node_property_list: is_blank_node_property_list.clone(),
                is_first: false,
            },
            Self::InBlankNodePropertyList {
                restore,
                cur_subject,
                ..
            } => &mut Self::InBlankNodePropertyList {
                restore: restore.clone(),
                cur_subject: cur_subject.clone(),
                is_first: false,
            },
            _ => return Err(TurtleParserError::IllegalTransition),
        };

        std::mem::swap(self, &mut new);

        Ok(())
    }

    fn to_triple_predicate(&mut self, cur_predicate: Arc<Term>) -> Result<(), TurtleParserError> {
        let mut new = match self {
            Self::InTripleSubject {
                ref cur_subject, ..
            } => Self::InTriplePredicate {
                restore: Box::new(self.clone()),
                cur_subject: cur_subject.clone(),
                cur_predicate,
            },
            Self::InBlankNodePropertyList {
                ref cur_subject, ..
            } => Self::InTriplePredicate {
                restore: Box::new(self.clone()),
                cur_subject: cur_subject.clone(),
                cur_predicate,
            },
            _ => return Err(TurtleParserError::IllegalTransition),
        };

        std::mem::swap(self, &mut new);

        Ok(())
    }

    fn to_triple_object(&mut self) -> Result<(), TurtleParserError> {
        let mut new = match self {
            Self::InTriplePredicate {
                ref cur_subject,
                ref cur_predicate,
                ..
            } => Self::InTripleObject {
                restore: Box::new(self.clone()),
                cur_subject: cur_subject.clone(),
                cur_predicate: cur_predicate.clone(),
            },
            _ => return Err(TurtleParserError::IllegalTransition),
        };

        std::mem::swap(self, &mut new);

        Ok(())
    }

    fn to_blank_node_property_list(&mut self) -> Result<(), TurtleParserError> {
        let node = Arc::new(Term::BlankNode(BlankNode::new()));
        let mut new = match self {
            Self::Outer => {
                let intermediate = Self::InTripleSubject {
                    cur_subject: node.clone(),
                    restore: Box::new(self.clone()),
                    is_blank_node_property_list: true,
                    is_first: true,
                };
                Self::InBlankNodePropertyList {
                    restore: Box::new(intermediate.clone()),
                    cur_subject: node.clone(),
                    is_first: true,
                }
            }
            Self::InTriplePredicate {
                ref cur_subject,
                ref cur_predicate,
                ..
            } => {
                let intermediate = Self::InTripleObject {
                    cur_subject: cur_subject.clone(),
                    cur_predicate: cur_predicate.clone(),
                    restore: Box::new(self.clone()),
                };
                Self::InBlankNodePropertyList {
                    restore: Box::new(intermediate.clone()),
                    cur_subject: node.clone(),
                    is_first: true,
                }
            }
            Self::InCollection { .. } => Self::InBlankNodePropertyList {
                restore: Box::new(self.clone()),
                cur_subject: node.clone(),
                is_first: true,
            },
            _ => return Err(TurtleParserError::IllegalTransition),
        };
        std::mem::swap(self, &mut new);

        Ok(())
    }

    fn to_collection(&mut self) -> Result<Arc<Term>, TurtleParserError> {
        let node = Arc::new(Term::BlankNode(BlankNode::new()));
        let mut new = match self {
            Self::Outer => {
                let intermediate = Self::InTripleSubject {
                    restore: Box::new(self.clone()),
                    cur_subject: node.clone(),
                    is_blank_node_property_list: false,
                    is_first: true,
                };
                Self::InCollection {
                    restore: Box::new(intermediate.clone()),
                    cur_subject: node.clone(),
                    is_first: true,
                }
            }
            Self::InTriplePredicate {
                ref cur_subject,
                ref cur_predicate,
                ..
            } => {
                let intermediate = Self::InTripleObject {
                    restore: Box::new(self.clone()),
                    cur_subject: cur_subject.clone(),
                    cur_predicate: cur_predicate.clone(),
                };
                Self::InCollection {
                    restore: Box::new(intermediate.clone()),
                    cur_subject: node.clone(),
                    is_first: true,
                }
            }
            Self::InCollection { .. } => {
                // FIXME probably needs fixing
                Self::InCollection {
                    restore: Box::new(self.clone()),
                    cur_subject: node.clone(),
                    is_first: true,
                }
            }
            _ => return Err(TurtleParserError::IllegalTransition),
        };

        std::mem::swap(self, &mut new);

        Ok(node)
    }

    fn to_collection_next(&mut self) -> Result<Arc<Term>, TurtleParserError> {
        let (mut new, next_node) = match self {
            Self::InCollection {
                restore,
                ref cur_subject,
                is_first,
            } => {
                let next_node = if *is_first {
                    cur_subject.clone()
                } else {
                    Arc::new(Term::BlankNode(BlankNode::new()))
                };
                (
                    Self::InCollection {
                        restore: restore.clone(),
                        cur_subject: next_node.clone(),
                        is_first: false,
                    },
                    next_node,
                )
            }
            _ => return Err(TurtleParserError::IllegalTransition),
        };

        std::mem::swap(self, &mut new);

        Ok(next_node)
    }

    fn restore(&mut self) -> Result<(Option<Arc<Term>>, Option<Arc<Term>>), TurtleParserError> {
        let (mut new, tuple) = match self {
            Self::InTripleSubject {
                restore,
                cur_subject,
                ..
            } => (restore.clone(), (Some(cur_subject.clone()), None)),
            Self::InTriplePredicate {
                restore,
                cur_subject,
                cur_predicate,
            } => (
                restore.clone(),
                (Some(cur_subject.clone()), Some(cur_predicate.clone())),
            ),
            Self::InTripleObject { restore, .. } => (restore.clone(), (None, None)),
            Self::InBlankNodePropertyList {
                restore,
                cur_subject,
                ..
            } => (restore.clone(), (Some(cur_subject.clone()), None)),
            Self::InCollection {
                restore,
                cur_subject,
                ..
            } => (restore.clone(), (Some(cur_subject.clone()), None)),
            Self::Outer => return Err(TurtleParserError::IllegalTransition),
        };

        std::mem::swap(self, &mut new);

        Ok(tuple)
    }
}

#[derive(Debug, Clone)]
pub struct State {
    profile: Arc<Mutex<BasicProfile>>,
    phase: Phase,
}

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

impl State {
    pub fn new() -> Self {
        Self {
            profile: Arc::new(BasicProfile::new().into()),
            phase: Phase::Outer,
        }
    }

    pub fn from_profile(profile: Arc<Mutex<BasicProfile>>) -> Self {
        Self {
            profile,
            phase: Phase::Outer,
        }
    }

    pub fn blank_node(&mut self, label: String) -> Arc<Term> {
        // FIXME probably move to an Environment struct
        let mut profile = self.profile.lock().unwrap();
        match profile.terms.resolve_unchecked(label.as_str()) {
            Some(t) => t.clone(),
            None => {
                let term = Arc::new(Term::BlankNode(BlankNode::new()));
                profile.terms.set(label, term.clone());
                term
            }
        }
    }
}

pub type TokenStream<'i> = Stateful<Partial<&'i str>, &'i mut State>;

pub const PN_CHARS_BASE: (
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
) = (
    ('A'..='Z'),
    ('a'..='z'),
    ('\u{00c0}'..='\u{00d6}'),
    ('\u{00d8}'..='\u{00f6}'),
    ('\u{00f8}'..='\u{02ff}'),
    ('\u{0370}'..='\u{037d}'),
    ('\u{037f}'..='\u{1fff}'),
    ('\u{200c}'..='\u{200d}'),
    ('\u{2070}'..='\u{218f}'),
    ('\u{2c00}'..='\u{2fef}'),
    ('\u{3001}'..='\u{d7ff}'),
    ('\u{f900}'..='\u{fdcf}'),
    ('\u{fdf0}'..='\u{fffd}'),
    ('\u{10000}'..='\u{effff}'),
);
pub const PN_CHARS_U: (
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    char,
) = (
    ('A'..='Z'),
    ('a'..='z'),
    ('\u{00c0}'..='\u{00d6}'),
    ('\u{00d8}'..='\u{00f6}'),
    ('\u{00f8}'..='\u{02ff}'),
    ('\u{0370}'..='\u{037d}'),
    ('\u{037f}'..='\u{1fff}'),
    ('\u{200c}'..='\u{200d}'),
    ('\u{2070}'..='\u{218f}'),
    ('\u{2c00}'..='\u{2fef}'),
    ('\u{3001}'..='\u{d7ff}'),
    ('\u{f900}'..='\u{fdcf}'),
    ('\u{fdf0}'..='\u{fffd}'),
    ('\u{10000}'..='\u{effff}'),
    '_',
);
pub const PN_CHARS: (
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
    char,
    char,
    std::ops::RangeInclusive<char>,
    char,
    std::ops::RangeInclusive<char>,
    std::ops::RangeInclusive<char>,
) = (
    ('A'..='Z'),
    ('a'..='z'),
    ('\u{00c0}'..='\u{00d6}'),
    ('\u{00d8}'..='\u{00f6}'),
    ('\u{00f8}'..='\u{02ff}'),
    ('\u{0370}'..='\u{037d}'),
    ('\u{037f}'..='\u{1fff}'),
    ('\u{200c}'..='\u{200d}'),
    ('\u{2070}'..='\u{218f}'),
    ('\u{2c00}'..='\u{2fef}'),
    ('\u{3001}'..='\u{d7ff}'),
    ('\u{f900}'..='\u{fdcf}'),
    ('\u{fdf0}'..='\u{fffd}'),
    ('\u{10000}'..='\u{effff}'),
    '_',
    '-',
    ('0'..='9'),
    '\u{00b7}',
    ('\u{0300}'..='\u{036f}'),
    ('\u{203f}'..='\u{2040}'),
);

pub fn blank_node_label_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    let start = input.checkpoint();

    let mut label = preceded("_:", take_while(0.., (PN_CHARS, '.')))
        .parse_next(input)?
        .to_string();

    if label.ends_with('.') {
        let end = input.checkpoint();
        let offset = end.offset_from(&start) - 1;
        input.reset(&start);
        take(offset).parse_next(input)?;
        label.pop();
    }

    Ok(input.state.blank_node(label))
}

pub fn uchar16_parser(input: &mut TokenStream<'_>) -> ModalResult<char> {
    let chars = p_trace(
        "uchar16_parser",
        preceded(
            "\\u",
            take_while(4..=4, (('A'..='F'), ('a'..='f'), ('0'..='9'))),
        ),
    )
    .parse_next(input)?;

    let mut res = 0u32;
    for c in chars.as_bytes() {
        let val = (*c as char).to_digit(16).expect("parser yielded digits");
        res = (res << 4) + val;
    }

    char::from_u32(res).ok_or(ErrMode::Cut(ContextError::new()))
}

pub fn uchar32_parser(input: &mut TokenStream<'_>) -> ModalResult<char> {
    let chars = p_trace(
        "uchar32_parser",
        preceded(
            "\\U",
            take_while(8..=8, (('A'..='F'), ('a'..='f'), ('0'..='9'))),
        ),
    )
    .parse_next(input)?;

    let mut res = 0u32;
    for c in chars.as_bytes() {
        let val = (*c as char).to_digit(16).expect("parser yielded digits");
        res = (res << 4) + val;
    }

    char::from_u32(res).ok_or(ErrMode::Cut(ContextError::new()))
}

pub fn iriref_parser(input: &mut TokenStream<'_>) -> ModalResult<Iri> {
    let iriref: String = p_trace(
        "iriref_parser",
        delimited(
            '<',
            repeat(
                0..,
                alt((
                    none_of((
                        ('\x00'..='\x20'),
                        '<',
                        '>',
                        '"',
                        '{',
                        '}',
                        '|',
                        '^',
                        '`',
                        '\\',
                    )),
                    uchar16_parser,
                    uchar32_parser,
                )),
            ),
            '>',
        ),
    )
    .parse_next(input)?;

    let iriref = IriRefBuf::new(iriref).map_err(|_| ErrMode::Cut(ContextError::new()))?;
    match input
        .state
        .profile
        .lock()
        .unwrap()
        .resolve_iri_ref(iriref.clone())
    {
        Ok(iri) => Ok(iri),
        Err(_) => iriref
            .try_into_iri()
            .map_err(|_| ErrMode::Cut(ContextError::new())),
    }
}

pub fn iriref_term_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    Ok(Arc::new(Term::NamedNode(iriref_parser.parse_next(input)?)))
}

pub fn collection_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    p_trace(
        "collection_parser",
        preceded(multispace0, ("(", multispace0)),
    )
    .parse_next(input)?;

    let mut triples = Vec::new();

    match input.state.phase {
        Phase::Outer => {
            input
                .state
                .phase
                .to_collection()
                .map_err(|_| ErrMode::Cut(ContextError::new()))?;
            return Ok(ResultType::InProduction);
        }
        Phase::InTriplePredicate {
            ref cur_subject,
            ref cur_predicate,
            ..
        } => {
            let cur_subject = cur_subject.clone();
            let cur_predicate = cur_predicate.clone();
            let node = input.state.phase.to_collection().unwrap();

            triples.push(
                Triple::new(cur_subject, cur_predicate, node.clone())
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?,
            );
        }
        Phase::InCollection {
            ref cur_subject,
            is_first,
            ..
        } => {
            let cur_subject = cur_subject.clone();

            if is_first {
                let node = input
                    .state
                    .phase
                    .to_collection()
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?;

                triples.push(
                    Triple::new(
                        cur_subject.clone(),
                        Arc::new(Term::NamedNode(
                            Iri::new(
                                "http://www.w3.org/1999/02/22-rdf-syntax-ns#first".to_string(),
                            )
                            .expect("static IRI"),
                        )),
                        node.clone(),
                    )
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?,
                );
            } else {
                let cur_subject = cur_subject.clone();
                let next_node = input.state.phase.to_collection_next().unwrap();
                let node = input.state.phase.to_collection().unwrap();

                triples.push(
                    Triple::new(
                        cur_subject.clone(),
                        Arc::new(Term::NamedNode(
                            Iri::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest".to_string())
                                .expect("static IRI"),
                        )),
                        next_node.clone(),
                    )
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?,
                );
                triples.push(
                    Triple::new(
                        next_node.clone(),
                        Arc::new(Term::NamedNode(
                            Iri::new(
                                "http://www.w3.org/1999/02/22-rdf-syntax-ns#first".to_string(),
                            )
                            .expect("static IRI"),
                        )),
                        node.clone(),
                    )
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?,
                );
            };
        }
        _ => unreachable!("we are only called in known phases"),
    };

    Ok(ResultType::Triples(triples))
}

pub fn collection_end_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    p_trace(
        "collection_end_parser",
        preceded(multispace0, (")", multispace0)),
    )
    .parse_next(input)?;
    let node = input
        .state
        .phase
        .restore()
        .map_err(|_| ErrMode::Cut(ContextError::new()))?
        .0
        .expect("must not be None if phase was valid on call");

    let mut triples = Vec::new();
    triples.push(
        Triple::new(
            node.clone(),
            Arc::new(Term::NamedNode(
                Iri::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest".to_string())
                    .expect("static IRI"),
            )),
            Arc::new(Term::NamedNode(
                Iri::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil".to_string())
                    .expect("static IRI"),
            )),
        )
        .map_err(|_| ErrMode::Cut(ContextError::new()))?,
    );

    match input.state.phase {
        Phase::InCollection { is_first, .. } => {
            if is_first {
                input
                    .state
                    .phase
                    .to_collection_next()
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?;
            }
        }
        _ => {}
    };

    Ok(ResultType::Triples(triples))
}

pub fn empty_collection_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    p_trace(
        "empty_collection_parser",
        preceded(multispace0, ("(", multispace0, ")")),
    )
    .parse_next(input)?;

    Ok(Arc::new(Term::NamedNode(
        Iri::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil".to_string()).expect("static IRI"),
    )))
}

pub fn anon_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    p_trace(
        "anon_parser",
        preceded(multispace0, ("[", multispace0, "]")),
    )
    .parse_next(input)?;

    Ok(Arc::new(Term::BlankNode(BlankNode::new())))
}

pub fn blank_node_property_list_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    p_trace(
        "blank_node_property_list_parser",
        preceded(multispace0, ("[", multispace0)),
    )
    .parse_next(input)?;
    input
        .state
        .phase
        .to_blank_node_property_list()
        .map_err(|_| ErrMode::Cut(ContextError::new()))?;

    Ok(ResultType::InProduction)
}

pub fn echar_parser(input: &mut TokenStream<'_>) -> ModalResult<char> {
    p_trace(
        "echar_parser",
        preceded(
            '\\',
            alt((
                't'.value('\t'),
                'b'.value('\x08'),
                'n'.value('\n'),
                'r'.value('\r'),
                'f'.value('\x0c'),
                '"'.value('"'),
                '\''.value('\''),
                '\\'.value('\\'),
            )),
        ),
    )
    .parse_next(input)
}

pub fn string_literal_single_quote_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    p_trace(
        "string_literal_single_quote_parser",
        delimited(
            '\'',
            repeat(
                0..,
                alt((
                    none_of(('\x27', '\x5c', '\x0a', '\x0d')),
                    echar_parser,
                    uchar16_parser,
                    uchar32_parser,
                )),
            ),
            '\'',
        ),
    )
    .parse_next(input)
}

pub fn string_literal_double_quote_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    p_trace(
        "string_literal_double_quote_parser",
        delimited(
            '"',
            repeat(
                0..,
                alt((
                    none_of(('\x22', '\x5c', '\x0a', '\x0d')),
                    echar_parser,
                    uchar16_parser,
                    uchar32_parser,
                )),
            ),
            '"',
        ),
    )
    .parse_next(input)
}

pub fn string_literal_long_single_quote_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    let start = input.checkpoint();

    r#"'''"#.parse_next(input)?;

    // FIXME optimise
    let mut content = String::new();
    loop {
        let res: ModalResult<(String, String)> = (
            repeat(0..=2, r#"'"#),
            repeat(
                0..,
                alt((
                    none_of(('\'', '\\')),
                    echar_parser,
                    uchar16_parser,
                    uchar32_parser,
                )),
            ),
        )
            .parse_next(input);
        match res {
            Ok((first, second)) => {
                if first.len() == 0 && second.len() == 0 {
                    input.reset(&start);
                    return Err(ErrMode::Backtrack(ContextError::new()));
                }
                if first.len() == 2 && second.len() == 0 {
                    match '\''.parse_next(input) {
                        Ok(_) => return Ok(content),
                        Err(e) => {
                            input.reset(&start);
                            return Err(e);
                        }
                    }
                }
                content.push_str(&first);
                content.push_str(&second);

                let res: ModalResult<&str> = r#"'''"#.parse_next(input);
                if res.is_ok() {
                    return Ok(content);
                }
            }
            Err(e) => {
                input.reset(&start);
                return Err(e);
            }
        }
    }
}

pub fn string_literal_long_double_quote_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    let start = input.checkpoint();

    r#"""""#.parse_next(input)?;

    // FIXME optimise
    let mut content = String::new();
    loop {
        let res: ModalResult<(String, String)> = (
            repeat(0..=2, r#"""#),
            repeat(
                0..,
                alt((
                    none_of(('"', '\\')),
                    echar_parser,
                    uchar16_parser,
                    uchar32_parser,
                )),
            ),
        )
            .parse_next(input);
        match res {
            Ok((first, second)) => {
                if first.len() == 0 && second.len() == 0 {
                    input.reset(&start);
                    return Err(ErrMode::Backtrack(ContextError::new()));
                }
                if first.len() == 2 && second.len() == 0 {
                    match '"'.parse_next(input) {
                        Ok(_) => return Ok(content),
                        Err(e) => {
                            input.reset(&start);
                            return Err(e);
                        }
                    }
                }
                content.push_str(&first);
                content.push_str(&second);

                let res: ModalResult<&str> = r#"""""#.parse_next(input);
                if res.is_ok() {
                    return Ok(content);
                }
            }
            Err(e) => {
                input.reset(&start);
                return Err(e);
            }
        }
    }
}

pub fn literal_datatype_parser(input: &mut TokenStream<'_>) -> ModalResult<Iri> {
    p_trace("literal_datatype_parser", preceded("^^", iri_parser)).parse_next(input)
}

pub fn langtag_parser(
    input: &mut TokenStream<'_>,
) -> ModalResult<(LanguageTag, Option<BaseDirection>)> {
    let (primary, subs, dir) = preceded(
        "@",
        (
            take_while(1.., (('A'..='Z'), ('a'..='z'))),
            repeat(
                0..,
                preceded(
                    '-',
                    take_while(1.., (('A'..='Z'), ('a'..='z'), ('0'..='9'))),
                ),
            )
            .fold(
                || String::with_capacity(11),
                |mut acc: String, item| {
                    acc.push('-');
                    acc.push_str(item);
                    acc
                },
            ),
            opt(preceded("--", take_while(1.., (('A'..='Z'), ('a'..='z'))))),
        ),
    )
    .parse_next(input)?;

    let mut tag = String::with_capacity(primary.len() + subs.len() + 1);
    tag.push_str(primary);
    tag.push_str(subs.as_str());

    let language =
        LanguageTag::parse(tag.as_str()).map_err(|_| ErrMode::Cut(ContextError::new()))?;
    let direction = match dir {
        Some(d) => Some(BaseDirection::try_from(d).map_err(|_| ErrMode::Cut(ContextError::new()))?),
        None => None,
    };

    Ok((language, direction))
}

pub fn string_literal_parser(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    let (lexical, datatype, tag) = p_trace(
        "string_literal_parser",
        (
            alt((
                string_literal_long_double_quote_parser,
                string_literal_double_quote_parser,
                string_literal_long_single_quote_parser,
                string_literal_single_quote_parser,
            )),
            opt(literal_datatype_parser),
            opt(langtag_parser),
        ),
    )
    .parse_next(input)?;

    Ok(if let Some((language, direction)) = tag {
        Literal::new(lexical, datatype, Some(language), direction)
            .map_err(|_| ErrMode::Cut(ContextError::new()))?
    } else {
        Literal::new(lexical, datatype, None, None)
            .map_err(|_| ErrMode::Cut(ContextError::new()))?
    })
}

pub fn literal_parser(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    p_trace(
        "literal_parser",
        alt((
            string_literal_parser,
            boolean_parser,
            double_parser,
            decimal_parser,
            integer_parser,
        )),
    )
    .parse_next(input)
}

pub fn literal_term_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    Ok(Arc::new(Term::Literal(literal_parser.parse_next(input)?)))
}

pub fn integer_parser(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    let (sig, digits) =
        p_trace("integer_parser", (opt(one_of(('+', '-'))), digit1)).parse_next(input)?;

    let mut lexical = String::with_capacity(digits.len() + if sig.is_some() { 1 } else { 0 });
    if let Some(ch) = sig {
        lexical.push(ch);
    }
    lexical.push_str(digits);

    Ok(Literal::new(
        lexical,
        Some(Iri::new("http://www.w3.org/2001/XMLSchema#integer".to_string()).expect("static IRI")),
        None,
        None,
    )
    .expect("parser has yielded digits"))
}

pub fn decimal_parser(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    let (sig, digits, _, decimal_digits) = p_trace(
        "decimal_parser",
        (opt(one_of(('+', '-'))), digit0, '.', digit1),
    )
    .parse_next(input)?;

    let mut lexical = String::with_capacity(
        digits.len() + if sig.is_some() { 1 } else { 0 } + decimal_digits.len() + 1,
    );
    if let Some(ch) = sig {
        lexical.push(ch);
    }
    lexical.push_str(digits);
    lexical.push('.');
    lexical.push_str(decimal_digits);

    Ok(Literal::new(
        lexical,
        Some(Iri::new("http://www.w3.org/2001/XMLSchema#decimal".to_string()).expect("static IRI")),
        None,
        None,
    )
    .expect("parser has yielded decimal chars"))
}

pub fn double_parser_full(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    let (sig, digits, _, decimal_digits, e, e_sig, e_digits) = p_trace(
        "double_parser_full",
        (
            opt(one_of(('+', '-'))),
            digit1,
            '.',
            digit0,
            Caseless("e"),
            opt(one_of(('+', '-'))),
            digit1,
        ),
    )
    .parse_next(input)?;

    let mut lexical = String::with_capacity(
        digits.len()
            + if sig.is_some() { 1 } else { 0 }
            + decimal_digits.len()
            + 2
            + if e_sig.is_some() { 1 } else { 0 }
            + e_digits.len(),
    );
    if let Some(ch) = sig {
        lexical.push(ch);
    }
    lexical.push_str(digits);
    lexical.push('.');
    lexical.push_str(decimal_digits);
    lexical.push_str(e);
    if let Some(ch) = e_sig {
        lexical.push(ch);
    }
    lexical.push_str(e_digits);

    Ok(Literal::new(
        lexical,
        Some(Iri::new("http://www.w3.org/2001/XMLSchema#double".to_string()).unwrap()),
        None,
        None,
    )
    .expect("parser has yielded double chars"))
}

pub fn double_parser_no_leading(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    let (sig, _, decimal_digits, e, e_sig, e_digits) = p_trace(
        "double_parser_no_leading",
        (
            opt(one_of(('+', '-'))),
            '.',
            digit1,
            Caseless("e"),
            opt(one_of(('+', '-'))),
            digit1,
        ),
    )
    .parse_next(input)?;

    let mut lexical = String::with_capacity(
        if sig.is_some() { 1 } else { 0 }
            + decimal_digits.len()
            + 2
            + if e_sig.is_some() { 1 } else { 0 }
            + e_digits.len(),
    );

    if let Some(ch) = sig {
        lexical.push(ch);
    }
    lexical.push('.');
    lexical.push_str(decimal_digits);
    lexical.push_str(e);
    if let Some(ch) = e_sig {
        lexical.push(ch);
    }
    lexical.push_str(e_digits);

    Ok(Literal::new(
        lexical,
        Some(Iri::new("http://www.w3.org/2001/XMLSchema#double".to_string()).unwrap()),
        None,
        None,
    )
    .expect("parser has yielded double chars"))
}

pub fn double_parser_no_trailing(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    let (sig, digits, e, e_sig, e_digits) = p_trace(
        "double_parser_no_trailing",
        (
            opt(one_of(('+', '-'))),
            digit1,
            Caseless("e"),
            opt(one_of(('+', '-'))),
            digit1,
        ),
    )
    .parse_next(input)?;

    let mut lexical = String::with_capacity(
        if sig.is_some() { 1 } else { 0 }
            + digits.len()
            + 1
            + if e_sig.is_some() { 1 } else { 0 }
            + e_digits.len(),
    );
    if let Some(ch) = sig {
        lexical.push(ch);
    }
    lexical.push_str(digits);
    lexical.push_str(e);
    if let Some(ch) = e_sig {
        lexical.push(ch);
    }
    lexical.push_str(e_digits);

    Ok(Literal::new(
        lexical,
        Some(Iri::new("http://www.w3.org/2001/XMLSchema#double".to_string()).unwrap()),
        None,
        None,
    )
    .expect("parser has yielded double chars"))
}

pub fn double_parser(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    p_trace(
        "double_parser",
        alt((
            double_parser_full,
            double_parser_no_leading,
            double_parser_no_trailing,
        )),
    )
    .parse_next(input)
}

pub fn boolean_parser(input: &mut TokenStream<'_>) -> ModalResult<Literal> {
    let value = match p_trace("boolean_parser", alt(("true", "false"))).parse_next(input)? {
        "true" => true,
        "false" => false,
        _ => unreachable!("parser does only match true and false"),
    };

    Ok(value.try_into().expect("parser has yielded boolean"))
}

pub fn pname_prefix_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    let start = input.checkpoint();

    let (first, mut rest): (char, Option<String>) = p_trace(
        "pname_prefix_parser",
        (
            one_of(PN_CHARS_BASE),
            opt(repeat(0.., one_of((PN_CHARS, '.')))),
        ),
    )
    .parse_next(input)?;

    if let Some(ref mut rest) = rest {
        if rest.ends_with('.') {
            let end = input.checkpoint();
            let offset = end.offset_from(&start) - 1;
            input.reset(&start);
            take(offset).parse_next(input)?;
            rest.pop();
        }
    }

    let mut prefix = String::with_capacity(if let Some(ref rest) = rest {
        rest.len() + 1
    } else {
        1
    });
    prefix.push(first);
    if let Some(ref rest) = rest {
        prefix.push_str(rest);
    }

    Ok(prefix)
}

pub fn pname_ns_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    let (prefix, _) =
        p_trace("pname_ns_parser", (opt(pname_prefix_parser), ":")).parse_next(input)?;

    if let Some(mut p) = prefix {
        p.push(':');
        Ok(p)
    } else {
        Ok(":".to_string())
    }
}

pub fn pname_ns_term_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    Ok(Arc::new(
        pname_ns_parser
            .parse_next(input)?
            .try_into()
            .map_err(|_| ErrMode::Cut(ContextError::new()))?,
    ))
}

pub fn pname_local_escape_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    Ok(p_trace(
        "pname_local_escape_parser",
        preceded(
            '\\',
            alt((
                '_', '~', '.', '-', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', '/',
                '?', '#', '@', '%',
            )),
        ),
    )
    .parse_next(input)?
    .into())
}

pub fn percent_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    let (a, b, c) = p_trace(
        "percent_parser",
        (
            '%',
            one_of(('0'..='9', 'A'..='F', 'a'..='f')),
            one_of(('0'..='9', 'A'..='F', 'a'..='f')),
        ),
    )
    .parse_next(input)?;

    let mut ret = String::with_capacity(3);
    ret.push(a);
    ret.push(b);
    ret.push(c);

    Ok(ret)
}

pub fn pname_local_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    let start = input.checkpoint();

    let (first, mut rest): (String, Option<String>) = p_trace(
        "pname_local_parser",
        (
            alt((
                one_of((PN_CHARS_U, ':', '0'..='9')).output_into(),
                percent_parser,
                pname_local_escape_parser,
            )),
            opt(
                repeat(
                    0..,
                    alt((
                        one_of((PN_CHARS, '.', ':')).output_into(),
                        percent_parser,
                        pname_local_escape_parser,
                    )),
                )
                .fold(String::new, |mut acc, item| {
                    acc.push_str(&item);
                    acc
                }),
                //one_of((PN_CHARS, ':')),
            ),
        ),
    )
    .parse_next(input)?;

    if let Some(ref mut rest) = rest {
        if rest.ends_with('.') {
            let end = input.checkpoint();
            let offset = end.offset_from(&start) - 2;
            input.reset(&start);
            take(offset).parse_next(input)?;
            let check = take(2usize).parse_next(input)?;
            if check != "\\." {
                input.reset(&start);
                take(offset + 1).parse_next(input)?;
            }

            rest.pop();
        }
    }

    let mut local = String::with_capacity(if let Some(ref middle) = rest {
        middle.len() + 2
    } else {
        1
    });
    local.push_str(&first);
    if let Some(middle) = rest {
        local.push_str(&middle);
        //local.push(last);
    }

    Ok(local)
}

pub fn pname_ln_parser(input: &mut TokenStream<'_>) -> ModalResult<String> {
    let (ns, local) = p_trace(
        "pname_ln_parser",
        (pname_ns_parser, opt(pname_local_parser)),
    )
    .parse_next(input)?;

    let curie = match local {
        Some(ln) => {
            let mut curie = ns.clone();
            curie.push_str(ln.as_str());
            curie
        }
        None => ns,
    };

    Ok(curie)
}

pub fn pname_ln_term_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    let curie = pname_ln_parser.parse_next(input)?;

    input
        .state
        .profile
        .lock()
        .unwrap()
        .resolve(&curie)
        .map_err(|_| ErrMode::Cut(ContextError::new()))
}

pub fn iri_parser(input: &mut TokenStream<'_>) -> ModalResult<Iri> {
    Ok(iri_term_parser
        .parse_next(input)?
        .to_iri()
        .expect("is definitely an IRI")
        .clone())
}

pub fn iri_term_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    p_trace(
        "iri_term_parser",
        alt((
            iriref_term_parser,
            pname_ln_term_parser,
            pname_ns_term_parser,
        )),
    )
    .parse_next(input)
}

pub fn subject_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    let res = p_trace(
        "subject_parser",
        preceded(
            multispace0,
            alt((
                iri_term_parser,
                blank_node_label_parser,
                anon_parser,
                empty_collection_parser,
            )),
        ),
    )
    .parse_next(input)?;

    input
        .state
        .phase
        .to_triple_subject(res.clone(), false, true)
        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
    Ok(ResultType::InProduction)
}

pub fn a_parser(input: &mut TokenStream<'_>) -> ModalResult<Arc<Term>> {
    let _: char = p_trace("a_parser", 'a').parse_next(input)?;

    Ok(Arc::new(Term::NamedNode(
        Iri::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string())
            .expect("static IRI"),
    )))
}

pub fn predicate_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    let res = p_trace(
        "predicate_parser",
        preceded(multispace0, alt((iri_term_parser, a_parser))),
    )
    .parse_next(input)?;

    // FIXME error handling
    input
        .state
        .phase
        .to_triple_predicate(res.clone())
        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
    Ok(ResultType::InProduction)
}

pub fn object_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    let res = p_trace(
        "object_parser",
        preceded(
            multispace0,
            alt((
                iri_term_parser,
                blank_node_label_parser,
                literal_term_parser,
                anon_parser,
                empty_collection_parser,
            )),
        ),
    )
    .parse_next(input)?;

    let mut triples = Vec::new();

    match input.state.phase {
        Phase::InCollection {
            ref cur_subject,
            is_first,
            ..
        } => {
            if is_first {
                triples.push(
                    Triple::new(
                        cur_subject.clone(),
                        Arc::new(Term::NamedNode(
                            Iri::new(
                                "http://www.w3.org/1999/02/22-rdf-syntax-ns#first".to_string(),
                            )
                            .expect("static IRI"),
                        )),
                        res.clone(),
                    )
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?,
                );
                input
                    .state
                    .phase
                    .to_collection_next()
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?;
            } else {
                let cur_subject = cur_subject.clone();
                let next_node = input
                    .state
                    .phase
                    .to_collection_next()
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?;

                triples.push(
                    Triple::new(
                        cur_subject.clone(),
                        Arc::new(Term::NamedNode(
                            Iri::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest".to_string())
                                .expect("static IRI"),
                        )),
                        next_node.clone(),
                    )
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?,
                );
                triples.push(
                    Triple::new(
                        next_node.clone(),
                        Arc::new(Term::NamedNode(
                            Iri::new(
                                "http://www.w3.org/1999/02/22-rdf-syntax-ns#first".to_string(),
                            )
                            .expect("static IRI"),
                        )),
                        res.clone(),
                    )
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?,
                );
            }
        }
        _ => {
            input
                .state
                .phase
                .to_triple_object()
                .map_err(|_| ErrMode::Cut(ContextError::new()))?;
            match input.state.phase {
                Phase::InTripleObject {
                    ref cur_subject,
                    ref cur_predicate,
                    ..
                } => {
                    // FIXME error handling
                    triples.push(
                        Triple::new(cur_subject.clone(), cur_predicate.clone(), res.clone())
                            .map_err(|_| ErrMode::Cut(ContextError::new()))?,
                    );
                }
                _ => unreachable!("must not be reached as we are only called for an object"),
            }
        }
    };

    Ok(ResultType::Triples(triples))
}

pub fn delimiter_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    let delimiter = p_trace(
        "delimiter_parser",
        preceded(multispace0, alt((',', ';', '.', ']'))),
    )
    .parse_next(input)?;

    match delimiter {
        ',' => match input.state.phase {
            Phase::InTripleObject { .. } => {
                input
                    .state
                    .phase
                    .restore()
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                Ok(ResultType::InProduction)
            }
            _ => Err(ErrMode::Cut(ContextError::new())),
        },
        ';' => match input.state.phase {
            Phase::InTripleObject { .. } => {
                input
                    .state
                    .phase
                    .restore()
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                input
                    .state
                    .phase
                    .restore()
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                input
                    .state
                    .phase
                    .to_not_first()
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                Ok(ResultType::InProduction)
            }
            Phase::InBlankNodePropertyList {
                is_first: false, ..
            } => Ok(ResultType::InProduction),
            Phase::InTripleSubject {
                is_first: false, ..
            } => Ok(ResultType::InProduction),
            _ => Err(ErrMode::Cut(ContextError::new())),
        },
        '.' => {
            match input.state.phase {
                Phase::InTripleObject { .. } => {
                    input
                        .state
                        .phase
                        .restore()
                        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                    input
                        .state
                        .phase
                        .restore()
                        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                    input
                        .state
                        .phase
                        .restore()
                        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                }
                Phase::InTripleSubject {
                    is_blank_node_property_list: true,
                    ..
                }
                | Phase::InTripleSubject {
                    is_first: false, ..
                } => {
                    input
                        .state
                        .phase
                        .restore()
                        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                }
                _ => return Err(ErrMode::Cut(ContextError::new())),
            }

            match input.state.phase {
                Phase::Outer { .. } => Ok(ResultType::InProduction),
                // FIXME raise more comprehensive error
                _ => Err(ErrMode::Cut(ContextError::new())),
            }
        }
        ']' => {
            // FIXME validation
            match input.state.phase {
                Phase::InTripleObject { .. } => {
                    input
                        .state
                        .phase
                        .restore()
                        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                    input
                        .state
                        .phase
                        .restore()
                        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
                }
                Phase::InBlankNodePropertyList { .. } => {}
                _ => return Err(ErrMode::Cut(ContextError::new())),
            };
            let node = match input.state.phase {
                Phase::InBlankNodePropertyList {
                    ref cur_subject, ..
                } => cur_subject.clone(),
                // FIXME raise more comprehensive error
                _ => return Err(ErrMode::Cut(ContextError::new())),
            };
            input
                .state
                .phase
                .restore()
                .map_err(|_| ErrMode::Cut(ContextError::new()))?;

            match input.state.phase {
                Phase::InTripleSubject { .. } => Ok(ResultType::InProduction),
                Phase::InTripleObject {
                    ref cur_subject,
                    ref cur_predicate,
                    ..
                } => {
                    let res = ResultType::Triples(vec![Triple::new(
                        cur_subject.clone(),
                        cur_predicate.clone(),
                        node.clone(),
                    )
                    .map_err(|_| ErrMode::Cut(ContextError::new()))?]);

                    Ok(res)
                }
                Phase::InCollection {
                    ref cur_subject, ..
                } => Ok(ResultType::Triples(vec![Triple::new(
                    cur_subject.clone(),
                    Arc::new(Term::NamedNode(
                        Iri::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#first".to_string())
                            .expect("static IRI"),
                    )),
                    node.clone(),
                )
                .map_err(|_| ErrMode::Cut(ContextError::new()))?])),
                ref phase => {
                    unreachable!("phase {:?}, must not happen as we are only called within a blank node property list", phase)
                }
            }
        }
        _ => todo!("error handling"),
    }
}

pub fn base_directive_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    let base = p_trace(
        "base_directive_parser",
        alt((
            delimited(("@base", space1), iriref_parser, (space0, ".")),
            delimited((Caseless("BASE"), space1), iriref_parser, space0),
        )),
    )
    .parse_next(input)?;

    input
        .state
        .profile
        .lock()
        .unwrap()
        .set_base_iri(Some(base.clone()));
    Ok(ResultType::Base(base))
}

pub fn prefix_directive_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    let (mut prefix, _, iri) = p_trace(
        "prefix_directive_parser",
        alt((
            delimited(
                ("@prefix", space1),
                (pname_ns_parser, space0, iriref_parser),
                (space0, "."),
            ),
            delimited(
                (Caseless("PREFIX"), space1),
                (pname_ns_parser, space0, iriref_parser),
                space0,
            ),
        )),
    )
    .parse_next(input)?;
    prefix.pop();

    input
        .state
        .profile
        .lock()
        .unwrap()
        .set_prefix(prefix.clone(), iri.to_string())
        .map_err(|_| ErrMode::Cut(ContextError::new()))?;
    Ok(ResultType::Prefix(prefix, iri))
}

pub fn comment_parser<'i>(input: &mut TokenStream<'i>) -> ModalResult<&'i str> {
    p_trace("comment_parser", preceded((space0, '#'), till_line_ending)).parse_next(input)
}

pub fn comment_or_blank_line_parser(input: &mut TokenStream) -> ModalResult<ResultType> {
    p_trace(
        "comment_or_blank_line_parser",
        (alt((comment_parser, space0)), alt((line_ending, eof))),
    )
    .parse_next(input)?;

    Ok(ResultType::CommentOrBlankLine)
}

#[derive(Debug, PartialEq)]
pub enum ResultType {
    Triples(Vec<Arc<Triple>>),
    Prefix(String, Iri),
    Base(Iri),
    InProduction,
    CommentOrBlankLine,
}

pub fn document_parser(input: &mut TokenStream<'_>) -> ModalResult<ResultType> {
    match input.state.phase {
        Phase::Outer => alt((
            subject_parser,
            blank_node_property_list_parser,
            collection_parser,
            prefix_directive_parser,
            base_directive_parser,
            comment_or_blank_line_parser,
        ))
        .parse_next(input),
        Phase::InTripleSubject { .. } => alt((
            predicate_parser,
            delimiter_parser,
            comment_or_blank_line_parser,
        ))
        .parse_next(input),
        Phase::InTriplePredicate { .. } => alt((
            object_parser,
            blank_node_property_list_parser,
            collection_parser,
            comment_or_blank_line_parser,
        ))
        .parse_next(input),
        Phase::InTripleObject { .. } => {
            alt((delimiter_parser, comment_or_blank_line_parser)).parse_next(input)
        }
        Phase::InBlankNodePropertyList { .. } => alt((
            predicate_parser,
            delimiter_parser,
            comment_or_blank_line_parser,
        ))
        .parse_next(input),
        Phase::InCollection { .. } => alt((
            object_parser,
            collection_end_parser,
            blank_node_property_list_parser,
            collection_parser,
            comment_or_blank_line_parser,
        ))
        .parse_next(input),
    }
}

const CHUNK_SIZE: usize = 1024;

/// # Example
///
/// ```
/// use taganak_core::{sources::parsers::turtle::TurtleParser, environment::profile::BasicProfile};
/// use std::sync::{Arc, Mutex};
/// use futures::{Stream, StreamExt};
///
/// const TRIPLES: &str = r##"
/// @prefix foo: <https://example.com/foo#> .
/// @prefix rdfs: <https://www.w3.org/2000/01/rdf-schema#> .
///
/// foo:Emil a foo:Turtle ;
///   foo:age 12 ;
///   foo:shell [ foo:color "#00ff00" ; foo:tiles 15 ] .
///
/// foo:Turtle rdfs:label "Schildkröte"@de , "tortoise"@fr .
/// "##;
///
/// let mut state = Arc::new(Mutex::new(BasicProfile::new()));
/// let mut parser = TurtleParser::new(TRIPLES.as_bytes(), state);
///
/// tokio_test::block_on(async {
///     assert_eq!(parser.next().await.unwrap().unwrap().to_string(), r#"<https://example.com/foo#Emil> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://example.com/foo#Turtle> ."#);
///     assert_eq!(parser.next().await.unwrap().unwrap().to_string(), r#"<https://example.com/foo#Emil> <https://example.com/foo#age> "12"^^<http://www.w3.org/2001/XMLSchema#integer> ."#);
///
///     let (s_shell1, _, color) = parser.next().await.unwrap().unwrap().spo_arc();
///     let (s_shell2, _, tiles) = parser.next().await.unwrap().unwrap().spo_arc();
///     let (_, _, o_shell) = parser.next().await.unwrap().unwrap().spo_arc();
///
///     assert_eq!(o_shell, s_shell1);
///     assert_eq!(o_shell, s_shell2);
///
///     let color: String = color.to_literal().unwrap().clone().try_into().unwrap();
///     let tiles: u8 = tiles.to_literal().unwrap().clone().try_into().unwrap();
///
///     assert_eq!(color, "#00ff00");
///     assert_eq!(tiles, 15);
/// });
/// ```
pub struct TurtleParser<Decoder> {
    // FIXME can we get by with a generic here?
    decoder: Decoder,
    state: Arc<Mutex<State>>,
    buf: String,
    read_buf: Option<(Vec<u8>, usize)>,
    triples: VecDeque<Arc<Triple>>,
}

impl<R> fmt::Debug for TurtleParser<R> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TurtleParser").finish()
    }
}

impl<R: AsyncRead> TurtleParser<Utf8Decoder<Compat<R>>> {
    pub fn new(stream: R, state: Arc<Mutex<BasicProfile>>) -> Self {
        trace!(
            "Initialising new TurtleParser with buffer size {}",
            CHUNK_SIZE * 2
        );
        Self {
            decoder: Utf8Decoder::with_capacity(CHUNK_SIZE, stream.compat()),
            state: Arc::new(State::from_profile(state).into()),
            buf: String::with_capacity(CHUNK_SIZE * 2),
            read_buf: None,
            triples: VecDeque::new(),
        }
    }
}

impl<R: BufRead> TurtleParser<R> {
    pub fn new_sync(reader: R, state: Arc<Mutex<BasicProfile>>) -> Self {
        Self {
            decoder: reader,
            state: Arc::new(State::from_profile(state).into()),
            buf: String::with_capacity(CHUNK_SIZE * 2),
            read_buf: None,
            triples: VecDeque::new(),
        }
    }
}

impl<R: AsyncRead + Unpin> Stream for TurtleParser<Utf8Decoder<Compat<R>>> {
    type Item = Result<Arc<Triple>, SourceError>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        if self.triples.len() > 0 {
            trace!("{} triples in cache; yielding one", self.triples.len());
            return Poll::Ready(Some(Ok(self.triples.pop_front().expect("we just checked"))));
        }

        loop {
            trace!("reading new Turtle data");
            match self.decoder.poll_next_unpin(cx) {
                Poll::Pending => {
                    trace!("read pending, buffer is {}", self.buf);
                    return Poll::Pending;
                }
                Poll::Ready(Some(Err(e))) => {
                    error!(?e, "error decoding/reading Turtle data");
                    return Poll::Ready(Some(Err(SourceError::Io(std::io::Error::new(
                        match &e {
                            DecodeError::IncompleteUtf8Sequence(_) => {
                                std::io::ErrorKind::InvalidData
                            }
                            DecodeError::Utf8Error(_) => std::io::ErrorKind::InvalidData,
                            DecodeError::IOError(error) => error.kind(),
                        },
                        e.to_string(),
                    )))));
                }
                Poll::Ready(ref res) => {
                    if let Some(Ok(data)) = res {
                        trace!("appending to buffer: {}", data);
                        self.buf.push_str(&data);
                    }

                    if self.buf.len() == 0 {
                        match self.state.lock().unwrap().phase {
                            Phase::Outer => return Poll::Ready(None),
                            _ => {
                                return Poll::Ready(Some(Err(SourceError::Syntax(
                                    "incomplete document".to_string(),
                                ))))
                            }
                        }
                    }

                    trace!("buffer is {}", self.buf);
                    trace!("Parsing {} bytes of Turtle data", self.buf.len());
                    let state = Arc::clone(&self.state);
                    let mut state = state.lock().unwrap();
                    let mut input = TokenStream {
                        input: Partial::new(self.buf.as_mut_str()),
                        state: &mut state,
                    };

                    if res.is_none() {
                        let _ = input.complete();
                    };

                    let start = input.checkpoint();

                    match document_parser.parse_next(&mut input) {
                        Err(ErrMode::Incomplete(_)) => {
                            trace!("not enough Turtle data to determine directive; repeating");
                            // FIXME could handle known needed bytes count here
                            continue;
                        }
                        Err(e) => {
                            trace!("{:?}", e);
                            error!("Failed to parse Turtle data");
                            debug!("Failed buffer chunk was: {}", self.buf);
                            return Poll::Ready(Some(Err(SourceError::Syntax(e.to_string()))));
                        }
                        Ok(res) => {
                            // FIXME use a more performant buffer type, e.g. circular
                            let offset = input.offset_from(&start);
                            self.buf = self.buf.split_off(offset);
                            match res {
                                ResultType::Triples(mut triples) => {
                                    trace!("parsing yielded {} triples", triples.len());
                                    while triples.len() > 0 {
                                        self.triples.push_back(triples.pop().unwrap());
                                    }
                                    cx.waker().wake_by_ref();
                                    return Poll::Pending;
                                }
                                res => {
                                    trace!("result did not yield triple but {:?}; repeating", res);
                                    continue;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

impl<R: BufRead> Iterator for TurtleParser<R> {
    type Item = Result<Arc<Triple>, SourceError>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.triples.len() > 0 {
            return Some(Ok(self.triples.pop_front().unwrap()));
        }

        let (buf, start) = self.read_buf.get_or_insert((Vec::new(), 0));
        let mut eof = false;
        let mut read_start = *start;
        let mut end = *start;
        loop {
            buf.extend_from_slice(&[0; CHUNK_SIZE]);
            let read = self.decoder.read(&mut buf[read_start..]);
            match read {
                Ok(0) => {
                    eof = true;
                    break;
                }
                Ok(CHUNK_SIZE) => {
                    read_start += CHUNK_SIZE;
                    end += CHUNK_SIZE;
                    continue;
                }
                Ok(n) => {
                    end += n;
                    break;
                }
                Err(e) => return Some(Err(SourceError::Io(e))),
            }
        }

        match str::from_utf8(&buf[*start..end]) {
            Ok(t) => {
                self.buf.push_str(t);
                buf.clear();
                *start = 0;
            }
            Err(e) => {
                if eof {
                    return Some(Err(SourceError::Io(std::io::Error::new(
                        std::io::ErrorKind::InvalidData,
                        "non-utf-8 data",
                    ))));
                }
                self.buf
                    .push_str(str::from_utf8(&buf[*start..*start + e.valid_up_to()]).unwrap());
                *start += e.valid_up_to();
            }
        };

        loop {
            let state = self.state.clone();
            let mut state = state.lock().unwrap();
            let mut input = TokenStream {
                input: Partial::new(self.buf.as_mut_str()),
                state: &mut state,
            };

            if eof {
                let _ = input.complete();
            }
            let start = input.checkpoint();
            match document_parser.parse_next(&mut input) {
                Err(ErrMode::Incomplete(_)) => {
                    trace!("not enough Turtle data to determine directive; repeating");
                    // FIXME could handle known needed bytes count here
                    continue;
                }
                Err(e) => {
                    trace!("{:?}", e);
                    error!("Failed to parse Turtle data");
                    debug!("Failed buffer chunk was: {}", self.buf);
                    return Some(Err(SourceError::Syntax(e.to_string())));
                }
                Ok(res) => {
                    // FIXME use a more performant buffer type, e.g. circular
                    let offset = input.offset_from(&start);
                    if self.buf.is_empty() {
                        match state.phase {
                            Phase::Outer => return None,
                            _ => {
                                return Some(Err(SourceError::Syntax(
                                    "incomplete document".to_string(),
                                )))
                            }
                        }
                    }
                    self.buf = self.buf.split_off(offset);
                    match res {
                        ResultType::Triples(mut triples) => {
                            trace!("parsing yielded {} triples", triples.len());
                            while !triples.is_empty() {
                                self.triples.push_back(triples.pop().unwrap());
                            }
                            return self.next();
                        }
                        res => {
                            trace!("result did not yield triple but {:?}; repeating", res);
                            continue;
                        }
                    }
                }
            }
        }
    }
}
