# sdl2.py - SDL2 constants, structures, bindings and cross-platform loader

import os
import sys
import tempfile
import shutil
import subprocess
import ssl
import hashlib
import urllib.request
from ctypes import *


# ============================================================
# SDL2 Library Loader
# ============================================================

_sdl_lib = None
_mix_lib = None


def _load_from_directory(directory):
    """Load SDL2 libraries from specified directory"""
    global _sdl_lib, _mix_lib
    platform = sys.platform

    if platform == "win32":
        sdl2_path = os.path.join(directory, "SDL2.dll")
        sdl2_mixer_path = os.path.join(directory, "SDL2_mixer.dll")
        if os.path.exists(sdl2_path) and os.path.exists(sdl2_mixer_path):
            if hasattr(os, 'add_dll_directory'):
                os.add_dll_directory(directory)
            os.environ['PATH'] = directory + os.pathsep + os.environ.get('PATH', '')
            _sdl_lib = CDLL(sdl2_path)
            _mix_lib = CDLL(sdl2_mixer_path)
            return True
        return False

    elif platform == "darwin":
        sdl2_path = os.path.join(directory, "SDL2.framework", "SDL2")
        sdl2_mixer_path = os.path.join(directory, "SDL2_mixer.framework", "SDL2_mixer")
        if os.path.exists(sdl2_path) and os.path.exists(sdl2_mixer_path):
            framework_dir = os.path.dirname(os.path.dirname(sdl2_path))
            if 'DYLD_FRAMEWORK_PATH' not in os.environ:
                os.environ['DYLD_FRAMEWORK_PATH'] = framework_dir
            else:
                os.environ['DYLD_FRAMEWORK_PATH'] = framework_dir + ':' + os.environ['DYLD_FRAMEWORK_PATH']
            _sdl_lib = CDLL(sdl2_path)
            _mix_lib = CDLL(sdl2_mixer_path)
            return True
        return False

    elif platform.startswith("linux"):
        names_to_try = [
            ("libSDL2.so", "libSDL2_mixer.so"),
            ("SDL2.so", "SDL2_mixer.so"),
        ]
        for sdl_name, mixer_name in names_to_try:
            sdl_path = os.path.join(directory, sdl_name)
            mixer_path = os.path.join(directory, mixer_name)
            if os.path.exists(sdl_path) and os.path.exists(mixer_path):
                if 'LD_LIBRARY_PATH' not in os.environ:
                    os.environ['LD_LIBRARY_PATH'] = directory
                else:
                    os.environ['LD_LIBRARY_PATH'] = directory + ':' + os.environ['LD_LIBRARY_PATH']
                _sdl_lib = CDLL(sdl_path)
                _mix_lib = CDLL(mixer_path)
                return True
        return False

    return False


def _load_from_system():
    """Try loading from system paths"""
    global _sdl_lib, _mix_lib
    platform = sys.platform

    try:
        import ctypes.util
        sdl_path = ctypes.util.find_library("SDL2")
        mixer_path = ctypes.util.find_library("SDL2_mixer")
        if sdl_path and mixer_path:
            _sdl_lib = CDLL(sdl_path)
            _mix_lib = CDLL(mixer_path)
            return True
    except:
        pass
    return False


def _load_user_config():
    """Load user-saved SDL2 paths from config file"""
    try:
        config_file = os.path.expanduser('~/.config/ap_ds/sdl_paths.conf')
        if os.path.exists(config_file):
            with open(config_file, 'r') as f:
                sdl_path = None
                mixer_path = None
                for line in f:
                    if line.startswith('SDL2_PATH='):
                        sdl_path = line.strip().split('=', 1)[1]
                    elif line.startswith('SDL2_MIXER_PATH='):
                        mixer_path = line.strip().split('=', 1)[1]
                if sdl_path and mixer_path and os.path.exists(sdl_path) and os.path.exists(mixer_path):
                    global _sdl_lib, _mix_lib
                    _sdl_lib = CDLL(sdl_path)
                    _mix_lib = CDLL(mixer_path)
                    return True
    except:
        pass
    return False


def _linux_auto_install():
    """Try automatic package manager installation on Linux"""
    try:
        import subprocess
        import shutil

        if shutil.which('apt-get'):
            print("📦 Detected apt-based system (Ubuntu/Debian)")
            result = subprocess.run(
                ['sudo', 'apt-get', 'install', '-y', 'libsdl2-dev', 'libsdl2-mixer-dev'],
                capture_output=True, text=True
            )
            if result.returncode == 0:
                if _load_from_system():
                    print("✅ SDL2 libraries installed and loaded")
                    return True

        elif shutil.which('dnf'):
            print("📦 Detected dnf-based system (Fedora)")
            result = subprocess.run(
                ['sudo', 'dnf', 'install', '-y', 'SDL2-devel', 'SDL2_mixer-devel'],
                capture_output=True, text=True
            )
            if result.returncode == 0:
                if _load_from_system():
                    print("✅ SDL2 libraries installed and loaded")
                    return True

        elif shutil.which('pacman'):
            print("📦 Detected pacman-based system (Arch)")
            result = subprocess.run(
                ['sudo', 'pacman', '-S', '--noconfirm', 'sdl2', 'sdl2_mixer'],
                capture_output=True, text=True
            )
            if result.returncode == 0:
                if _load_from_system():
                    print("✅ SDL2 libraries installed and loaded")
                    return True

    except Exception as e:
        print(f"⚠️ Automatic installation failed: {e}")
    return False


def _linux_interactive_setup():
    """Linux interactive setup for SDL2"""
    global _sdl_lib, _mix_lib
    print("\n" + "="*70)
    print("Linux SDL2 Library Loading")
    print("="*70)
    print("Options:")
    print("1. Use system-installed libraries (re-check)")
    print("2. Specify path to your compiled .so files")
    print("3. Show installation instructions")
    print("="*70)

    while True:
        choice = input("\nChoose option (1/2/3): ").strip()

        if choice == "1":
            if _load_from_system():
                print("✅ SDL2 libraries loaded from system")
                return True
            print("❌ System libraries not found")
            continue

        elif choice == "2":
            sdl_path = input("Enter full path to libSDL2.so: ").strip()
            mixer_path = input("Enter full path to libSDL2_mixer.so: ").strip()
            if os.path.exists(sdl_path) and os.path.exists(mixer_path):
                try:
                    _sdl_lib = CDLL(sdl_path)
                    _mix_lib = CDLL(mixer_path)
                    # Save for future
                    try:
                        config_dir = os.path.expanduser('~/.config/ap_ds')
                        os.makedirs(config_dir, exist_ok=True)
                        with open(os.path.join(config_dir, 'sdl_paths.conf'), 'w') as f:
                            f.write(f"SDL2_PATH={sdl_path}\n")
                            f.write(f"SDL2_MIXER_PATH={mixer_path}\n")
                    except:
                        pass
                    print("✅ Libraries loaded from user-specified paths")
                    return True
                except Exception as e:
                    print(f"❌ Failed to load: {e}")
            else:
                print("❌ One or both files not found")
            continue

        elif choice == "3":
            print("\n📚 Installation instructions:")
            print("="*50)
            print("For Ubuntu/Debian:")
            print("  sudo apt-get install libsdl2-dev libsdl2-mixer-dev")
            print("\nFor Fedora:")
            print("  sudo dnf install SDL2-devel SDL2_mixer-devel")
            print("\nFor Arch:")
            print("  sudo pacman -S sdl2 sdl2_mixer")
            print("\nManual compilation:")
            print("1. Download SDL2 from: https://www.libsdl.org/download-2.0.php")
            print("2. Download SDL2_mixer from: https://www.libsdl.org/projects/SDL_mixer/")
            print("3. Compile: ./configure && make && sudo make install")
            print("="*50)
            continue

        else:
            print("❌ Invalid choice. Please enter 1, 2, or 3.")


def _check_sdl_libraries_exist(directory):
    """Check if SDL2 libraries exist in directory"""
    platform = sys.platform

    if platform == "win32":
        return os.path.exists(os.path.join(directory, "SDL2.dll")) and \
               os.path.exists(os.path.join(directory, "SDL2_mixer.dll"))
    elif platform == "darwin":
        return os.path.exists(os.path.join(directory, "SDL2.framework")) and \
               os.path.exists(os.path.join(directory, "SDL2_mixer.framework"))
    elif platform.startswith("linux"):
        return os.path.exists(os.path.join(directory, "libSDL2.so")) and \
               os.path.exists(os.path.join(directory, "libSDL2_mixer.so"))
    return False


def _check_sdl2_loaded():
    """Check if SDL2 is already loaded"""
    return _sdl_lib is not None and _mix_lib is not None


def download_sdl_libraries():
    """Download SDL2 libraries to package directory based on platform with file hash verification"""
    FILE_HASHES = {
        "SDL2.dll": "520d0459b91efa32fbccf9027a9ca1fc5aae657e679ce8e90f179f9cf5afd279",
        "SDL2_mixer.dll": "2a0fc5e9f72c2eaec3240cb82b7594a58ccda609485981f256b94d0a4dd8d6f8",
        "SDL2.dmg": "2bf2cb8f6b44d584b14e8d4ca7437080d1d968fe3962303be27217b336b82249",
        "SDL2_mixer.dmg": "d74052391ee4d91836bf1072a060f1d821710f3498a54996c66b9a17c79a72d1",
    }

    current_dir = os.path.dirname(os.path.abspath(__file__))
    print(f"Package directory: {current_dir}")
    platform = sys.platform

    def download_file(url, expected_hash=None):
        temp_file = tempfile.NamedTemporaryFile(delete=False)
        temp_file.close()

        try:
            print(f"  Attempting download with SSL verification...")
            try:
                req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
                with urllib.request.urlopen(req, timeout=30) as response:
                    content = response.read()
                print(f"  ✅ Download successful with SSL verification")
            except (urllib.error.URLError, ssl.SSLError, Exception) as e:
                print(f"  SSL verification failed: {e}")
                print(f"  Attempting download without SSL verification...")
                context = ssl.create_default_context()
                context.check_hostname = False
                context.verify_mode = ssl.CERT_NONE
                req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
                with urllib.request.urlopen(req, timeout=30, context=context) as response:
                    content = response.read()
                print(f"  ✅ Download successful without SSL verification")

            with open(temp_file.name, 'wb') as f:
                f.write(content)
            print(f"  Downloaded {len(content)} bytes")

            if expected_hash and expected_hash != "expected_sha256_hash_of_xxx":
                file_hash = hashlib.sha256(content).hexdigest()
                print(f"  File SHA256: {file_hash}")
                if file_hash.lower() != expected_hash.lower():
                    raise Exception(f"Hash verification failed! Expected: {expected_hash}, Got: {file_hash}")
                print(f"  ✅ Hash verification passed")
            else:
                print(f"  ⚠️  No hash verification (hash not configured)")

            return content, temp_file.name

        except Exception as e:
            print(f"  ❌ Download failed: {str(e)}")
            try:
                os.unlink(temp_file.name)
            except:
                pass
            raise

    def verify_file_hash(file_path, expected_hash):
        if not expected_hash or expected_hash == "expected_sha256_hash_of_xxx":
            print(f"  ⚠️  No hash configured for {os.path.basename(file_path)}, skipping verification")
            return True

        try:
            with open(file_path, 'rb') as f:
                content = f.read()
            file_hash = hashlib.sha256(content).hexdigest()
            print(f"  Existing file SHA256: {file_hash}")
            if file_hash.lower() == expected_hash.lower():
                print(f"  ✅ Hash verification passed")
                return True
            else:
                print(f"  ❌ Hash verification failed! Expected: {expected_hash}, Got: {file_hash}")
                return False
        except Exception as e:
            print(f"  ❌ Error verifying hash: {e}")
            return False

    # Windows
    if platform == "win32":
        files = [
            {"url": "https://dvsyun.top/ap_ds/download/SDL2", "filename": "SDL2.dll", "expected_hash": FILE_HASHES.get("SDL2.dll")},
            {"url": "https://dvsyun.top/ap_ds/download/SDL2_M", "filename": "SDL2_mixer.dll", "expected_hash": FILE_HASHES.get("SDL2_mixer.dll")}
        ]
        for file_info in files:
            file_path = os.path.join(current_dir, file_info["filename"])
            if os.path.exists(file_path):
                print(f"\n📁 {file_info['filename']} already exists, verifying hash...")
                if verify_file_hash(file_path, file_info["expected_hash"]):
                    print(f"✅ {file_info['filename']} is valid, skipping download")
                    continue
                else:
                    print(f"⚠️  {file_info['filename']} hash mismatch, re-downloading...")
                    try:
                        os.remove(file_path)
                    except:
                        pass
            print(f"\n📥 Downloading {file_info['filename']}...")
            try:
                content, temp_path = download_file(file_info["url"], file_info["expected_hash"])
                shutil.move(temp_path, file_path)
                print(f"✅ Successfully downloaded {file_info['filename']}")
            except Exception as e:
                print(f"❌ Failed to download {file_info['filename']}: {str(e)}")
                continue

    # macOS
    elif platform == "darwin":
        frameworks = [
            {"name": "SDL2", "url": "https://dvsyun.top/ap_ds/download/SDL2/MAC", "dmg_filename": "SDL2.dmg", "framework_name": "SDL2.framework", "expected_hash": FILE_HASHES.get("SDL2.dmg")},
            {"name": "SDL2_mixer", "url": "https://dvsyun.top/ap_ds/download/SDL2_M/MAC", "dmg_filename": "SDL2_mixer.dmg", "framework_name": "SDL2_mixer.framework", "expected_hash": FILE_HASHES.get("SDL2_mixer.dmg")}
        ]
        for framework_info in frameworks:
            framework_path = os.path.join(current_dir, framework_info["framework_name"])
            if os.path.exists(framework_path):
                print(f"\n📁 {framework_info['framework_name']} already exists, skipping download")
                continue
            print(f"\n📥 Downloading {framework_info['dmg_filename']}...")
            temp_dmg = None
            try:
                content, temp_dmg = download_file(framework_info["url"], framework_info["expected_hash"])
                print(f"✅ Downloaded {framework_info['dmg_filename']}")
                mount_point = tempfile.mkdtemp(prefix=f"{framework_info['name']}_mount_")
                try:
                    cmd = ["hdiutil", "attach", temp_dmg, "-mountpoint", mount_point, "-nobrowse", "-quiet"]
                    result = subprocess.run(cmd, capture_output=True, text=True)
                    if result.returncode != 0:
                        print(f"❌ Failed to mount {framework_info['dmg_filename']}: {result.stderr}")
                        continue
                    framework_src = None
                    for root, dirs, files in os.walk(mount_point):
                        if framework_info["framework_name"] in dirs:
                            framework_src = os.path.join(root, framework_info["framework_name"])
                            break
                    if not framework_src:
                        possible_paths = [
                            os.path.join(mount_point, framework_info["framework_name"]),
                            os.path.join(mount_point, framework_info["name"], framework_info["framework_name"]),
                            os.path.join(mount_point, "Frameworks", framework_info["framework_name"]),
                        ]
                        for path in possible_paths:
                            if os.path.exists(path):
                                framework_src = path
                                break
                    if not framework_src:
                        print(f"❌ Could not find {framework_info['framework_name']} in dmg")
                        subprocess.run(["hdiutil", "detach", mount_point, "-quiet"])
                        continue
                    shutil.copytree(framework_src, framework_path)
                    print(f"✅ Extracted {framework_info['framework_name']} to package directory")
                    subprocess.run(["hdiutil", "detach", mount_point, "-quiet"])
                except Exception as e:
                    print(f"❌ Extraction failed: {e}")
                    try:
                        subprocess.run(["hdiutil", "detach", mount_point, "-force", "-quiet"])
                    except:
                        pass
                    continue
                finally:
                    if temp_dmg and os.path.exists(temp_dmg):
                        os.unlink(temp_dmg)
                    if os.path.exists(mount_point):
                        try:
                            os.rmdir(mount_point)
                        except:
                            pass
            except Exception as e:
                print(f"❌ Download failed: {str(e)}")
                if temp_dmg and os.path.exists(temp_dmg):
                    try:
                        os.unlink(temp_dmg)
                    except:
                        pass
                continue

    # Linux
    elif platform.startswith("linux"):
        print("\n" + "="*70)
        print("⚠️  Linux Support Notice")
        print("="*70)
        print("For Linux systems, SDL2 libraries are NOT provided via automatic download.")
        print("Reason: There are too many Linux distributions and library dependencies.")
        print("")
        print("To use ap_ds on Linux:")
        print("1. Install SDL2 and SDL2_mixer using your package manager:")
        print("   - Ubuntu/Debian: sudo apt-get install libsdl2-dev libsdl2-mixer-dev")
        print("   - Fedora: sudo dnf install SDL2-devel SDL2_mixer-devel")
        print("   - Arch: sudo pacman -S sdl2 sdl2_mixer")
        print("2. Or compile from source:")
        print("   - Download from: https://www.libsdl.org/")
        print("   - Build instructions: https://wiki.libsdl.org/Installation")
        print("")
        print("After installation, run ap_ds again.")
        print("="*70 + "\n")

        response = input("Do you have pre-compiled .so files? (y/n): ").strip().lower()
        if response == 'y':
            sdl2_path = input("Enter full path to libSDL2.so: ").strip()
            sdl2_mixer_path = input("Enter full path to libSDL2_mixer.so: ").strip()
            if os.path.exists(sdl2_path) and os.path.exists(sdl2_mixer_path):
                shutil.copy2(sdl2_path, os.path.join(current_dir, "libSDL2.so"))
                shutil.copy2(sdl2_mixer_path, os.path.join(current_dir, "libSDL2_mixer.so"))
                print("✅ Libraries copied to package directory")
            else:
                print("❌ One or both library files not found")
        else:
            print("Please install SDL2 libraries and try again.")
        return

    print(f"\n📂 Files in package directory: {os.listdir(current_dir)}")


def import_sdl2():
    """Main function: Import SDL2 libraries with cross-platform support"""
    global _sdl_lib, _mix_lib

    # Already loaded?
    if _check_sdl2_loaded():
        return _sdl_lib, _mix_lib

    current_dir = os.path.dirname(os.path.abspath(__file__))
    platform = sys.platform

    # Windows
    if platform == "win32":
        # Try current directory
        if _load_from_directory(current_dir):
            print("✅ SDL2 loaded from package directory")
            return _sdl_lib, _mix_lib
        # Try system path
        try:
            _sdl_lib = CDLL("SDL2.dll")
            _mix_lib = CDLL("SDL2_mixer.dll")
            print("✅ SDL2 loaded from system path")
            return _sdl_lib, _mix_lib
        except:
            pass
        # Download
        print("SDL2 libraries not found, downloading...")
        download_sdl_libraries()
        if _load_from_directory(current_dir):
            print("✅ SDL2 loaded after download")
            return _sdl_lib, _mix_lib
        raise ImportError("Failed to load SDL2 libraries after download")

    # macOS
    elif platform == "darwin":
        # Try current directory
        if _load_from_directory(current_dir):
            print("✅ SDL2 loaded from package directory")
            return _sdl_lib, _mix_lib
        # Try system framework paths
        if _load_from_system():
            print("✅ SDL2 loaded from system")
            return _sdl_lib, _mix_lib
        # Download
        print("SDL2 frameworks not found, downloading...")
        download_sdl_libraries()
        if _load_from_directory(current_dir):
            print("✅ SDL2 loaded after download")
            return _sdl_lib, _mix_lib
        raise ImportError("Failed to load SDL2 frameworks after download")

    # Linux
    elif platform.startswith("linux"):
        # Layer 1: Load from current directory
        if _load_from_directory(current_dir):
            print("✅ SDL2 loaded from package directory")
            return _sdl_lib, _mix_lib

        # Layer 2: User config
        if _load_user_config():
            print("✅ SDL2 loaded from user config")
            return _sdl_lib, _mix_lib

        # Layer 3: System libraries
        if _load_from_system():
            print("✅ SDL2 loaded from system")
            return _sdl_lib, _mix_lib

        # Layer 4: Auto install
        if _linux_auto_install():
            return _sdl_lib, _mix_lib

        # Layer 5: Interactive setup
        if _linux_interactive_setup():
            return _sdl_lib, _mix_lib

        raise ImportError("Failed to load SDL2 libraries on Linux")

    else:
        raise ImportError(f"Unsupported platform: {platform}")


# ============================================================
# SDL2 Constants
# ============================================================

SDL_bool = c_int
SDL_TRUE = 1
SDL_FALSE = 0

SDL_INIT_TIMER = 0x00000001
SDL_INIT_AUDIO = 0x00000010
SDL_INIT_VIDEO = 0x00000020
SDL_INIT_JOYSTICK = 0x00000200
SDL_INIT_HAPTIC = 0x00001000
SDL_INIT_GAMECONTROLLER = 0x00002000
SDL_INIT_EVENTS = 0x00004000
SDL_INIT_EVERYTHING = (SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO |
                       SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC |
                       SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS)

AUDIO_U8 = 0x0008
AUDIO_S8 = 0x8008
AUDIO_U16LSB = 0x0010
AUDIO_S16LSB = 0x8010
AUDIO_U16MSB = 0x1010
AUDIO_S16MSB = 0x9010
AUDIO_U16 = AUDIO_U16LSB
AUDIO_S16 = AUDIO_S16LSB
AUDIO_S32LSB = 0x8020
AUDIO_S32MSB = 0x9020
AUDIO_S32 = AUDIO_S32LSB
AUDIO_F32LSB = 0x8120
AUDIO_F32MSB = 0x9120
AUDIO_F32 = AUDIO_F32LSB

if sys.byteorder == 'little':
    AUDIO_U16SYS = AUDIO_U16LSB
    AUDIO_S16SYS = AUDIO_S16LSB
    AUDIO_S32SYS = AUDIO_S32LSB
    AUDIO_F32SYS = AUDIO_F32LSB
else:
    AUDIO_U16SYS = AUDIO_U16MSB
    AUDIO_S16SYS = AUDIO_S16MSB
    AUDIO_S32SYS = AUDIO_S32MSB
    AUDIO_F32SYS = AUDIO_F32MSB

MIX_DEFAULT_FORMAT = AUDIO_S16SYS

MIX_INIT_FLAC = 0x00000001
MIX_INIT_MOD = 0x00000002
MIX_INIT_MP3 = 0x00000008
MIX_INIT_OGG = 0x00000010
MIX_INIT_MID = 0x00000020
MIX_INIT_OPUS = 0x00000040

MIX_CHANNEL_POST = -2
MIX_DEFAULT_CHANNELS = 2

MUS_NONE = 0
MUS_CMD = 1
MUS_WAV = 2
MUS_MOD = 3
MUS_MID = 4
MUS_OGG = 5
MUS_MP3 = 6
MUS_FLAC = 7
MUS_OPUS = 8


# ============================================================
# SDL2 Structures
# ============================================================

class SDL_AudioSpec(Structure):
    _fields_ = [
        ("freq", c_int),
        ("format", c_uint16),
        ("channels", c_uint8),
        ("silence", c_uint8),
        ("samples", c_uint16),
        ("padding", c_uint16),
        ("size", c_uint32),
        ("callback", c_void_p),
        ("userdata", c_void_p)
    ]


class Mix_Chunk(Structure):
    _fields_ = [
        ("allocated", c_int),
        ("abuf", POINTER(c_uint8)),
        ("alen", c_uint32),
        ("volume", c_uint8)
    ]


# ============================================================
# SDL2 Function Bindings (set up AFTER loading)
# ============================================================

# These are set up after _sdl_lib and _mix_lib are loaded
# We define them as functions that will use the global _sdl_lib and _mix_lib


def SDL_Init(flags):
    return _sdl_lib.SDL_Init(flags)


def SDL_InitSubSystem(flags):
    return _sdl_lib.SDL_InitSubSystem(flags)


def SDL_Quit():
    _sdl_lib.SDL_Quit()


def SDL_QuitSubSystem(flags):
    _sdl_lib.SDL_QuitSubSystem(flags)


def SDL_WasInit(flags):
    return _sdl_lib.SDL_WasInit(flags)


def SDL_GetError():
    return _sdl_lib.SDL_GetError()


def SDL_RWFromFile(file, mode):
    if isinstance(file, str):
        file = file.encode('utf-8')
    if isinstance(mode, str):
        mode = mode.encode('utf-8')
    return _sdl_lib.SDL_RWFromFile(file, mode)


def SDL_Delay(ms):
    _sdl_lib.SDL_Delay(ms)


def Mix_OpenAudio(frequency, format, channels, chunksize):
    return _mix_lib.Mix_OpenAudio(frequency, format, channels, chunksize)


def Mix_CloseAudio():
    _mix_lib.Mix_CloseAudio()


def Mix_QuerySpec(frequency, format, channels):
    return _mix_lib.Mix_QuerySpec(byref(frequency), byref(format), byref(channels))


def Mix_LoadWAV(file):
    if isinstance(file, str):
        file = file.encode('utf-8')
    return _mix_lib.Mix_LoadWAV_RW(SDL_RWFromFile(file, b"rb"), 1)


def Mix_LoadMUS(file):
    if isinstance(file, str):
        file = file.encode('utf-8')
    return _mix_lib.Mix_LoadMUS_RW(SDL_RWFromFile(file, b"rb"), 1)


def Mix_FreeChunk(chunk):
    _mix_lib.Mix_FreeChunk(chunk)


def Mix_FreeMusic(music):
    _mix_lib.Mix_FreeMusic(music)


def Mix_PlayChannel(channel, chunk, loops):
    return _mix_lib.Mix_PlayChannel(channel, chunk, loops)


def Mix_PlayMusic(music, loops):
    return _mix_lib.Mix_PlayMusic(music, loops)


def Mix_Pause(channel):
    _mix_lib.Mix_Pause(channel)


def Mix_PauseMusic():
    _mix_lib.Mix_PauseMusic()


def Mix_Resume(channel):
    _mix_lib.Mix_Resume(channel)


def Mix_ResumeMusic():
    _mix_lib.Mix_ResumeMusic()


def Mix_HaltChannel(channel):
    return _mix_lib.Mix_HaltChannel(channel)


def Mix_HaltMusic():
    return _mix_lib.Mix_HaltMusic()


def Mix_SetMusicPosition(position):
    return _mix_lib.Mix_SetMusicPosition(position)


def Mix_MusicDuration(music):
    return _mix_lib.Mix_MusicDuration(music)


def Mix_Volume(channel, volume):
    return _mix_lib.Mix_Volume(channel, volume)


def Mix_VolumeMusic(volume):
    return _mix_lib.Mix_VolumeMusic(volume)


def Mix_AllocateChannels(numchans):
    return _mix_lib.Mix_AllocateChannels(numchans)


def Mix_GetMusicType(music):
    return _mix_lib.Mix_GetMusicType(music)


def Mix_FadingMusic():
    return _mix_lib.Mix_FadingMusic()


def Mix_FadeInMusic(music, loops, ms):
    return _mix_lib.Mix_FadeInMusic(music, loops, ms)


def Mix_FadeOutMusic(ms):
    return _mix_lib.Mix_FadeOutMusic(ms)


def Mix_FadeInChannel(channel, chunk, loops, ms):
    return _mix_lib.Mix_FadeInChannel(channel, chunk, loops, ms)


def Mix_FadeOutChannel(channel, ms):
    return _mix_lib.Mix_FadeOutChannel(channel, ms)


def Mix_Playing(channel):
    return _mix_lib.Mix_Playing(channel)


def Mix_PlayingMusic():
    return _mix_lib.Mix_PlayingMusic()


def Mix_Paused(channel):
    return _mix_lib.Mix_Paused(channel)


def Mix_PausedMusic():
    return _mix_lib.Mix_PausedMusic()


def Mix_SetPanning(channel, left, right):
    return _mix_lib.Mix_SetPanning(channel, left, right)


def Mix_SetDistance(channel, distance):
    return _mix_lib.Mix_SetDistance(channel, distance)


def Mix_SetPosition(channel, angle, distance):
    return _mix_lib.Mix_SetPosition(channel, angle, distance)


def Mix_SetReverseStereo(channel, flip):
    return _mix_lib.Mix_SetReverseStereo(channel, flip)


def Mix_FadeInMusicPos(music, loops, ms, position):
    return _mix_lib.Mix_FadeInMusicPos(music, loops, ms, position)


# ============================================================
# Function Prototypes (set after libraries are loaded)
# ============================================================

def _setup_prototypes():
    """Set up function prototypes for SDL2 and SDL2_mixer"""
    # SDL function prototypes
    _sdl_lib.SDL_Init.argtypes = [c_uint32]
    _sdl_lib.SDL_Init.restype = c_int

    _sdl_lib.SDL_InitSubSystem.argtypes = [c_uint32]
    _sdl_lib.SDL_InitSubSystem.restype = c_int

    _sdl_lib.SDL_Quit.argtypes = []
    _sdl_lib.SDL_Quit.restype = None

    _sdl_lib.SDL_QuitSubSystem.argtypes = [c_uint32]
    _sdl_lib.SDL_QuitSubSystem.restype = None

    _sdl_lib.SDL_WasInit.argtypes = [c_uint32]
    _sdl_lib.SDL_WasInit.restype = c_uint32

    _sdl_lib.SDL_GetError.argtypes = []
    _sdl_lib.SDL_GetError.restype = c_char_p

    _sdl_lib.SDL_RWFromFile.argtypes = [c_char_p, c_char_p]
    _sdl_lib.SDL_RWFromFile.restype = c_void_p

    _sdl_lib.SDL_Delay.argtypes = [c_uint32]
    _sdl_lib.SDL_Delay.restype = None

    # SDL_mixer function prototypes
    _mix_lib.Mix_OpenAudio.argtypes = [c_int, c_uint16, c_int, c_int]
    _mix_lib.Mix_OpenAudio.restype = c_int

    _mix_lib.Mix_CloseAudio.argtypes = []
    _mix_lib.Mix_CloseAudio.restype = None

    if hasattr(_mix_lib, 'Mix_QuerySpec'):
        _mix_lib.Mix_QuerySpec.argtypes = [POINTER(c_int), POINTER(c_uint16), POINTER(c_int)]
        _mix_lib.Mix_QuerySpec.restype = c_int

    if hasattr(_mix_lib, 'Mix_LoadWAV_RW'):
        _mix_lib.Mix_LoadWAV_RW.argtypes = [c_void_p, c_int]
        _mix_lib.Mix_LoadWAV_RW.restype = POINTER(Mix_Chunk)

    if hasattr(_mix_lib, 'Mix_LoadMUS_RW'):
        _mix_lib.Mix_LoadMUS_RW.argtypes = [c_void_p, c_int]
        _mix_lib.Mix_LoadMUS_RW.restype = c_void_p

    if hasattr(_mix_lib, 'Mix_FreeChunk'):
        _mix_lib.Mix_FreeChunk.argtypes = [POINTER(Mix_Chunk)]
        _mix_lib.Mix_FreeChunk.restype = None

    if hasattr(_mix_lib, 'Mix_FreeMusic'):
        _mix_lib.Mix_FreeMusic.argtypes = [c_void_p]
        _mix_lib.Mix_FreeMusic.restype = None

    if hasattr(_mix_lib, 'Mix_FadeInMusicPos'):
        _mix_lib.Mix_FadeInMusicPos.argtypes = [c_void_p, c_int, c_int, c_double]
        _mix_lib.Mix_FadeInMusicPos.restype = c_int

    if hasattr(_mix_lib, 'Mix_PlayChannel'):
        _mix_lib.Mix_PlayChannel.argtypes = [c_int, POINTER(Mix_Chunk), c_int]
        _mix_lib.Mix_PlayChannel.restype = c_int
    elif hasattr(_mix_lib, 'Mix_PlayChannelTimed'):
        _mix_lib.Mix_PlayChannelTimed.argtypes = [c_int, POINTER(Mix_Chunk), c_int, c_int]
        _mix_lib.Mix_PlayChannelTimed.restype = c_int

    if hasattr(_mix_lib, 'Mix_PlayMusic'):
        _mix_lib.Mix_PlayMusic.argtypes = [c_void_p, c_int]
        _mix_lib.Mix_PlayMusic.restype = c_int

    if hasattr(_mix_lib, 'Mix_Pause'):
        _mix_lib.Mix_Pause.argtypes = [c_int]
        _mix_lib.Mix_Pause.restype = None

    if hasattr(_mix_lib, 'Mix_PauseMusic'):
        _mix_lib.Mix_PauseMusic.argtypes = []
        _mix_lib.Mix_PauseMusic.restype = None

    if hasattr(_mix_lib, 'Mix_Resume'):
        _mix_lib.Mix_Resume.argtypes = [c_int]
        _mix_lib.Mix_Resume.restype = None

    if hasattr(_mix_lib, 'Mix_ResumeMusic'):
        _mix_lib.Mix_ResumeMusic.argtypes = []
        _mix_lib.Mix_ResumeMusic.restype = None

    if hasattr(_mix_lib, 'Mix_HaltChannel'):
        _mix_lib.Mix_HaltChannel.argtypes = [c_int]
        _mix_lib.Mix_HaltChannel.restype = c_int

    if hasattr(_mix_lib, 'Mix_HaltMusic'):
        _mix_lib.Mix_HaltMusic.argtypes = []
        _mix_lib.Mix_HaltMusic.restype = c_int

    if hasattr(_mix_lib, 'Mix_SetMusicPosition'):
        _mix_lib.Mix_SetMusicPosition.argtypes = [c_double]
        _mix_lib.Mix_SetMusicPosition.restype = c_int

    if hasattr(_mix_lib, 'Mix_MusicDuration'):
        _mix_lib.Mix_MusicDuration.argtypes = [c_void_p]
        _mix_lib.Mix_MusicDuration.restype = c_float

    if hasattr(_mix_lib, 'Mix_Volume'):
        _mix_lib.Mix_Volume.argtypes = [c_int, c_int]
        _mix_lib.Mix_Volume.restype = c_int

    if hasattr(_mix_lib, 'Mix_VolumeMusic'):
        _mix_lib.Mix_VolumeMusic.argtypes = [c_int]
        _mix_lib.Mix_VolumeMusic.restype = c_int

    if hasattr(_mix_lib, 'Mix_AllocateChannels'):
        _mix_lib.Mix_AllocateChannels.argtypes = [c_int]
        _mix_lib.Mix_AllocateChannels.restype = c_int

    if hasattr(_mix_lib, 'Mix_GetMusicType'):
        _mix_lib.Mix_GetMusicType.argtypes = [c_void_p]
        _mix_lib.Mix_GetMusicType.restype = c_int

    if hasattr(_mix_lib, 'Mix_FadingMusic'):
        _mix_lib.Mix_FadingMusic.argtypes = []
        _mix_lib.Mix_FadingMusic.restype = c_int

    if hasattr(_mix_lib, 'Mix_FadeInMusic'):
        _mix_lib.Mix_FadeInMusic.argtypes = [c_void_p, c_int, c_int]
        _mix_lib.Mix_FadeInMusic.restype = c_int

    if hasattr(_mix_lib, 'Mix_FadeOutMusic'):
        _mix_lib.Mix_FadeOutMusic.argtypes = [c_int]
        _mix_lib.Mix_FadeOutMusic.restype = c_int

    if hasattr(_mix_lib, 'Mix_FadeInChannel'):
        _mix_lib.Mix_FadeInChannel.argtypes = [c_int, POINTER(Mix_Chunk), c_int, c_int]
        _mix_lib.Mix_FadeInChannel.restype = c_int
    elif hasattr(_mix_lib, 'Mix_FadeInChannelTimed'):
        _mix_lib.Mix_FadeInChannelTimed.argtypes = [c_int, POINTER(Mix_Chunk), c_int, c_int, c_int]
        _mix_lib.Mix_FadeInChannelTimed.restype = c_int

    if hasattr(_mix_lib, 'Mix_FadeOutChannel'):
        _mix_lib.Mix_FadeOutChannel.argtypes = [c_int, c_int]
        _mix_lib.Mix_FadeOutChannel.restype = c_int

    if hasattr(_mix_lib, 'Mix_Playing'):
        _mix_lib.Mix_Playing.argtypes = [c_int]
        _mix_lib.Mix_Playing.restype = c_int

    if hasattr(_mix_lib, 'Mix_PlayingMusic'):
        _mix_lib.Mix_PlayingMusic.argtypes = []
        _mix_lib.Mix_PlayingMusic.restype = c_int

    if hasattr(_mix_lib, 'Mix_Paused'):
        _mix_lib.Mix_Paused.argtypes = [c_int]
        _mix_lib.Mix_Paused.restype = c_int

    if hasattr(_mix_lib, 'Mix_PausedMusic'):
        _mix_lib.Mix_PausedMusic.argtypes = []
        _mix_lib.Mix_PausedMusic.restype = c_int

    if hasattr(_mix_lib, 'Mix_SetPanning'):
        _mix_lib.Mix_SetPanning.argtypes = [c_int, c_uint8, c_uint8]
        _mix_lib.Mix_SetPanning.restype = c_int

    if hasattr(_mix_lib, 'Mix_SetDistance'):
        _mix_lib.Mix_SetDistance.argtypes = [c_int, c_uint8]
        _mix_lib.Mix_SetDistance.restype = c_int

    if hasattr(_mix_lib, 'Mix_SetPosition'):
        _mix_lib.Mix_SetPosition.argtypes = [c_int, c_uint16, c_uint8]
        _mix_lib.Mix_SetPosition.restype = c_int

    if hasattr(_mix_lib, 'Mix_SetReverseStereo'):
        _mix_lib.Mix_SetReverseStereo.argtypes = [c_int, c_int]
        _mix_lib.Mix_SetReverseStereo.restype = c_int


# ============================================================
# Initialize: Load SDL2 and set up prototypes
# ============================================================

# Load SDL2 libraries
_sdl_lib, _mix_lib = import_sdl2()

# Set up function prototypes
_setup_prototypes()
