use std::{
    collections::HashMap,
    io,
    path::{Path, PathBuf},
};

mod loc;

pub use loc::Loc;
use rowan::ast::AstNode;

use crate::{
    ast::ParseError,
    lst_parser::{self, error::SyntaxError, input, output::Output},
    world::{error::Error, files::source_file::SourceFile},
};

#[derive(Default)]
pub struct Files {
    inner: Vec<source_file::SourceFile>,
    path_to_id_map: HashMap<PathBuf, FileId>,
}

impl Files {
    pub fn add_file(&mut self, path: &Path) -> Result<(FileId, Vec<Error>), OpenFileError> {
        if !path.exists() {
            return Err(OpenFileError::NotFound(path.to_owned()));
        }

        let file_id = FileId(self.inner.len());
        let (source_file, errs) = match SourceFile::open(path) {
            Ok((source_file, errs)) => {
                let errs = errs
                    .into_iter()
                    .map(|(ptr, err)| Error::Syntax(Loc::from_ptr(ptr, file_id), err))
                    .collect::<Vec<_>>();
                (source_file, errs)
            }
            Err(e) => return Err(OpenFileError::IoError(path.to_path_buf(), e)),
        };

        self.inner.push(source_file);
        self.path_to_id_map.insert(path.to_path_buf(), file_id);

        Ok((file_id, errs))
    }
}

pub enum OpenFileError {
    NotFound(PathBuf),
    IoError(PathBuf, std::io::Error),
}

#[derive(Copy, Clone, Debug)]
pub struct FileId(usize);

mod source_file;
