use crate::{kw, Spanned};
use proc_macro2::Span;
use std::fmt;
use syn::{
    parse::{Parse, ParseStream},
    token::{Brace, Bracket, Paren},
    Result, Token,
};
mod assembly;
pub use assembly::{AssemblyFlags, StmtAssembly};
mod blocks;
pub use blocks::{Block, UncheckedBlock};
mod r#break;
pub use r#break::StmtBreak;
mod r#continue;
pub use r#continue::StmtContinue;
mod do_while;
pub use do_while::StmtDoWhile;
mod emit;
pub use emit::StmtEmit;
mod expr;
pub use expr::StmtExpr;
mod r#for;
pub use r#for::{ForInitStmt, StmtFor};
mod r#if;
pub use r#if::StmtIf;
mod r#return;
pub use r#return::StmtReturn;
mod revert;
pub use revert::StmtRevert;
mod r#try;
pub use r#try::{CatchClause, StmtTry};
mod var_decl;
pub use var_decl::{StmtVarDecl, VarDeclDecl, VarDeclTuple};
mod r#while;
pub use r#while::StmtWhile;
#[derive(Clone)]
pub enum Stmt {
    Assembly(StmtAssembly),
    Block(Block),
    Break(StmtBreak),
    Continue(StmtContinue),
    DoWhile(StmtDoWhile),
    Emit(StmtEmit),
    Expr(StmtExpr),
    For(StmtFor),
    If(StmtIf),
    Return(StmtReturn),
    Revert(StmtRevert),
    Try(StmtTry),
    UncheckedBlock(UncheckedBlock),
    VarDecl(StmtVarDecl),
    While(StmtWhile),
}
impl fmt::Debug for Stmt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("Stmt::")?;
        match self {
            Self::Assembly(stmt) => stmt.fmt(f),
            Self::Block(block) => block.fmt(f),
            Self::Break(stmt) => stmt.fmt(f),
            Self::Continue(stmt) => stmt.fmt(f),
            Self::DoWhile(stmt) => stmt.fmt(f),
            Self::Emit(stmt) => stmt.fmt(f),
            Self::Expr(stmt) => stmt.fmt(f),
            Self::For(stmt) => stmt.fmt(f),
            Self::If(stmt) => stmt.fmt(f),
            Self::Return(stmt) => stmt.fmt(f),
            Self::Revert(stmt) => stmt.fmt(f),
            Self::Try(stmt) => stmt.fmt(f),
            Self::UncheckedBlock(block) => block.fmt(f),
            Self::VarDecl(stmt) => stmt.fmt(f),
            Self::While(stmt) => stmt.fmt(f),
        }
    }
}
impl Parse for Stmt {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let _ = input.call(syn::Attribute::parse_outer)?;
        debug!("  > Stmt: {:?}", input.to_string());
        let stmt = Self::parse_simple(input)?;
        debug!("  < Stmt: {stmt:?}\n");
        Ok(stmt)
    }
}
impl Spanned for Stmt {
    fn span(&self) -> Span {
        match self {
            Self::Assembly(stmt) => stmt.span(),
            Self::Block(block) => block.span(),
            Self::Break(stmt) => stmt.span(),
            Self::Continue(stmt) => stmt.span(),
            Self::DoWhile(stmt) => stmt.span(),
            Self::Emit(stmt) => stmt.span(),
            Self::Expr(stmt) => stmt.span(),
            Self::For(stmt) => stmt.span(),
            Self::If(stmt) => stmt.span(),
            Self::Return(stmt) => stmt.span(),
            Self::Revert(stmt) => stmt.span(),
            Self::Try(stmt) => stmt.span(),
            Self::UncheckedBlock(block) => block.span(),
            Self::VarDecl(stmt) => stmt.span(),
            Self::While(stmt) => stmt.span(),
        }
    }
    fn set_span(&mut self, span: Span) {
        match self {
            Self::Assembly(stmt) => stmt.set_span(span),
            Self::Block(block) => block.set_span(span),
            Self::Break(stmt) => stmt.set_span(span),
            Self::Continue(stmt) => stmt.set_span(span),
            Self::DoWhile(stmt) => stmt.set_span(span),
            Self::Emit(stmt) => stmt.set_span(span),
            Self::Expr(stmt) => stmt.set_span(span),
            Self::For(stmt) => stmt.set_span(span),
            Self::If(stmt) => stmt.set_span(span),
            Self::Return(stmt) => stmt.set_span(span),
            Self::Revert(stmt) => stmt.set_span(span),
            Self::Try(stmt) => stmt.set_span(span),
            Self::UncheckedBlock(block) => block.set_span(span),
            Self::VarDecl(stmt) => stmt.set_span(span),
            Self::While(stmt) => stmt.set_span(span),
        }
    }
}
impl Stmt {
    fn parse_simple(input: ParseStream<'_>) -> Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(Brace) {
            input.parse().map(Self::Block)
        } else if lookahead.peek(Paren) {
            StmtVarDecl::parse_or_expr(input)
        } else if lookahead.peek(Bracket) {
            input.parse().map(Self::Expr)
        } else if lookahead.peek(kw::unchecked) {
            input.parse().map(Self::UncheckedBlock)
        } else if lookahead.peek(Token![if]) {
            input.parse().map(Self::If)
        } else if lookahead.peek(Token![for]) {
            input.parse().map(Self::For)
        } else if lookahead.peek(Token![while]) {
            input.parse().map(Self::While)
        } else if lookahead.peek(Token![do]) {
            input.parse().map(Self::DoWhile)
        } else if lookahead.peek(Token![continue]) {
            input.parse().map(Self::Continue)
        } else if lookahead.peek(Token![break]) {
            input.parse().map(Self::Break)
        } else if lookahead.peek(Token![try]) {
            input.parse().map(Self::Try)
        } else if lookahead.peek(Token![return]) {
            input.parse().map(Self::Return)
        } else if lookahead.peek(kw::emit) {
            input.parse().map(Self::Emit)
        } else if lookahead.peek(kw::revert) {
            input.parse().map(Self::Revert)
        } else if lookahead.peek(kw::assembly) {
            input.parse().map(Self::Assembly)
        } else {
            StmtVarDecl::parse_or_expr(input)
        }
    }
}