use crate::encoding::Encoding;
use crate::error::MarcError;
use crate::format::engine::{load_config, CompiledConfig};
use crate::raw::{OwnedRawRecord, RawRecord};
use crate::record::Record;

use lazy_static::lazy_static;

const UNIMARC_JSON: &str = include_str!("../../resources/unimarc.json");

lazy_static! {
    static ref COMPILED: CompiledConfig = {
        let (_, compiled) = load_config(UNIMARC_JSON).expect("invalid unimarc.json config");
        compiled
    };
}

pub fn detect_encoding(record: &RawRecord<'_>) -> Result<Encoding, MarcError> {
    let mut encoding = Encoding::Utf8;
    if let Ok(fields) = record.fields() {
        for f in fields {
            if let crate::raw::RawField::Data { tag, body, .. } = f {
                if &tag == b"100" {
                    let mut pos = 0;
                    while pos < body.len() {
                        if body[pos] == 0x1F {
                            if pos + 1 >= body.len() {
                                break;
                            }
                            let code = body[pos + 1];
                            let start = pos + 2;
                            let mut end = start;
                            while end < body.len() && body[end] != 0x1F && body[end] != 0x1E {
                                end += 1;
                            }
                            if code == b'a' && end >= start + 28 {
                                encoding = match &body[start + 26..start + 28] {
                                    b"50" => Encoding::Utf8,
                                    b"01" => Encoding::Iso5426,
                                    b"02" => Encoding::Other(encoding_rs::ISO_8859_2),
                                    b"03" => Encoding::Other(encoding_rs::ISO_8859_3),
                                    b"05" => Encoding::Other(encoding_rs::ISO_8859_5),
                                    _ => Encoding::Utf8,
                                };
                                break;
                            }
                            pos = end;
                        } else if body[pos] == 0x1E {
                            break;
                        } else {
                            pos += 1;
                        }
                    }
                }
            }
        }
    }
    Ok(encoding)
}

pub fn to_record(encoding: &Encoding, record: &RawRecord<'_>) -> Result<Record, MarcError> {
    COMPILED.to_record(encoding, record)
}

pub fn to_raw(encoding: &Encoding, record: &Record) -> Result<OwnedRawRecord, MarcError> {
    COMPILED.to_raw(encoding, record)
}
