use std::{ffi::CStr, ops::Range};

use wgpu::naga::FastHashMap;
use zerocopy::{FromBytes, Immutable, KnownLayout};

pub struct BgmFormat {
  pub tracks: FastHashMap<String, BgmTrack>,
}

#[derive(Clone)]
pub struct BgmTrack {
  pub(super) track_offset: u32,
  pub(super) intro_size: u32,
  pub(super) track_size: u32,
}

impl BgmFormat {
  pub fn new(bytes: Vec<u8>) -> BgmFormat {
    #[derive(FromBytes, KnownLayout, Immutable)]
    #[repr(C, packed)]
    struct Track {
      track_offset: u32,
      _preload_size: u32,
      intro_size: u32,
      track_size: u32,

      format_type: u16,
      channels: u16,
      samples_per_sec: u32,
      average_bytes_per_sec: u32,
      block_align: u16,
      bits_per_sample: u16,
      extra_byte_count: u16,
      pad: u16,
    }

    type TrackName = [u8; 16];

    const TRACK_NAME_SIZE: usize = size_of::<TrackName>();
    const TRACK_SIZE: usize = size_of::<TrackName>() + size_of::<Track>();

    assert_eq!(TRACK_SIZE, 0x34);

    let mut count = 0;
    let mut tracks = FastHashMap::default();
    loop {
      let start = TRACK_SIZE * count;
      let track_name = TrackName::ref_from_bytes(&bytes[start..(start + TRACK_NAME_SIZE)]).unwrap();
      let track_name = CStr::from_bytes_until_nul(track_name).expect("name has no null byte");
      if track_name.count_bytes() == 0 {
        break;
      }

      let track_name = track_name.to_str().unwrap().to_owned();
      println!("track: {:?}", track_name);
      let track = Track::ref_from_bytes(&bytes[(start + TRACK_NAME_SIZE)..(start + TRACK_SIZE)]).unwrap();
      tracks.insert(
        track_name,
        BgmTrack {
          track_offset: track.track_offset,
          intro_size: track.intro_size,
          track_size: track.track_size,
        },
      );

      count += 1;
    }

    Self { tracks }
  }

  pub fn get_track_buffers(&mut self, file_name: &str) -> (Range<usize>, Range<usize>) {
    let track = self.tracks.get(file_name).expect("failed to find bgm file");

    let intro_start = track.track_offset as usize;
    let track_start = intro_start + track.intro_size as usize;
    let track_end = intro_start + track.track_size as usize;

    (intro_start..track_start, track_start..track_end)
  }
}
