use std::{
  collections::HashMap, fs::{read_dir, read_to_string, File}
};

use serde::Serialize;
use truth::{context::RootEmitter, io::BinReader, AnmFile, Game, Mapfile, StdFile};

// #[derive(Serialize)]
// struct Count {
//   name: String,
//   count: u32
// }

fn parse_files(
  map_file_name: &str,
  ext: &str,
  csv_path: &str,
  hit_file: impl Fn(&mut BinReader) -> Vec<u16>,
) {
  let map_file = Mapfile::load(map_file_name, Some(Game::Th11), &RootEmitter::new_stderr(), |path| {
    Ok((None, read_to_string(map_file_name).unwrap()))
  })
  .unwrap();
  let dir = read_dir("assets").unwrap();
  let mut map = HashMap::new();
  for file in dir {
    let file = file.unwrap();
    if file.path().extension().unwrap_or_default() != ext {
      continue;
    }

    log::info!("file: {file:?}");
    for op in hit_file(
          &mut BinReader::from_reader(
            &RootEmitter::new_stderr(),
            &file.file_name().to_string_lossy().to_owned(),
            &File::open(file.path()).unwrap(),
          ),
        ) {
          *map.entry(op).or_insert(0) += 1;
        
    };
  }

  let mut csv = csv::Writer::from_path(csv_path).unwrap();
  let mut vec = map.into_iter().collect::<Vec<_>>();
  vec.sort_by_key(|c| c.0);
  for (op, count) in vec {
    log::info!("hitting op {op}");
    let str_name = map_file
      .ins_names
      .iter()
      .find(|i| i.0 == op as i32)
      .map(|v| v.1.value.as_str().to_owned())
      .unwrap_or(format!("unk_inst_{op}"));
    csv.write_field(str_name).unwrap();
    csv.write_field(op.to_string()).unwrap();
    csv.write_field(count.to_string()).unwrap();
    csv.write_record(None::<&[u8]>).unwrap();
  }

  csv.flush().unwrap();
}

fn main() {
  env_logger::init();

  parse_files("utils/th11.eclm", "ecl", "target/ecl_inst.csv", |reader| {
    let file = truth::StackEclFile::read_from_stream(reader, Game::Th11).unwrap();

    file.subs.values().flat_map(|sub| sub.iter().map(|f| f.opcode)).collect()
  });
  parse_files("utils/th095.stdm", "std", "target/std_inst.csv", | reader| {
    let file = StdFile::read_from_stream(reader, Game::Th11).unwrap();

    file.script.into_iter().map(|f|f.opcode).collect()
  });
  parse_files("utils/v4.anmm", "anm", "target/anm_inst.csv", | reader| {
    let file = AnmFile::read_from_stream(reader, Game::Th11, false).unwrap();

    file.entries.iter().flat_map(|f|f.scripts.values()).flat_map(|f|f.instrs.iter()).map(|f|f.opcode).collect()
  });
}
