use nalgebra_glm::exp;
use once_cell::sync::Lazy;
use regex::Regex;
use std::{
    cell::RefCell,
    collections::{HashMap, HashSet},
    env::var_os,
    fmt::{write, Display},
    hash::{Hash, Hasher},
    rc::Rc,
};

use nom::{
    branch::alt,
    bytes::complete::{tag, take_till, take_until, take_while, take_while1},
    character::complete::{
        alpha1, char, digit0, digit1, multispace0, multispace1, none_of, space0, space1,
    },
    combinator::{cut, map, map_res, opt, recognize, verify},
    multi::{fold_many0, many0, many0_count, many1, separated_list0, separated_list1},
    sequence::{delimited, pair, preceded, separated_pair, terminated, tuple},
    Finish, IResult, InputIter,
};

use crate::{components::Snippet, errors::*};

static SINGLE_COMMENT_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r#"//.*"#).unwrap());
static MULTI_COMMENT_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r#"/\*[\s\S]*?\*/"#).unwrap());

static VERSION_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r#"#version\s+\d+(\s+.+)?"#).unwrap());

type RFC<T> = Rc<RefCell<T>>;

const KEEP_WORDS: [&str; 80] = [
    "#version",
    "#define",
    "#include",
    "layout",
    "in",
    "out",
    "uniform",
    "const",
    "static",
    "extern",
    "if",
    "else",
    "while",
    "do",
    "for",
    "+",
    "-",
    "*",
    "/",
    "!",
    "<",
    ">",
    "=",
    "(",
    ")",
    "{",
    "}",
    "[",
    "]",
    "void",
    "return",
    "vec4",
    "float",
    "int",
    "bool",
    "true",
    "false",
    "struct",
    "main",
    "vec3",
    "vec2",
    "mat4",
    "mat3",
    "mat2",
    "sampler2D",
    "samplerCube",
    "sampler2DShadow",
    "samplerCubeShadow",
    "sampler2DArray",
    "sampler2DArrayShadow",
    "sampler3D",
    "sampler2DMS",
    "sampler2DMSArray",
    "samplerBuffer",
    "sampler2DRect",
    "sampler1D",
    "sampler1DShadow",
    "sampler1DArray",
    "sampler1DArrayShadow",
    "sampler2DRectShadow",
    "sampler1DArray",
    "sampler1DArrayShadow",
    "sampler2DRectShadow",
    "sampler1DArray",
    "sampler1DArrayShadow",
    "sampler2DRectShadow",
    "sampler1DArray",
    "sampler1DArrayShadow",
    "sampler2DRectShadow",
    "sampler1DArray",
    "sampler1DArrayShadow",
    "sampler2DRectShadow",
    "sampler1DArray",
    "sampler1DArrayShadow",
    "sampler2DRectShadow",
    "sampler1DArray",
    "sampler1DArrayShadow",
    "sampler2DRectShadow",
    "sampler1DArray",
    "sampler1DArrayShadow",
];

#[derive(Debug, Clone)]
pub struct Include {
    pub path: String,
}

impl Display for Include {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "#include \"{}\"", self.path)
    }
}

fn include(input: &str) -> IResult<&str, Include> {
    let (input, _) = tag("#include")(input)?;
    let (input, _) = space1(input)?;
    let (input, path) = delimited(char('"'), take_while(|c| c != '"'), char('"'))(input)?;
    Ok((
        input,
        Include {
            path: path.to_string(),
        },
    ))
}

#[derive(Debug, Clone, PartialEq)]
pub enum Modifier {
    Const,
    Static,
    Extern,
    Uniform,
    Layout(Option<Vec<Expression>>),
    In,
    Out,
    Flat,
}

impl Display for Modifier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Modifier::Const => write!(f, "const"),
            Modifier::Static => write!(f, "static"),
            Modifier::Extern => write!(f, "extern"),
            Modifier::Uniform => write!(f, "uniform"),
            Modifier::In => write!(f, "in"),
            Modifier::Layout(Some(v)) => write!(
                f,
                "layout({})",
                v.iter()
                    .map(|v| v.to_string())
                    .collect::<Vec<_>>()
                    .join(",")
            ),
            Modifier::Layout(None) => write!(f, "layout"),
            Modifier::Out => write!(f, "out"),
            Modifier::Flat => write!(f, "flat"),
        }
    }
}

fn modifier(input: &str) -> IResult<&str, Modifier> {
    alt((
        map_res(tag("const"), |_| {
            Ok::<Modifier, nom::error::Error<&str>>(Modifier::Const)
        }),
        map_res(tag("static"), |_| {
            Ok::<Modifier, nom::error::Error<&str>>(Modifier::Static)
        }),
        map_res(tag("extern"), |_| {
            Ok::<Modifier, nom::error::Error<&str>>(Modifier::Extern)
        }),
        map_res(tag("uniform"), |_| {
            Ok::<Modifier, nom::error::Error<&str>>(Modifier::Uniform)
        }),
        map_res(
            tuple((
                tag("layout"),
                opt(delimited(
                    char('('),
                    separated_list0(char(','), ws(alt((parse_assignment, parse_variable)))),
                    char(')'),
                )),
            )),
            |(_, v)| Ok::<Modifier, nom::error::Error<&str>>(Modifier::Layout(v)),
        ),
        map_res(tag("flat"), |_| {
            Ok::<Modifier, nom::error::Error<&str>>(Modifier::Flat)
        }),
        map_res(tag("in"), |_| {
            Ok::<Modifier, nom::error::Error<&str>>(Modifier::In)
        }),
        map_res(tag("out"), |_| {
            Ok::<Modifier, nom::error::Error<&str>>(Modifier::Out)
        }),
    ))(input)
}

#[derive(Debug, Clone)]
pub struct ShareVariable {
    pub modif: Vec<Modifier>,
    pub typ: String,
    pub name: String,

    pub value: Option<String>,
}

impl Display for ShareVariable {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{} {} {}{}",
            self.modif
                .iter()
                .map(|v| format!("{}", v))
                .collect::<Vec<_>>()
                .join(" "),
            self.typ,
            self.name,
            self.value
                .as_ref()
                .map(|v| format!(" = {}", v))
                .unwrap_or("".to_string())
        )
    }
}

fn array_parse(input: &str) -> IResult<&str, Option<usize>> {
    let (input, v) = delimited(pair(char('['), space0), digit0, ws(char(']')))(input)?;
    Ok((input, v.parse().ok()))
}

fn _share_variable(input: &str) -> IResult<&str, ShareVariable> {
    let (input, modifier) = many0(terminated(modifier, multispace1))(input)?;
    // let (input, _) = multispace1(input)?;
    let (input, typ) = type_name(input)?;
    let (input, _) = multispace1(input)?;
    let (input, name) = recognize(pair(identifier, opt(array_parse)))(input)?;

    let (input, value) = opt(preceded(ws(char('=')), ws(take_till(|c| c == ';'))))(input)?;
    Ok((
        input,
        ShareVariable {
            modif: modifier,
            typ: typ.to_string(),
            name: name.to_string(),
            value: value.map(|v| v.to_string()),
        },
    ))
}

fn geom_layout(input: &str) -> IResult<&str, Vec<Modifier>> {
    let (input, modifier) = separated_list1(space1, modifier)(input)?;
    Ok((input, modifier))
}

#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub enum PreprocessorDirective {
    IfDef { condition: String },
    IfNDef { condition: String },
    EndIf,
    Macro { name: String, value: Option<String> },
}

impl Display for PreprocessorDirective {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            PreprocessorDirective::IfDef { condition } => write!(f, "#ifdef {}", condition),
            PreprocessorDirective::IfNDef { condition } => write!(f, "#ifndef {}", condition),
            PreprocessorDirective::EndIf => write!(f, "#endif"),
            PreprocessorDirective::Macro { name, value } => {
                if let Some(v) = value {
                    write!(f, "#define {} {}", name, v)
                } else {
                    write!(f, "#define {}", name)
                }
            }
        }
    }
}

fn parse_ifdef(input: &str) -> IResult<&str, PreprocessorDirective> {
    let (input, v) = map(
        preceded(pair(tag("#ifdef"), multispace1), identifier),
        |condition| PreprocessorDirective::IfDef {
            condition: condition.to_string(),
        },
    )(input)?;
    Ok((input, v))
}

fn parse_ifndef(input: &str) -> IResult<&str, PreprocessorDirective> {
    map(
        preceded(tuple((tag("#ifndef"), space1)), identifier),
        |condition| PreprocessorDirective::IfNDef {
            condition: condition.to_string(),
        },
    )(input)
}

fn parse_endif(input: &str) -> IResult<&str, PreprocessorDirective> {
    map(ws(tag("#endif")), |_| PreprocessorDirective::EndIf)(input)
}

fn parse_multiline_macro(input: &str) -> IResult<&str, &str> {
    alt((
        recognize(pair(
            terminated(take_till(|c| c == '\\'), tag("\\\n")),
            parse_multiline_macro,
        )),
        terminated(take_till(|c: char| c == '\n'), char('\n')),
    ))(input)
}

fn parse_macro(input: &str) -> IResult<&str, PreprocessorDirective> {
    let (input, (name, value)) = tuple((
        preceded(pair(tag("#define"), space1), identifier),
        opt(preceded(space1, parse_multiline_macro)),
    ))(input)?;

    Ok((
        input,
        PreprocessorDirective::Macro {
            name: name.to_string(),
            value: value.map(|v| v.to_string()),
        },
    ))
}

#[derive(Debug, Clone)]
pub struct Function {
    pub return_type: String,
    pub name: String,
    pub parameters: Vec<(String, String)>,
    pub body: Statement,
}

impl Display for Function {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} {}(", self.return_type, self.name)?;
        for (i, (typ, name)) in self.parameters.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            write!(f, "{} {}", typ, name)?;
        }
        write!(f, ") ")?;

        self.body.fmt_with_indent(f, 4)?;

        write!(f, "\n")

        // write!(f, ") {} \n  ", self.body)
    }
}

// Helper function to parse identifiers (function names, variable names, etc.)
fn identifier(input: &str) -> IResult<&str, &str> {
    recognize(pair(
        take_while1(|c: char| c.is_alphabetic() || c == '_'),
        many0_count(take_while1(|c: char| c.is_alphanumeric() || c == '_')),
    ))(input)
}

// Helper function to parse types (e.g., "int", "float")
fn type_name(input: &str) -> IResult<&str, &str> {
    identifier(input)
}

// Parse a single function parameter (e.g., "int a")
fn parameter(input: &str) -> IResult<&str, (String, String)> {
    let (input, (typ, _, name)) = tuple((type_name, multispace0, identifier))(input)?;
    Ok((input, (typ.to_string(), name.to_string())))
}

// Parse the parameter list of a function
fn parameters(input: &str) -> IResult<&str, Vec<(String, String)>> {
    delimited(
        char('('),
        separated_list0(delimited(multispace0, char(','), multispace0), parameter),
        char(')'),
    )(input)
}

// Parse the body of a function
fn function_body(input: &str) -> IResult<&str, Statement> {
    let (input, body) =
        delimited(char('{'), many0(_statement), pair(multispace0, char('}')))(input)?;

    Ok((
        input,
        Statement::Block(body.into_iter().map(|v| Box::new(v)).collect()),
    ))
}

// Parse the entire function
pub fn function(input: &str) -> IResult<&str, Function> {
    let (input, (return_type, _, name, _, params, _, body)) = tuple((
        type_name,
        multispace0,
        identifier,
        multispace0,
        parameters,
        multispace0,
        function_body,
    ))(input)?;

    // println!("{}", body);

    Ok((
        input,
        Function {
            return_type: return_type.to_string(),
            name: name.to_string(),
            parameters: params,
            body,
        },
    ))
}

fn skip(input: &str) -> IResult<&str, &str> {
    let (input, a) = multispace0(input)?;
    Ok((input, a))
}

pub fn ws<'a, F: 'a, O>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O>
where
    F: FnMut(&'a str) -> IResult<&'a str, O>,
{
    preceded(skip, inner)
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Hook {
    pub name: String,
    pub subhooks: Vec<String>,
    pub parameters: Option<Vec<String>>,
}

fn hook(input: &str) -> IResult<&str, Hook> {
    map(
        delimited(
            char('<'),
            pair(
                separated_list1(char('.'), identifier),
                opt(delimited(
                    char('('),
                    separated_list0(char(','), ws(expression)),
                    char(')'),
                )),
            ),
            char('>'),
        ),
        |(names, paras)| Hook {
            name: names[0].to_string(),
            subhooks: names[1..].iter().map(|v| v.to_string()).collect(),
            parameters: paras.map(|v| v.iter().map(|v| v.to_string()).collect()),
        },
    )(input)
}

impl Display for Hook {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "<{}", self.name)?;
        for sub in self.subhooks.iter() {
            write!(f, ".{}", sub)?;
        }

        if !self.parameters.is_none() {
            write!(f, "(")?;
            let para = self.parameters.as_ref().unwrap().join(",");
            write!(f, "{}", para)?;
            write!(f, ")")?;
        }

        write!(f, ">")
    }
}

#[derive(Debug, Clone)]
pub enum Code {
    Struct(Rc<RefCell<Struct>>),
    Include(Rc<RefCell<Include>>),
    Function(Rc<RefCell<Function>>),
    PreprocessorDirective(Rc<RefCell<PreprocessorDirective>>, Box<Vec<Code>>),
    Macro(Rc<RefCell<PreprocessorDirective>>),
    ShareVariable(Rc<RefCell<ShareVariable>>),
    GeomLayout(Rc<RefCell<Vec<Modifier>>>),
}

impl Code {
    pub fn parse(input: &str) -> IResult<&str, Self> {
        ws(alt((
            map(
                tuple((
                    alt((parse_ifdef, parse_ifndef)),
                    many0(Self::parse),
                    cut(parse_endif),
                )),
                |(pre, inner, _)| {
                    Code::PreprocessorDirective(Rc::new(RefCell::new(pre)), Box::new(inner))
                },
            ),
            map(parse_macro, |v| Code::Macro(Rc::new(RefCell::new(v)))),
            map(include, |v| Code::Include(Rc::new(RefCell::new(v)))),
            map(function, |v| Code::Function(Rc::new(RefCell::new(v)))),
            map(terminated(parse_struct, pair(space0, char(';'))), |s| {
                Code::Struct(Rc::new(RefCell::new(s)))
            }),
            map(terminated(_share_variable, char(';')), |v| {
                Code::ShareVariable(Rc::new(RefCell::new(v)))
            }),
            map(terminated(geom_layout, char(';')), |v| {
                Code::GeomLayout(Rc::new(RefCell::new(v)))
            }),
        )))(input)
    }

    pub fn new(input: &str) -> Self {
        Self::parse(input).finish().unwrap().1
    }
}

#[derive(Debug, Clone)]
pub enum Statement {
    Declaration {
        name: String,
        typ: String,
        value: Option<Expression>,
    },
    Block(Vec<Box<Statement>>),
    If {
        condition: Expression,
        block: Box<Statement>,
    },

    Else {
        block: Box<Statement>,
    },

    While {
        condition: Expression,
        block: Box<Statement>,
    },

    For {
        condition: Expression,
        block: Box<Statement>,
    },
    DoWhile {
        block: Box<Statement>,
        condition: Expression,
    },

    Break,
    Continue,

    // Hook(Hook),
    Expression(Expression),

    Discard,

    Return(Option<Expression>),
}

impl Statement {
    fn fmt_with_indent(&self, f: &mut std::fmt::Formatter<'_>, indent: usize) -> std::fmt::Result {
        let indent_str = " ".repeat(indent);
        match self {
            Statement::Block(statements) => {
                writeln!(f, "{{")?;
                for statement in statements {
                    statement.fmt_with_indent(f, indent + 4)?;
                }
                writeln!(f, "}}")
            }
            Statement::While { condition, block } => {
                writeln!(f, "{}while ({})", indent_str, condition)?;
                block.fmt_with_indent(f, indent + 4)
            }
            Statement::DoWhile { block, condition } => {
                writeln!(f, "{}do", indent_str)?;
                block.fmt_with_indent(f, indent + 4)?;
                writeln!(f, "{}while ({});", indent_str, condition)
            }
            Statement::If { condition, block } => {
                writeln!(f, "{}if ({})", indent_str, condition)?;
                block.fmt_with_indent(f, indent + 4)
            }
            Statement::Else { block } => {
                if let Statement::If { condition, block } = &(**block) {
                    writeln!(f, "{}else if ({}) ", indent_str, condition)?;

                    block.fmt_with_indent(f, indent + 4)
                } else {
                    writeln!(f, "{}else ", indent_str)?;

                    block.fmt_with_indent(f, indent + 4)
                }
            }
            Statement::For { condition, block } => {
                writeln!(f, "{}for ({})", indent_str, condition)?;
                block.fmt_with_indent(f, indent + 4)
            }
            Statement::Expression(expr) => {
                writeln!(f, "{}{};", indent_str, expr)
            }

            Statement::Discard => {
                writeln!(f, "{}discard;", indent_str)
            }

            Statement::Declaration { name, typ, value } => {
                writeln!(
                    f,
                    "{}{} {}{};",
                    indent_str,
                    typ,
                    name,
                    value
                        .as_ref()
                        .map_or("".to_string(), |v| format!(" = {}", v))
                )
            }
            Statement::Return(expr) => {
                if let Some(exp) = expr {
                    writeln!(f, "{}return {};", indent_str, exp)
                } else {
                    writeln!(f, "{}return;", indent_str)
                }
            }

            Statement::Break => {
                writeln!(f, "{}break;", indent_str)
            }

            Statement::Continue => {
                writeln!(f, "{}continue;", indent_str)
            }
        }
    }
}

impl Display for Statement {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Statement::Declaration { name, typ, value } => {
                write!(
                    f,
                    "{} {}{};",
                    typ,
                    name,
                    value
                        .as_ref()
                        .map_or("".to_string(), |v| format!(" = {}", v))
                )
            }
            Statement::Block(v) => {
                write!(f, "{{\n")?;
                for statement in v.iter() {
                    write!(f, "{}\n", statement)?;
                }
                write!(f, "}}")?;
                Ok(())
            }
            Statement::If { condition, block } => {
                write!(f, "if ({}) {}", condition, block)
            }
            Statement::Else { block } => {
                if let Statement::If { condition, block } = &(**block) {
                    write!(f, "else if ({}) {}", condition, block)
                } else {
                    write!(f, "else {}", block)
                }
            }
            Statement::While { condition, block } => {
                write!(f, "while ({}) {}", condition, block)
            }
            Statement::For { condition, block } => {
                write!(f, "for ({}) {}", condition, block)
            }
            Statement::DoWhile { block, condition } => {
                write!(f, "do {}\n while ({})", block, condition)
            }

            // Statement::Hook(hook) => write!(f, "{}", hook),
            Statement::Expression(v) => write!(f, "{};", v),

            Statement::Return(v) => {
                if let Some(exp) = v {
                    write!(f, "return {};", exp)
                } else {
                    write!(f, "return;",)
                }
            }

            Statement::Discard => write!(f, "discard;"),

            Statement::Break => write!(f, "break;"),
            Statement::Continue => write!(f, "continue;"),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    Binary {
        left: Box<Expression>,
        right: Box<Expression>,
        operator: String,
    },
    Unary {
        right: Box<Expression>,
        operator: String,
        left_or_right: bool,
    },
    Variable(String),
    Literal(Literal),
    FunctionCall {
        name: String,
        parameters: Vec<Expression>,
    },
    Assignment {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    StructMemberAccess {
        base: Box<Expression>,
        member: String,
    },
    StructPointerAccess {
        base: Box<Expression>,
        member: String,
    },
    ArrayAccess {
        base: Box<Expression>,
        member: Box<Expression>,
    },

    Hook(Hook),
}

impl Display for Expression {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Expression::Binary {
                left,
                right,
                operator,
            } => {
                if operator == "+" {
                    write!(f, "({} {} {})", left, operator, right)
                } else {
                    write!(f, "{} {} {}", left, operator, right)
                }
            }
            Expression::Unary {
                right,
                operator,
                left_or_right,
            } => {
                if *left_or_right {
                    write!(f, "{}{}", operator, right)
                } else {
                    write!(f, "{}{}", right, operator)
                }
            }
            Expression::Variable(v) => write!(f, "{}", v),
            Expression::Literal(v) => write!(f, "{}", v),
            Expression::FunctionCall { name, parameters } => {
                write!(f, "{}(", name)?;
                for (i, param) in parameters.iter().enumerate() {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    write!(f, "{}", param)?;
                }
                write!(f, ")")
            }

            Expression::Assignment { left, right } => write!(f, "{} = {}", left, right),
            Expression::Hook(hook) => write!(f, "{}", hook),
            Expression::StructMemberAccess { base, member } => write!(f, "{}.{}", base, member),
            Expression::StructPointerAccess { base, member } => write!(f, "{}->{}", base, member),
            Expression::ArrayAccess { base, member } => {
                write!(f, "{}[{}]", base, member)
            }
        }
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Literal {
    Integer(i64),
    Float(f64, usize),
    String(String),
    Char(char),
}

impl Display for Literal {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Literal::Integer(v) => write!(f, "{}", v),
            Literal::Float(v, presicion) => {
                let r = format!("{:.*}", presicion, v);
                write!(f, "{}", r)
            }
            Literal::String(v) => write!(f, "\"{}\"", v),
            Literal::Char(v) => write!(f, "'{}'", v),
        }
    }
}

fn parse_integer(input: &str) -> IResult<&str, Literal> {
    map_res(recognize(digit1), |s: &str| {
        s.parse::<i64>().map(Literal::Integer)
    })(input)
}

fn parse_float(input: &str) -> IResult<&str, Literal> {
    map_res(
        recognize(tuple((
            digit1,
            char('.'),
            digit1,
            opt(pair(alt((char('e'), char('E'))), opt(char('-')))),
            opt(digit1),
        ))),
        |s: &str| {
            s.parse::<f64>()
                .map(|v| Literal::Float(v, s.split('.').last().unwrap().len()))
        },
    )(input)
}

fn parse_string(input: &str) -> IResult<&str, Literal> {
    map(
        delimited(char('"'), take_until("\""), char('"')),
        |s: &str| Literal::String(s.to_string()),
    )(input)
}

fn parse_char(input: &str) -> IResult<&str, Literal> {
    map(
        delimited(char('\''), none_of("'"), char('\'')),
        |c: char| Literal::Char(c),
    )(input)
}

fn parse_literal(input: &str) -> IResult<&str, Literal> {
    alt((parse_float, parse_integer, parse_string, parse_char))(input)
}

fn parse_assignment(input: &str) -> IResult<&str, Expression> {
    let (input, left) = alt((
        parse_struct_member_access,
        parse_struct_pointer_access,
        parse_variable,
    ))(input)?;
    let (input, _) = space0(input)?;
    let (input, _) = char('=')(input)?;
    let (input, _) = space0(input)?;
    let (input, right) = expression(input)?;
    Ok((
        input,
        Expression::Assignment {
            left: Box::new(left),
            right: Box::new(right),
        },
    ))
}

fn parse_unary(input: &str) -> IResult<&str, Expression> {
    let left = alt((char('-'), char('+'), char('!')));

    alt((
        map(pair(left, parse_factor), |(left, fac)| Expression::Unary {
            right: Box::new(fac),
            operator: left.to_string(),
            left_or_right: true,
        }),
        map(
            pair(alt((tag("++"), tag("--"))), parse_variable),
            |(t, fac)| Expression::Unary {
                right: Box::new(fac),
                operator: t.to_string(),
                left_or_right: true,
            },
        ),
        map(
            pair(parse_variable, alt((tag("++"), tag("--")))),
            |(fac, right)| Expression::Unary {
                right: Box::new(fac),
                operator: right.to_string(),
                left_or_right: false,
            },
        ),
    ))(input)
}

fn parse_term(input: &str) -> IResult<&str, Expression> {
    let (input, init) = parse_factor(input)?;
    fold_many0(
        pair(alt((tag("*"), tag("/"))), parse_factor),
        move || init.clone(),
        |acc, (op, val): (&str, Expression)| Expression::Binary {
            left: Box::new(acc),
            operator: op.to_string(),
            right: Box::new(val),
        },
    )(input)
}

fn parse_arith(input: &str) -> IResult<&str, Expression> {
    let (input, init) = parse_term(input)?;
    fold_many0(
        pair(alt((tag("+"), tag("-"))), parse_term),
        move || init.clone(),
        |acc, (op, val): (&str, Expression)| Expression::Binary {
            left: Box::new(acc),
            operator: op.to_string(),
            right: Box::new(val),
        },
    )(input)
}

fn parse_array_access(input: &str) -> IResult<&str, Expression> {
    let inn = delimited(
        char('['),
        alt((map(parse_integer, Expression::Literal), parse_variable)),
        char(']'),
    );
    let (input, rs) = pair(parse_variable, inn)(input)?;

    Ok((
        input,
        Expression::ArrayAccess {
            base: Box::new(rs.0),
            member: Box::new(rs.1),
        },
    ))
}

fn parse_factor(input: &str) -> IResult<&str, Expression> {
    delimited(
        multispace0,
        alt((
            delimited(char('('), expression, char(')')),
            parse_struct_member_access,
            parse_struct_pointer_access,
            parse_array_access,
            parse_unary,
            parse_function_call,
            parse_assignment,
            map(parse_literal, |l| Expression::Literal(l)),
            map(identifier, |i| Expression::Variable(i.to_string())),
            map(hook, |hook| Expression::Hook(hook)),
        )),
        multispace0,
    )(input)
}

fn parse_comparison(input: &str) -> IResult<&str, Expression> {
    let (input, init) = parse_arith(input)?;
    fold_many0(
        pair(
            alt((
                tag("=="),
                tag("!="),
                tag("<="),
                tag(">="),
                tag("<"),
                tag(">"),
            )),
            parse_arith,
        ),
        move || init.clone(),
        |acc, (op, val): (&str, Expression)| Expression::Binary {
            left: Box::new(acc),
            operator: op.to_string(),
            right: Box::new(val),
        },
    )(input)
}

fn parse_logical(input: &str) -> IResult<&str, Expression> {
    let (input, init) = parse_comparison(input)?;
    fold_many0(
        pair(alt((tag("&&"), tag("||"))), parse_comparison),
        move || init.clone(),
        |acc, (op, val): (&str, Expression)| Expression::Binary {
            left: Box::new(acc),
            operator: op.to_string(),
            right: Box::new(val),
        },
    )(input)
}

fn parse_variable(input: &str) -> IResult<&str, Expression> {
    map(identifier, |v: &str| Expression::Variable(v.to_owned()))(input)
}

fn parse_function_call(input: &str) -> IResult<&str, Expression> {
    let (input, name) = identifier(input)?;
    let (input, args) = delimited(
        char('('),
        separated_list0(delimited(multispace0, char(','), multispace0), expression),
        cut(char(')')),
    )(input)?;
    Ok((
        input,
        Expression::FunctionCall {
            name: name.to_string(),
            parameters: args,
        },
    ))
}

fn parse_struct_member_access(input: &str) -> IResult<&str, Expression> {
    map(
        separated_pair(
            alt((
                // parse_struct_member_access,
                // parse_struct_pointer_access,
                map(hook, Expression::Hook),
                parse_array_access,
                map(identifier, |v| Expression::Variable(v.to_string())),
            )),
            char('.'),
            identifier,
        ),
        |(base, member)| Expression::StructMemberAccess {
            base: Box::new(base),
            member: member.to_string(),
        },
    )(input)
}

fn parse_struct_pointer_access(input: &str) -> IResult<&str, Expression> {
    map(
        separated_pair(
            alt((
                map(hook, Expression::Hook),
                map(identifier, |v| Expression::Variable(v.to_string())),
            )),
            tag("->"),
            identifier,
        ),
        |(base, member)| Expression::StructPointerAccess {
            base: Box::new(base),
            member: member.to_string(),
        },
    )(input)
}

fn expression(input: &str) -> IResult<&str, Expression> {
    parse_logical(input)
}

fn _condition(input: &str) -> IResult<&str, Expression> {
    let (input, exp) = expression(input)?;
    Ok((input, exp))
}

fn _statement(input: &str) -> IResult<&str, Statement> {
    ws(alt((
        map(
            delimited(char('{'), many0(_statement), pair(multispace0, char('}'))),
            |statements| Statement::Block(statements.into_iter().map(|v| Box::new(v)).collect()),
        ),
        map(
            preceded(tag("while"), pair(_condition, _statement)),
            |(cond, statement)| Statement::While {
                condition: cond,
                block: Box::new(statement),
            },
        ),
        map(
            preceded(
                pair(tag("do"), space0),
                tuple((
                    _statement,
                    preceded(space0, tag("while")),
                    space0,
                    _condition,
                )),
            ),
            |(block, _, _, condition)| Statement::DoWhile {
                block: Box::new(block),
                condition: condition,
            },
        ),
        map(
            preceded(tag("if"), pair(_condition, _statement)),
            |(cond, state)| Statement::If {
                condition: cond,
                block: Box::new(state),
            },
        ),
        map(preceded(tag("else"), _statement), |state| Statement::Else {
            block: Box::new(state),
        }),
        map(
            preceded(tag("for"), pair(_condition, _statement)),
            |(cond, state)| Statement::For {
                condition: cond,
                block: Box::new(state),
            },
        ),
        map(
            delimited(pair(tag("return"), multispace0), opt(expression), char(';')),
            |e| Statement::Return(e),
        ),
        map(terminated(tag("discard"), pair(space0, char(';'))), |_| {
            Statement::Discard
        }),
        map(terminated(tag("break"), pair(space0, char(';'))), |_| {
            Statement::Break
        }),
        map(terminated(tag("continue"), pair(space0, char(';'))), |_| {
            Statement::Continue
        }),
        map(terminated(hook, pair(multispace0, char(';'))), |hook| {
            Statement::Expression(Expression::Hook(hook))
        }),
        map(terminated(parse_assignment, char(';')), |v| {
            Statement::Expression(v)
        }),
        map(terminated(parse_function_call, char(';')), |v| {
            Statement::Expression(v)
        }),
        map(
            terminated(
                tuple((
                    type_name,
                    space1,
                    identifier,
                    space0,
                    opt(tuple((char('='), space0, expression))),
                )),
                char(';'),
            ),
            |(typ, _, name, _, v)| Statement::Declaration {
                name: name.to_string(),
                typ: typ.to_string(),
                value: v.map(|v| v.2),
            },
        ),
    )))(input)
}

#[derive(Debug, Clone)]
struct Struct {
    name: String,
    members: Vec<(String, String)>,
}

impl Display for Struct {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "struct {} {{\n", self.name)?;
        for (typ, name) in self.members.iter() {
            write!(f, "  {} {};\n", typ, name)?;
        }
        write!(f, "}};")
    }
}

fn parse_struct(input: &str) -> IResult<&str, Struct> {
    let (input, _) = tag("struct")(input)?;
    let (input, _) = space1(input)?;
    let (input, name) = identifier(input)?;
    let (input, body) = delimited(
        pair(multispace0, char('{')),
        many0(ws(tuple((
            type_name,
            space1,
            identifier,
            space0,
            char(';'),
        )))),
        pair(multispace0, char('}')),
    )(input)?;
    Ok((
        input,
        Struct {
            name: name.to_string(),
            members: body
                .into_iter()
                .map(|(typ, _, name, _, _)| (typ.to_string(), name.to_string()))
                .collect(),
        },
    ))
}

impl Display for Code {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Code::Struct(v) => write!(f, "{}", *v.borrow()),
            Code::Include(v) => write!(f, "{}", *v.borrow()),
            Code::Function(v) => write!(f, "{}", *v.borrow()),
            Code::PreprocessorDirective(v, c) => {
                write!(f, "{}\n", *v.borrow())?;
                for code in c.iter() {
                    write!(f, "{}\n", code)?;
                }
                write!(f, "\n{}", PreprocessorDirective::EndIf)?;
                Ok(())
            }
            Code::Macro(v) => write!(f, "{}", *v.borrow()),
            Code::ShareVariable(v) => write!(f, "{};", *v.borrow()),
            Code::GeomLayout(v) => {
                write!(
                    f,
                    "{}",
                    v.borrow()
                        .iter()
                        .map(|v| v.to_string())
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
                writeln!(f, ";")?;

                Ok(())
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct SnippetCode {
    pub includes: Vec<RFC<Include>>,
    pub variables: Vec<RFC<ShareVariable>>,
    pub functions: Vec<RFC<Function>>,
    pub preprocessor_directives: HashMap<SnippetPreprocessorKey, Box<SnippetCode>>,
}

#[derive(Debug, Clone)]
pub struct SnippetPreprocessorKey(pub RFC<PreprocessorDirective>);

impl PartialEq for SnippetPreprocessorKey {
    fn eq(&self, other: &Self) -> bool {
        self.0.borrow().eq(&*other.0.borrow())
    }
}

impl Eq for SnippetPreprocessorKey {}

impl Hash for SnippetPreprocessorKey {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.borrow().hash(state)
    }
}

impl From<RFC<PreprocessorDirective>> for SnippetPreprocessorKey {
    fn from(v: RFC<PreprocessorDirective>) -> Self {
        SnippetPreprocessorKey(v)
    }
}

#[derive(Debug, Clone)]
pub struct CodeBlock {
    pub codes: Vec<Code>,
    pub snippet: SnippetCode,
}

impl Display for CodeBlock {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for code in self.codes.iter() {
            write!(f, "{}\n\n", code)?;
        }
        Ok(())
    }
}

impl CodeBlock {
    pub fn new(input: &str) -> Result<Self> {
        let clean = SINGLE_COMMENT_RE.replace_all(input, "");
        let clean = MULTI_COMMENT_RE.replace_all(&clean, "");
        let clean = VERSION_RE.replace_all(&clean, "");

        let (_, codes) = Self::_parse(&clean)
            .map_err(|e| crate::errors::Error::InvalidSnippet(format!("{:?}", e)))?;

        let snippets = SnippetCode::new(&codes)?;

        Ok(CodeBlock {
            codes: codes,
            snippet: snippets,
        })
    }

    fn _parse(input: &str) -> IResult<&str, Vec<Code>> {
        many1(Code::parse)(input)
    }

    pub fn public_variables(&self) -> Vec<&RFC<ShareVariable>> {
        self.snippet.variables.iter().collect()
    }

    pub fn public_functions(&self) -> Vec<&RFC<Function>> {
        self.snippet.functions.iter().collect()
    }

    pub fn public_includes(&self) -> Vec<&RFC<Include>> {
        self.snippet.includes.iter().collect()
    }

    pub fn preprocessor_directives(&self) -> Vec<&SnippetPreprocessorKey> {
        self.snippet.preprocessor_directives.keys().collect()
    }

    pub fn all_variables(&self) -> Vec<&RFC<ShareVariable>> {
        self.snippet.all_variables()
    }

    pub fn all_functions(&self) -> Vec<&RFC<Function>> {
        self.snippet.all_functions()
    }

    pub fn all_includes(&self) -> Vec<&RFC<Include>> {
        self.snippet.all_includes()
    }

    pub fn rename(&mut self, old: &str, new: &str) {
        for code in self.codes.iter_mut() {
            match code {
                Code::ShareVariable(v) => {
                    if v.borrow().name == old {
                        v.borrow_mut().name = new.to_string();
                    }
                }

                Code::Function(v) => {
                    if v.borrow().name == old {
                        v.borrow_mut().name = new.to_string();
                    }
                }
                _ => {}
            }
        }
    }

    pub fn mangling<S: AsRef<str>>(&mut self, suffix: S) -> HashMap<String, String> {
        let mut map = HashMap::new();

        fn _expression_mangling(exp: &mut Expression, map: &HashMap<String, String>) {
            match exp {
                Expression::Variable(v) => {
                    if let Some(new) = map.get(v) {
                        *v = new.clone();
                    }
                }

                Expression::FunctionCall { parameters, name } => {
                    if let Some(new) = map.get(name) {
                        *name = new.clone();
                    }
                    for para in parameters.iter_mut() {
                        _expression_mangling(para, map);
                    }
                }

                Expression::Assignment { left, right } => {
                    _expression_mangling(left, map);
                    _expression_mangling(right, map);
                }

                Expression::Binary { left, right, .. } => {
                    _expression_mangling(left, map);
                    _expression_mangling(right, map);
                }

                Expression::Unary { right, .. } => {
                    _expression_mangling(right, map);
                }

                Expression::StructMemberAccess { base, .. } => {
                    _expression_mangling(base, map);
                }

                Expression::StructPointerAccess { base, .. } => _expression_mangling(base, map),

                Expression::ArrayAccess { base, member } => {
                    _expression_mangling(base, map);
                    _expression_mangling(member, map);
                }

                _ => {}
            }
        }

        fn _mangling_block(block: &mut Statement, map: &HashMap<String, String>) {
            if let Statement::Block(b) = block {
                for statement in b.iter_mut() {
                    match statement.as_mut() {
                        Statement::DoWhile { condition, .. } => {
                            _expression_mangling(condition, map);
                        }

                        Statement::Expression(e) => {
                            _expression_mangling(e, map);
                        }

                        Statement::If { condition, block } => {
                            _expression_mangling(condition, map);
                            _mangling_block(block, map);
                        }

                        Statement::Else { block } => {
                            if let Statement::If { condition, block } = block.as_mut() {
                                _expression_mangling(condition, map);
                                _mangling_block(block, map);
                            } else {
                                _mangling_block(block, map);
                            }
                        }

                        Statement::While { condition, block } => {
                            _expression_mangling(condition, map);
                            _mangling_block(block, map);
                        }

                        Statement::For { condition, block } => {
                            _expression_mangling(condition, map);
                            _mangling_block(block, map);
                        }

                        Statement::Declaration { value, .. } => {
                            if let Some(v) = value {
                                _expression_mangling(v, map);
                            }
                        }

                        Statement::Return(v) => {
                            if let Some(v) = v {
                                _expression_mangling(v, map);
                            }
                        }

                        _ => {}
                    }
                }
            }
        }

        fn _mangling(codes: &mut Vec<Code>, suffix: &str, map: &mut HashMap<String, String>) {
            for code in codes.iter_mut() {
                match code {
                    Code::Function(v) => {
                        let raw_name = v.borrow().name.clone();
                        let new_name = format!("{}_{}", raw_name, suffix);

                        v.borrow_mut().name = new_name.clone();
                        map.insert(raw_name, new_name);
                        _mangling_block(&mut v.borrow_mut().body, map);
                    }

                    Code::ShareVariable(v) => {
                        if v.borrow().modif.contains(&Modifier::Const) {
                            return;
                        }

                        let raw_name = v.borrow().name.clone();
                        let new_name = format!("{}_{}", raw_name, suffix);

                        v.borrow_mut().name = new_name.clone();
                        map.insert(raw_name, new_name);
                    }

                    Code::PreprocessorDirective(_, c) => {
                        _mangling(&mut (*c), suffix, map);
                    }
                    _ => {}
                }
            }
        }

        _mangling(&mut self.codes, suffix.as_ref(), &mut map);

        map
    }

    pub fn all_hooks(&self) -> HashSet<Hook> {
        self.snippet.all_hooks()
    }

    pub fn set_hook(&mut self, old: &str, code: &Snippet) {
        if self.snippet.set_hook(old, code) {
            let prepare_code = code.prepare_code();

            let clean = SINGLE_COMMENT_RE.replace_all(prepare_code, "");
            let clean = MULTI_COMMENT_RE.replace_all(&clean, "");
            let clean = VERSION_RE.replace_all(&clean, "");

            let (_, mut prepare_code) = Self::_parse(&clean).unwrap();

            let old = std::mem::replace(&mut self.codes, vec![]);
            prepare_code.extend(old);

            self.codes = prepare_code;
        }
    }

    pub fn find_variable(&self, name: &str) -> Option<&RFC<ShareVariable>> {
        self.snippet.find_variable(name)
    }

    pub fn insert(&mut self, code: Code, index: usize) {
        self.codes.insert(index, code);
    }
}

impl SnippetCode {
    pub fn new(codes: &Vec<Code>) -> Result<Self> {
        Self::check(codes)
    }

    pub fn update(&mut self, codes: &Vec<Code>) -> Result<()> {
        let new = Self::check(codes)?;
        self.includes.extend(new.includes);
        self.variables.extend(new.variables);
        self.functions.extend(new.functions);

        Ok(())
    }

    fn check(codes: &Vec<Code>) -> Result<SnippetCode> {
        let includes = codes
            .iter()
            .filter_map(|v| match v {
                Code::Include(v) => Some(v.clone()),
                _ => None,
            })
            .collect();

        let variables = codes
            .iter()
            .filter_map(|v| match v {
                Code::ShareVariable(v) => Some(v.clone()),
                _ => None,
            })
            .collect();

        let functions = codes
            .iter()
            .filter_map(|v| match v {
                Code::Function(v) => Some(v.clone()),
                _ => None,
            })
            .collect();

        let mut preprocessor_directives = vec![];

        for code in codes.into_iter() {
            match code {
                Code::PreprocessorDirective(p, v) => {
                    let c = Self::check(v)?;

                    preprocessor_directives.push((p.clone().into(), Box::new(c)));
                }
                _ => {}
            }
        }

        Ok(SnippetCode {
            includes: includes,
            variables: variables,
            functions: functions,
            preprocessor_directives: preprocessor_directives.into_iter().collect(),
        })
    }

    fn all_variables(&self) -> Vec<&RFC<ShareVariable>> {
        let mut result = vec![];

        for v in self.variables.iter() {
            result.push(v);
        }

        for (_, v) in self.preprocessor_directives.iter() {
            result.extend(v.all_variables());
        }

        result
    }

    fn all_functions(&self) -> Vec<&RFC<Function>> {
        let mut result = vec![];

        for v in self.functions.iter() {
            result.push(v);
        }

        for (_, v) in self.preprocessor_directives.iter() {
            result.extend(v.all_functions());
        }

        result
    }

    fn all_includes(&self) -> Vec<&RFC<Include>> {
        let mut result = vec![];

        for v in self.includes.iter() {
            result.push(v);
        }

        for (_, v) in self.preprocessor_directives.iter() {
            result.extend(v.all_includes());
        }

        result
    }

    fn _hook<'b, 'a: 'b>(stat: &'a Box<Statement>, res: &'b mut HashSet<Hook>) {
        match stat.as_ref() {
            Statement::Block(body) => {
                for statement in body.iter() {
                    Self::_hook(statement, res);
                }
            }

            Statement::Else { block } => {
                Self::_hook(block, res);
            }

            Statement::If { block, .. } => {
                Self::_hook(block, res);
            }

            Statement::While { block, .. } => {
                Self::_hook(block, res);
            }

            Statement::For { block, .. } => {
                Self::_hook(block, res);
            }

            Statement::DoWhile { block, .. } => {
                Self::_hook(block, res);
            }

            Statement::Declaration { value, .. } => {
                if let Some(v) = value {
                    if let Expression::Hook(hook) = v {
                        res.insert(hook.to_owned());
                    }
                }
            }
            Statement::Expression(Expression::Hook(hook)) => {
                res.insert(hook.to_owned());
            }

            _ => {}
        }
    }

    fn all_hooks(&self) -> HashSet<Hook> {
        let mut result = HashSet::new();

        for v in self.functions.iter() {
            if let Statement::Block(body) = &v.borrow().body {
                for statement in body.iter() {
                    Self::_hook(statement, &mut result);
                }
            }
        }

        result
    }

    fn find_variable(&self, name: &str) -> Option<&RFC<ShareVariable>> {
        for v in self.variables.iter() {
            if v.borrow().name == name {
                return Some(v);
            }
        }

        for (_, v) in self.preprocessor_directives.iter() {
            if let Some(v) = v.find_variable(name) {
                return Some(v);
            }
        }

        None
    }

    fn _replace_hook(snippet: &Snippet, hook: &Hook) -> Expression {
        let ow_subhooks = !hook.subhooks.is_empty();

        let txt = if let Some(paras) = hook.parameters.as_ref() {
            if ow_subhooks {
                let call_name = snippet
                    .find_symbol(hook.subhooks.first().unwrap())
                    .expect(format!("{} not found", hook.subhooks.first().unwrap()).as_str());
                let paras = paras.join(", ");
                format!("{}({})", call_name, paras)
            } else {
                snippet.call(paras).unwrap()
            }
        } else {
            if ow_subhooks {
                let var = snippet
                    .find_variable(hook.subhooks.first().unwrap())
                    .unwrap();
                if let Some(v) = var.borrow().value.as_ref() {
                    v.to_owned()
                } else {
                    var.borrow().name.to_owned()
                }
            } else {
                snippet.call(&vec![]).unwrap()
            }
        };

        expression(&txt).unwrap().1
    }

    fn _check_hook_exp(old: &str, code: &Snippet, exp: &mut Expression, valid: &mut bool) {
        match exp {
            Expression::Assignment { right, .. } => {
                Self::_check_hook_exp(old, code, right, valid);
            }
            Expression::Binary { left, right, .. } => {
                Self::_check_hook_exp(old, code, left, valid);
                Self::_check_hook_exp(old, code, right, valid);
            }
            Expression::FunctionCall {
                parameters, name, ..
            } => {
                for v in parameters.iter_mut() {
                    Self::_check_hook_exp(old, code, v, valid);
                }
            }
            Expression::StructMemberAccess { base, .. } => {
                Self::_check_hook_exp(old, code, base, valid);
            }

            Expression::StructPointerAccess { base, .. } => {
                Self::_check_hook_exp(old, code, base, valid);
            }

            Expression::Unary { right, .. } => {
                Self::_check_hook_exp(old, code, right, valid);
            }

            Expression::Hook(hook) => {
                if hook.name == old {
                    let new_exp = Self::_replace_hook(code, hook);
                    *exp = new_exp;
                    *valid = true;
                }
            }

            _ => {}
        }
    }

    fn _set_hook<'b, 'a: 'b>(
        stat: &'a mut Box<Statement>,
        old: &str,
        code: &Snippet,
        valid: &mut bool,
    ) {
        match stat.as_mut() {
            Statement::Block(body) => {
                for statement in body.iter_mut() {
                    Self::_set_hook(statement, old, code, valid);
                }
            }

            Statement::Else { block } => {
                Self::_set_hook(block, old, code, valid);
            }

            Statement::If { block, .. } => {
                Self::_set_hook(block, old, code, valid);
            }

            Statement::While { block, .. } => {
                Self::_set_hook(block, old, code, valid);
            }

            Statement::For { block, .. } => {
                Self::_set_hook(block, old, code, valid);
            }

            Statement::DoWhile { block, .. } => {
                Self::_set_hook(block, old, code, valid);
            }

            Statement::Declaration { value, .. } => {
                if let Some(v) = value {
                    Self::_check_hook_exp(old, code, v, valid);
                }
            }

            Statement::Expression(v) => {
                Self::_check_hook_exp(old, code, v, valid);
            }

            Statement::Return(v) => {
                v.as_mut()
                    .map(|v| Self::_check_hook_exp(old, code, v, valid));
            }

            _ => {}
        }
    }

    fn set_hook(&mut self, old: &str, code: &Snippet) -> bool {
        let mut valid = false;
        for v in self.functions.iter_mut() {
            if let Statement::Block(body) = &mut v.borrow_mut().body {
                for statement in body.iter_mut() {
                    Self::_set_hook(statement, old, code, &mut valid);
                }
            }
        }

        valid
    }
}

#[test]
fn test() {
    let input = r#"
    #include "transform/polar.glsl"

    layout(points) in;
    layout(triangle_strip, max_vertices = 4) out;

    // conf: Elevation, Range, Resolution, 0.0
    uniform vec4 conf;

    out float eth;
    out float rng;

    out vec4 vrange;


    void main() {

        // Resolution
        vec4 reso = vec4(conf.x/2.0, conf.y/2.0, 0.0, 0.0);
        float c = cos(reso.x);

        vec4 po = gl_in[0].gl_Position;

        vrange = vec4(po.x - reso.x, po.y - reso.y, po.x + reso.x, po.y + reso.y);

        gl_Position = po - reso;
        eth = gl_Position.x;
        rng = gl_Position.y;
        gl_Position = <transform(forward(gl_Position))>;
        EmitVertex();

        gl_Position = po + vec4(-reso.x, reso.y, 0.0, 0.0);   
        gl_Position.y = gl_Position.y / c;
        eth = gl_Position.x;
        rng = gl_Position.y;
        gl_Position = <transform(forward(gl_Position))>;
        EmitVertex();

        gl_Position = po + reso;
        gl_Position.y = gl_Position.y / c;
        eth = gl_Position.x;
        rng = gl_Position.y;
        gl_Position = <transform(forward(gl_Position))>;
        EmitVertex();

        gl_Position = po + vec4(reso.x, -reso.y, 0.0, 0.0);
        eth = gl_Position.x;
        rng = gl_Position.y;
        gl_Position = <transform(forward(gl_Position))>;
        EmitVertex();

        EndPrimitive();

    }

    "#;

    let result = CodeBlock::new(input).unwrap();

    print!("{}", result);
}

#[test]
fn test_expression() {
    let input = r#"
    void main() {

        vec4 reso = vec4(conf.x/2.0, conf.y/2.0, 0.0, 0.0);
        float c = cos(reso.x);

        vec4 po = gl_in[0].gl_Position;

        vrange = vec4(po.x - reso.x, po.y - reso.y, po.x + reso.x, po.y + reso.y);

        gl_Position = po - reso;
        eth = gl_Position.x;
        rng = gl_Position.y;
        gl_Position = <transform(forward(gl_Position))>;
        EmitVertex();

        gl_Position = po + vec4(-reso.x, reso.y, 0.0, 0.0);   
        gl_Position.y = gl_Position.y / c;
        eth = gl_Position.x;
        rng = gl_Position.y;
        gl_Position = <transform(forward(gl_Position))>;
        EmitVertex();

        gl_Position = po + reso;
        gl_Position.y = gl_Position.y / c;
        eth = gl_Position.x;
        rng = gl_Position.y;
        gl_Position = <transform(forward(gl_Position))>;
        EmitVertex();

        gl_Position = po + vec4(reso.x, -reso.y, 0.0, 0.0);
        eth = gl_Position.x;
        rng = gl_Position.y;
        gl_Position = <transform(forward(gl_Position))>;
        EmitVertex();

        EndPrimitive();

    }

    "#;
    let result = ws(function)(input).unwrap();
    println!("{}", result.1);
}

#[test]
fn test_expression_2() {
    let input = r#"
<transform(forward(gl_Position))>
    "#;

    let result = ws(hook)(input).unwrap();

    println!("{}", result.1);
}

#[test]
fn test_shared_variable() {
    let input = r#"
    in float in_value[];
    layout(points) in;
    
    
    "#;

    let result = ws(_share_variable)(input).unwrap();

    println!("{}", result.1);
}

#[test]
fn test_function() {
    let input = r#"
    float norm_rad(float rad) {
    float result = mod(angle, M_2_PI);
    if(result < 0.0) {
        result = M_2_PI + result;
    }

    return result;
}
    
    "#;

    let result = ws(function)(input).unwrap();

    println!("{}", result.1);
}

#[test]
fn test_struct() {
    let input = r#"
    struct vertex {
        vec4 position;
        vec4 color;
    }
    
}"#;

    let stru = ws(parse_struct)(input).unwrap();

    println!("{}", stru.1);
}
