Skip to main content

ry_gfx/
lib.rs

1//! # RyDit Graphics Layer (rydit-gfx)
2//!
3//! **Sincronización entre Rust (arquitecto) y Raylib (pincel)**
4//!
5//! Esta capa abstrae las inconsistencias de la API de raylib-rs,
6//! proporcionando una interfaz consistente para RyDit.
7//!
8//! ## Filosofía
9//!
10//! - **Rust = Arquitecto**: Controla el game loop, input, decisiones
11//! - **Raylib = Pincel**: Solo ejecuta dibujos
12//! - **rydit-gfx = Puente**: Sincroniza ambos mundos
13//!
14//! ## Ejemplo de Uso
15//!
16//! ```rust,no_run
17//! use ry_gfx::{RyditGfx, ColorRydit, Key};
18//!
19//! fn main() {
20//!     let mut gfx = RyditGfx::new("Mi Juego RyDit", 800, 600);
21//!
22//!     while !gfx.should_close() {
23//!         gfx.begin_draw();
24//!
25//!         gfx.clear_background(ColorRydit::Negro);
26//!         gfx.draw_circle(400, 300, 50, ColorRydit::Rojo);
27//!         gfx.draw_rect(100, 100, 100, 100, ColorRydit::Verde);
28//!
29//!         if gfx.is_key_pressed(Key::Escape) {
30//!             break;
31//!         }
32//!
33//!         gfx.end_draw();
34//!     }
35//! }
36//! ```
37
38#![allow(clippy::too_many_arguments)]
39
40// Módulo de partículas v0.5.3
41pub mod particles;
42
43// Módulo de cámara 2D v0.9.0
44pub mod camera;
45
46// Módulo de debug log v0.8.5
47pub mod debug_log;
48
49// Módulo de render queue v0.9.0 - Command Queue + Double Buffering + Platform Sync
50pub mod render_queue;
51
52// Módulo de ECS render v0.10.0 - ECS + rlgl integration
53// 🗑️ ecs_render eliminado v0.13.1 — usaba ry-ecs (eliminado)
54
55// Módulo de GPU Instancing v0.10.1 - FFI OpenGL + Shaders GLSL
56pub mod gpu_instancing;
57
58// Módulo de Input SDL2 v0.10.4 - Eventos para Termux-X11/Android
59pub mod input_sdl2;
60
61// Módulo de Backend SDL2 v0.10.6 - Ventana + OpenGL + Assets
62pub mod backend_sdl2;
63
64// Módulo de Audio SDL2 v0.10.8 - SDL2_mixer (pendiente)
65pub mod audio_sdl2;
66
67// Módulo de Fuentes SDL2 v0.10.8 - SDL2_ttf (pendiente)
68pub mod font_sdl2;
69
70// Módulo de FFI Nativo SDL2 v0.10.8 - Texturas, Audio, Fuentes (nativo)
71pub mod sdl2_ffi;
72
73// 🆕 UI Toolkit v0.11.0 - Botones, Labels, Paneles
74pub mod toolkit;
75
76// 🆕 FSR 1.0 v0.11.4 - FidelityFX Super Resolution (Upscale + Sharpen)
77pub mod fsr;
78
79use raylib::consts::KeyboardKey;
80use raylib::prelude::*;
81
82// Importar migui para implementar el backend
83#[cfg(feature = "migui")]
84use migui::{Color as MiguiColor, MiguiBackend, Rect as MiguiRect};
85
86// SDL2 para carga de texturas
87
88// ============================================================================
89// AUDIO SYSTEM - v0.5.2
90// ============================================================================
91
92use std::collections::HashMap;
93use std::ffi::CString;
94use std::str::FromStr;
95
96/// Sistema de audio con raylib
97/// Nota: Sound y Music son structs FFI que contienen pointers internos
98pub struct AudioSystem {
99    initialized: bool,
100    sounds: HashMap<String, raylib::ffi::Sound>,
101    music: Option<raylib::ffi::Music>,
102}
103
104impl AudioSystem {
105    /// Inicializar sistema de audio
106    pub fn new() -> Self {
107        unsafe {
108            raylib::ffi::InitAudioDevice();
109            println!("[AUDIO] Dispositivo de audio inicializado");
110        }
111        Self {
112            initialized: true,
113            sounds: HashMap::new(),
114            music: None,
115        }
116    }
117
118    /// Cargar sonido desde archivo
119    pub fn load_sound(&mut self, id: &str, path: &str) -> Result<(), String> {
120        if !self.initialized {
121            return Err("Audio no inicializado".into());
122        }
123
124        let c_path = CString::new(path).map_err(|e| format!("Error en path: {}", e))?;
125
126        unsafe {
127            let sound = raylib::ffi::LoadSound(c_path.as_ptr());
128            // Verificamos que el buffer sea válido
129            if !sound.stream.buffer.is_null() || sound.frameCount > 0 {
130                println!("[AUDIO] Sonido '{}' cargado: {}", id, path);
131                self.sounds.insert(id.to_string(), sound);
132                Ok(())
133            } else {
134                Err(format!("Error cargando sonido '{}'", path))
135            }
136        }
137    }
138
139    /// Reproducir sonido
140    pub fn play_sound(&self, id: &str) -> bool {
141        if let Some(sound) = self.sounds.get(id) {
142            unsafe {
143                raylib::ffi::PlaySound(*sound);
144            }
145            true
146        } else {
147            false
148        }
149    }
150
151    /// Detener sonido
152    pub fn stop_sound(&self, id: &str) -> bool {
153        if let Some(sound) = self.sounds.get(id) {
154            unsafe {
155                raylib::ffi::StopSound(*sound);
156            }
157            true
158        } else {
159            false
160        }
161    }
162
163    /// Configurar volumen de sonido (0.0 - 1.0)
164    pub fn set_sound_volume(&self, id: &str, volume: f32) -> bool {
165        if let Some(sound) = self.sounds.get(id) {
166            unsafe {
167                raylib::ffi::SetSoundVolume(*sound, volume);
168            }
169            true
170        } else {
171            false
172        }
173    }
174
175    /// Cargar música desde archivo
176    pub fn load_music(&mut self, path: &str) -> Result<(), String> {
177        if !self.initialized {
178            return Err("Audio no inicializado".into());
179        }
180
181        let c_path = CString::new(path).map_err(|e| format!("Error en path: {}", e))?;
182
183        unsafe {
184            let music = raylib::ffi::LoadMusicStream(c_path.as_ptr());
185            // Verificamos que el buffer sea válido
186            if !music.stream.buffer.is_null() || music.frameCount > 0 {
187                println!("[AUDIO] Música cargada: {}", path);
188                self.music = Some(music);
189                Ok(())
190            } else {
191                Err(format!("Error cargando música '{}'", path))
192            }
193        }
194    }
195
196    /// Reproducir música
197    pub fn play_music(&mut self) {
198        if let Some(ref music) = self.music {
199            unsafe {
200                raylib::ffi::PlayMusicStream(*music);
201            }
202            println!("[AUDIO] Reproduciendo música");
203        }
204    }
205
206    /// Detener música
207    pub fn stop_music(&mut self) {
208        if let Some(ref music) = self.music {
209            unsafe {
210                raylib::ffi::StopMusicStream(*music);
211            }
212            println!("[AUDIO] Música detenida");
213        }
214    }
215
216    /// Actualizar música (llamar en cada frame)
217    pub fn update_music(&mut self) {
218        if let Some(ref music) = self.music {
219            unsafe {
220                raylib::ffi::UpdateMusicStream(*music);
221            }
222        }
223    }
224
225    /// Configurar volumen de música (0.0 - 1.0)
226    pub fn set_music_volume(&mut self, volume: f32) {
227        if let Some(ref music) = self.music {
228            unsafe {
229                raylib::ffi::SetMusicVolume(*music, volume);
230            }
231        }
232    }
233
234    /// Verificar si la música está reproduciendo
235    pub fn is_music_playing(&self) -> bool {
236        if let Some(ref music) = self.music {
237            unsafe { raylib::ffi::IsMusicStreamPlaying(*music) }
238        } else {
239            false
240        }
241    }
242
243    /// Descargar sonido y liberar memoria
244    pub fn unload_sound(&mut self, id: &str) {
245        if let Some(sound) = self.sounds.remove(id) {
246            unsafe {
247                raylib::ffi::UnloadSound(sound);
248            }
249            println!("[AUDIO] Sonido '{}' descargado", id);
250        }
251    }
252
253    /// Descargar música y liberar memoria
254    pub fn unload_music(&mut self) {
255        if let Some(music) = self.music.take() {
256            unsafe {
257                raylib::ffi::UnloadMusicStream(music);
258            }
259            println!("[AUDIO] Música descargada");
260        }
261    }
262
263    /// Verificar si existe un sonido
264    pub fn has_sound(&self, id: &str) -> bool {
265        self.sounds.contains_key(id)
266    }
267
268    /// Cantidad de sonidos cargados
269    pub fn sound_count(&self) -> usize {
270        self.sounds.len()
271    }
272}
273
274impl Default for AudioSystem {
275    fn default() -> Self {
276        Self::new()
277    }
278}
279
280impl Drop for AudioSystem {
281    fn drop(&mut self) {
282        println!("[AUDIO] Cerrando sistema de audio...");
283        // Descargar todos los sonidos
284        for (_, sound) in self.sounds.drain() {
285            unsafe {
286                raylib::ffi::UnloadSound(sound);
287            }
288        }
289        // Descargar música
290        if let Some(music) = self.music.take() {
291            unsafe {
292                raylib::ffi::UnloadMusicStream(music);
293            }
294        }
295        // Cerrar dispositivo
296        if self.initialized {
297            unsafe {
298                raylib::ffi::CloseAudioDevice();
299            }
300            println!("[AUDIO] Dispositivo cerrado");
301        }
302    }
303}
304
305// Colores manuales (raylib nobuild no incluye colors::prelude)
306pub const RED: Color = Color {
307    r: 230,
308    g: 41,
309    b: 55,
310    a: 255,
311};
312pub const GREEN: Color = Color {
313    r: 117,
314    g: 203,
315    b: 100,
316    a: 255,
317};
318pub const BLUE: Color = Color {
319    r: 51,
320    g: 122,
321    b: 206,
322    a: 255,
323};
324pub const YELLOW: Color = Color {
325    r: 253,
326    g: 249,
327    b: 0,
328    a: 255,
329};
330pub const WHITE: Color = Color {
331    r: 255,
332    g: 255,
333    b: 255,
334    a: 255,
335};
336pub const BLACK: Color = Color {
337    r: 0,
338    g: 0,
339    b: 0,
340    a: 255,
341};
342pub const MAGENTA: Color = Color {
343    r: 255,
344    g: 0,
345    b: 255,
346    a: 255,
347};
348pub const PINK: Color = Color {
349    r: 255,
350    g: 192,
351    b: 203,
352    a: 255,
353};
354pub const ORANGE: Color = Color {
355    r: 255,
356    g: 165,
357    b: 0,
358    a: 255,
359};
360pub const GRAY: Color = Color {
361    r: 128,
362    g: 128,
363    b: 128,
364    a: 255,
365};
366
367// Colores adicionales v0.2.0
368pub const CYAN: Color = Color {
369    r: 0,
370    g: 255,
371    b: 255,
372    a: 255,
373};
374pub const PURPLE: Color = Color {
375    r: 128,
376    g: 0,
377    b: 128,
378    a: 255,
379};
380pub const BROWN: Color = Color {
381    r: 165,
382    g: 42,
383    b: 42,
384    a: 255,
385};
386pub const LIME: Color = Color {
387    r: 0,
388    g: 255,
389    b: 0,
390    a: 255,
391};
392pub const NAVY: Color = Color {
393    r: 0,
394    g: 0,
395    b: 128,
396    a: 255,
397};
398pub const OLIVE: Color = Color {
399    r: 128,
400    g: 128,
401    b: 0,
402    a: 255,
403};
404pub const TEAL: Color = Color {
405    r: 0,
406    g: 128,
407    b: 128,
408    a: 255,
409};
410pub const MAROON: Color = Color {
411    r: 128,
412    g: 0,
413    b: 0,
414    a: 255,
415};
416
417// Teclas
418pub const KEY_ESCAPE: KeyboardKey = unsafe { std::mem::transmute(256i32) };
419pub const KEY_SPACE: KeyboardKey = unsafe { std::mem::transmute(32i32) };
420pub const KEY_ENTER: KeyboardKey = unsafe { std::mem::transmute(257i32) };
421pub const KEY_UP: KeyboardKey = unsafe { std::mem::transmute(265i32) };
422pub const KEY_DOWN: KeyboardKey = unsafe { std::mem::transmute(264i32) };
423pub const KEY_LEFT: KeyboardKey = unsafe { std::mem::transmute(263i32) };
424pub const KEY_RIGHT: KeyboardKey = unsafe { std::mem::transmute(262i32) };
425pub const KEY_A: KeyboardKey = unsafe { std::mem::transmute(65i32) };
426pub const KEY_B: KeyboardKey = unsafe { std::mem::transmute(66i32) };
427pub const KEY_C: KeyboardKey = unsafe { std::mem::transmute(67i32) };
428pub const KEY_D: KeyboardKey = unsafe { std::mem::transmute(68i32) };
429pub const KEY_E: KeyboardKey = unsafe { std::mem::transmute(69i32) };
430pub const KEY_F: KeyboardKey = unsafe { std::mem::transmute(70i32) };
431pub const KEY_G: KeyboardKey = unsafe { std::mem::transmute(71i32) };
432pub const KEY_H: KeyboardKey = unsafe { std::mem::transmute(72i32) };
433pub const KEY_I: KeyboardKey = unsafe { std::mem::transmute(73i32) };
434pub const KEY_J: KeyboardKey = unsafe { std::mem::transmute(74i32) };
435pub const KEY_K: KeyboardKey = unsafe { std::mem::transmute(75i32) };
436pub const KEY_L: KeyboardKey = unsafe { std::mem::transmute(76i32) };
437pub const KEY_M: KeyboardKey = unsafe { std::mem::transmute(77i32) };
438pub const KEY_N: KeyboardKey = unsafe { std::mem::transmute(78i32) };
439pub const KEY_O: KeyboardKey = unsafe { std::mem::transmute(79i32) };
440pub const KEY_P: KeyboardKey = unsafe { std::mem::transmute(80i32) };
441pub const KEY_Q: KeyboardKey = unsafe { std::mem::transmute(81i32) };
442pub const KEY_R: KeyboardKey = unsafe { std::mem::transmute(82i32) };
443pub const KEY_S: KeyboardKey = unsafe { std::mem::transmute(83i32) };
444pub const KEY_T: KeyboardKey = unsafe { std::mem::transmute(84i32) };
445pub const KEY_U: KeyboardKey = unsafe { std::mem::transmute(85i32) };
446pub const KEY_V: KeyboardKey = unsafe { std::mem::transmute(86i32) };
447pub const KEY_W: KeyboardKey = unsafe { std::mem::transmute(87i32) };
448pub const KEY_X: KeyboardKey = unsafe { std::mem::transmute(88i32) };
449pub const KEY_Y: KeyboardKey = unsafe { std::mem::transmute(89i32) };
450pub const KEY_Z: KeyboardKey = unsafe { std::mem::transmute(90i32) };
451pub const KEY_ZERO: KeyboardKey = unsafe { std::mem::transmute(48i32) };
452pub const KEY_ONE: KeyboardKey = unsafe { std::mem::transmute(49i32) };
453pub const KEY_TWO: KeyboardKey = unsafe { std::mem::transmute(50i32) };
454pub const KEY_THREE: KeyboardKey = unsafe { std::mem::transmute(51i32) };
455pub const KEY_FOUR: KeyboardKey = unsafe { std::mem::transmute(52i32) };
456pub const KEY_FIVE: KeyboardKey = unsafe { std::mem::transmute(53i32) };
457pub const KEY_SIX: KeyboardKey = unsafe { std::mem::transmute(54i32) };
458pub const KEY_SEVEN: KeyboardKey = unsafe { std::mem::transmute(55i32) };
459pub const KEY_EIGHT: KeyboardKey = unsafe { std::mem::transmute(56i32) };
460pub const KEY_NINE: KeyboardKey = unsafe { std::mem::transmute(57i32) };
461
462// Teclas adicionales v0.9.2 (100+ teclas)
463pub const KEY_TAB: KeyboardKey = unsafe { std::mem::transmute(258i32) };
464pub const KEY_CAPS_LOCK: KeyboardKey = unsafe { std::mem::transmute(259i32) };
465pub const KEY_LEFT_SHIFT: KeyboardKey = unsafe { std::mem::transmute(340i32) };
466pub const KEY_RIGHT_SHIFT: KeyboardKey = unsafe { std::mem::transmute(344i32) };
467pub const KEY_LEFT_CONTROL: KeyboardKey = unsafe { std::mem::transmute(341i32) };
468pub const KEY_RIGHT_CONTROL: KeyboardKey = unsafe { std::mem::transmute(345i32) };
469pub const KEY_LEFT_ALT: KeyboardKey = unsafe { std::mem::transmute(342i32) };
470pub const KEY_RIGHT_ALT: KeyboardKey = unsafe { std::mem::transmute(346i32) };
471pub const KEY_PAGE_UP: KeyboardKey = unsafe { std::mem::transmute(266i32) };
472pub const KEY_PAGE_DOWN: KeyboardKey = unsafe { std::mem::transmute(267i32) };
473pub const KEY_HOME: KeyboardKey = unsafe { std::mem::transmute(268i32) };
474pub const KEY_END: KeyboardKey = unsafe { std::mem::transmute(269i32) };
475pub const KEY_INSERT: KeyboardKey = unsafe { std::mem::transmute(260i32) };
476pub const KEY_DELETE: KeyboardKey = unsafe { std::mem::transmute(261i32) };
477pub const KEY_F1: KeyboardKey = unsafe { std::mem::transmute(290i32) };
478pub const KEY_F2: KeyboardKey = unsafe { std::mem::transmute(291i32) };
479pub const KEY_F3: KeyboardKey = unsafe { std::mem::transmute(292i32) };
480pub const KEY_F4: KeyboardKey = unsafe { std::mem::transmute(293i32) };
481pub const KEY_F5: KeyboardKey = unsafe { std::mem::transmute(294i32) };
482pub const KEY_F6: KeyboardKey = unsafe { std::mem::transmute(295i32) };
483pub const KEY_F7: KeyboardKey = unsafe { std::mem::transmute(296i32) };
484pub const KEY_F8: KeyboardKey = unsafe { std::mem::transmute(297i32) };
485pub const KEY_F9: KeyboardKey = unsafe { std::mem::transmute(298i32) };
486pub const KEY_F10: KeyboardKey = unsafe { std::mem::transmute(299i32) };
487pub const KEY_F11: KeyboardKey = unsafe { std::mem::transmute(300i32) };
488pub const KEY_F12: KeyboardKey = unsafe { std::mem::transmute(301i32) };
489
490// ============================================================================
491// COLORES RYDIT
492// ============================================================================
493
494/// Colores básicos para RyDit
495#[derive(Debug, Clone, Copy, PartialEq)]
496pub enum ColorRydit {
497    Rojo,
498    Verde,
499    Azul,
500    Amarillo,
501    Blanco,
502    Negro,
503    Magenta,
504    Rosa,
505    Naranja,
506    Gris,
507    // Colores v0.2.0
508    Cyan,
509    Morado,
510    Cafe,
511    Lima,
512    AzulOscuro,
513    Oliva,
514    Turquesa,
515    Vino,
516}
517
518impl ColorRydit {
519    /// Convertir a Color de raylib
520    pub fn to_color(&self) -> Color {
521        match self {
522            ColorRydit::Rojo => RED,
523            ColorRydit::Verde => GREEN,
524            ColorRydit::Azul => BLUE,
525            ColorRydit::Amarillo => YELLOW,
526            ColorRydit::Blanco => WHITE,
527            ColorRydit::Negro => BLACK,
528            ColorRydit::Magenta => MAGENTA,
529            ColorRydit::Rosa => PINK,
530            ColorRydit::Naranja => ORANGE,
531            ColorRydit::Gris => GRAY,
532            ColorRydit::Cyan => CYAN,
533            ColorRydit::Morado => PURPLE,
534            ColorRydit::Cafe => BROWN,
535            ColorRydit::Lima => LIME,
536            ColorRydit::AzulOscuro => NAVY,
537            ColorRydit::Oliva => OLIVE,
538            ColorRydit::Turquesa => TEAL,
539            ColorRydit::Vino => MAROON,
540        }
541    }
542
543    /// Convertir a componentes RGB (r, g, b)
544    pub fn to_rgb(&self) -> (u8, u8, u8) {
545        match self {
546            ColorRydit::Rojo => (255, 0, 0),
547            ColorRydit::Verde => (0, 255, 0),
548            ColorRydit::Azul => (0, 0, 255),
549            ColorRydit::Amarillo => (255, 255, 0),
550            ColorRydit::Blanco => (255, 255, 255),
551            ColorRydit::Negro => (0, 0, 0),
552            ColorRydit::Magenta => (255, 0, 255),
553            ColorRydit::Rosa => (255, 192, 203),
554            ColorRydit::Naranja => (255, 165, 0),
555            ColorRydit::Gris => (128, 128, 128),
556            ColorRydit::Cyan => (0, 255, 255),
557            ColorRydit::Morado => (128, 0, 128),
558            ColorRydit::Cafe => (165, 42, 42),
559            ColorRydit::Lima => (0, 255, 0),
560            ColorRydit::AzulOscuro => (0, 0, 139),
561            ColorRydit::Oliva => (128, 128, 0),
562            ColorRydit::Turquesa => (64, 224, 208),
563            ColorRydit::Vino => (128, 0, 64),
564        }
565    }
566
567    /// Crear desde Color de migui (solo con feature migui)
568    #[cfg(feature = "migui")]
569    pub fn from_migui(color: MiguiColor) -> Self {
570        // Convertir componentes RGB directamente
571        // migui::Color tiene campos públicos: r, g, b, a
572        // Usamos aproximación a los colores RyDit más cercanos
573        let r = color.r;
574        let g = color.g;
575        let b = color.b;
576
577        // Calcular distancia a cada color RyDit y elegir el más cercano
578        let colores = vec![
579            (ColorRydit::Rojo, RED),
580            (ColorRydit::Verde, GREEN),
581            (ColorRydit::Azul, BLUE),
582            (ColorRydit::Amarillo, YELLOW),
583            (ColorRydit::Blanco, WHITE),
584            (ColorRydit::Negro, BLACK),
585            (ColorRydit::Gris, GRAY),
586            (ColorRydit::Naranja, ORANGE),
587            (ColorRydit::Cyan, CYAN),
588            (ColorRydit::Morado, PURPLE),
589            (ColorRydit::Cafe, BROWN),
590            (ColorRydit::Lima, LIME),
591            (ColorRydit::AzulOscuro, NAVY),
592            (ColorRydit::Oliva, OLIVE),
593            (ColorRydit::Turquesa, TEAL),
594            (ColorRydit::Vino, MAROON),
595        ];
596
597        let mut mejor_color = ColorRydit::Blanco;
598        let mut mejor_distancia = f32::MAX;
599
600        for (ry_color, raylib_color) in colores {
601            let dr = (r as i32 - raylib_color.r as i32).pow(2) as f32;
602            let dg = (g as i32 - raylib_color.g as i32).pow(2) as f32;
603            let db = (b as i32 - raylib_color.b as i32).pow(2) as f32;
604            let distancia = dr + dg + db;
605
606            if distancia < mejor_distancia {
607                mejor_distancia = distancia;
608                mejor_color = ry_color;
609            }
610        }
611
612        mejor_color
613    }
614}
615
616// Implementación del trait FromStr para ColorRydit
617impl FromStr for ColorRydit {
618    type Err = ();
619
620    fn from_str(s: &str) -> Result<Self, Self::Err> {
621        match s.to_lowercase().as_str() {
622            "rojo" | "red" => Ok(ColorRydit::Rojo),
623            "verde" | "green" => Ok(ColorRydit::Verde),
624            "azul" | "blue" => Ok(ColorRydit::Azul),
625            "amarillo" | "yellow" => Ok(ColorRydit::Amarillo),
626            "blanco" | "white" => Ok(ColorRydit::Blanco),
627            "negro" | "black" => Ok(ColorRydit::Negro),
628            "magenta" | "fucsia" => Ok(ColorRydit::Magenta),
629            "rosa" | "pink" => Ok(ColorRydit::Rosa),
630            "naranja" | "orange" => Ok(ColorRydit::Naranja),
631            "gris" | "gray" | "grey" => Ok(ColorRydit::Gris),
632            "cyan" | "celeste" => Ok(ColorRydit::Cyan),
633            "morado" | "purple" | "violeta" => Ok(ColorRydit::Morado),
634            "cafe" | "brown" | "marron" => Ok(ColorRydit::Cafe),
635            "lima" | "lime" => Ok(ColorRydit::Lima),
636            "azuloscuro" | "navy" | "azul oscuro" => Ok(ColorRydit::AzulOscuro),
637            "oliva" | "olive" => Ok(ColorRydit::Oliva),
638            "turquesa" | "teal" => Ok(ColorRydit::Turquesa),
639            "vino" | "maroon" | "granate" => Ok(ColorRydit::Vino),
640            _ => Ok(ColorRydit::Blanco),
641        }
642    }
643}
644
645// ============================================================================
646// TECLAS
647// ============================================================================
648
649/// Teclas para input
650/// v0.9.2: 100+ teclas
651#[derive(Debug, Clone, Copy, PartialEq)]
652pub enum Key {
653    Escape,
654    Space,
655    Enter,
656    Tab,
657    CapsLock,
658    LeftShift,
659    RightShift,
660    LeftControl,
661    RightControl,
662    LeftAlt,
663    RightAlt,
664    PageUp,
665    PageDown,
666    Home,
667    End,
668    Insert,
669    Delete,
670    F1,
671    F2,
672    F3,
673    F4,
674    F5,
675    F6,
676    F7,
677    F8,
678    F9,
679    F10,
680    F11,
681    F12,
682    ArrowUp,
683    ArrowDown,
684    ArrowLeft,
685    ArrowRight,
686    A,
687    B,
688    C,
689    D,
690    E,
691    F,
692    G,
693    H,
694    I,
695    J,
696    K,
697    L,
698    M,
699    N,
700    O,
701    P,
702    Q,
703    R,
704    S,
705    T,
706    U,
707    V,
708    W,
709    X,
710    Y,
711    Z,
712    Num0,
713    Num1,
714    Num2,
715    Num3,
716    Num4,
717    Num5,
718    Num6,
719    Num7,
720    Num8,
721    Num9,
722}
723
724impl Key {
725    /// Convertir a KeyboardKey de raylib
726    pub fn to_raylib(&self) -> KeyboardKey {
727        match self {
728            // Especiales
729            Key::Escape => KEY_ESCAPE,
730            Key::Space => KEY_SPACE,
731            Key::Enter => KEY_ENTER,
732            Key::Tab => KEY_TAB,
733            Key::CapsLock => KEY_CAPS_LOCK,
734            Key::LeftShift => KEY_LEFT_SHIFT,
735            Key::RightShift => KEY_RIGHT_SHIFT,
736            Key::LeftControl => KEY_LEFT_CONTROL,
737            Key::RightControl => KEY_RIGHT_CONTROL,
738            Key::LeftAlt => KEY_LEFT_ALT,
739            Key::RightAlt => KEY_RIGHT_ALT,
740
741            // Navegación
742            Key::PageUp => KEY_PAGE_UP,
743            Key::PageDown => KEY_PAGE_DOWN,
744            Key::Home => KEY_HOME,
745            Key::End => KEY_END,
746            Key::Insert => KEY_INSERT,
747            Key::Delete => KEY_DELETE,
748
749            // Función F1-F12
750            Key::F1 => KEY_F1,
751            Key::F2 => KEY_F2,
752            Key::F3 => KEY_F3,
753            Key::F4 => KEY_F4,
754            Key::F5 => KEY_F5,
755            Key::F6 => KEY_F6,
756            Key::F7 => KEY_F7,
757            Key::F8 => KEY_F8,
758            Key::F9 => KEY_F9,
759            Key::F10 => KEY_F10,
760            Key::F11 => KEY_F11,
761            Key::F12 => KEY_F12,
762
763            // Flechas
764            Key::ArrowUp => KEY_UP,
765            Key::ArrowDown => KEY_DOWN,
766            Key::ArrowLeft => KEY_LEFT,
767            Key::ArrowRight => KEY_RIGHT,
768
769            // Letras A-Z
770            Key::A => KEY_A,
771            Key::B => KEY_B,
772            Key::C => KEY_C,
773            Key::D => KEY_D,
774            Key::E => KEY_E,
775            Key::F => KEY_F,
776            Key::G => KEY_G,
777            Key::H => KEY_H,
778            Key::I => KEY_I,
779            Key::J => KEY_J,
780            Key::K => KEY_K,
781            Key::L => KEY_L,
782            Key::M => KEY_M,
783            Key::N => KEY_N,
784            Key::O => KEY_O,
785            Key::P => KEY_P,
786            Key::Q => KEY_Q,
787            Key::R => KEY_R,
788            Key::S => KEY_S,
789            Key::T => KEY_T,
790            Key::U => KEY_U,
791            Key::V => KEY_V,
792            Key::W => KEY_W,
793            Key::X => KEY_X,
794            Key::Y => KEY_Y,
795            Key::Z => KEY_Z,
796
797            // Números 0-9
798            Key::Num0 => KEY_ZERO,
799            Key::Num1 => KEY_ONE,
800            Key::Num2 => KEY_TWO,
801            Key::Num3 => KEY_THREE,
802            Key::Num4 => KEY_FOUR,
803            Key::Num5 => KEY_FIVE,
804            Key::Num6 => KEY_SIX,
805            Key::Num7 => KEY_SEVEN,
806            Key::Num8 => KEY_EIGHT,
807            Key::Num9 => KEY_NINE,
808        }
809    }
810}
811
812// ============================================================================
813// RYDIT GFX - EL PUENTE
814// ============================================================================
815
816/// RyDit Graphics Layer
817///
818/// Sincroniza Rust (arquitecto) con Raylib (pincel)
819///
820/// ## Características:
821/// - API consistente (oculta inconsistencias de raylib-rs)
822/// - Rust siempre controla, Raylib siempre ejecuta
823/// - Manejo seguro de handles y threads
824pub struct RyditGfx {
825    handle: RaylibHandle,
826    thread: RaylibThread,
827    width: i32,
828    height: i32,
829    fps: i32,
830    // ✅ v0.10.4: Input SDL2 para Termux-X11
831    pub input_sdl2: input_sdl2::InputState,
832    // Contexto SDL2 para eventos
833    #[allow(dead_code)]
834    sdl_context: Option<sdl2::Sdl>,
835    sdl_event_pump: Option<sdl2::EventPump>,
836    // ✅ v0.13.1: FSR Upscaler
837    #[allow(dead_code)]
838    fsr: Option<fsr::FsrUpscaler>,
839    #[allow(dead_code)]
840    fsr_enabled: bool,
841}
842
843impl RyditGfx {
844    /// Crear nueva ventana gráfica
845    pub fn new(title: &str, width: i32, height: i32) -> Self {
846        let (handle, thread) = raylib::init().size(width, height).title(title).build();
847
848        println!("[RYDIT-GFX]: Ventana creada {}x{}", width, height);
849        println!("[RYDIT-GFX]: Rust = Arquitecto, Raylib = Pincel");
850        println!(
851            "[RYDIT-GFX]: DISPLAY={}",
852            std::env::var("DISPLAY").unwrap_or_else(|_| "NO SET".to_string())
853        );
854
855        // Inicializar SDL2 para eventos
856        let sdl_context = sdl2::init().ok();
857
858        // ✅ v0.13.0: Configurar hints SDL2 para Android/Termux-X11
859        if let Some(_) = &sdl_context {
860            // Forzar backend X11 (Termux-X11)
861            sdl2::hint::set("SDL_VIDEODRIVER", "x11");
862            // Forzar EGL sobre GLX (más estable en Android)
863            sdl2::hint::set("SDL_HINT_VIDEO_X11_FORCE_EGL", "1");
864            // Separar eventos mouse y touch
865            sdl2::hint::set("SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH", "1");
866            // Mapear touch a mouse events
867            sdl2::hint::set("SDL_HINT_TOUCH_MOUSE_EVENTS", "1");
868            // Habilitar teclado virtual en pantalla
869            sdl2::hint::set("SDL_HINT_ENABLE_SCREEN_KEYBOARD", "1");
870            // Activar input de texto por defecto (para textbox)
871            sdl2::hint::set("SDL_HINT_IME_SHOW_UI", "1");
872
873            eprintln!("[RYDIT-GFX] SDL2 hints configurados para Android/Termux-X11");
874        }
875
876        let sdl_event_pump = sdl_context.as_ref().and_then(|ctx| ctx.event_pump().ok());
877
878        Self {
879            handle,
880            thread,
881            width,
882            height,
883            fps: 60,
884            input_sdl2: input_sdl2::InputState::new(),
885            sdl_context,
886            sdl_event_pump,
887            fsr: None,        // FSR se inicializa manualmente si se desea
888            fsr_enabled: false,
889        }
890    }
891
892    /// Configurar FPS objetivo
893    pub fn set_target_fps(&mut self, fps: i32) {
894        self.fps = fps;
895        self.handle.set_target_fps(fps as u32);
896    }
897
898    /// Obtener FPS objetivo
899    pub fn get_target_fps(&self) -> i32 {
900        self.fps
901    }
902
903    /// Obtener FPS reales
904    pub fn get_fps(&self) -> i32 {
905        self.handle.get_fps() as i32
906    }
907
908    /// Verificar si la ventana debe cerrarse
909    pub fn should_close(&self) -> bool {
910        self.handle.window_should_close()
911    }
912
913    // ========================================================================
914    // INPUT SDL2 - v0.10.4: Para Termux-X11/Android
915    // ========================================================================
916
917    /// Procesar eventos SDL2 (debe llamarse en cada frame)
918    pub fn procesar_eventos_sdl2(&mut self) {
919        // Limpiar eventos del frame anterior
920        self.input_sdl2.limpiar_frame();
921
922        // Obtener event pump (temporalmente)
923        if let Some(ref mut event_pump) = self.sdl_event_pump {
924            // Procesar eventos
925            for event in event_pump.poll_iter() {
926                match event {
927                    sdl2::event::Event::KeyDown {
928                        keycode: Some(keycode),
929                        repeat: false,
930                        ..
931                    } => {
932                        self.input_sdl2.teclas.insert(keycode, true);
933                        self.input_sdl2.teclas_pressionadas_frame.push(keycode);
934                    }
935                    sdl2::event::Event::KeyUp {
936                        keycode: Some(keycode),
937                        ..
938                    } => {
939                        self.input_sdl2.teclas.insert(keycode, false);
940                    }
941                    _ => {}
942                }
943            }
944        }
945    }
946
947    /// Verificar si una tecla está presionada (vía SDL2)
948    pub fn is_key_pressed_sdl2(&self, nombre: &str) -> bool {
949        self.input_sdl2.is_key_pressed(nombre)
950    }
951
952    /// Verificar si una tecla fue presionada este frame (vía SDL2)
953    pub fn is_key_just_pressed_sdl2(&self, nombre: &str) -> bool {
954        self.input_sdl2.is_key_just_pressed(nombre)
955    }
956
957    /// Obtener ancho de ventana
958    pub fn width(&self) -> i32 {
959        self.width
960    }
961
962    /// Obtener alto de ventana
963    pub fn height(&self) -> i32 {
964        self.height
965    }
966
967    /// Iniciar dibujo (obtener DrawHandle)
968    pub fn begin_draw(&mut self) -> DrawHandle<'_> {
969        let d = self.handle.begin_drawing(&self.thread);
970        DrawHandle::new(d)
971    }
972
973    /// Finalizar dibujo (automático con Drop de DrawHandle)
974    pub fn end_draw(&mut self) {
975        // Automático cuando DrawHandle se va de scope
976    }
977
978    // ========================================================================
979    // FSR 1.0 — v0.13.1
980    // ========================================================================
981
982    /// Inicializar FSR upscaler
983    pub fn init_fsr(&mut self, quality: fsr::FsrQuality) -> Result<(), String> {
984        match fsr::FsrUpscaler::new() {
985            Ok(fsr) => {
986                eprintln!("[FSR] Inicializado: {:?}", quality);
987                self.fsr = Some(fsr);
988                self.fsr_enabled = true;
989                Ok(())
990            }
991            Err(e) => {
992                eprintln!("[FSR] Error al inicializar: {} (FSR desactivado)", e);
993                self.fsr = None;
994                self.fsr_enabled = false;
995                Err(e)
996            }
997        }
998    }
999
1000    /// Activar/desactivar FSR
1001    pub fn set_fsr_enabled(&mut self, enabled: bool) {
1002        if let Some(ref mut fsr) = self.fsr {
1003            fsr.set_enabled(enabled);
1004            self.fsr_enabled = enabled;
1005        }
1006    }
1007
1008    /// Cambiar calidad FSR
1009    pub fn set_fsr_quality(&mut self, quality: fsr::FsrQuality) {
1010        if let Some(ref mut fsr) = self.fsr {
1011            fsr.set_quality(quality);
1012        }
1013    }
1014
1015    /// Cycle FSR quality modes
1016    pub fn cycle_fsr_quality(&mut self) {
1017        if let Some(ref mut fsr) = self.fsr {
1018            fsr.cycle_quality();
1019        }
1020    }
1021
1022    /// Verificar si FSR está activo
1023    pub fn is_fsr_enabled(&self) -> bool {
1024        self.fsr_enabled && self.fsr.is_some()
1025    }
1026
1027    /// Obtener calidad FSR actual
1028    pub fn fsr_quality(&self) -> Option<fsr::FsrQuality> {
1029        self.fsr.as_ref().map(|f| f.quality())
1030    }
1031
1032    /// Limpiar pantalla
1033    pub fn clear_background(&mut self, color: ColorRydit) {
1034        let mut d = self.begin_draw();
1035        d.clear(color);
1036        // end_draw automático
1037    }
1038
1039    /// Dibujar círculo
1040    pub fn draw_circle(&mut self, x: i32, y: i32, radius: i32, color: ColorRydit) {
1041        {
1042            let mut d = self.begin_draw();
1043            d.draw_circle(x, y, radius, color);
1044            drop(d); // ← Flush EXPLÍCITO para Zink/Vulkan
1045        }
1046    }
1047
1048    /// Dibujar rectángulo
1049    pub fn draw_rect(&mut self, x: i32, y: i32, w: i32, h: i32, color: ColorRydit) {
1050        {
1051            let mut d = self.begin_draw();
1052            d.draw_rectangle(x, y, w, h, color);
1053            drop(d); // ← Flush EXPLÍCITO para Zink/Vulkan
1054        }
1055    }
1056
1057    /// Dibujar línea
1058    pub fn draw_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: ColorRydit) {
1059        {
1060            let mut d = self.begin_draw();
1061            d.draw_line(x1, y1, x2, y2, color);
1062            drop(d); // ← Flush EXPLÍCITO para Zink/Vulkan
1063        }
1064    }
1065
1066    /// Dibujar texto
1067    pub fn draw_text(&mut self, text: &str, x: i32, y: i32, size: i32, color: ColorRydit) {
1068        {
1069            let mut d = self.begin_draw();
1070            d.draw_text(text, x, y, size, color);
1071            drop(d); // ← Flush EXPLÍCITO para Zink/Vulkan
1072        }
1073    }
1074
1075    /// Cargar textura desde archivo (placeholder)
1076    pub fn load_texture(&mut self, path: &str) -> Texture2D {
1077        // ⚠️ Placeholder: Implementación completa pendiente
1078        // Intentar cargar con raylib FFI
1079        unsafe {
1080            let c_path = std::ffi::CString::new(path).unwrap();
1081            let tex = raylib::ffi::LoadTexture(c_path.as_ptr());
1082            // Convertir FFI Texture2D a raylib::prelude::Texture2D
1083            std::mem::transmute(tex)
1084        }
1085    }
1086
1087    /// Dibujar textura (placeholder - dibuja rect)
1088    pub fn draw_texture(&mut self, _texture: &Texture2D, x: i32, y: i32, color: ColorRydit) {
1089        // ⚠️ Placeholder: Usar textura real cuando esté implementado
1090        self.draw_rect(x, y, 32, 32, color);
1091    }
1092
1093    /// Dibujar textura escalada (placeholder)
1094    pub fn draw_texture_ex(
1095        &mut self,
1096        _texture: &Texture2D,
1097        x: i32,
1098        y: i32,
1099        scale: f32,
1100        color: ColorRydit,
1101    ) {
1102        // ⚠️ Placeholder: Usar textura real cuando esté implementado
1103        let size = (32.0 * scale) as i32;
1104        self.draw_rect(x, y, size, size, color);
1105    }
1106
1107    /// Verificar tecla presionada
1108    pub fn is_key_pressed(&self, key: Key) -> bool {
1109        self.handle.is_key_pressed(key.to_raylib())
1110    }
1111
1112    /// Verificar tecla mantenida
1113    pub fn is_key_down(&self, key: Key) -> bool {
1114        self.handle.is_key_down(key.to_raylib())
1115    }
1116
1117    /// Obtener posición X del mouse
1118    pub fn get_mouse_x(&self) -> i32 {
1119        self.handle.get_mouse_x()
1120    }
1121
1122    /// Obtener posición Y del mouse
1123    pub fn get_mouse_y(&self) -> i32 {
1124        self.handle.get_mouse_y()
1125    }
1126
1127    // ========================================================================
1128    // INPUT MOUSE AVANZADO - V0.3.0
1129    // ========================================================================
1130
1131    /// Obtener posición del mouse como (x, y)
1132    pub fn get_mouse_position(&self) -> (i32, i32) {
1133        (self.handle.get_mouse_x(), self.handle.get_mouse_y())
1134    }
1135
1136    /// Verificar si botón del mouse está presionado (0=izq, 1=der, 2=medio)
1137    pub fn is_mouse_button_pressed(&self, button: i32) -> bool {
1138        // Raylib usa valores: 0=MOUSE_LEFT_BUTTON, 1=MOUSE_RIGHT_BUTTON, 2=MOUSE_MIDDLE_BUTTON
1139        unsafe {
1140            let ffi_button = button;
1141            raylib::ffi::IsMouseButtonPressed(ffi_button)
1142        }
1143    }
1144
1145    /// Verificar si botón del mouse está mantenido
1146    pub fn is_mouse_button_down(&self, button: i32) -> bool {
1147        unsafe {
1148            let ffi_button = button;
1149            raylib::ffi::IsMouseButtonDown(ffi_button)
1150        }
1151    }
1152
1153    /// Obtener movimiento del mouse (delta X, delta Y)
1154    pub fn get_mouse_delta(&self) -> (i32, i32) {
1155        let delta = self.handle.get_mouse_delta();
1156        (delta.x as i32, delta.y as i32)
1157    }
1158
1159    /// Obtener scroll del mouse (Y axis)
1160    pub fn get_mouse_wheel(&self) -> f32 {
1161        // get_mouse_wheel_move retorna un f32 con el scroll en Y
1162        self.handle.get_mouse_wheel_move()
1163    }
1164}
1165
1166impl Drop for RyditGfx {
1167    fn drop(&mut self) {
1168        println!("[RYDIT-GFX]: Cerrando ventana...");
1169    }
1170}
1171
1172// ============================================================================
1173// DRAW HANDLE - EL PINCEL
1174// ============================================================================
1175
1176/// Handle de dibujo (Raylib = Pincel)
1177///
1178/// Se obtiene de `RyditGfx::begin_draw()` y se usa para dibujar.
1179/// Al salir de scope, automáticamente finaliza el dibujo.
1180pub struct DrawHandle<'a> {
1181    pub draw: RaylibDrawHandle<'a>,
1182}
1183
1184impl<'a> DrawHandle<'a> {
1185    fn new(draw: RaylibDrawHandle<'a>) -> Self {
1186        Self { draw }
1187    }
1188
1189    /// Limpiar con color
1190    pub fn clear(&mut self, color: ColorRydit) {
1191        self.draw.clear_background(color.to_color());
1192    }
1193
1194    /// Dibujar círculo
1195    pub fn draw_circle(&mut self, x: i32, y: i32, radius: i32, color: ColorRydit) {
1196        self.draw.draw_circle(x, y, radius as f32, color.to_color());
1197    }
1198
1199    /// Dibujar rectángulo
1200    pub fn draw_rectangle(&mut self, x: i32, y: i32, w: i32, h: i32, color: ColorRydit) {
1201        self.draw.draw_rectangle(x, y, w, h, color.to_color());
1202    }
1203
1204    /// Dibujar línea
1205    pub fn draw_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: ColorRydit) {
1206        self.draw.draw_line(x1, y1, x2, y2, color.to_color());
1207    }
1208
1209    /// Dibujar texto
1210    pub fn draw_text(&mut self, text: &str, x: i32, y: i32, size: i32, color: ColorRydit) {
1211        self.draw.draw_text(text, x, y, size, color.to_color());
1212    }
1213
1214    // ========================================================================
1215    // FUNCIONES DRAW V0.2.0 - NUEVAS FORMAS
1216    // ========================================================================
1217
1218    /// Dibujar triángulo
1219    pub fn draw_triangle(
1220        &mut self,
1221        v1: (i32, i32),
1222        v2: (i32, i32),
1223        v3: (i32, i32),
1224        color: ColorRydit,
1225    ) {
1226        let v1_raylib = Vector2::new(v1.0 as f32, v1.1 as f32);
1227        let v2_raylib = Vector2::new(v2.0 as f32, v2.1 as f32);
1228        let v3_raylib = Vector2::new(v3.0 as f32, v3.1 as f32);
1229        self.draw
1230            .draw_triangle(v1_raylib, v2_raylib, v3_raylib, color.to_color());
1231    }
1232
1233    /// Dibujar triángulo con líneas (outline)
1234    pub fn draw_triangle_lines(
1235        &mut self,
1236        v1: (i32, i32),
1237        v2: (i32, i32),
1238        v3: (i32, i32),
1239        color: ColorRydit,
1240    ) {
1241        self.draw_line(v1.0, v1.1, v2.0, v2.1, color);
1242        self.draw_line(v2.0, v2.1, v3.0, v3.1, color);
1243        self.draw_line(v3.0, v3.1, v1.0, v1.1, color);
1244    }
1245
1246    /// Dibujar rectángulo con líneas (outline)
1247    pub fn draw_rectangle_lines(&mut self, x: i32, y: i32, w: i32, h: i32, color: ColorRydit) {
1248        self.draw.draw_rectangle_lines(x, y, w, h, color.to_color());
1249    }
1250
1251    /// Dibujar anillo (ring)
1252    pub fn draw_ring(
1253        &mut self,
1254        center: (i32, i32),
1255        _inner_radius: i32,
1256        outer_radius: i32,
1257        color: ColorRydit,
1258    ) {
1259        // Simplificación: dibujamos solo el círculo exterior
1260        self.draw
1261            .draw_circle(center.0, center.1, outer_radius as f32, color.to_color());
1262    }
1263
1264    /// Dibujar elipse
1265    pub fn draw_ellipse(
1266        &mut self,
1267        center: (i32, i32),
1268        radius_h: i32,
1269        radius_v: i32,
1270        color: ColorRydit,
1271    ) {
1272        self.draw.draw_ellipse(
1273            center.0,
1274            center.1,
1275            radius_h as f32,
1276            radius_v as f32,
1277            color.to_color(),
1278        );
1279    }
1280
1281    /// Dibujar línea gruesa
1282    pub fn draw_line_thick(
1283        &mut self,
1284        start_pos: (i32, i32),
1285        end_pos: (i32, i32),
1286        thick: f32,
1287        color: ColorRydit,
1288    ) {
1289        let start = Vector2::new(start_pos.0 as f32, start_pos.1 as f32);
1290        let end = Vector2::new(end_pos.0 as f32, end_pos.1 as f32);
1291        self.draw.draw_line_ex(start, end, thick, color.to_color());
1292    }
1293
1294    /// Dibujar rectángulo rotado
1295    pub fn draw_rectangle_pro(
1296        &mut self,
1297        x: i32,
1298        y: i32,
1299        width: i32,
1300        height: i32,
1301        angle: f32,
1302        color: ColorRydit,
1303    ) {
1304        let origin = Vector2::new(width as f32 / 2.0, height as f32 / 2.0);
1305        let rect = Rectangle::new(x as f32, y as f32, width as f32, height as f32);
1306        self.draw
1307            .draw_rectangle_pro(rect, origin, angle, color.to_color());
1308    }
1309
1310    /// Dibujar textura avanzada
1311    pub fn draw_texture_ex(
1312        &mut self,
1313        texture: &Texture2D,
1314        position: Vector2,
1315        rotation: f32,
1316        scale: f32,
1317        color: Color,
1318    ) {
1319        self.draw
1320            .draw_texture_ex(texture, position, rotation, scale, color);
1321    }
1322}
1323
1324impl<'a> Drop for DrawHandle<'a> {
1325    fn drop(&mut self) {
1326        // Finalizar dibujo automáticamente
1327        // (RaylibDrawHandle lo hace en su Drop)
1328    }
1329}
1330
1331// ============================================================================
1332// ASSETS MANAGER - v0.5.0 (Sprites)
1333// ============================================================================
1334
1335/// Gestor de assets (texturas)
1336/// Nota: La carga de texturas se hace desde RyDit usando raylib directamente
1337pub struct Assets {
1338    textures: HashMap<String, Texture2D>,
1339}
1340
1341impl Assets {
1342    pub fn new() -> Self {
1343        Self {
1344            textures: HashMap::new(),
1345        }
1346    }
1347
1348    /// Cargar textura desde archivo (función estática usando FFI)
1349    pub fn load_texture_from_path(path: &str) -> Result<Texture2D, String> {
1350        use std::ffi::CString;
1351        use std::path::Path;
1352
1353        if Path::new(path).exists() {
1354            // Usar FFI para cargar textura sin handle
1355            unsafe {
1356                let c_path =
1357                    CString::new(path).map_err(|e| format!("Error convirtiendo path: {}", e))?;
1358                let ffi_texture = raylib::ffi::LoadTexture(c_path.as_ptr());
1359                if ffi_texture.id != 0 {
1360                    // Usar from_raw para crear Texture2D desde FFI
1361                    Ok(Texture2D::from_raw(ffi_texture))
1362                } else {
1363                    Err(format!("Error cargando textura '{}'", path))
1364                }
1365            }
1366        } else {
1367            Err(format!("Archivo '{}' no encontrado", path))
1368        }
1369    }
1370
1371    // ==================== TEXTURAS ====================
1372
1373    /// Insertar textura cargada desde RyDit
1374    pub fn insert_texture(&mut self, id: String, texture: Texture2D) {
1375        self.textures.insert(id, texture);
1376    }
1377
1378    /// Obtener textura por ID
1379    pub fn get_texture(&self, id: &str) -> Option<&Texture2D> {
1380        self.textures.get(id)
1381    }
1382
1383    /// Descargar textura y liberar memoria
1384    pub fn unload_texture(&mut self, id: &str) -> bool {
1385        self.textures.remove(id).is_some()
1386    }
1387
1388    /// Dibujar textura en pantalla
1389    ///
1390    /// # Arguments (en orden):
1391    /// 1. `d` - Draw handle de raylib
1392    /// 2. `id` - ID del sprite cargado
1393    ///    3-4. `x, y` - Posición en pantalla
1394    ///    5-6. `_w, _h` - Dimensiones (actualmente no usadas)
1395    /// 7. `color` - Color de tinte
1396    ///
1397    /// # Nota
1398    /// Los parámetros `_w` y `_h` son reservados para futura implementación de escalado.
1399    #[allow(clippy::too_many_arguments)]
1400    pub fn draw_texture(
1401        &self,
1402        d: &mut RaylibDrawHandle,
1403        id: &str,
1404        x: f32,
1405        y: f32,
1406        _w: f32,
1407        _h: f32,
1408        color: Color,
1409    ) {
1410        if let Some(texture) = self.textures.get(id) {
1411            d.draw_texture_ex(texture, Vector2::new(x, y), 0.0, 1.0, color);
1412        }
1413    }
1414
1415    /// Dibujar textura escalada
1416    pub fn draw_texture_scaled(
1417        &self,
1418        d: &mut RaylibDrawHandle,
1419        id: &str,
1420        x: f32,
1421        y: f32,
1422        scale: f32,
1423        color: Color,
1424    ) {
1425        if let Some(texture) = self.textures.get(id) {
1426            d.draw_texture_ex(texture, Vector2::new(x, y), 0.0, scale, color);
1427        }
1428    }
1429
1430    /// Dibujar textura con rotación y escala (para RenderQueue)
1431    pub fn draw_texture_ex_by_id(
1432        &self,
1433        d: &mut RaylibDrawHandle,
1434        id: &str,
1435        x: f32,
1436        y: f32,
1437        scale: f32,
1438        rotation: f32,
1439        color: ColorRydit,
1440    ) {
1441        if let Some(texture) = self.textures.get(id) {
1442            d.draw_texture_ex(
1443                texture,
1444                Vector2::new(x, y),
1445                rotation,
1446                scale,
1447                color.to_color(),
1448            );
1449        }
1450    }
1451
1452    /// Dibujar rectángulo de textura (para tilesets)
1453    pub fn draw_texture_rec(
1454        &self,
1455        d: &mut RaylibDrawHandle,
1456        id: &str,
1457        source: Rectangle,
1458        dest: Rectangle,
1459        color: Color,
1460    ) {
1461        if let Some(texture) = self.textures.get(id) {
1462            d.draw_texture_rec(texture, source, Vector2::new(dest.x, dest.y), color);
1463        }
1464    }
1465
1466    /// Verificar si existe una textura
1467    pub fn has_texture(&self, id: &str) -> bool {
1468        self.textures.contains_key(id)
1469    }
1470
1471    /// Cantidad de texturas cargadas
1472    pub fn texture_count(&self) -> usize {
1473        self.textures.len()
1474    }
1475
1476    /// Limpiar todas las texturas
1477    pub fn clear(&mut self) {
1478        self.textures.clear();
1479    }
1480
1481    // ==================== FUNCIONES SDL2 (ESTÁTICAS) ====================
1482
1483    /// Cargar textura SDL2 desde archivo (usando FFI nativo)
1484    /// Retorna la textura que debe ser gestionada por el caller
1485    /// Nota: El lifetime de la textura está ligado al texture_creator
1486    pub fn load_texture_sdl2<'a>(
1487        path: &str,
1488        texture_creator: &'a sdl2::render::TextureCreator<sdl2::video::WindowContext>,
1489    ) -> Result<sdl2::render::Texture<'a>, String> {
1490        use crate::sdl2_ffi::TextureFFI;
1491        use sdl2::surface::Surface;
1492        use std::path::Path;
1493
1494        if !Path::new(path).exists() {
1495            return Err(format!("Archivo '{}' no encontrado", path));
1496        }
1497
1498        // Cargar superficie con SDL2_image FFI
1499        let texture_ffi = TextureFFI::load(path)?;
1500        let surface_ptr = texture_ffi.surface();
1501
1502        unsafe {
1503            // Envolver raw pointer en Surface
1504            let sdl_surface = Surface::from_ll(surface_ptr as *mut sdl2::sys::SDL_Surface);
1505
1506            // Crear textura desde superficie
1507            let texture = texture_creator
1508                .create_texture_from_surface(&sdl_surface)
1509                .map_err(|e| format!("Error creando textura SDL2: {}", e))?;
1510
1511            Ok(texture)
1512        }
1513    }
1514
1515    /// Dibujar textura SDL2 directamente (sin gestor de assets)
1516    pub fn draw_texture_sdl2(
1517        canvas: &mut sdl2::render::Canvas<sdl2::video::Window>,
1518        texture: &sdl2::render::Texture,
1519        x: i32,
1520        y: i32,
1521        width: u32,
1522        height: u32,
1523    ) -> Result<(), String> {
1524        let rect = sdl2::rect::Rect::new(x, y, width, height);
1525        canvas
1526            .copy(texture, None, rect)
1527            .map_err(|e| format!("Error dibujando textura SDL2: {}", e))?;
1528
1529        Ok(())
1530    }
1531}
1532
1533impl Default for Assets {
1534    fn default() -> Self {
1535        Self::new()
1536    }
1537}
1538
1539// ============================================================================
1540// MIGUI BACKEND IMPLEMENTATION
1541// ============================================================================
1542
1543/// Implementación de MiguiBackend para RyditGfx (solo con feature migui)
1544///
1545/// Conecta los DrawCommand de migui con las funciones de dibujo de raylib
1546///
1547/// Nota: Las funciones asumen que begin_draw() ya fue llamado.
1548/// Usar con render_commands_frame() que maneja el begin/end draw.
1549#[cfg(feature = "migui")]
1550impl MiguiBackend for RyditGfx {
1551    fn clear(&mut self, color: MiguiColor) {
1552        let color_rydit = ColorRydit::from_migui(color);
1553        let mut d = self.begin_draw();
1554        d.clear(color_rydit);
1555    }
1556
1557    fn draw_rect(&mut self, rect: MiguiRect, color: MiguiColor) {
1558        let color_rydit = ColorRydit::from_migui(color);
1559        let mut d = self.begin_draw();
1560        d.draw_rectangle(
1561            rect.x as i32,
1562            rect.y as i32,
1563            rect.w as i32,
1564            rect.h as i32,
1565            color_rydit,
1566        );
1567    }
1568
1569    fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: MiguiColor) {
1570        let color_rydit = ColorRydit::from_migui(color);
1571        let mut d = self.begin_draw();
1572        d.draw_text(text, x as i32, y as i32, size as i32, color_rydit);
1573    }
1574
1575    fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: MiguiColor, thickness: f32) {
1576        let color_rydit = ColorRydit::from_migui(color);
1577        let mut d = self.begin_draw();
1578        d.draw.draw_line_ex(
1579            Vector2::new(x1, y1),
1580            Vector2::new(x2, y2),
1581            thickness,
1582            color_rydit.to_color(),
1583        );
1584    }
1585}
1586
1587// ============================================================================
1588// FUNCIONES DE RENDERIZADO MIGUI OPTIMIZADAS (solo con feature migui)
1589// ============================================================================
1590
1591#[cfg(feature = "migui")]
1592impl RyditGfx {
1593    /// Renderizar comandos migui en un frame (con begin/end draw único)
1594    pub fn render_migui_frame(&mut self, commands: &[migui::DrawCommand]) {
1595        let mut d = self.begin_draw();
1596        d.clear(ColorRydit::Negro);
1597
1598        for cmd in commands {
1599            match cmd {
1600                migui::DrawCommand::Clear { color } => {
1601                    d.clear(ColorRydit::from_migui(*color));
1602                }
1603                migui::DrawCommand::DrawRect { rect, color } => {
1604                    d.draw_rectangle(
1605                        rect.x as i32,
1606                        rect.y as i32,
1607                        rect.w as i32,
1608                        rect.h as i32,
1609                        ColorRydit::from_migui(*color),
1610                    );
1611                }
1612                migui::DrawCommand::DrawText {
1613                    text,
1614                    x,
1615                    y,
1616                    size,
1617                    color,
1618                } => {
1619                    d.draw_text(
1620                        text,
1621                        *x as i32,
1622                        *y as i32,
1623                        *size as i32,
1624                        ColorRydit::from_migui(*color),
1625                    );
1626                }
1627                migui::DrawCommand::DrawLine {
1628                    x1,
1629                    y1,
1630                    x2,
1631                    y2,
1632                    color,
1633                    thickness,
1634                } => {
1635                    d.draw.draw_line_ex(
1636                        Vector2::new(*x1, *y1),
1637                        Vector2::new(*x2, *y2),
1638                        *thickness,
1639                        ColorRydit::from_migui(*color).to_color(),
1640                    );
1641                }
1642            }
1643        }
1644    }
1645}
1646
1647// ============================================================================
1648// TESTS
1649// ============================================================================
1650
1651#[cfg(test)]
1652mod tests {
1653    use super::*;
1654
1655    #[test]
1656    fn test_color_from_str() {
1657        assert_eq!(ColorRydit::from_str("rojo").unwrap(), ColorRydit::Rojo);
1658        assert_eq!(ColorRydit::from_str("RED").unwrap(), ColorRydit::Rojo);
1659        assert_eq!(ColorRydit::from_str("verde").unwrap(), ColorRydit::Verde);
1660        assert_eq!(ColorRydit::from_str("azul").unwrap(), ColorRydit::Azul);
1661        assert_eq!(
1662            ColorRydit::from_str("amarillo").unwrap(),
1663            ColorRydit::Amarillo
1664        );
1665        assert_eq!(ColorRydit::from_str("blanco").unwrap(), ColorRydit::Blanco);
1666        assert_eq!(ColorRydit::from_str("negro").unwrap(), ColorRydit::Negro);
1667        assert_eq!(
1668            ColorRydit::from_str("desconocido").unwrap(),
1669            ColorRydit::Blanco
1670        );
1671    }
1672
1673    #[test]
1674    fn test_key_to_raylib() {
1675        // Solo verificamos que no panic
1676        let _ = Key::Escape.to_raylib();
1677        let _ = Key::Space.to_raylib();
1678        let _ = Key::A.to_raylib();
1679        let _ = Key::Num0.to_raylib();
1680    }
1681
1682    #[test]
1683    fn test_color_to_color() {
1684        let color = ColorRydit::Rojo.to_color();
1685        assert_eq!(color.r, 230);
1686        assert_eq!(color.g, 41);
1687        assert_eq!(color.b, 55);
1688        assert_eq!(color.a, 255);
1689    }
1690
1691    // ========================================================================
1692    // TESTS V0.1.9 - GRÁFICOS Y COLORES
1693    // ========================================================================
1694
1695    #[test]
1696    fn test_draw_circle_colores() {
1697        // Verificar que todos los colores básicos funcionan
1698        let colores = vec!["rojo", "verde", "azul", "amarillo", "blanco", "negro"];
1699
1700        for color_str in colores {
1701            let color_rydit = ColorRydit::from_str(color_str).unwrap();
1702            let color = color_rydit.to_color();
1703            // Solo verificamos que el alpha sea 255 (completamente opaco)
1704            assert_eq!(color.a, 255, "Color {} debe tener alpha 255", color_str);
1705        }
1706    }
1707
1708    #[test]
1709    fn test_draw_rect_dimensiones() {
1710        // Verificar conversión de dimensiones para rectángulos
1711        // Las dimensiones son enteros en raylib
1712        let x: i32 = 100;
1713        let y: i32 = 200;
1714        let ancho: i32 = 50;
1715        let alto: i32 = 75;
1716
1717        // Verificamos que los valores se mantienen
1718        assert_eq!(x, 100);
1719        assert_eq!(y, 200);
1720        assert_eq!(ancho, 50);
1721        assert_eq!(alto, 75);
1722
1723        // Un rectángulo con estas dimensiones debería ser válido
1724        let rect = raylib::prelude::Rectangle::new(x as f32, y as f32, ancho as f32, alto as f32);
1725        assert_eq!(rect.x, 100.0);
1726        assert_eq!(rect.y, 200.0);
1727        assert!((rect.width - 50.0).abs() < 0.01);
1728        assert!((rect.height - 75.0).abs() < 0.01);
1729    }
1730
1731    // ========================================================================
1732    // TESTS V0.3.0 - INPUT MOUSE AVANZADO
1733    // ========================================================================
1734
1735    #[test]
1736    fn test_mouse_functions_exist() {
1737        // Solo verificamos que las funciones existen y compilan
1738        // No podemos probar mouse real sin ventana
1739        let _ = RyditGfx::new("Test", 800, 600);
1740        // gfx.get_mouse_position()  // Retorna (i32, i32)
1741        // gfx.is_mouse_button_pressed(0)  // Retorna bool
1742        // gfx.get_mouse_wheel()  // Retorna (f32, f32)
1743    }
1744
1745    #[test]
1746    fn test_mouse_button_mapping() {
1747        // Verificar mapeo de botones
1748        assert_eq!(0, 0); // Left button index
1749        assert_eq!(1, 1); // Right button index
1750        assert_eq!(2, 2); // Middle button index
1751    }
1752
1753    // ========================================================================
1754    // TESTS V0.4.1 - MIGUI BACKEND
1755    // ========================================================================
1756
1757    #[cfg(feature = "migui")]
1758    #[test]
1759    fn test_migui_backend_exists() {
1760        // Verificar que RyditGfx implementa MiguiBackend
1761        let _ = RyditGfx::new("Test", 800, 600);
1762        // El backend existe y compila
1763    }
1764}