// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! Data structures for configuring a Python interpreter.

use python3_sys as pyffi;
use std::ffi::CString;

/// Defines which allocator to use for the raw domain.
#[derive(Clone, Debug)]
pub enum PythonRawAllocator {
    /// Use jemalloc.
    Jemalloc,
    /// Use the Rust global allocator.
    Rust,
    /// Use the system allocator.
    System,
}

/// Defines Python code to run.
#[derive(Clone, Debug)]
pub enum PythonRunMode {
    /// No-op.
    None,
    /// Run a Python REPL.
    Repl,
    /// Run a Python module as the main module.
    Module { module: String },
    /// Evaluate Python code from a string.
    Eval { code: String },
}

/// Defines `terminfo`` database resolution semantics.
#[derive(Clone, Debug)]
pub enum TerminfoResolution {
    /// Resolve `terminfo` database using appropriate behavior for current OS.
    Dynamic,
    /// Do not attempt to resolve the `terminfo` database. Basically a no-op.
    None,
    /// Use a specified string as the `TERMINFO_DIRS` value.
    Static(String),
}

/// Defines an extra extension module to load.
#[derive(Clone, Debug)]
pub struct ExtensionModule {
    /// Name of the extension module.
    pub name: CString,

    /// Extension module initialization function.
    pub init_func: unsafe extern "C" fn() -> *mut pyffi::PyObject,
}

/// Holds the configuration of an embedded Python interpreter.
///
/// Instances of this struct can be used to construct Python interpreters.
///
/// Each instance contains the total state to define the run-time behavior of
/// a Python interpreter.
#[derive(Clone, Debug)]
pub struct PythonConfig {
    /// Name of encoding for stdio handles.
    pub standard_io_encoding: Option<String>,

    /// Name of encoding error mode for stdio handles.
    pub standard_io_errors: Option<String>,

    /// Python optimization level.
    pub opt_level: i32,

    /// Whether to load our custom frozen importlib bootstrap modules.
    pub use_custom_importlib: bool,

    /// Whether to load the filesystem-based sys.meta_path finder.
    pub filesystem_importer: bool,

    /// Filesystem paths to add to sys.path.
    ///
    /// ``$ORIGIN`` will resolve to the directory of the application at
    /// run-time.
    pub sys_paths: Vec<String>,

    /// Whether to load the site.py module at initialization time.
    pub import_site: bool,

    /// Whether to load a user-specific site module at initialization time.
    pub import_user_site: bool,

    /// Whether to ignore various PYTHON* environment variables.
    pub ignore_python_env: bool,

    /// Whether to suppress writing of ``.pyc`` files when importing ``.py``
    /// files from the filesystem. This is typically irrelevant since modules
    /// are imported from memory.
    pub dont_write_bytecode: bool,

    /// Whether stdout and stderr streams should be unbuffered.
    pub unbuffered_stdio: bool,

    /// Bytecode for the importlib._bootstrap / _frozen_importlib module.
    pub frozen_importlib_data: &'static [u8],

    /// Bytecode for the importlib._bootstrap_external / _frozen_importlib_external module.
    pub frozen_importlib_external_data: &'static [u8],

    /// Reference to raw Python modules data.
    ///
    /// The referenced data is produced as part of PyOxidizer packaging. This
    /// likely comes from an include_bytes!(...) of a file generated by PyOxidizer.
    pub py_modules_data: &'static [u8],

    /// Reference to raw Python resources data.
    ///
    /// The referenced data is produced as part of PyOxidizer packaging. This
    /// likely comes from an include_bytes!(...) of a file generated by PyOxidizer.
    pub py_resources_data: &'static [u8],

    /// Extra extension modules to make available to the interpreter.
    ///
    /// The values will effectively be passed to ``PyImport_ExtendInitTab()``.
    pub extra_extension_modules: Vec<ExtensionModule>,

    /// Whether to set sys.argvb with bytes versions of process arguments.
    ///
    /// On Windows, bytes will be UTF-16. On POSIX, bytes will be raw char*
    /// values passed to `int main()`.
    pub argvb: bool,

    /// Which memory allocator to use for the raw domain.
    pub raw_allocator: PythonRawAllocator,

    /// How to resolve the `terminfo` database.
    pub terminfo_resolution: TerminfoResolution,

    /// Environment variable holding the directory to write a loaded modules file.
    ///
    /// If this value is set and the environment it refers to is set,
    /// on interpreter shutdown, we will write a ``modules-<random>`` file to
    /// the directory specified containing a ``\n`` delimited list of modules
    /// loaded in ``sys.modules``.
    pub write_modules_directory_env: Option<String>,

    /// Defines what code to run by default.
    ///
    pub run: PythonRunMode,
}
