use {
    crate::{
        open,
        tokens::{Symbol, Token, TokenKind, TreeDelim, tokenize},
    },
    anyhow::{Context, Result, bail},
    bstr::ByteSlice,
    std::{cell::Cell, collections::HashMap, io::Write, mem, os::unix::ffi::OsStrExt, rc::Rc},
};

struct Parser<'a> {
    pos: usize,
    tokens: &'a [Token<'a>],
    ext_idx: Option<usize>,
}

impl<'a> Parser<'a> {
    fn new(tokens: &'a [Token<'a>]) -> Self {
        Self {
            pos: 0,
            tokens,
            ext_idx: None,
        }
    }

    fn parse(&mut self, ext_idx: &mut usize) -> Result<Protocol> {
        let mut first = true;
        let mut res = Protocol {
            extension: None,
            structs: vec![],
            requests: vec![],
            bitmasks: vec![],
            enums: vec![],
            events: vec![],
            eventcopies: vec![],
        };
        while !self.eof() {
            let (line, ty) = self.expect_ident()?;
            match ty.as_bytes() {
                b"ext" => {
                    if !first {
                        bail!("In line {}: ext must be the first directive", line);
                    }
                    res.extension = Some(self.parse_extension(line)?);
                    self.ext_idx = Some(*ext_idx);
                    *ext_idx += 1;
                }
                b"struct" => res.structs.push(self.parse_struct(line)?),
                b"request" => res.requests.push(self.parse_request(line)?),
                b"bitmask" => res.bitmasks.push(self.parse_bitmask(line)?),
                b"event" => res.events.push(self.parse_event(line, false)?),
                b"xge" => res.events.push(self.parse_event(line, true)?),
                b"eventcopy" => res.eventcopies.push(self.parse_event_copy(line)?),
                b"enum" => res.enums.push(self.parse_enum(line)?),
                _ => bail!("In line {}: Unexpected entry {:?}", line, ty),
            }
            first = false;
        }
        Ok(res)
    }

    fn eof(&self) -> bool {
        self.pos == self.tokens.len()
    }

    fn not_eof(&self) -> Result<()> {
        if self.eof() {
            bail!("Unexpected eof");
        }
        Ok(())
    }

    fn parse_extension(&mut self, line: u32) -> Result<String> {
        let res: Result<_> = (|| {
            let (_, name) = self.expect_string()?;
            Ok(name.to_owned())
        })();
        res.with_context(|| format!("While parsing extension starting in line {}", line))
    }

    fn parse_request(&mut self, line: u32) -> Result<Request> {
        let res: Result<_> = (|| {
            let (_, name) = self.expect_ident()?;
            self.expect_symbol(Symbol::Equals)?;
            let (_, opcode) = self.expect_num()?;
            let (_, body) = self.expect_tree(TreeDelim::Paren)?;
            let mut parser = Parser::new(body);
            let request = parser.parse_struct_body(name)?;
            self.not_eof()?;
            let reply = if self.tokens[self.pos].kind == TokenKind::Symbol(Symbol::Semicolon) {
                self.pos += 1;
                None
            } else {
                let (_, body) = self.expect_tree(TreeDelim::Brace)?;
                let mut parser = Parser::new(body);
                Some(parser.parse_struct_body(name)?)
            };
            Ok(Request {
                opcode,
                ext_idx: self.ext_idx,
                request,
                reply,
            })
        })();
        res.with_context(|| format!("While parsing request starting at line {}", line))
    }

    fn parse_struct(&mut self, line: u32) -> Result<Struct> {
        let res: Result<_> = (|| {
            let (_, name) = self.expect_ident()?;
            let (_, body) = self.expect_tree(TreeDelim::Brace)?;
            let mut parser = Parser::new(body);
            parser.parse_struct_body(name)
        })();
        res.with_context(|| format!("While parsing struct starting at line {}", line))
    }

    fn parse_event_copy(&mut self, line: u32) -> Result<EventCopy> {
        let res: Result<_> = (|| {
            let (_, name) = self.expect_ident()?;
            self.expect_symbol(Symbol::Equals)?;
            let (_, opcode) = self.expect_num()?;
            self.expect_symbol(Symbol::Equals)?;
            let (_, original) = self.expect_ident()?;
            self.expect_symbol(Symbol::Semicolon)?;
            Ok(EventCopy {
                name: name.to_owned(),
                opcode,
                original: original.to_owned(),
            })
        })();
        res.with_context(|| format!("While parsing eventcopy starting at line {}", line))
    }

    fn parse_event(&mut self, line: u32, xge: bool) -> Result<Event> {
        let res: Result<_> = (|| {
            let (_, name) = self.expect_ident()?;
            self.expect_symbol(Symbol::Equals)?;
            let (_, opcode) = self.expect_num()?;
            let (_, body) = self.expect_tree(TreeDelim::Brace)?;
            let mut parser = Parser::new(body);
            let data = parser.parse_struct_body(name)?;
            Ok(Event {
                opcode,
                xge,
                data,
                ext_idx: self.ext_idx,
            })
        })();
        res.with_context(|| format!("While parsing event starting at line {}", line))
    }

    fn parse_enum(&mut self, line: u32) -> Result<Enum> {
        let res: Result<_> = (|| {
            let (_, name) = self.expect_ident()?;
            let (_, body) = self.expect_tree(TreeDelim::Brace)?;
            let mut parser = Parser::new(body);
            let mut variants = vec![];
            while !parser.eof() {
                let (_, name) = parser.expect_ident()?;
                parser.expect_symbol(Symbol::Colon)?;
                let ty = parser.parse_type()?;
                parser.expect_symbol(Symbol::Equals)?;
                let (_, value) = parser.expect_num()?;
                if !parser.eof() {
                    parser.expect_symbol(Symbol::Comma)?;
                }
                variants.push(EnumVariant {
                    name: name.to_owned(),
                    ty,
                    value,
                });
            }
            Ok(Enum {
                name: name.to_owned(),
                variants,
            })
        })();
        res.with_context(|| format!("While parsing enum starting at line {}", line))
    }

    fn parse_bitmask(&mut self, line: u32) -> Result<Bitmask> {
        let res: Result<_> = (|| {
            let (_, name) = self.expect_ident()?;
            let (_, body) = self.expect_tree(TreeDelim::Brace)?;
            let mut parser = Parser::new(body);
            let mut variants = vec![];
            while !parser.eof() {
                let (_, name) = parser.expect_ident()?;
                parser.expect_symbol(Symbol::Colon)?;
                let ty = parser.parse_type()?;
                parser.expect_symbol(Symbol::Equals)?;
                let (_, bit) = parser.expect_num()?;
                if !parser.eof() {
                    parser.expect_symbol(Symbol::Comma)?;
                }
                variants.push(BitmaskVariant {
                    name: name.to_owned(),
                    ty,
                    bit,
                });
            }
            Ok(Bitmask {
                name: name.to_owned(),
                variants,
            })
        })();
        res.with_context(|| format!("While parsing bitmask starting at line {}", line))
    }

    fn parse_struct_body(&mut self, name: &str) -> Result<Struct> {
        let mut fields = vec![];
        while !self.eof() {
            fields.push(self.parse_field()?);
        }
        Ok(Struct {
            name: name.to_owned(),
            fields,
            needs_lt: Cell::new(None),
            has_fds: Cell::new(None),
        })
    }

    fn parse_field(&mut self) -> Result<Field> {
        self.not_eof()?;
        let line = self.tokens[self.pos].line;
        let res: Result<_> = (|| {
            let field = if self.tokens[self.pos].kind == TokenKind::Symbol(Symbol::At) {
                self.pos += 1;
                let (_, name) = self.expect_ident()?;
                match name.as_bytes() {
                    b"pad" => Field::Pad(self.expect_num()?.1),
                    b"align" => Field::Align(self.expect_num()?.1),
                    _ => bail!("Unexpected directive {}", name),
                }
            } else {
                let (_, name) = self.expect_ident()?;
                self.expect_symbol(Symbol::Colon)?;
                let ty = self.parse_type()?;
                let mut value = None;
                if !self.eof() {
                    if self.tokens[self.pos].kind == TokenKind::Symbol(Symbol::Equals) {
                        self.pos += 1;
                        value = Some(self.parse_expr()?);
                    }
                }
                Field::Real(RealField {
                    name: name.to_owned(),
                    ty,
                    value,
                })
            };
            if !self.eof() {
                self.expect_symbol(Symbol::Comma)?;
            }
            Ok(field)
        })();
        res.with_context(|| format!("While parsing field starting at line {}", line))
    }

    fn expect_num(&mut self) -> Result<(u32, u32)> {
        self.not_eof()?;
        let token = &self.tokens[self.pos];
        self.pos += 1;
        match &token.kind {
            TokenKind::Num(id) => Ok((token.line, *id)),
            k => bail!(
                "In line {}: Expected number, found {}",
                token.line,
                k.name()
            ),
        }
    }

    fn expect_ident(&mut self) -> Result<(u32, &'a str)> {
        self.not_eof()?;
        let token = &self.tokens[self.pos];
        self.pos += 1;
        match &token.kind {
            TokenKind::Ident(id) => Ok((token.line, *id)),
            k => bail!(
                "In line {}: Expected identifier, found {}",
                token.line,
                k.name()
            ),
        }
    }

    fn expect_string(&mut self) -> Result<(u32, &'a String)> {
        self.not_eof()?;
        let token = &self.tokens[self.pos];
        self.pos += 1;
        match &token.kind {
            TokenKind::String(id) => Ok((token.line, id)),
            k => bail!(
                "In line {}: Expected identifier, found {}",
                token.line,
                k.name()
            ),
        }
    }

    fn expect_symbol(&mut self, symbol: Symbol) -> Result<()> {
        self.not_eof()?;
        let token = &self.tokens[self.pos];
        self.pos += 1;
        match &token.kind {
            TokenKind::Symbol(s) if *s == symbol => Ok(()),
            k => bail!(
                "In line {}: Expected {}, found {}",
                token.line,
                symbol.name(),
                k.name()
            ),
        }
    }

    fn expect_tree_(&mut self) -> Result<(u32, TreeDelim, &'a [Token<'a>])> {
        self.not_eof()?;
        let token = &self.tokens[self.pos];
        self.pos += 1;
        match &token.kind {
            TokenKind::Tree { delim, body } => Ok((token.line, *delim, body)),
            k => bail!("In line {}: Expected tree, found {}", token.line, k.name()),
        }
    }

    fn expect_tree(&mut self, exp_delim: TreeDelim) -> Result<(u32, &'a [Token<'a>])> {
        let (line, delim, tokens) = self.expect_tree_()?;
        if delim == exp_delim {
            Ok((line, tokens))
        } else {
            bail!(
                "In line {}: Expected {:?}-delimited tree, found {:?}-delimited tree",
                line,
                exp_delim,
                delim.opening()
            )
        }
    }

    fn parse_type(&mut self) -> Result<Type> {
        let (_, ty) = self.expect_ident()?;
        let ty = match ty.as_bytes() {
            b"i8" => Type::I8,
            b"u8" => Type::U8,
            b"i16" => Type::I16,
            b"u16" => Type::U16,
            b"i32" => Type::I32,
            b"u32" => Type::U32,
            b"i64" => Type::I64,
            b"u64" => Type::U64,
            b"fd" => Type::Fd,
            b"str" => {
                let (line, body) = self.expect_tree(TreeDelim::Paren)?;
                let mut parser = Parser::new(body);
                let ty: Result<_> = (|| {
                    let len = parser.parse_expr()?;
                    if !parser.eof() {
                        bail!("Trailing tokens in str length");
                    }
                    Ok(Type::String(len))
                })();
                ty.with_context(|| format!("While parsing string starting in line {}", line))?
            }
            b"list" => {
                let (line, body) = self.expect_tree(TreeDelim::Paren)?;
                let mut parser = Parser::new(body);
                let ty: Result<_> = (|| {
                    let ty = parser.parse_type()?;
                    let mut len = None;
                    if !parser.eof() {
                        parser.expect_symbol(Symbol::Comma)?;
                        len = Some(parser.parse_expr()?);
                        if !parser.eof() {
                            bail!("Trailing tokens in list type");
                        }
                    }
                    Ok(Type::List(Box::new(ty), len))
                })();
                ty.with_context(|| format!("While parsing list starting in line {}", line))?
            }
            b"bitmask" => {
                let (line, body) = self.expect_tree(TreeDelim::Paren)?;
                let mut parser = Parser::new(body);
                let ty: Result<_> = (|| {
                    let (_, name) = parser.expect_ident()?;
                    parser.expect_symbol(Symbol::Comma)?;
                    let len = parser.parse_expr()?;
                    if !parser.eof() {
                        bail!("Trailing tokens in bitmask type");
                    }
                    Ok(Type::Bitmask(name.to_owned(), len))
                })();
                ty.with_context(|| format!("While parsing bitmask starting in line {}", line))?
            }
            b"enum" => {
                let (line, body) = self.expect_tree(TreeDelim::Paren)?;
                let mut parser = Parser::new(body);
                let ty: Result<_> = (|| {
                    let ty = parser.parse_type()?;
                    parser.expect_symbol(Symbol::Comma)?;
                    let len = parser.parse_expr()?;
                    if !parser.eof() {
                        bail!("Trailing tokens in enum type");
                    }
                    Ok(Type::Enum(Box::new(ty), len))
                })();
                ty.with_context(|| format!("While parsing enum starting in line {}", line))?
            }
            _ => Type::Named(ty.to_owned()),
        };
        Ok(ty)
    }

    fn parse_expr(&mut self) -> Result<Expr> {
        let (line, expr) = self.expect_ident()?;
        let res: Result<_> = (|| {
            let expr = match expr.as_bytes() {
                b"len" => Expr::Len(self.parse_field_name_expr()?),
                b"field" => Expr::Field(self.parse_field_name_expr()?),
                b"literal" => Expr::Literal(self.parse_literal_expr()?),
                b"bitmask" => Expr::Bitmask(self.parse_field_name_expr()?),
                b"variant" => Expr::Variant(self.parse_field_name_expr()?),
                b"sum" => Expr::Sum(self.parse_sum_expr()?),
                b"iter" => Expr::Iter(self.parse_iter_expr()?),
                b"it" => Expr::It,
                b"popcount" => Expr::Popcount(self.parse_popcount_expr()?),
                b"div" => {
                    let (left, right) = self.parse_div_expr()?;
                    Expr::Div(left, right)
                }
                b"plus" => {
                    let (left, right) = self.parse_plus_expr()?;
                    Expr::Plus(left, right)
                }
                b"map" => {
                    let (iter, fun) = self.parse_map_expr()?;
                    Expr::Map(iter, fun)
                }
                b"mul" => {
                    let (left, right) = self.parse_mul_expr()?;
                    Expr::Mul(left, right)
                }
                _ => bail!("Unknown expression {}", expr),
            };
            Ok(expr)
        })();
        res.with_context(|| format!("While parsing expression starting in line {}", line))
    }

    fn parse_literal_expr(&mut self) -> Result<u32> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let (_, name) = parser.expect_num()?;
        if !parser.eof() {
            bail!("Trailing tokens in literal");
        }
        Ok(name)
    }

    fn parse_field_name_expr(&mut self) -> Result<String> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let (_, name) = parser.expect_ident()?;
        if !parser.eof() {
            bail!("Trailing tokens in field name");
        }
        Ok(name.to_owned())
    }

    fn parse_sum_expr(&mut self) -> Result<Box<Expr>> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let expr = parser.parse_expr()?;
        if !parser.eof() {
            bail!("Trailing tokens in sum body");
        }
        Ok(Box::new(expr))
    }

    fn parse_iter_expr(&mut self) -> Result<Box<Expr>> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let expr = parser.parse_expr()?;
        if !parser.eof() {
            bail!("Trailing tokens in iter body");
        }
        Ok(Box::new(expr))
    }

    fn parse_popcount_expr(&mut self) -> Result<Box<Expr>> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let expr = parser.parse_expr()?;
        if !parser.eof() {
            bail!("Trailing tokens in popcount body");
        }
        Ok(Box::new(expr))
    }

    fn parse_mul_expr(&mut self) -> Result<(Box<Expr>, Box<Expr>)> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let left = parser.parse_expr()?;
        parser.expect_symbol(Symbol::Comma)?;
        let right = parser.parse_expr()?;
        if !parser.eof() {
            bail!("Trailing tokens in mul body");
        }
        Ok((Box::new(left), Box::new(right)))
    }

    fn parse_map_expr(&mut self) -> Result<(Box<Expr>, Box<Expr>)> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let iter = parser.parse_expr()?;
        parser.expect_symbol(Symbol::Comma)?;
        let fun = parser.parse_expr()?;
        if !parser.eof() {
            bail!("Trailing tokens in map body");
        }
        Ok((Box::new(iter), Box::new(fun)))
    }

    fn parse_div_expr(&mut self) -> Result<(Box<Expr>, Box<Expr>)> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let iter = parser.parse_expr()?;
        parser.expect_symbol(Symbol::Comma)?;
        let fun = parser.parse_expr()?;
        if !parser.eof() {
            bail!("Trailing tokens in div body");
        }
        Ok((Box::new(iter), Box::new(fun)))
    }

    fn parse_plus_expr(&mut self) -> Result<(Box<Expr>, Box<Expr>)> {
        let (_, tree) = self.expect_tree(TreeDelim::Paren)?;
        let mut parser = Parser::new(tree);
        let iter = parser.parse_expr()?;
        parser.expect_symbol(Symbol::Comma)?;
        let fun = parser.parse_expr()?;
        if !parser.eof() {
            bail!("Trailing tokens in plus body");
        }
        Ok((Box::new(iter), Box::new(fun)))
    }
}

fn needs_lifetime(ty: &Type, protocols: &Protocols) -> Result<bool> {
    let res = match ty {
        Type::I8 => false,
        Type::U8 => false,
        Type::I16 => false,
        Type::U16 => false,
        Type::I32 => false,
        Type::U32 => false,
        Type::I64 => false,
        Type::U64 => false,
        Type::Fd => false,
        Type::List(_, _) => true,
        Type::Named(n) => named_needs_lt(n, protocols)?,
        Type::String(_) => true,
        Type::Bitmask(_, _) => false,
        Type::Enum(n, _) => needs_lifetime(n, protocols)?,
    };
    Ok(res)
}

fn named_needs_lt(name: &str, protocols: &Protocols) -> Result<bool> {
    let s = match protocols.types_by_name.get(name) {
        Some(s) => s,
        _ => bail!("Struct {} referenced but not defined", name),
    };
    match s {
        NamedType::Struct(s) => struct_needs_lt(s, protocols),
        NamedType::Bitmask(_) => Ok(false),
        NamedType::Enum(s) => enum_needs_lt(s, protocols),
    }
}

fn enum_needs_lt(s: &Enum, protocols: &Protocols) -> Result<bool> {
    for v in &s.variants {
        if needs_lifetime(&v.ty, protocols)? {
            return Ok(true);
        }
    }
    Ok(false)
}

fn struct_needs_lt(s: &Struct, protocols: &Protocols) -> Result<bool> {
    if let Some(lt) = s.needs_lt.get() {
        return Ok(lt);
    }
    let mut needs_lt = false;
    for field in &s.fields {
        if let Field::Real(f) = field
            && f.value.is_none()
            && needs_lifetime(&f.ty, protocols)?
        {
            needs_lt = true;
            break;
        }
    }
    s.needs_lt.set(Some(needs_lt));
    Ok(needs_lt)
}

fn type_is_pod(ty: &Type) -> bool {
    match ty {
        Type::I8 => true,
        Type::U8 => true,
        Type::I16 => true,
        Type::U16 => true,
        Type::I32 => true,
        Type::U32 => true,
        Type::I64 => true,
        Type::U64 => true,
        Type::String(_) => false,
        Type::Fd => false,
        Type::List(..) => false,
        Type::Named(..) => false,
        Type::Bitmask(..) => false,
        Type::Enum(..) => false,
    }
}

fn type_has_fds(ty: &Type, protocols: &Protocols) -> Result<bool> {
    let res = match ty {
        Type::I8 => false,
        Type::U8 => false,
        Type::I16 => false,
        Type::U16 => false,
        Type::I32 => false,
        Type::U32 => false,
        Type::I64 => false,
        Type::U64 => false,
        Type::String(_) => false,
        Type::Fd => true,
        Type::List(ty, _) => return type_has_fds(ty, protocols),
        Type::Named(n) => return named_has_fds(n, protocols),
        Type::Bitmask(_, _) => false,
        Type::Enum(n, _) => return type_has_fds(n, protocols),
    };
    Ok(res)
}

fn named_has_fds(s: &str, protocols: &Protocols) -> Result<bool> {
    let s = match protocols.types_by_name.get(s) {
        Some(s) => s,
        _ => bail!("Struct {} referenced but not defined", s),
    };
    match s {
        NamedType::Struct(s) => struct_has_fds(s, protocols),
        NamedType::Bitmask(_) => Ok(false),
        NamedType::Enum(e) => enum_has_fds(e, protocols),
    }
}

fn enum_has_fds(s: &Enum, protocols: &Protocols) -> Result<bool> {
    for v in &s.variants {
        if type_has_fds(&v.ty, protocols)? {
            return Ok(true);
        }
    }
    Ok(false)
}

fn struct_has_fds(s: &Struct, protocols: &Protocols) -> Result<bool> {
    if let Some(lt) = s.has_fds.get() {
        return Ok(lt);
    }
    let mut has_fds = false;
    for field in &s.fields {
        if let Field::Real(f) = field
            && type_has_fds(&f.ty, protocols)?
        {
            has_fds = true;
            break;
        }
    }
    s.has_fds.set(Some(has_fds));
    Ok(has_fds)
}

#[derive(Debug, Clone)]
enum Type {
    I8,
    U8,
    I16,
    U16,
    I32,
    U32,
    I64,
    U64,
    Fd,
    String(Expr),
    List(Box<Type>, Option<Expr>),
    Bitmask(String, Expr),
    Enum(Box<Type>, Expr),
    Named(String),
}

#[derive(Debug, Clone)]
enum Expr {
    It,
    Field(String),
    Len(String),
    Literal(u32),
    Bitmask(String),
    Variant(String),
    Sum(Box<Expr>),
    Mul(Box<Expr>, Box<Expr>),
    Map(Box<Expr>, Box<Expr>),
    Div(Box<Expr>, Box<Expr>),
    Plus(Box<Expr>, Box<Expr>),
    Iter(Box<Expr>),
    Popcount(Box<Expr>),
}

#[derive(Debug, Clone)]
struct RealField {
    name: String,
    ty: Type,
    value: Option<Expr>,
}

#[derive(Debug, Clone)]
enum Field {
    Pad(u32),
    Align(u32),
    Real(RealField),
    Opcode(u32),
    ExtMajor,
}

#[derive(Debug)]
struct Struct {
    name: String,
    fields: Vec<Field>,
    needs_lt: Cell<Option<bool>>,
    has_fds: Cell<Option<bool>>,
}

#[derive(Debug)]
struct BitmaskVariant {
    name: String,
    ty: Type,
    bit: u32,
}

#[derive(Debug)]
struct Bitmask {
    name: String,
    variants: Vec<BitmaskVariant>,
}

#[derive(Debug)]
struct EnumVariant {
    name: String,
    ty: Type,
    value: u32,
}

#[derive(Debug)]
struct Enum {
    name: String,
    variants: Vec<EnumVariant>,
}

#[derive(Debug)]
struct Event {
    opcode: u32,
    xge: bool,
    data: Struct,
    ext_idx: Option<usize>,
}

#[derive(Debug)]
struct EventCopy {
    name: String,
    opcode: u32,
    original: String,
}

#[derive(Debug)]
struct Request {
    opcode: u32,
    ext_idx: Option<usize>,
    request: Struct,
    reply: Option<Struct>,
}

#[derive(Debug)]
struct Protocol {
    extension: Option<String>,
    structs: Vec<Struct>,
    requests: Vec<Request>,
    bitmasks: Vec<Bitmask>,
    enums: Vec<Enum>,
    events: Vec<Event>,
    eventcopies: Vec<EventCopy>,
}

#[derive(Debug)]
struct Extension {
    name: String,
    ident: String,
}

#[derive(Debug)]
enum NamedType {
    Struct(Rc<Struct>),
    Bitmask(#[expect(dead_code)] Rc<Bitmask>),
    Enum(Rc<Enum>),
}

#[derive(Debug)]
struct Protocols {
    extensions: Vec<Extension>,
    structs: Vec<Rc<Struct>>,
    bitmasks: Vec<Rc<Bitmask>>,
    enums: Vec<Rc<Enum>>,
    requests: Vec<Request>,
    types_by_name: HashMap<String, NamedType>,
    events_by_name: HashMap<String, Rc<Event>>,
    events: Vec<Rc<Event>>,
    eventcopies: Vec<EventCopy>,
}

fn parse_protocol(path: &str, ext_idx: &mut usize) -> Result<Protocol> {
    let s = std::fs::read_to_string(path)?;
    let tokens = tokenize(s.as_bytes())?;
    Parser::new(&tokens).parse(ext_idx)
}

fn write_type<F: Write>(f: &mut F, ty: &Type, protocols: &Protocols) -> Result<()> {
    let ty = match ty {
        Type::I8 => "i8",
        Type::U8 => "u8",
        Type::I16 => "i16",
        Type::U16 => "u16",
        Type::I32 => "i32",
        Type::U32 => "u32",
        Type::I64 => "i64",
        Type::U64 => "u64",
        Type::String(_) => "&'a BStr",
        Type::Fd => "Rc<OwnedFd>",
        Type::Bitmask(n, _) => {
            write!(f, "{}", n)?;
            return Ok(());
        }
        Type::Enum(n, _) => {
            write_type(f, n, protocols)?;
            return Ok(());
        }
        Type::List(ty, _) => {
            if type_is_pod(ty) {
                write!(f, "&'a [")?;
                write_type(f, ty, protocols)?;
                write!(f, "]")?;
            } else {
                write!(f, "Cow<'a, [")?;
                write_type(f, ty, protocols)?;
                write!(f, "]>")?;
            }
            return Ok(());
        }
        Type::Named(n) => {
            let lt = if named_needs_lt(n, protocols)? {
                "<'a>"
            } else {
                ""
            };
            write!(f, "{n}{lt}")?;
            return Ok(());
        }
    };
    write!(f, "{}", ty)?;
    Ok(())
}

fn write_expr<F: Write>(f: &mut F, e: &Expr, prefix: &str) -> Result<()> {
    match e {
        Expr::Field(n) => write!(f, "{prefix}{}", n)?,
        Expr::Len(n) => write!(f, "{prefix}{}.len()", n)?,
        Expr::Literal(n) => write!(f, "{}", n)?,
        Expr::Bitmask(n) => write!(f, "{prefix}{}.bitmask()", n)?,
        Expr::Variant(n) => write!(f, "{prefix}{}.variant()", n)?,
        Expr::Sum(e) => {
            write_expr(f, e, prefix)?;
            write!(f, ".reduce(|a, b| a + b).unwrap_or(0)")?;
        }
        Expr::Map(iter, map) => {
            write_expr(f, iter, prefix)?;
            write!(f, ".map(|val| ")?;
            write_expr(f, map, "val.")?;
            write!(f, ")")?;
        }
        Expr::Iter(ex) => {
            write_expr(f, ex, prefix)?;
            write!(f, ".iter()")?;
        }
        Expr::It => write!(f, "val")?,
        Expr::Popcount(ex) => {
            write_expr(f, ex, prefix)?;
            write!(f, ".count_ones()")?;
        }
        Expr::Div(l, r) | Expr::Plus(l, r) | Expr::Mul(l, r) => {
            write!(f, "(")?;
            write_expr(f, l, prefix)?;
            let c = match e {
                Expr::Div(..) => "/",
                Expr::Plus(..) => "+",
                Expr::Mul(..) => "*",
                _ => unreachable!(),
            };
            write!(f, " as u32 {} ", c)?;
            write_expr(f, r, prefix)?;
            write!(f, " as u32)")?;
        }
    }
    Ok(())
}

#[derive(Copy, Clone)]
enum StructUsecase<'a> {
    None,
    Request {
        request: &'a Request,
    },
    Reply,
    Event {
        xge: bool,
    },
    EventCopy {
        copy: &'a EventCopy,
        original: &'a Struct,
        xge: bool,
    },
}

fn format_xevent<F: Write>(
    f: &mut F,
    name: &str,
    s: &Struct,
    opcode: u32,
    protocols: &Protocols,
    ext: Option<usize>,
) -> Result<()> {
    let lt_a = if struct_needs_lt(s, protocols)? {
        "<'a>"
    } else {
        ""
    };
    writeln!(f)?;
    writeln!(f, "impl<'a> XEvent<'a> for {}{lt_a} {{", name)?;
    writeln!(f, "    const EXTENSION: Option<usize> = {:?};", ext)?;
    writeln!(f, "    const OPCODE: u16 = {:?};", opcode)?;
    writeln!(f, "}}")?;
    Ok(())
}

fn format_event<F: Write>(f: &mut F, s: &Event, protocols: &Protocols) -> Result<()> {
    format_struct(f, &s.data, protocols, &StructUsecase::Event { xge: s.xge })?;
    format_xevent(f, &s.data.name, &s.data, s.opcode, protocols, s.ext_idx)
}

fn format_eventcopy<F: Write>(f: &mut F, s: &EventCopy, protocols: &Protocols) -> Result<()> {
    let original = match protocols.events_by_name.get(&s.original) {
        Some(o) => o,
        _ => bail!("Event {} referenced but not defined", s.original),
    };
    format_struct(
        f,
        &original.data,
        protocols,
        &StructUsecase::EventCopy {
            copy: s,
            original: &original.data,
            xge: original.xge,
        },
    )?;
    format_xevent(
        f,
        &s.name,
        &original.data,
        s.opcode,
        protocols,
        original.ext_idx,
    )
}

fn format_request<F: Write>(f: &mut F, s: &Request, protocols: &Protocols) -> Result<()> {
    format_struct(
        f,
        &s.request,
        protocols,
        &StructUsecase::Request { request: s },
    )?;
    let mut reply_has_lt = false;
    if let Some(reply) = &s.reply {
        reply_has_lt = struct_needs_lt(reply, protocols)?;
        format_struct(f, reply, protocols, &StructUsecase::Reply)?;
    }
    let lt_a = if struct_needs_lt(&s.request, protocols)? {
        "<'a>"
    } else {
        ""
    };
    writeln!(f)?;
    writeln!(f, "impl<'a> Request<'a> for {}{lt_a} {{", s.request.name)?;
    write!(f, "    type Reply = ")?;
    if s.reply.is_some() {
        let lt_static = if reply_has_lt { "<'static>" } else { "" };
        writeln!(f, "{}Reply{};", s.request.name, lt_static)?;
    } else {
        writeln!(f, "();")?;
    }
    writeln!(f, "    const EXTENSION: Option<usize> = {:?};", s.ext_idx)?;
    writeln!(f, "    const IS_VOID: bool = {};", s.reply.is_none())?;
    writeln!(f, "}}")?;
    Ok(())
}

enum FieldGroup {
    Pods { len: u32, fields: Vec<Field> },
    Single(Field),
}

fn create_groups(s: &Struct, usecase: &StructUsecase) -> Vec<FieldGroup> {
    let mut fields = &s.fields[..];
    let mut res = vec![];
    let mut current_len = 0;
    let mut current = vec![];
    let flush_current =
        |res: &mut Vec<FieldGroup>, current: &mut Vec<Field>, current_len: &mut u32| {
            if !current.is_empty() {
                res.push(FieldGroup::Pods {
                    len: mem::take(current_len),
                    fields: mem::take(current),
                });
            }
        };
    match usecase {
        StructUsecase::None => {}
        StructUsecase::Request { request } => {
            if request.ext_idx.is_some() {
                current.push(Field::ExtMajor);
                current.push(Field::Opcode(request.opcode));
                current.push(Field::Pad(2));
            } else {
                current.push(Field::Opcode(request.opcode));
                if let Some((first, rest)) = fields.split_first() {
                    fields = rest;
                    current.push(first.clone());
                    current.push(Field::Pad(2));
                } else {
                    current.push(Field::Pad(3));
                }
            }
            current_len = 4;
        }
        StructUsecase::Reply => {
            if let Some((first, rest)) = fields.split_first() {
                fields = rest;
                current.push(Field::Pad(1));
                current.push(first.clone());
                current.push(Field::Pad(6));
            } else {
                current.push(Field::Pad(8));
            }
            current_len = 8;
        }
        StructUsecase::Event { xge } => {
            current_len = 4;
            if *xge {
                current.push(Field::Pad(10));
                current_len = 10;
            } else if let Some((first, rest)) = fields.split_first() {
                fields = rest;
                current.push(Field::Pad(1));
                current.push(first.clone());
                current.push(Field::Pad(2));
            } else {
                current.push(Field::Pad(4));
            }
        }
        StructUsecase::EventCopy { .. } => unreachable!(),
    }
    for field in fields {
        match field {
            Field::Pad(n) => {
                current.push(field.clone());
                current_len += n;
            }
            Field::Align(_) => {
                flush_current(&mut res, &mut current, &mut current_len);
                res.push(FieldGroup::Single(field.clone()));
            }
            Field::Real(rf) => match rf.ty {
                Type::I8 | Type::U8 => {
                    current.push(field.clone());
                    current_len += 1;
                }
                Type::I16 | Type::U16 => {
                    current.push(field.clone());
                    current_len += 2;
                }
                Type::I32 | Type::U32 => {
                    current.push(field.clone());
                    current_len += 4;
                }
                Type::I64 | Type::U64 => {
                    current.push(field.clone());
                    current_len += 8;
                }
                _ => {
                    flush_current(&mut res, &mut current, &mut current_len);
                    res.push(FieldGroup::Single(field.clone()));
                }
            },
            Field::Opcode(_) => unreachable!(),
            Field::ExtMajor => unreachable!(),
        }
    }
    flush_current(&mut res, &mut current, &mut current_len);
    res
}

fn format_enum<F: Write>(f: &mut F, s: &Enum, protocols: &Protocols) -> Result<()> {
    let needs_lt = enum_needs_lt(s, protocols)?;
    let lt_a = if needs_lt { "<'a>" } else { "" };
    writeln!(f)?;
    writeln!(f, "#[derive(Debug, Clone)]")?;
    writeln!(f, "pub enum {}{lt_a} {{", s.name)?;
    for field in &s.variants {
        write!(f, "    {}(", field.name)?;
        write_type(f, &field.ty, protocols)?;
        writeln!(f, "),")?;
    }
    writeln!(f, "}}")?;
    writeln!(f)?;
    writeln!(f, "impl{lt_a} {}{lt_a} {{", s.name)?;
    writeln!(f, "    pub fn variant(&self) -> u32 {{")?;
    writeln!(f, "        match self {{")?;
    for field in &s.variants {
        writeln!(
            f,
            "            Self::{}(..) => {},",
            field.name, field.value
        )?;
    }
    writeln!(f, "        }}")?;
    writeln!(f, "    }}")?;
    writeln!(f)?;
    writeln!(
        f,
        "    pub fn serialize(&self, formatter: &mut Formatter) {{"
    )?;
    writeln!(f, "        match self {{")?;
    for field in &s.variants {
        writeln!(
            f,
            "            Self::{}(v) => v.serialize(formatter),",
            field.name
        )?;
    }
    writeln!(f, "        }}")?;
    writeln!(f, "    }}")?;
    writeln!(f)?;
    writeln!(
        f,
        "    pub fn deserialize(parser: &mut Parser{lt_a}, value: u32) -> Result<Self, XconError> {{"
    )?;
    writeln!(f, "        let res = match value {{")?;
    for field in &s.variants {
        writeln!(
            f,
            "            {} => Self::{}(parser.unmarshal()?),",
            field.value, field.name
        )?;
    }
    writeln!(
        f,
        "            _ => return Err(XconError::UnknownEnumVariant),"
    )?;
    writeln!(f, "        }};")?;
    writeln!(f, "        Ok(res)")?;
    writeln!(f, "    }}")?;
    writeln!(f, "}}")?;
    Ok(())
}

fn format_bitmask<F: Write>(f: &mut F, s: &Bitmask, protocols: &Protocols) -> Result<()> {
    writeln!(f)?;
    writeln!(f, "#[derive(Debug, Clone, Default)]")?;
    writeln!(f, "pub struct {} {{", s.name)?;
    for field in &s.variants {
        write!(f, "    pub {}: Option<", field.name)?;
        write_type(f, &field.ty, protocols)?;
        writeln!(f, ">,")?;
    }
    writeln!(f, "}}")?;
    writeln!(f)?;
    writeln!(f, "impl {} {{", s.name)?;
    writeln!(f, "    pub fn bitmask(&self) -> u32 {{")?;
    writeln!(f, "        let mut res = 0;")?;
    for field in &s.variants {
        writeln!(
            f,
            "        res |= (self.{}.is_some() as u32) << {};",
            field.name, field.bit
        )?;
    }
    writeln!(f, "        res")?;
    writeln!(f, "    }}")?;
    writeln!(f)?;
    writeln!(
        f,
        "    pub fn serialize(&self, formatter: &mut Formatter) {{"
    )?;
    writeln!(f, "        let mut bytes = [0; {}];", s.variants.len() * 4)?;
    writeln!(f, "        let mut pos = 0;")?;
    for field in &s.variants {
        writeln!(f, "        if let Some(val) = self.{} {{", field.name)?;
        writeln!(
            f,
            "            bytes[pos..pos+4].copy_from_slice(&(val as u32).to_ne_bytes());"
        )?;
        writeln!(f, "            pos += 4;")?;
        writeln!(f, "        }}")?;
    }
    writeln!(f, "        formatter.write_bytes(&bytes[..pos]);")?;
    writeln!(f, "    }}")?;
    writeln!(f)?;
    writeln!(
        f,
        "    pub fn deserialize(&self, parser: &mut Parser, bitmask: u32) -> Result<Self, XconError> {{"
    )?;
    writeln!(
        f,
        "        let b = parser.read_slice(bitmask.count_ones() as usize * 4)?;"
    )?;
    writeln!(f, "        let mut p = 0;")?;
    writeln!(f, "        Ok(Self {{")?;
    for field in &s.variants {
        writeln!(
            f,
            "            {}: if bitmask & (1 << {}) != 0 {{",
            field.name, field.bit
        )?;
        writeln!(
            f,
            "                let v = u32::from_ne_bytes([b[p], b[p+1], b[p+2], b[p+3]]);"
        )?;
        writeln!(f, "                p += 4;")?;
        writeln!(f, "                Some(v as _)")?;
        writeln!(f, "            }} else {{")?;
        writeln!(f, "                None")?;
        writeln!(f, "            }},")?;
    }
    writeln!(f, "        }})")?;
    writeln!(f, "    }}")?;
    writeln!(f, "}}")?;
    Ok(())
}

fn format_struct<F: Write>(
    f: &mut F,
    s: &Struct,
    protocols: &Protocols,
    usecase: &StructUsecase,
) -> Result<()> {
    let has_fds = struct_has_fds(s, protocols)?;
    let groups = match usecase {
        StructUsecase::EventCopy { .. } => vec![],
        _ => create_groups(s, usecase),
    };
    let struct_name = match usecase {
        StructUsecase::EventCopy { copy, .. } => format!("{}", copy.name),
        StructUsecase::Reply => format!("{}Reply", s.name),
        _ => s.name.to_string(),
    };
    let needs_lt = struct_needs_lt(s, protocols)?;
    let (lt_a, lt_b) = if needs_lt { ("<'a>", "<'b>") } else { ("", "") };
    writeln!(f)?;
    writeln!(f, "#[derive(Debug, Clone)]")?;
    writeln!(f, "pub struct {}{lt_a} {{", struct_name)?;
    if let StructUsecase::EventCopy { original, .. } = usecase {
        writeln!(f, "    pub data: {}{lt_a},", original.name)?;
    } else {
        for field in &s.fields {
            if let Field::Real(rf) = field {
                if rf.value.is_none() {
                    write!(f, "    pub {}: ", rf.name)?;
                    write_type(f, &rf.ty, protocols)?;
                    writeln!(f, ",")?;
                }
            }
        }
    }
    writeln!(f, "}}")?;
    if let StructUsecase::EventCopy { original, .. } = usecase {
        writeln!(f)?;
        writeln!(f, "impl{lt_a} std::ops::Deref for {}{lt_a} {{", struct_name)?;
        writeln!(f, "    type Target = {}{lt_a};", original.name)?;
        writeln!(f)?;
        writeln!(f, "    fn deref(&self) -> &Self::Target {{")?;
        writeln!(f, "        &self.data")?;
        writeln!(f, "    }}")?;
        writeln!(f, "}}")?;
    }
    writeln!(f)?;
    writeln!(
        f,
        "unsafe impl<'a> Message<'a> for {}{lt_a} {{",
        struct_name
    )?;
    writeln!(f, "    type Generic<'b> = {}{lt_b};", struct_name)?;
    writeln!(f, "    const IS_POD: bool = false;")?;
    writeln!(f, "    const HAS_FDS: bool = {has_fds};")?;
    let mut write_serialize = true;
    if matches!(
        usecase,
        StructUsecase::Reply
            | StructUsecase::Event { xge: true }
            | StructUsecase::EventCopy { xge: true, .. }
    ) {
        write_serialize = false;
    }
    if write_serialize {
        writeln!(f)?;
        writeln!(f, "    fn serialize(&self, formatter: &mut Formatter) {{")?;
        if let StructUsecase::EventCopy { .. } = usecase {
            writeln!(f, "        self.data.serialize(formatter);")?;
        } else {
            for group in &groups {
                match group {
                    FieldGroup::Pods { fields, .. } => {
                        writeln!(f, "        {{")?;
                        for field in fields {
                            if let Field::Real(rf) = field {
                                write!(f, "            let {}_bytes = ", rf.name)?;
                                match &rf.value {
                                    Some(e) => {
                                        writeln!(f, "{{")?;
                                        write!(f, "                let tmp: ")?;
                                        write_type(f, &rf.ty, protocols)?;
                                        write!(f, " = (")?;
                                        write_expr(f, e, "self.")?;
                                        writeln!(f, ") as _;")?;
                                        writeln!(f, "                tmp.to_ne_bytes()")?;
                                        writeln!(f, "            }};")?;
                                    }
                                    _ => writeln!(f, "self.{}.to_ne_bytes();", rf.name)?,
                                }
                            }
                        }
                        writeln!(f, "            formatter.write_bytes(&[")?;
                        for field in fields {
                            match field {
                                Field::Pad(n) => {
                                    write!(f, "               ")?;
                                    for _ in 0..*n {
                                        write!(f, " 0,")?;
                                    }
                                    writeln!(f)?;
                                }
                                Field::Real(rf) => {
                                    let num_bytes = match rf.ty {
                                        Type::I8 | Type::U8 => 1,
                                        Type::I16 | Type::U16 => 2,
                                        Type::I32 | Type::U32 => 4,
                                        Type::I64 | Type::U64 => 8,
                                        _ => unreachable!(),
                                    };
                                    write!(f, "               ")?;
                                    for i in 0..num_bytes {
                                        write!(f, " {}_bytes[{}],", rf.name, i)?;
                                    }
                                    writeln!(f)?;
                                }
                                Field::Opcode(n) => {
                                    writeln!(f, "                {},", n)?;
                                }
                                Field::ExtMajor => {
                                    writeln!(f, "                formatter.ext_opcode(),")?;
                                }
                                _ => unreachable!(),
                            }
                        }
                        writeln!(f, "            ]);")?;
                        writeln!(f, "        }}")?;
                    }
                    FieldGroup::Single(field) => match field {
                        Field::Align(n) => writeln!(f, "        formatter.align({n});")?,
                        Field::Real(rf) => match &rf.value {
                            Some(v) => {
                                writeln!(f, "        {{")?;
                                write!(f, "            let tmp: ")?;
                                write_type(f, &rf.ty, protocols)?;
                                write!(f, " = ")?;
                                write_expr(f, v, "self.")?;
                                writeln!(f, " as _;")?;
                                writeln!(f, "            tmp.serialize(formatter);")?;
                                writeln!(f, "        }}")?;
                            }
                            _ => writeln!(f, "        self.{}.serialize(formatter);", rf.name)?,
                        },
                        _ => unreachable!(),
                    },
                }
            }
        }
        writeln!(f, "    }}")?;
    }
    if !matches!(usecase, StructUsecase::Request { .. }) {
        writeln!(f)?;
        writeln!(
            f,
            "    fn deserialize(parser: &mut Parser<'a>) -> Result<Self, XconError> {{"
        )?;
        if let StructUsecase::EventCopy { original, .. } = usecase {
            writeln!(f, "        Ok(Self {{")?;
            writeln!(
                f,
                "            data: {}::deserialize(parser)?,",
                original.name
            )?;
            writeln!(f, "        }})")?;
        } else {
            for group in &groups {
                match group {
                    FieldGroup::Pods { len, fields } => {
                        writeln!(f, "        let bytes_ = parser.read_bytes::<{}>()?;", len)?;
                        let mut pos = 0;
                        for field in fields {
                            match field {
                                Field::Pad(n) => pos += n,
                                Field::Real(rf) => {
                                    write!(f, "        let {} = ", rf.name)?;
                                    write_type(f, &rf.ty, protocols)?;
                                    write!(f, "::from_ne_bytes([")?;
                                    let num_bytes = match rf.ty {
                                        Type::I8 | Type::U8 => 1,
                                        Type::I16 | Type::U16 => 2,
                                        Type::I32 | Type::U32 => 4,
                                        Type::I64 | Type::U64 => 8,
                                        _ => unreachable!(),
                                    };
                                    for i in 0..num_bytes {
                                        if i != 0 {
                                            write!(f, ", ")?;
                                        }
                                        write!(f, "bytes_[{}]", pos + i)?;
                                    }
                                    writeln!(f, "]);")?;
                                    pos += num_bytes;
                                }
                                Field::Opcode(_) => pos += 1,
                                Field::ExtMajor => pos += 1,
                                _ => unreachable!(),
                            }
                        }
                    }
                    FieldGroup::Single(field) => match field {
                        Field::Align(n) => writeln!(f, "        parser.align({n})?;")?,
                        Field::Real(rf) => {
                            write!(f, "        let {}: ", rf.name)?;
                            write_type(f, &rf.ty, protocols)?;
                            write!(f, " = ")?;
                            match &rf.ty {
                                Type::List(el, len) => {
                                    writeln!(f, "{{")?;
                                    write!(f, "            let len: Option<usize> = ")?;
                                    if let Some(len) = len {
                                        write!(f, "Some(")?;
                                        write_expr(f, len, "")?;
                                        writeln!(f, " as _);")?;
                                    } else {
                                        writeln!(f, "None;")?;
                                    }
                                    if type_is_pod(el) {
                                        writeln!(f, "            parser.read_list_slice(len)?")?;
                                    } else {
                                        writeln!(f, "            parser.read_list(len)?")?;
                                    }
                                    writeln!(f, "        }};")?;
                                }
                                Type::String(len) => {
                                    writeln!(f, "{{")?;
                                    write!(f, "            let len: usize = ")?;
                                    write_expr(f, len, "")?;
                                    writeln!(f, " as _;")?;
                                    writeln!(f, "            parser.read_string(len)?")?;
                                    writeln!(f, "        }};")?;
                                }
                                Type::Bitmask(n, bm) => {
                                    write!(f, "{}::deserialize(parser, ", n)?;
                                    write_expr(f, bm, "")?;
                                    writeln!(f, " as _)?;")?;
                                }
                                Type::Enum(n, bm) => {
                                    write!(f, "<")?;
                                    write_type(f, n, protocols)?;
                                    write!(f, ">::deserialize(parser, ")?;
                                    write_expr(f, bm, "")?;
                                    writeln!(f, " as _)?;")?;
                                }
                                _ => writeln!(f, "parser.unmarshal()?;")?,
                            }
                        }
                        _ => unreachable!(),
                    },
                }
            }
            writeln!(f, "        Ok(Self {{")?;
            for field in &s.fields {
                if let Field::Real(rf) = field
                    && rf.value.is_none()
                {
                    writeln!(f, "            {},", rf.name)?;
                }
            }
            writeln!(f, "        }})")?;
        }
        writeln!(f, "    }}")?;
    }
    writeln!(f, "}}")?;
    Ok(())
}

pub fn main() -> Result<()> {
    println!("cargo:rerun-if-changed=wire-xcon");

    let mut f = open("wire_xcon.rs")?;

    let mut files = vec![];
    for file in std::fs::read_dir("wire-xcon")? {
        files.push(file?.file_name());
    }
    files.sort();

    let mut protocols = Protocols {
        extensions: vec![],
        structs: vec![],
        bitmasks: vec![],
        enums: vec![],
        requests: vec![],
        types_by_name: Default::default(),
        events_by_name: Default::default(),
        events: vec![],
        eventcopies: vec![],
    };
    let mut ext_idx = 0;

    for file in files {
        let path = format!("wire-xcon/{}", file.as_bytes().as_bstr());
        let protocol = parse_protocol(&path, &mut ext_idx)
            .with_context(|| format!("While parsing {}", path))?;
        for s in protocol.structs {
            let s = Rc::new(s);
            protocols.structs.push(s.clone());
            protocols
                .types_by_name
                .insert(s.name.clone(), NamedType::Struct(s));
        }
        for s in protocol.bitmasks {
            let s = Rc::new(s);
            protocols.bitmasks.push(s.clone());
            protocols
                .types_by_name
                .insert(s.name.clone(), NamedType::Bitmask(s));
        }
        for s in protocol.enums {
            let s = Rc::new(s);
            protocols.enums.push(s.clone());
            protocols
                .types_by_name
                .insert(s.name.clone(), NamedType::Enum(s));
        }
        protocols.requests.extend(protocol.requests);
        for s in protocol.events {
            let s = Rc::new(s);
            protocols.events.push(s.clone());
            protocols.events_by_name.insert(s.data.name.clone(), s);
        }
        protocols.eventcopies.extend(protocol.eventcopies);
        if let Some(ext) = protocol.extension {
            let mut ident = String::new();
            for c in ext.chars() {
                if matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9') {
                    ident.push(c);
                }
            }
            protocols.extensions.push(Extension { name: ext, ident });
        }
    }

    writeln!(f, "#[derive(Copy, Clone, Debug, Eq, PartialEq)]")?;
    writeln!(f, "pub enum Extension {{")?;
    for ext in &protocols.extensions {
        writeln!(f, r#"    {},"#, ext.ident)?;
    }
    writeln!(f, "}}")?;
    writeln!(f)?;
    writeln!(f, "impl Extension {{")?;
    writeln!(f, "    pub fn name(self) -> &'static str {{")?;
    writeln!(f, "        match self {{")?;
    for ext in &protocols.extensions {
        writeln!(f, r#"            Self::{} => "{}","#, ext.ident, ext.name)?;
    }
    writeln!(f, "        }}")?;
    writeln!(f, "    }}")?;
    writeln!(f, "}}")?;
    writeln!(f)?;
    writeln!(f, "pub const EXTENSIONS: &[Extension] = &[")?;
    for ext in &protocols.extensions {
        writeln!(f, r#"    Extension::{},"#, ext.ident)?;
    }
    writeln!(f, "];")?;

    for s in &protocols.bitmasks {
        format_bitmask(&mut f, s, &protocols)?;
    }

    for s in &protocols.structs {
        format_struct(&mut f, s, &protocols, &StructUsecase::None)?;
    }

    for s in &protocols.enums {
        format_enum(&mut f, s, &protocols)?;
    }

    for s in &protocols.requests {
        format_request(&mut f, s, &protocols)?;
    }

    for s in &protocols.events {
        format_event(&mut f, s, &protocols)?;
    }

    for s in &protocols.eventcopies {
        format_eventcopy(&mut f, s, &protocols)?;
    }

    Ok(())
}
