use crate::lexer::*;
use crate::*;

use core::error::Error;
use std::fmt;

#[derive(Debug)]
struct ParseError {
    position: Position,
    what: String,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Error parsing input: {}", "yeah")
    }
}

impl Error for ParseError { }

struct Context<'a> {
    cur: [Option<Symbol<'a>>;2],
    icur: usize,
    ll: Lexer<'a>,
}

impl<'a> Context<'a> {
    pub fn new( mut ll: Lexer<'a> ) -> Self {
        let cur = [ll.next(), ll.next()];
        //println!(" - cur: {:?}, peek: {:?}", cur[0], cur[1] );
        Self {
            cur,
            icur: 0,
            ll
        }
    }
    
    pub fn next(&mut self) -> Option<Symbol<'a>> {
        self.cur[self.icur] = self.ll.next();
        self.icur = (self.icur+1)%2;
        //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() );
        self.cur[self.icur]
    }

    pub fn cur(&self) -> Option<Symbol<'a>> {
        self.cur[self.icur]
    }

    pub fn peek(&self) -> Option<Symbol<'a>> {
        self.cur[(self.icur+1)%2]
    }
}

pub struct Parser { 
}

/**
 * input: input tag
 *      | input tag_pair
 *      | input text
 *      |
 *      ;
 *
 * tag: '[|' tag_guts '|]'
 *    ;
 *
 * tag_pair: open_tag tag_body close_tag
 *         ;
 *
 * tag_body: tag_body tag
 *         | tag_body tag_pair
 *         | tag_body text
 *         |
 *         ;
 *
 * open_tag: '[|' tag_guts '|>'
 *         ;
 *
 * close_tag: '<|' token '|]'
 *          ;
 *
 * tag_guts: 'fragment' literal props
 *         | 'section' literal
 *         | 'output' literal
 *         ;
 *
 * literal: '"' [^"]* '"'
 *        ;
 *
 * props: props token '=' literal
 *      |
 *      ;
 *
 * disambiguate (tm):
 *
 * input: input tag
 *      | input text
 *      |
 *      ;
 *
 * tag: open_tag_base unary_tag
 *    | open_tag_base binary_tag
 *    ;
 *
 * open_tag_base: '[|' tag_guts
 *              ;
 *
 * unary_tag: '|]'
 *          ;
 *
 * binary_tag: '|>' tag_body close_tag
 *           ;
 *
 * tag_body: tag_body tag
 *         | tag_body text
 *         |
 *         ;
 *
 * close_tag: '<|' token '|]'
 *          ;
 *
 * tag_guts: 'fragment' literal props
 *         | 'section' literal
 *         | 'output' literal
 *         ;
 *
 * literal: '"' [^"]* '"'
 *        ;
 *
 * props: props token '=' literal
 *      |
 *      ;
 */
impl Parser {
    pub fn new() -> Self {
        Self {
        }
    }

    fn lex_error(&self, error: &Symbol) -> Result<Token,Box<dyn Error>> {
        if let SymbolType::Error{what} = error.symbol() {
            Err(Box::new(ParseError {
                position: error.start().clone(),
                what: format!("What: {:?}", *what)
            }))
        } else {
            Err(Box::new(ParseError {
                position: Position::new(0,0),
                what: "Not an error?".to_string(),
            }))
        }
    }

    pub fn parse(&mut self, src: &str ) -> Result<Token,Box<dyn Error>> {
        let mut ll = Lexer::new( src );
        let mut ctx = Context::new( ll );

        // Parse the root of the file, the input context
        self.p_input( &mut ctx )
    }

    fn p_input(&mut self, ctx: &mut Context ) -> Result<Token, Box<dyn Error>> {
        let mut children = Vec::new();
        loop {
            if ctx.cur().is_none() {
                break;
            }
            match ctx.cur().unwrap().symbol() {
                SymbolType::Text(_) => { /* Skip top level text */ }
                SymbolType::StartFlat => {
                    children.push( self.parse_tag( ctx )? );
                }
                SymbolType::Error{..} => {
                    return self.lex_error( &ctx.cur().unwrap() );
                }
                _ => {

                }
            }
            ctx.next();
        }
        Ok(Token::Root(children))
    }

    fn parse_tag(&mut self, ctx: &mut Context) -> Result<Token, Box<dyn Error>> {
        let mut tb = TagBuilder::new();
        
        println!("--> Begin parse_tag <--");

        let start_sym = ctx.cur().unwrap();

        println!("--> Start sym: {:?}", start_sym );

        if ctx.next().is_some() {
            match ctx.cur().unwrap().symbol() {
                SymbolType::Fragment | SymbolType::Section |
                SymbolType::Output => {
                    let name_sym = ctx.cur().unwrap();
                    ctx.next();
                    tb.set_name( name_sym );
                }
                _ => {
                    return Err(Box::new(ParseError{position: ctx.cur().unwrap().start().clone(), what: "Unexpecetd symbol".to_string()}));
                }
            }
        } else {
            return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}));
        }

        println!("--> Begin parse_tag_params <--");
        self.parse_tag_params( ctx, &mut tb )?;
        println!("--> End parse_tag_params <--");
        println!("--> Begin parse_tag_props <--");
        self.parse_tag_props( ctx, &mut tb )?;
        println!("--> End parse_tag_props <--");

        println!("---> {:?}", ctx.cur() );
        if let Some(end_sym) = ctx.cur() {
            match end_sym.symbol() {
                SymbolType::EndPoint => {
                    tb.set_type( TagType::BinaryOpen );
                }
                SymbolType::EndFlat => {
                    tb.set_type( TagType::Unary );
                }
                _ => {
                    return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}));
                }
            }
        }

        tb.build()
    }

    fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> {
        loop {
            if ctx.cur().is_none() {

                return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}));
            }
            match ctx.cur().unwrap().symbol() {
                SymbolType::Literal(s) => {
                    tb.add_param( s.to_string() );
                }
                _ => {
                    break;
                }
            }
            ctx.next();
        }
        Ok(())
    }
    
    fn parse_tag_props(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> {
        loop {
            if ctx.cur().is_none() {
                return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}));
            }
            if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() {
                if ctx.peek().is_some_and(|s|s.check_type(|t| matches!(t,SymbolType::Equals))) {
                    ctx.next();
                    if let Some(sym) = ctx.next() &&
                        let SymbolType::Literal(lv) = sym.symbol() {
                        tb.add_prop( s.to_string(), lv.to_string() );
                    } else {
                        return Err(Box::new(ParseError{position: Position::new(0,0), what: "Expected quoted literal string".to_string()}));
                    }
                } else {
                    return Err(Box::new(ParseError{position:Position::new(0,0), what: "Expected = ".to_string()}));
                }
            } else {
                break;
            }
            ctx.next();
        }
        Ok(())
    }
}

#[derive(PartialEq)]
enum TagType {
    Unknown,
    Unary,
    BinaryOpen,
    BinaryClose,
}

struct TagBuilder<'a> {
    name: Option<Symbol<'a>>,
    params: Vec<String>,
    props: Properties,
    children: Vec<Token>,
    tag_type: TagType,
}

impl<'a> TagBuilder<'a> {
    pub fn new() -> TagBuilder<'a> {
        TagBuilder {
            name: None,
            params: Vec::new(),
            props: Properties::new(),
            children: Vec::new(),
            tag_type: TagType::Unknown,
        }
    }

    pub fn has_children(&self) -> bool {
        if self.tag_type == TagType::BinaryOpen {
            true
        } else {
            false
        }
    }

    pub fn is_same_name(&self, name: Symbol<'a>) -> bool {
        if let Some(a) = self.name {
            a.symbol() == name.symbol()
        } else {
            false
        }
    }

    pub fn set_name(&mut self, name: Symbol<'a>) {
        self.name = Some(name);
        println!("Tag name: {:?}", self.name );
    }

    pub fn add_param(&mut self, param: String) {
        println!("Add param: {:?}", param );
        self.params.push( param );
    }

    pub fn add_prop(&mut self, key: String, value: String) {
        println!("Add prop: {:?} = {:?}", key, value );
        self.props.insert( key, value );
    }

    pub fn add_child(&mut self, token: Token) {
        self.children.push( token );
    }

    pub fn set_type(&mut self, tag_type: TagType) {
        self.tag_type = tag_type;
    }

    pub fn build(mut self) -> Result<Token,Box<dyn Error>> {
        if let Some(sym) = self.name {
            match sym.symbol() {
                SymbolType::Fragment => {
                    Ok(Token::Fragment(self.params.swap_remove(0), self.props, self.children))
                }
                SymbolType::Output => {
                    Ok(Token::Section(self.params.swap_remove(0), self.props, self.children))
                }
                _ => {
                    println!("Unkown tag type");
                    Err(Box::new(ParseError {
                        position: Position::new(0,0),
                        what: "Bad tag type".to_string(),
                    }))
                }
            }
        } else {
                    Err(Box::new(ParseError {
                        position: Position::new(0,0),
                        what: "Bad tag type".to_string(),
                    }))
        }
    }
}
