const std = @import("std");
pub const c = @import("c.zig").c;

pub const PropertiesID = u32;

pub const IOStream = opaque {
    pub inline fn loadWAV_IO(iostream: *IOStream, closeio: bool, spec: ?*AudioSpec, audio_buf: [*c][*c]u8, audio_len: *u32) bool {
        return @bitCast(c.SDL_LoadWAV_IO(iostream, @bitCast(closeio), spec, audio_buf, @ptrCast(audio_len)));
    }
};

pub const AudioFormat = enum(c_int) {
    audioUnknown = 0x0000, //Unspecified audio format
    audioU8 = 0x0008, //Unsigned 8-bit samples
    audioS8 = 0x8008, //Signed 8-bit samples
    audioS16le = 0x8010, //Signed 16-bit samples
    audioS16be = 0x9010, //As above, but big-endian byte order
    audioS32le = 0x8020, //32-bit integer samples
    audioS32be = 0x9020, //As above, but big-endian byte order
    audioF32le = 0x8120, //32-bit floating point samples
    audioF32be = 0x9120, //As above, but big-endian byte order
};

pub const AudioDeviceID = u32;

pub const AudioSpec = extern struct {
    format: AudioFormat, // Audio data format
    channels: c_int, // Number of channels: 1 mono, 2 stereo, etc
    freq: c_int, // sample rate: sample frames per second
};

pub const AudioStream = opaque {
    pub inline fn unbindAudioStream(audiostream: *AudioStream) void {
        return c.SDL_UnbindAudioStream(audiostream);
    }

    pub inline fn getAudioStreamDevice(audiostream: *AudioStream) AudioDeviceID {
        return c.SDL_GetAudioStreamDevice(audiostream);
    }

    pub inline fn getAudioStreamProperties(audiostream: *AudioStream) PropertiesID {
        return c.SDL_GetAudioStreamProperties(audiostream);
    }

    pub inline fn getAudioStreamFormat(audiostream: *AudioStream, src_spec: ?*AudioSpec, dst_spec: ?*AudioSpec) bool {
        return @bitCast(c.SDL_GetAudioStreamFormat(audiostream, src_spec, dst_spec));
    }

    pub inline fn setAudioStreamFormat(audiostream: *AudioStream, src_spec: *const AudioSpec, dst_spec: *const AudioSpec) bool {
        return @bitCast(c.SDL_SetAudioStreamFormat(audiostream, @ptrCast(src_spec), @ptrCast(dst_spec)));
    }

    pub inline fn getAudioStreamFrequencyRatio(audiostream: *AudioStream) f32 {
        return c.SDL_GetAudioStreamFrequencyRatio(audiostream);
    }

    pub inline fn setAudioStreamFrequencyRatio(audiostream: *AudioStream, ratio: f32) bool {
        return @bitCast(c.SDL_SetAudioStreamFrequencyRatio(audiostream, ratio));
    }

    pub inline fn getAudioStreamGain(audiostream: *AudioStream) f32 {
        return c.SDL_GetAudioStreamGain(audiostream);
    }

    pub inline fn setAudioStreamGain(audiostream: *AudioStream, gain: f32) bool {
        return @bitCast(c.SDL_SetAudioStreamGain(audiostream, gain));
    }

    pub inline fn getAudioStreamInputChannelMap(audiostream: *AudioStream, count: *c_int) *c_int {
        return @ptrCast(c.SDL_GetAudioStreamInputChannelMap(audiostream, @ptrCast(count)));
    }

    pub inline fn getAudioStreamOutputChannelMap(audiostream: *AudioStream, count: *c_int) *c_int {
        return @ptrCast(c.SDL_GetAudioStreamOutputChannelMap(audiostream, @ptrCast(count)));
    }

    pub inline fn setAudioStreamInputChannelMap(audiostream: *AudioStream, chmap: [*c]const c_int, count: c_int) bool {
        return @bitCast(c.SDL_SetAudioStreamInputChannelMap(audiostream, chmap, count));
    }

    pub inline fn setAudioStreamOutputChannelMap(audiostream: *AudioStream, chmap: [*c]const c_int, count: c_int) bool {
        return @bitCast(c.SDL_SetAudioStreamOutputChannelMap(audiostream, chmap, count));
    }

    pub inline fn putAudioStreamData(audiostream: *AudioStream, buf: ?*const anyopaque, len: c_int) bool {
        return @bitCast(c.SDL_PutAudioStreamData(audiostream, buf, len));
    }

    pub inline fn getAudioStreamData(audiostream: *AudioStream, buf: ?*anyopaque, len: c_int) c_int {
        return c.SDL_GetAudioStreamData(audiostream, buf, len);
    }

    pub inline fn getAudioStreamAvailable(audiostream: *AudioStream) c_int {
        return c.SDL_GetAudioStreamAvailable(audiostream);
    }

    pub inline fn getAudioStreamQueued(audiostream: *AudioStream) c_int {
        return c.SDL_GetAudioStreamQueued(audiostream);
    }

    pub inline fn flushAudioStream(audiostream: *AudioStream) bool {
        return @bitCast(c.SDL_FlushAudioStream(audiostream));
    }

    pub inline fn clearAudioStream(audiostream: *AudioStream) bool {
        return @bitCast(c.SDL_ClearAudioStream(audiostream));
    }

    pub inline fn pauseAudioStreamDevice(audiostream: *AudioStream) bool {
        return @bitCast(c.SDL_PauseAudioStreamDevice(audiostream));
    }

    pub inline fn resumeAudioStreamDevice(audiostream: *AudioStream) bool {
        return @bitCast(c.SDL_ResumeAudioStreamDevice(audiostream));
    }

    pub inline fn lockAudioStream(audiostream: *AudioStream) bool {
        return @bitCast(c.SDL_LockAudioStream(audiostream));
    }

    pub inline fn unlockAudioStream(audiostream: *AudioStream) bool {
        return @bitCast(c.SDL_UnlockAudioStream(audiostream));
    }

    pub inline fn setAudioStreamGetCallback(audiostream: *AudioStream, callback: AudioStreamCallback, userdata: ?*anyopaque) bool {
        return @bitCast(c.SDL_SetAudioStreamGetCallback(audiostream, callback, userdata));
    }

    pub inline fn setAudioStreamPutCallback(audiostream: *AudioStream, callback: AudioStreamCallback, userdata: ?*anyopaque) bool {
        return @bitCast(c.SDL_SetAudioStreamPutCallback(audiostream, callback, userdata));
    }

    pub inline fn destroyAudioStream(audiostream: *AudioStream) void {
        return c.SDL_DestroyAudioStream(audiostream);
    }
};

pub inline fn getNumAudioDrivers() c_int {
    return c.SDL_GetNumAudioDrivers();
}

pub inline fn getAudioDriver(index: c_int) [*c]const u8 {
    return c.SDL_GetAudioDriver(index);
}

pub inline fn getCurrentAudioDriver() [*c]const u8 {
    return c.SDL_GetCurrentAudioDriver();
}

pub inline fn getAudioPlaybackDevices(count: *c_int) ?*AudioDeviceID {
    return c.SDL_GetAudioPlaybackDevices(@ptrCast(count));
}

pub inline fn getAudioRecordingDevices(count: *c_int) ?*AudioDeviceID {
    return c.SDL_GetAudioRecordingDevices(@ptrCast(count));
}

pub inline fn getAudioDeviceName(devid: AudioDeviceID) [*c]const u8 {
    return c.SDL_GetAudioDeviceName(devid);
}

pub inline fn getAudioDeviceFormat(devid: AudioDeviceID, spec: ?*AudioSpec, sample_frames: *c_int) bool {
    return @bitCast(c.SDL_GetAudioDeviceFormat(devid, spec, @ptrCast(sample_frames)));
}

pub inline fn getAudioDeviceChannelMap(devid: AudioDeviceID, count: *c_int) *c_int {
    return @ptrCast(c.SDL_GetAudioDeviceChannelMap(devid, @ptrCast(count)));
}

pub inline fn openAudioDevice(devid: AudioDeviceID, spec: *const AudioSpec) AudioDeviceID {
    return c.SDL_OpenAudioDevice(devid, @ptrCast(spec));
}

pub inline fn isAudioDevicePhysical(devid: AudioDeviceID) bool {
    return @bitCast(c.SDL_IsAudioDevicePhysical(devid));
}

pub inline fn isAudioDevicePlayback(devid: AudioDeviceID) bool {
    return @bitCast(c.SDL_IsAudioDevicePlayback(devid));
}

pub inline fn pauseAudioDevice(dev: AudioDeviceID) bool {
    return @bitCast(c.SDL_PauseAudioDevice(dev));
}

pub inline fn resumeAudioDevice(dev: AudioDeviceID) bool {
    return @bitCast(c.SDL_ResumeAudioDevice(dev));
}

pub inline fn audioDevicePaused(dev: AudioDeviceID) bool {
    return @bitCast(c.SDL_AudioDevicePaused(dev));
}

pub inline fn getAudioDeviceGain(devid: AudioDeviceID) f32 {
    return c.SDL_GetAudioDeviceGain(devid);
}

pub inline fn setAudioDeviceGain(devid: AudioDeviceID, gain: f32) bool {
    return @bitCast(c.SDL_SetAudioDeviceGain(devid, gain));
}

pub inline fn closeAudioDevice(devid: AudioDeviceID) void {
    return c.SDL_CloseAudioDevice(devid);
}

pub inline fn bindAudioStreams(devid: AudioDeviceID, streams: [*c]*const AudioStream, num_streams: c_int) bool {
    return @bitCast(c.SDL_BindAudioStreams(devid, streams, num_streams));
}

pub inline fn bindAudioStream(devid: AudioDeviceID, stream: ?*AudioStream) bool {
    return @bitCast(c.SDL_BindAudioStream(devid, stream));
}

pub inline fn unbindAudioStreams(streams: [*c]*const AudioStream, num_streams: c_int) void {
    return c.SDL_UnbindAudioStreams(streams, num_streams);
}

pub inline fn createAudioStream(src_spec: *const AudioSpec, dst_spec: *const AudioSpec) ?*AudioStream {
    return c.SDL_CreateAudioStream(@ptrCast(src_spec), @ptrCast(dst_spec));
}

pub const AudioStreamCallback = c.SDL_AudioStreamCallback;

pub inline fn openAudioDeviceStream(devid: AudioDeviceID, spec: *const AudioSpec, callback: AudioStreamCallback, userdata: ?*anyopaque) ?*AudioStream {
    return c.SDL_OpenAudioDeviceStream(devid, @ptrCast(spec), callback, userdata);
}

pub const AudioPostmixCallback = c.SDL_AudioPostmixCallback;

pub inline fn setAudioPostmixCallback(devid: AudioDeviceID, callback: AudioPostmixCallback, userdata: ?*anyopaque) bool {
    return @bitCast(c.SDL_SetAudioPostmixCallback(devid, callback, userdata));
}

pub inline fn loadWAV(path: [*c]const u8, spec: ?*AudioSpec, audio_buf: [*c][*c]u8, audio_len: *u32) bool {
    return @bitCast(c.SDL_LoadWAV(path, spec, audio_buf, @ptrCast(audio_len)));
}

pub inline fn mixAudio(dst: [*c]u8, src: [*c]const u8, format: AudioFormat, len: u32, volume: f32) bool {
    return @bitCast(c.SDL_MixAudio(dst, src, @bitCast(format), len, volume));
}

pub inline fn convertAudioSamples(src_spec: *const AudioSpec, src_data: [*c]const u8, src_len: c_int, dst_spec: *const AudioSpec, dst_data: [*c][*c]u8, dst_len: *c_int) bool {
    return @bitCast(c.SDL_ConvertAudioSamples(@ptrCast(src_spec), src_data, src_len, @ptrCast(dst_spec), dst_data, @ptrCast(dst_len)));
}

pub inline fn getAudioFormatName(format: AudioFormat) [*c]const u8 {
    return c.SDL_GetAudioFormatName(@bitCast(format));
}

pub inline fn getSilenceValueForFormat(format: AudioFormat) c_int {
    return c.SDL_GetSilenceValueForFormat(@bitCast(format));
}
