mod date;

mod game;
pub use game::{GameNode, GameRecord, GameTree, MoveNode, Player};

mod parser;
pub use parser::{parse_collection, Move, Size};

mod types;
pub use types::*;

pub use date::Date;

use std::{fs::File, io::Read};
use thiserror::Error;

#[derive(Debug)]
pub enum Error {
    InvalidField,
    InvalidBoardSize,
    Incomplete,
    InvalidSgf(VerboseNomError),
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct VerboseNomError(nom::error::VerboseError<String>);

impl From<nom::error::VerboseError<&str>> for VerboseNomError {
    fn from(err: nom::error::VerboseError<&str>) -> Self {
        VerboseNomError(nom::error::VerboseError {
            errors: err
                .errors
                .into_iter()
                .map(|err| (err.0.to_owned(), err.1))
                .collect(),
        })
    }
}

impl From<nom::Err<nom::error::VerboseError<&str>>> for Error {
    fn from(err: nom::Err<nom::error::VerboseError<&str>>) -> Self {
        match err {
            nom::Err::Incomplete(_) => Error::Incomplete,
            nom::Err::Error(e) => Error::InvalidSgf(VerboseNomError::from(e)),
            nom::Err::Failure(e) => Error::InvalidSgf(VerboseNomError::from(e)),
        }
    }
}

#[derive(Debug, PartialEq, Error)]
pub enum ParseError {
    #[error("An unknown error was found")]
    NomError(nom::error::Error<String>),
}

impl From<nom::error::Error<&str>> for ParseError {
    fn from(err: nom::error::Error<&str>) -> Self {
        Self::NomError(nom::error::Error {
            input: err.input.to_owned(),
            code: err.code,
        })
    }
}

/// Given raw text of an SGF file, parse all of the games within that file.
///
/// The outermost Result is for any errors that happen in opening and reading the file, or if hte
/// outermost part of the file format is invalid.
///
/// The inner Result is for errors in each individual game in the file. All of the other games can
/// still be kept as valid.
pub fn parse_sgf(input: &str) -> Result<Vec<Result<GameRecord, game::GameError>>, Error> {
    let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(input)?;
    let games = games
        .into_iter()
        .map(GameRecord::try_from)
        .collect::<Vec<Result<GameRecord, game::GameError>>>();

    Ok(games)
}

/// Given a path, parse all of the games stored in that file.
///
/// See also `parse_sgf`
pub fn parse_sgf_file(
    path: &std::path::Path,
) -> Result<Vec<Result<GameRecord, game::GameError>>, Error> {
    let mut file = File::open(path).unwrap();
    let mut text = String::new();
    let _ = file.read_to_string(&mut text);

    parse_sgf(&text)
}

/*
pub fn parse_sgf(_input: &str) -> Result<Vec<Game>, Error> {
    Ok(vec![Game::new(
        GameType::Go,
        Size {
            width: 19,
            height: 19,
        },
        Player {
            name: None,
            rank: None,
            team: None,
        },
        Player {
            name: None,
            rank: None,
            team: None,
        },
    )])
}
*/
