//! PDF parser - High-level parsing of PDF objects from raw bytes.

use crate::fitz::error::{Error, Result};
use crate::pdf::lexer::{LexBuf, Lexer, Token};
use crate::pdf::object::{Array, Dict, Name, ObjRef, Object, PdfString};
use crate::pdf::xref::{XrefEntry, XrefSubsection, XrefTable};
use std::collections::HashMap;

struct StashedToken {
    token: Token,
    int_val: i64,
    float_val: f64,
    buf_str: String,
}

/// Parser for PDF content.
pub struct Parser<'a> {
    lexer: Lexer<'a>,
    buf: LexBuf,
    stash: Vec<StashedToken>,
}

impl<'a> Parser<'a> {
    /// Create a new parser from a byte slice.
    pub fn new(data: &'a [u8]) -> Self {
        Self {
            lexer: Lexer::new(data),
            buf: LexBuf::new(),
            stash: Vec::new(),
        }
    }

    fn next_token(&mut self) -> Result<Token> {
        if let Some(s) = self.stash.pop() {
            self.buf.int_value = s.int_val;
            self.buf.float_value = s.float_val;
            self.buf.buffer = s.buf_str;
            return Ok(s.token);
        }
        self.lexer.lex(&mut self.buf)
    }

    fn stash_token(&mut self, token: Token) {
        self.stash.push(StashedToken {
            token,
            int_val: self.buf.int_value,
            float_val: self.buf.float_value,
            buf_str: self.buf.buffer.clone(),
        });
    }

    fn stash_with(&mut self, token: Token, int_val: i64, float_val: f64, buf_str: String) {
        self.stash.push(StashedToken {
            token,
            int_val,
            float_val,
            buf_str,
        });
    }

    /// Parse any PDF object.
    pub fn parse_object(&mut self) -> Result<Object> {
        let token = self.next_token()?;
        self.parse_object_from_token(token)
    }

    fn parse_object_from_token(&mut self, token: Token) -> Result<Object> {
        match token {
            Token::Null => Ok(Object::Null),
            Token::True => Ok(Object::Bool(true)),
            Token::False => Ok(Object::Bool(false)),
            Token::Int => {
                let n1 = self.buf.as_int();
                let t2 = self.next_token()?;
                if t2 == Token::Int {
                    let n2 = self.buf.as_int();
                    let t3 = self.next_token()?;
                    if t3 == Token::R {
                        return Ok(Object::Ref(ObjRef::new(n1 as i32, n2 as i32)));
                    }
                    self.stash_token(t3);
                    self.stash_with(t2, n2, 0.0, String::new());
                } else {
                    self.stash_token(t2);
                }
                Ok(Object::Int(n1))
            }
            Token::Real => Ok(Object::Real(self.buf.as_float())),
            Token::String => {
                let bytes = self.buf.as_str().as_bytes().to_vec();
                Ok(Object::String(PdfString::new(bytes)))
            }
            Token::HexString => {
                let bytes = hex_to_bytes(self.buf.as_str());
                Ok(Object::String(PdfString::new(bytes)))
            }
            Token::Name => Ok(Object::Name(Name::new(self.buf.as_str()))),
            Token::OpenArray => self.parse_array_from_open().map(Object::Array),
            Token::OpenDict => self.parse_dict_from_open().map(Object::Dict),
            Token::R => Err(Error::Generic(
                "Unexpected R without preceding object ref".into(),
            )),
            Token::Eof => Err(Error::Eof),
            Token::Error => Err(Error::Generic("Lexer error".into())),
            _ => Err(Error::Syntax(format!("Unexpected token: {:?}", token))),
        }
    }

    fn parse_array_from_open(&mut self) -> Result<Array> {
        let mut arr = Vec::new();
        loop {
            let token = self.next_token()?;
            if token == Token::CloseArray {
                return Ok(arr);
            }
            arr.push(self.parse_object_from_token(token)?);
        }
    }

    fn parse_dict_from_open(&mut self) -> Result<Dict> {
        let mut dict = HashMap::new();
        loop {
            let token = self.next_token()?;
            if token == Token::CloseDict {
                return Ok(dict);
            }
            let key = match token {
                Token::Name => Name::new(self.buf.as_str()),
                _ => return Err(Error::Syntax("Dictionary key must be a name".into())),
            };
            let value_token = self.next_token()?;
            let value = if value_token == Token::OpenDict {
                let d = self.parse_dict_from_open()?;
                let next = self.next_token()?;
                if next == Token::Stream {
                    let length = d
                        .get(&Name::new("Length"))
                        .and_then(|o| o.as_int())
                        .map(|i| i as usize);
                    let data = self.parse_stream_data(length)?;
                    Object::Stream { dict: d, data }
                } else {
                    self.stash_token(next);
                    Object::Dict(d)
                }
            } else {
                self.parse_object_from_token(value_token)?
            };
            dict.insert(key, value);
        }
    }

    fn parse_stream_data(&mut self, dict_length: Option<usize>) -> Result<Vec<u8>> {
        self.skip_to_newline();
        let data = self.lexer.remaining();
        let len = if let Some(length) = dict_length {
            length.min(data.len())
        } else {
            let end_marker = b"endstream";
            data.windows(end_marker.len())
                .position(|w| w == end_marker)
                .unwrap_or(data.len())
        };
        let stream_data = data[..len].to_vec();
        let after_stream = &data[len..];
        let endstream_offset = after_stream
            .windows(9)
            .position(|w| w == b"endstream")
            .map(|p| p + 9)
            .unwrap_or(0);
        self.lexer.advance_by(len + endstream_offset);
        let _ = self.next_token()?;
        Ok(stream_data)
    }

    fn skip_to_newline(&mut self) {
        let data = self.lexer.remaining();
        let mut n = 0;
        for (i, &b) in data.iter().enumerate() {
            if b == b'\n' {
                n = i + 1;
                break;
            }
            if b == b'\r' && data.get(i + 1) == Some(&b'\n') {
                n = i + 2;
                break;
            }
        }
        self.lexer.advance_by(n);
    }

    /// Parse a dictionary (expects OpenDict as next token).
    pub fn parse_dict(&mut self) -> Result<Dict> {
        let token = self.next_token()?;
        if token != Token::OpenDict {
            return Err(Error::Syntax("Expected <<".into()));
        }
        self.parse_dict_from_open()
    }

    /// Parse an array (expects OpenArray as next token).
    pub fn parse_array(&mut self) -> Result<Array> {
        let token = self.next_token()?;
        if token != Token::OpenArray {
            return Err(Error::Syntax("Expected [".into()));
        }
        self.parse_array_from_open()
    }

    /// Parse an indirect object: N G obj ... endobj. Returns (num, generation, object).
    pub fn parse_indirect_object(&mut self) -> Result<(i32, i32, Object)> {
        let num_token = self.next_token()?;
        let num = match num_token {
            Token::Int => self.buf.as_int() as i32,
            _ => return Err(Error::Syntax("Expected object number".into())),
        };
        let gen_token = self.next_token()?;
        let generation = match gen_token {
            Token::Int => self.buf.as_int() as i32,
            _ => return Err(Error::Syntax("Expected generation number".into())),
        };
        let obj_token = self.next_token()?;
        if obj_token != Token::Obj {
            return Err(Error::Syntax("Expected obj keyword".into()));
        }
        let token = self.next_token()?;
        let obj = if token == Token::OpenDict {
            let dict = self.parse_dict_from_open()?;
            let next = self.next_token()?;
            if next == Token::Stream {
                let length = dict
                    .get(&Name::new("Length"))
                    .and_then(|o| o.as_int())
                    .map(|i| i as usize);
                let data = self.parse_stream_data(length)?;
                Object::Stream { dict, data }
            } else {
                self.stash_token(next);
                Object::Dict(dict)
            }
        } else {
            self.parse_object_from_token(token)?
        };
        let end_token = self.next_token()?;
        if end_token != Token::EndObj {
            return Err(Error::Syntax("Expected endobj".into()));
        }
        Ok((num, generation, obj))
    }

    /// Parse a traditional xref table (expects xref keyword or position at start).
    pub fn parse_xref_table(&mut self) -> Result<XrefTable> {
        let token = self.next_token()?;
        if token != Token::Xref {
            return Err(Error::Syntax("Expected xref keyword".into()));
        }
        let mut table = XrefTable::new();
        loop {
            let start_token = self.next_token()?;
            if start_token == Token::Trailer {
                return Ok(table);
            }
            let start = match start_token {
                Token::Int => self.buf.as_int() as i32,
                Token::Eof => return Err(Error::Eof),
                _ => return Err(Error::Syntax("Expected subsection start".into())),
            };
            let count_token = self.next_token()?;
            let count = match count_token {
                Token::Int => self.buf.as_int() as usize,
                _ => return Err(Error::Syntax("Expected subsection count".into())),
            };
            let mut subsec = XrefSubsection::new(start, count);
            let data = self.lexer.remaining();
            let mut pos = 0;
            for _ in 0..count {
                pos = skip_to_line_start(&data[pos..]) + pos;
                if pos >= data.len() {
                    break;
                }
                let line = &data[pos..];
                if line.len() < 18 {
                    break;
                }
                let offset_str = std::str::from_utf8(&line[0..10])
                    .map_err(|_| Error::Format("Invalid xref offset".into()))?;
                let offset = offset_str
                    .trim()
                    .parse::<i64>()
                    .map_err(|_| Error::Format("Invalid xref offset".into()))?;
                let gen_str = std::str::from_utf8(&line[11..16])
                    .map_err(|_| Error::Format("Invalid xref generation".into()))?;
                let generation = gen_str
                    .trim()
                    .parse::<u16>()
                    .map_err(|_| Error::Format("Invalid xref generation".into()))?;
                let flag = line[17];
                let obj_num = start + subsec.entries.len() as i32;
                let entry = if flag == b'f' {
                    XrefEntry::free(obj_num, generation)
                } else {
                    XrefEntry::in_use(obj_num, generation, offset)
                };
                subsec.add(entry);
                pos += 20;
            }
            self.lexer.advance_by(pos);
            table.add_subsection(subsec);
        }
    }

    /// Parse a trailer dictionary (expects trailer keyword).
    pub fn parse_trailer(&mut self) -> Result<Dict> {
        let token = self.next_token()?;
        if token != Token::Trailer {
            return Err(Error::Syntax("Expected trailer keyword".into()));
        }
        self.parse_dict()
    }

    /// Get the current byte offset in the input.
    pub fn position(&self) -> usize {
        self.lexer.position()
    }
}

pub(crate) fn hex_to_bytes(hex: &str) -> Vec<u8> {
    let hex = hex.replace(|c: char| c.is_ascii_whitespace(), "");
    let mut bytes = Vec::with_capacity((hex.len() + 1) / 2);
    let mut chars = hex.chars().peekable();
    while chars.peek().is_some() {
        let h = chars.next().and_then(|c| c.to_digit(16)).unwrap_or(0) as u8;
        let l = chars.next().and_then(|c| c.to_digit(16)).unwrap_or(0) as u8;
        bytes.push((h << 4) | l);
    }
    bytes
}

fn skip_to_line_start(data: &[u8]) -> usize {
    let mut i = 0;
    while i < data.len() && (data[i] == b'\n' || data[i] == b'\r') {
        i += 1;
    }
    i
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_null() {
        let data = b"null";
        let mut p = Parser::new(data);
        let obj = p.parse_object().unwrap();
        assert!(obj.is_null());
    }

    #[test]
    fn test_parse_bool() {
        let data = b"true false";
        let mut p = Parser::new(data);
        assert_eq!(p.parse_object().unwrap().as_bool(), Some(true));
        assert_eq!(p.parse_object().unwrap().as_bool(), Some(false));
    }

    #[test]
    fn test_parse_numbers() {
        let data = b"42 -17 3.14";
        let mut p = Parser::new(data);
        assert_eq!(p.parse_object().unwrap().as_int(), Some(42));
        assert_eq!(p.parse_object().unwrap().as_int(), Some(-17));
        assert!((p.parse_object().unwrap().as_real().unwrap() - 3.14).abs() < 0.01);
    }

    #[test]
    fn test_parse_string() {
        let data = b"(Hello World)";
        let mut p = Parser::new(data);
        let obj = p.parse_object().unwrap();
        assert_eq!(obj.as_string().unwrap().as_bytes(), b"Hello World");
    }

    #[test]
    fn test_parse_hex_string() {
        let data = b"<48656c6c6f>";
        let mut p = Parser::new(data);
        let obj = p.parse_object().unwrap();
        assert_eq!(obj.as_string().unwrap().as_bytes(), b"Hello");
    }

    #[test]
    fn test_parse_name() {
        let data = b"/Type";
        let mut p = Parser::new(data);
        let obj = p.parse_object().unwrap();
        assert_eq!(obj.as_name().unwrap().as_str(), "Type");
    }

    #[test]
    fn test_parse_array() {
        let data = b"[1 2 3]";
        let mut p = Parser::new(data);
        let obj = p.parse_object().unwrap();
        let arr = obj.as_array().unwrap();
        assert_eq!(arr.len(), 3);
        assert_eq!(arr[0].as_int(), Some(1));
        assert_eq!(arr[1].as_int(), Some(2));
        assert_eq!(arr[2].as_int(), Some(3));
    }

    #[test]
    fn test_parse_dict() {
        let data = b"<< /Key /Value >>";
        let mut p = Parser::new(data);
        let obj = p.parse_object().unwrap();
        let dict = obj.as_dict().unwrap();
        assert_eq!(dict.len(), 1);
        assert_eq!(
            dict.get(&Name::new("Key"))
                .unwrap()
                .as_name()
                .unwrap()
                .as_str(),
            "Value"
        );
    }

    #[test]
    fn test_parse_ref() {
        let data = b"5 0 R";
        let mut p = Parser::new(data);
        let obj = p.parse_object().unwrap();
        if let Object::Ref(r) = obj {
            assert_eq!(r.num, 5);
            assert_eq!(r.generation, 0);
        } else {
            panic!("Expected Ref");
        }
    }

    #[test]
    fn test_parse_indirect_object() {
        let data = b"1 0 obj 42 endobj";
        let mut p = Parser::new(data);
        let (num, generation, obj) = p.parse_indirect_object().unwrap();
        assert_eq!(num, 1);
        assert_eq!(generation, 0);
        assert_eq!(obj.as_int(), Some(42));
    }

    #[test]
    fn test_parse_indirect_object_dict() {
        let data = b"2 0 obj << /Type /Page >> endobj";
        let mut p = Parser::new(data);
        let (num, generation, obj) = p.parse_indirect_object().unwrap();
        assert_eq!(num, 2);
        assert_eq!(generation, 0);
        let dict = obj.as_dict().unwrap();
        assert_eq!(
            dict.get(&Name::new("Type"))
                .unwrap()
                .as_name()
                .unwrap()
                .as_str(),
            "Page"
        );
    }

    #[test]
    fn test_parse_trailer() {
        let data = b"trailer << /Size 5 /Root 4 0 R >>";
        let mut p = Parser::new(data);
        let dict = p.parse_trailer().unwrap();
        assert_eq!(dict.get(&Name::new("Size")).unwrap().as_int(), Some(5));
    }

    #[test]
    fn test_parse_xref_table() {
        let data =
            b"xref\n0 3\n0000000000 65535 f \n0000000015 00000 n \n0000000102 00000 n \ntrailer";
        let mut p = Parser::new(data);
        let table = p.parse_xref_table().unwrap();
        assert_eq!(table.len(), 3);
        let e0 = table.get(0).unwrap();
        assert!(e0.is_free());
        let e1 = table.get(1).unwrap();
        assert!(e1.is_in_use());
        assert_eq!(e1.offset, 15);
        let e2 = table.get(2).unwrap();
        assert!(e2.is_in_use());
        assert_eq!(e2.offset, 102);
    }

    #[test]
    fn test_hex_to_bytes() {
        assert_eq!(hex_to_bytes("48656c6c6f"), b"Hello");
        assert_eq!(hex_to_bytes("48656c6c6f20"), b"Hello ");
    }
}
