use std::{
    cell::{Cell, RefCell},
    collections::HashMap,
    rc::Rc,
    sync::{Arc, Mutex},
    thread::{self, JoinHandle},
};

use bevy::{
    asset::Asset,
    ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel},
    math::Vec3,
    reflect::TypePath,
};
use deno_core::{JsRuntime as DenoRuntime, RuntimeOptions};

pub use deno_core::v8;
use serde::Deserialize;

use crate::{
    ENTITY_VAR_NAME, FuncArgs, Runtime, ScriptingError,
    assets::GetExtensions,
    callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine},
    promise::Promise,
};

/// Boxed registered callback function as stored on the JavaScript thread.
type CallbackFn =
    dyn Fn((), Vec<JsValue>) -> Result<Promise<(), JsValue>, ScriptingError> + 'static;

/// A queue of value ids that have been dropped on another thread and need to be
/// removed from the value registry the next time the JavaScript thread runs.
type FreeQueue = Arc<Mutex<Vec<u64>>>;

thread_local! {
    /// Registry of live `v8::Global` values, keyed by the id carried in a [JsValue].
    static REGISTRY: RefCell<HashMap<u64, v8::Global<v8::Value>>> = RefCell::new(HashMap::new());
    /// Registry of pending promises, keyed by the id stored in the script-side
    /// promise object.
    static PROMISES: RefCell<HashMap<u64, Promise<(), JsValue>>> = RefCell::new(HashMap::new());
    /// Registered Rust callback functions, keyed by their script-visible name.
    static CALLBACKS: RefCell<HashMap<String, Rc<CallbackFn>>> = RefCell::new(HashMap::new());
    static NEXT_VALUE_ID: Cell<u64> = const { Cell::new(0) };
    static NEXT_PROMISE_ID: Cell<u64> = const { Cell::new(0) };
    static FREE_QUEUE: FreeQueue = Arc::new(Mutex::new(Vec::new()));
}

/// A handle to a value living inside the JavaScript engine.
///
/// The actual `v8::Global` is kept in a thread-local registry on the JavaScript
/// thread - this handle only carries an id (plus a shared free-list so it can be
/// cleaned up when dropped from any thread), which makes it cheap to `Clone` and
/// safe to `Send`.
#[derive(Clone)]
pub struct JsValue(Arc<JsValueInner>);

struct JsValueInner {
    id: u64,
    free: FreeQueue,
}

impl Drop for JsValueInner {
    fn drop(&mut self) {
        if let Ok(mut queue) = self.free.lock() {
            queue.push(self.id);
        }
    }
}

/// Removes any values that were dropped on other threads from the registry.
/// Must be called on the JavaScript thread while the isolate is entered.
fn drain_free_queue() {
    let ids: Vec<u64> = FREE_QUEUE.with(|queue| {
        queue
            .lock()
            .map(|mut queue| queue.drain(..).collect())
            .unwrap_or_default()
    });
    if !ids.is_empty() {
        REGISTRY.with(|registry| {
            let mut registry = registry.borrow_mut();
            for id in ids {
                registry.remove(&id);
            }
        });
    }
}

/// Stores a `v8::Local` value in the registry and returns a [JsValue] handle for it.
fn store_value(scope: &mut v8::PinScope, local: v8::Local<v8::Value>) -> JsValue {
    let global = v8::Global::new(scope, local);
    let id = NEXT_VALUE_ID.with(|next| {
        let id = next.get();
        next.set(id + 1);
        id
    });
    REGISTRY.with(|registry| registry.borrow_mut().insert(id, global));
    let free = FREE_QUEUE.with(|queue| queue.clone());
    JsValue(Arc::new(JsValueInner { id, free }))
}

/// Loads the `v8::Local` value referenced by a [JsValue] handle.
fn load_value<'s>(scope: &mut v8::PinScope<'s, '_>, value: &JsValue) -> v8::Local<'s, v8::Value> {
    let global = REGISTRY
        .with(|registry| registry.borrow().get(&value.0.id).cloned())
        .expect("JsValue referenced a value that is no longer in the registry");
    v8::Local::new(scope, global)
}

fn v8_string<'s>(scope: &mut v8::PinScope<'s, '_>, value: &str) -> v8::Local<'s, v8::Value> {
    v8::String::new(scope, value)
        .expect("Failed to allocate JavaScript string")
        .into()
}

fn global_object<'s>(scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Object> {
    scope.get_current_context().global(scope)
}

fn set_entity(scope: &mut v8::PinScope, entity: Entity) {
    let global = global_object(scope);
    let key = v8_string(scope, ENTITY_VAR_NAME);
    let value = BevyEntity(entity).to_v8(scope);
    global.set(scope, key, value);
}

fn clear_entity(scope: &mut v8::PinScope) {
    let global = global_object(scope);
    let key = v8_string(scope, ENTITY_VAR_NAME);
    let undefined: v8::Local<v8::Value> = v8::undefined(scope).into();
    global.set(scope, key, undefined);
}

/// Wraps a bevy_scriptum [Promise] into a script-side object exposing `and_then`.
fn promise_to_v8<'s>(
    scope: &mut v8::PinScope<'s, '_>,
    promise: Promise<(), JsValue>,
) -> v8::Local<'s, v8::Value> {
    let id = NEXT_PROMISE_ID.with(|next| {
        let id = next.get();
        next.set(id + 1);
        id
    });
    PROMISES.with(|promises| promises.borrow_mut().insert(id, promise));

    let object = v8::Object::new(scope);
    let key = v8_string(scope, "and_then");
    let data: v8::Local<v8::Value> = v8::Number::new(scope, id as f64).into();
    let function = v8::Function::builder(and_then_callback)
        .data(data)
        .build(scope)
        .expect("Failed to create and_then function");
    object.set(scope, key, function.into());
    object.into()
}

/// `and_then` method on script-side promise objects.
fn and_then_callback(
    scope: &mut v8::PinScope,
    args: v8::FunctionCallbackArguments,
    mut rv: v8::ReturnValue<v8::Value>,
) {
    let id = args
        .data()
        .number_value(scope)
        .expect("Promise object is missing its id") as u64;
    let callback = store_value(scope, args.get(0));

    let following = PROMISES.with(|promises| {
        let mut promises = promises.borrow_mut();
        let promise = promises
            .get_mut(&id)
            .expect("Promise referenced by script no longer exists");
        promise.then(callback)
    });

    let object = promise_to_v8(scope, following);
    rv.set(object);
}

/// Callback invoked by V8 for every registered Rust function.
fn native_callback(
    scope: &mut v8::PinScope,
    args: v8::FunctionCallbackArguments,
    mut rv: v8::ReturnValue<v8::Value>,
) {
    let name = args.data().to_rust_string_lossy(scope);

    let mut params = Vec::with_capacity(args.length() as usize);
    for i in 0..args.length() {
        params.push(store_value(scope, args.get(i)));
    }

    let callback = CALLBACKS.with(|callbacks| callbacks.borrow().get(&name).cloned());
    if let Some(callback) = callback {
        let promise = callback((), params).expect("Failed to call registered function");
        let object = promise_to_v8(scope, promise);
        rv.set(object);
    }
}

fn print_callback(
    scope: &mut v8::PinScope,
    args: v8::FunctionCallbackArguments,
    _rv: v8::ReturnValue<v8::Value>,
) {
    let mut parts = Vec::with_capacity(args.length() as usize);
    for i in 0..args.length() {
        parts.push(args.get(i).to_rust_string_lossy(scope));
    }
    println!("{}", parts.join(" "));
}

/// `Vec3(x, y, z)` global constructor available to all scripts.
fn vec3_callback(
    scope: &mut v8::PinScope,
    args: v8::FunctionCallbackArguments,
    mut rv: v8::ReturnValue<v8::Value>,
) {
    let component = |scope: &mut v8::PinScope, index: i32| {
        args.get(index).number_value(scope).unwrap_or(0.0) as f32
    };
    let x = component(scope, 0);
    let y = component(scope, 1);
    let z = component(scope, 2);
    let value = BevyVec3::new(x, y, z).to_v8(scope);
    rv.set(value);
}

/// Registers a global function on the engine's global object.
fn register_global_function(
    scope: &mut v8::PinScope,
    name: &str,
    callback: impl v8::MapFnTo<v8::FunctionCallback>,
) {
    let global = global_object(scope);
    let key = v8_string(scope, name);
    let function = v8::Function::new(scope, callback)
        .unwrap_or_else(|| panic!("Failed to create `{name}` function"));
    global.set(scope, key, function.into());
}

/// Calls a global script function by name with the provided arguments.
fn call_global_fn(
    scope: &mut v8::PinScope,
    name: &str,
    args: &[JsValue],
) -> Result<JsValue, ScriptingError> {
    let global = global_object(scope);
    let key = v8_string(scope, name);
    let value = global
        .get(scope, key)
        .ok_or_else(|| ScriptingError::RuntimeError(format!("function `{name}` not found")))?;
    let function = value
        .try_cast::<v8::Function>()
        .map_err(|_| ScriptingError::RuntimeError(format!("`{name}` is not a function")))?;
    let arguments: Vec<v8::Local<v8::Value>> =
        args.iter().map(|arg| load_value(scope, arg)).collect();
    call_function(scope, function, &arguments)
}

/// Calls a `v8::Function` with the provided arguments, capturing runtime errors.
fn call_function(
    scope: &mut v8::PinScope,
    function: v8::Local<v8::Function>,
    arguments: &[v8::Local<v8::Value>],
) -> Result<JsValue, ScriptingError> {
    let recv: v8::Local<v8::Value> = v8::undefined(scope).into();
    v8::tc_scope!(let scope, scope);
    match function.call(scope, recv, arguments) {
        Some(result) => Ok(store_value(scope, result)),
        None => {
            let message = scope
                .exception()
                .map(|exception| exception.to_rust_string_lossy(scope))
                .unwrap_or_else(|| String::from("unknown JavaScript error"));
            Err(ScriptingError::RuntimeError(message))
        }
    }
}

/// The JavaScript engine - a thin wrapper around a deno_core [JsRuntime] that
/// allows obtaining a V8 scope through a shared reference (the rest of
/// bevy_scriptum only hands out `&RawEngine`).
pub struct JsEngine {
    runtime: RefCell<DenoRuntime>,
}

impl JsEngine {
    fn new() -> Self {
        let engine = Self {
            runtime: RefCell::new(DenoRuntime::new(RuntimeOptions::default())),
        };
        engine.with_scope(|scope| {
            register_global_function(scope, "print", print_callback);
            register_global_function(scope, "Vec3", vec3_callback);
        });
        engine
    }

    /// Runs `f` with a V8 handle/context scope ready for use.
    pub fn with_scope<R>(&self, f: impl FnOnce(&mut v8::PinScope) -> R) -> R {
        let mut runtime = self.runtime.borrow_mut();
        let context = runtime.main_context();
        let isolate = &mut *runtime.v8_isolate();
        v8::scope!(let scope, isolate);
        let context = v8::Local::new(scope, context);
        let scope = &mut v8::ContextScope::new(scope, context);
        drain_free_queue();
        f(scope)
    }
}

#[derive(Resource)]
pub struct JsRuntime {
    thread: Option<JsThread>,
}

impl Default for JsRuntime {
    fn default() -> Self {
        Self {
            thread: Some(JsThread::spawn()),
        }
    }
}

impl JsRuntime {
    fn execute_in_thread<T: Send + 'static>(
        &self,
        f: impl FnOnce(&mut JsEngine) -> T + Send + 'static,
    ) -> T {
        self.thread
            .as_ref()
            .expect("JavaScript thread is gone")
            .execute(Box::new(f))
    }

    /// Runs `f` with a V8 scope on the JavaScript thread. Useful for directly
    /// interacting with the engine, e.g. from tests.
    pub fn with_scope<T: Send + 'static>(
        &self,
        f: impl FnOnce(&mut v8::PinScope) -> T + Send + 'static,
    ) -> T {
        self.execute_in_thread(move |engine| engine.with_scope(f))
    }
}

type JsClosure = Box<dyn FnOnce(&mut JsEngine) + Send>;

/// A dedicated thread owning the (non-`Send`) V8 isolate.
struct JsThread {
    sender: Option<crossbeam_channel::Sender<JsClosure>>,
    handle: Option<JoinHandle<()>>,
}

impl JsThread {
    fn spawn() -> Self {
        let (sender, receiver) = crossbeam_channel::unbounded::<JsClosure>();

        let handle = thread::spawn(move || {
            let mut engine = JsEngine::new();
            while let Ok(f) = receiver.recv() {
                f(&mut engine);
            }
        });

        JsThread {
            sender: Some(sender),
            handle: Some(handle),
        }
    }

    fn execute<T: Send + 'static>(&self, f: Box<dyn FnOnce(&mut JsEngine) -> T + Send>) -> T {
        let (return_sender, return_receiver) = crossbeam_channel::bounded(1);
        self.sender
            .as_ref()
            .expect("JavaScript thread sender is gone")
            .send(Box::new(move |engine| {
                let _ = return_sender.send(f(engine));
            }))
            .expect("Failed to send execution unit to JavaScript thread");
        return_receiver
            .recv()
            .expect("Failed to receive callback return value from JavaScript thread")
    }
}

impl Drop for JsThread {
    fn drop(&mut self) {
        drop(self.sender.take());
        if let Some(handle) = self.handle.take() {
            let _ = handle.join();
        }
    }
}

#[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)]
pub struct JsSchedule;

#[derive(Asset, Debug, Deserialize, TypePath)]
pub struct JsScript(pub String);

impl GetExtensions for JsScript {
    fn extensions() -> &'static [&'static str] {
        &["js"]
    }
}

impl From<String> for JsScript {
    fn from(value: String) -> Self {
        Self(value)
    }
}

#[derive(Component)]
pub struct JsScriptData;

#[derive(Debug, Clone, Copy)]
pub struct BevyEntity(pub Entity);

impl BevyEntity {
    pub fn index(&self) -> u32 {
        self.0.index_u32()
    }
}

#[derive(Debug, Clone, Copy)]
pub struct BevyVec3(pub Vec3);

impl BevyVec3 {
    pub fn new(x: f32, y: f32, z: f32) -> Self {
        Self(Vec3::new(x, y, z))
    }

    pub fn x(&self) -> f32 {
        self.0.x
    }

    pub fn y(&self) -> f32 {
        self.0.y
    }

    pub fn z(&self) -> f32 {
        self.0.z
    }
}

impl Runtime for JsRuntime {
    type Schedule = JsSchedule;
    type ScriptAsset = JsScript;
    type ScriptData = JsScriptData;
    type CallContext = ();
    type Value = JsValue;
    type RawEngine = JsEngine;

    fn with_engine_send_mut<T: Send + 'static>(
        &mut self,
        f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static,
    ) -> T {
        self.execute_in_thread(f)
    }

    fn with_engine_send<T: Send + 'static>(
        &self,
        f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static,
    ) -> T {
        self.execute_in_thread(move |engine| f(engine))
    }

    fn with_engine_mut<T>(&mut self, _f: impl FnOnce(&mut Self::RawEngine) -> T) -> T {
        unimplemented!(
            "JavaScript runtime requires sending execution to another thread, use `with_engine_send_mut`"
        );
    }

    fn with_engine<T>(&self, _f: impl FnOnce(&Self::RawEngine) -> T) -> T {
        unimplemented!(
            "JavaScript runtime requires sending execution to another thread, use `with_engine_send`"
        );
    }

    fn eval(
        &self,
        script: &Self::ScriptAsset,
        entity: Entity,
    ) -> Result<Self::ScriptData, ScriptingError> {
        let source = script.0.clone();
        self.execute_in_thread(move |engine| {
            engine.with_scope(|scope| set_entity(scope, entity));
            let result = engine
                .runtime
                .borrow_mut()
                .execute_script("[bevy_scriptum]", source);
            engine.with_scope(clear_entity);
            result
                .map(|_| JsScriptData)
                .map_err(|e| ScriptingError::RuntimeError(e.to_string()))
        })
    }

    fn register_fn(
        &mut self,
        name: String,
        _arg_types: Vec<std::any::TypeId>,
        f: impl Fn(
            Self::CallContext,
            Vec<Self::Value>,
        ) -> Result<Promise<Self::CallContext, Self::Value>, ScriptingError>
        + Send
        + Sync
        + 'static,
    ) -> Result<(), ScriptingError> {
        self.execute_in_thread(move |engine| {
            CALLBACKS.with(|callbacks| {
                callbacks.borrow_mut().insert(name.clone(), Rc::new(f));
            });
            engine.with_scope(|scope| {
                let global = global_object(scope);
                let key = v8_string(scope, &name);
                let data = v8_string(scope, &name);
                let function = v8::Function::builder(native_callback)
                    .data(data)
                    .build(scope)
                    .expect("Failed to create registered function");
                global.set(scope, key, function.into());
            });
        });
        Ok(())
    }

    fn call_fn(
        &self,
        name: &str,
        _script_data: &mut Self::ScriptData,
        entity: Entity,
        args: impl for<'a> FuncArgs<'a, Self::Value, Self> + Send + 'static,
    ) -> Result<Self::Value, ScriptingError> {
        let name = name.to_string();
        self.execute_in_thread(move |engine| {
            let args = args.parse(engine);
            engine.with_scope(|scope| {
                set_entity(scope, entity);
                let result = call_global_fn(scope, &name, &args);
                clear_entity(scope);
                result
            })
        })
    }

    fn call_fn_from_value(
        &self,
        value: &Self::Value,
        _context: &Self::CallContext,
        args: Vec<Self::Value>,
    ) -> Result<Self::Value, ScriptingError> {
        let value = value.clone();
        self.execute_in_thread(move |engine| {
            engine.with_scope(|scope| {
                let local = load_value(scope, &value);
                let function = local.try_cast::<v8::Function>().map_err(|_| {
                    ScriptingError::RuntimeError(String::from("value is not a function"))
                })?;
                let arguments: Vec<v8::Local<v8::Value>> =
                    args.iter().map(|arg| load_value(scope, arg)).collect();
                call_function(scope, function, &arguments)
            })
        })
    }
}

/// Conversion from a Rust value into a V8 value.
trait ToV8 {
    fn to_v8<'s>(self, scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Value>;
}

/// Conversion from a V8 value into a Rust value.
trait FromV8 {
    fn from_v8(scope: &mut v8::PinScope, value: v8::Local<v8::Value>) -> Self;
}

macro_rules! impl_number {
    ($($t:ty),*) => {$(
        impl ToV8 for $t {
            fn to_v8<'s>(self, scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Value> {
                v8::Number::new(scope, self as f64).into()
            }
        }

        impl FromV8 for $t {
            fn from_v8(scope: &mut v8::PinScope, value: v8::Local<v8::Value>) -> Self {
                value.number_value(scope).unwrap_or(0.0) as $t
            }
        }
    )*};
}

impl_number!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64);

impl ToV8 for String {
    fn to_v8<'s>(self, scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Value> {
        v8_string(scope, &self)
    }
}

impl FromV8 for String {
    fn from_v8(scope: &mut v8::PinScope, value: v8::Local<v8::Value>) -> Self {
        value.to_rust_string_lossy(scope)
    }
}

impl ToV8 for bool {
    fn to_v8<'s>(self, scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Value> {
        v8::Boolean::new(scope, self).into()
    }
}

impl FromV8 for bool {
    fn from_v8(scope: &mut v8::PinScope, value: v8::Local<v8::Value>) -> Self {
        value.boolean_value(scope)
    }
}

impl ToV8 for () {
    fn to_v8<'s>(self, scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Value> {
        v8::undefined(scope).into()
    }
}

impl FromV8 for () {
    fn from_v8(_scope: &mut v8::PinScope, _value: v8::Local<v8::Value>) -> Self {}
}

impl ToV8 for BevyEntity {
    fn to_v8<'s>(self, scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Value> {
        let object = v8::Object::new(scope);

        let key = v8_string(scope, "index");
        let value: v8::Local<v8::Value> =
            v8::Integer::new_from_unsigned(scope, self.index()).into();
        object.set(scope, key, value);

        let key = v8_string(scope, "__entity_bits");
        let value: v8::Local<v8::Value> = v8::BigInt::new_from_u64(scope, self.0.to_bits()).into();
        object.set(scope, key, value);

        object.into()
    }
}

impl FromV8 for BevyEntity {
    fn from_v8(scope: &mut v8::PinScope, value: v8::Local<v8::Value>) -> Self {
        let object = value.to_object(scope).expect("entity is not an object");
        let key = v8_string(scope, "__entity_bits");
        let bits = object
            .get(scope, key)
            .expect("entity object is missing `__entity_bits`")
            .try_cast::<v8::BigInt>()
            .expect("`__entity_bits` is not a BigInt");
        let (bits, _) = bits.u64_value();
        BevyEntity(Entity::from_bits(bits))
    }
}

impl ToV8 for BevyVec3 {
    fn to_v8<'s>(self, scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Value> {
        let object = v8::Object::new(scope);
        for (name, component) in [("x", self.0.x), ("y", self.0.y), ("z", self.0.z)] {
            let key = v8_string(scope, name);
            let value: v8::Local<v8::Value> = v8::Number::new(scope, component as f64).into();
            object.set(scope, key, value);
        }
        object.into()
    }
}

impl FromV8 for BevyVec3 {
    fn from_v8(scope: &mut v8::PinScope, value: v8::Local<v8::Value>) -> Self {
        let object = value.to_object(scope).expect("Vec3 is not an object");
        let read = |scope: &mut v8::PinScope, name: &str| {
            let key = v8_string(scope, name);
            object
                .get(scope, key)
                .and_then(|value| value.number_value(scope))
                .unwrap_or(0.0) as f32
        };
        let x = read(scope, "x");
        let y = read(scope, "y");
        let z = read(scope, "z");
        BevyVec3(Vec3::new(x, y, z))
    }
}

impl<'a, T: ToV8> IntoRuntimeValueWithEngine<'a, T, JsRuntime> for T {
    fn into_runtime_value_with_engine(value: T, engine: &'a JsEngine) -> JsValue {
        engine.with_scope(|scope| {
            let local = value.to_v8(scope);
            store_value(scope, local)
        })
    }
}

impl<'a, T: FromV8> FromRuntimeValueWithEngine<'a, JsRuntime> for T {
    fn from_runtime_value_with_engine(value: JsValue, engine: &'a JsEngine) -> Self {
        engine.with_scope(|scope| {
            let local = load_value(scope, &value);
            T::from_v8(scope, local)
        })
    }
}

impl FuncArgs<'_, JsValue, JsRuntime> for () {
    fn parse(self, _engine: &JsEngine) -> Vec<JsValue> {
        Vec::new()
    }
}

impl<T: ToV8> FuncArgs<'_, JsValue, JsRuntime> for Vec<T> {
    fn parse(self, engine: &JsEngine) -> Vec<JsValue> {
        engine.with_scope(|scope| {
            self.into_iter()
                .map(|value| {
                    let local = value.to_v8(scope);
                    store_value(scope, local)
                })
                .collect()
        })
    }
}

pub mod prelude {
    pub use super::{BevyEntity, BevyVec3, JsRuntime, JsScript, JsScriptData};
}

macro_rules! impl_tuple {
    ($($idx:tt $t:tt),+) => {
        impl<$($t: ToV8,)+> FuncArgs<'_, JsValue, JsRuntime> for ($($t,)+) {
            fn parse(self, engine: &JsEngine) -> Vec<JsValue> {
                engine.with_scope(|scope| {
                    vec![
                        $({
                            let local = self.$idx.to_v8(scope);
                            store_value(scope, local)
                        },)+
                    ]
                })
            }
        }
    };
}

impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X, 24 Y, 25 Z);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X, 24 Y);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
impl_tuple!(0 A, 1 B, 2 C, 3 D);
impl_tuple!(0 A, 1 B, 2 C);
impl_tuple!(0 A, 1 B);
impl_tuple!(0 A);
