import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../app/tema.dart'; import 'muzik_form_state.dart'; import 'muzik_kataloglari.dart'; import 'sihirbaz_secimleri.dart'; /// Çip bazlı müzik sihirbazı — seçimler canlı olarak tags'e derlenir. class MuzikSihirbazi extends ConsumerWidget { const MuzikSihirbazi({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final sihirbaz = ref.watch(muzikFormProvider.select((s) => s.sihirbaz)); final notifier = ref.read(muzikFormProvider.notifier); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _Bolum( baslik: 'Hızlı Stil', ikon: Icons.flash_on, renk: FikoRenkler.neonAmber, cocuk: Wrap( spacing: 5, runSpacing: 5, children: [ for (final o in MuzikKataloglari.hizliStiller) _MiniCip( etiket: o.etiketTr, secili: sihirbaz.ekstraNot.contains(o.promptEn), renk: FikoRenkler.neonAmber, onTap: () { final mevcut = sihirbaz.ekstraNot.contains(o.promptEn) ? sihirbaz.ekstraNot .replaceAll(o.promptEn, '') .replaceAll(RegExp(r',\s*,'), ',') .trim() : (sihirbaz.ekstraNot.trim().isEmpty ? o.promptEn : '${sihirbaz.ekstraNot.trim()}, ${o.promptEn}'); notifier.sihirbazGuncelle( sihirbaz.kopyala(ekstraNot: mevcut)); }, ), ], ), ), _KatalogBolumu( baslik: 'Şarkıcı Referansı', ikon: Icons.star_rounded, renk: FikoRenkler.neonAmber, ogeler: MuzikKataloglari.sanatcilar, secili: sihirbaz.sanatcilar, aramali: true, onDegisti: (s) => notifier.sihirbazGuncelle(sihirbaz.kopyala(sanatcilar: s)), ), _KatalogBolumu( baslik: 'Stil / Prodüksiyon Referansı', ikon: Icons.auto_awesome_rounded, renk: FikoRenkler.neonPembe, ogeler: MuzikKataloglari.stilReferanslari, secili: sihirbaz.stilReferanslari, onDegisti: (s) => notifier.sihirbazGuncelle(sihirbaz.kopyala(stilReferanslari: s)), ), _VokalModuSecici(sihirbaz: sihirbaz, notifier: notifier), const SizedBox(height: 6), _KatalogBolumu( baslik: 'Kültür', ikon: Icons.public, ogeler: MuzikKataloglari.kulturler, secili: sihirbaz.kulturler, onDegisti: (s) => notifier.sihirbazGuncelle(sihirbaz.kopyala(kulturler: s)), ), _KatalogBolumu( baslik: 'Tür', ikon: Icons.library_music, ogeler: const [ ...MuzikKataloglari.turler, ...MuzikKataloglari.konusmaTurleri, ], secili: sihirbaz.turler, onDegisti: (s) => notifier.sihirbazGuncelle(sihirbaz.kopyala(turler: s)), ), _KatalogBolumu( baslik: 'Atmosfer / Mood', ikon: Icons.mood, ogeler: MuzikKataloglari.moodlar, secili: sihirbaz.moodlar, onDegisti: (s) => notifier.sihirbazGuncelle(sihirbaz.kopyala(moodlar: s)), ), if (!sihirbaz.enstrumantalMi) _KatalogBolumu( baslik: 'Vokal Karakteri', ikon: Icons.record_voice_over, ogeler: const [ ...MuzikKataloglari.vokalKarakterleri, ...MuzikKataloglari.konusmaVokalleri, ], secili: sihirbaz.vokalKarakterleri, onDegisti: (s) => notifier .sihirbazGuncelle(sihirbaz.kopyala(vokalKarakterleri: s)), ), if (!sihirbaz.enstrumantalMi) _KatalogBolumu( baslik: 'Vokal Detayları', ikon: Icons.queue_music, renk: FikoRenkler.neonCyan, ogeler: MuzikKataloglari.vokalDetaylari, secili: sihirbaz.vokalDetaylari, onDegisti: (s) => notifier.sihirbazGuncelle(sihirbaz.kopyala(vokalDetaylari: s)), ), _EnstrumanBolumu(sihirbaz: sihirbaz, notifier: notifier), _TekSecimBolumu( baslik: 'Prodüksiyon Kalitesi', ikon: Icons.tune, renk: FikoRenkler.neonAmber, ogeler: MuzikKataloglari.prodKalitesi, secili: sihirbaz.prodKalitesi, onSec: (id) => notifier .sihirbazGuncelle(sihirbaz.kopyala(prodKalitesi: id)), ), _KatalogBolumu( baslik: 'Miks / Atmosfer', ikon: Icons.graphic_eq, renk: FikoRenkler.neonPembe, ogeler: MuzikKataloglari.miksSecenekleri, secili: sihirbaz.miksSecenekleri, onDegisti: (s) => notifier.sihirbazGuncelle(sihirbaz.kopyala(miksSecenekleri: s)), ), // 🔞 NSFW temalar (opsiyonel — 18+ toggle) Padding( padding: const EdgeInsets.only(top: 6, bottom: 2), child: Row(children: [ const Text('🔞', style: TextStyle(fontSize: 13)), const SizedBox(width: 5), const Text('Yetişkin Temalar (18+)', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w700, color: Color(0xFFFF2E6A))), const SizedBox(width: 8), Switch( value: sihirbaz.nsfwAcik, activeTrackColor: const Color(0xFFFF2E6A), onChanged: (v) => notifier.sihirbazGuncelle( sihirbaz.kopyala(nsfwAcik: v)), ), ]), ), if (sihirbaz.nsfwAcik) _KatalogBolumu( baslik: 'NSFW Temaları', ikon: Icons.local_fire_department, renk: const Color(0xFFFF2E6A), ogeler: MuzikKataloglari.nsfwTemalar, secili: sihirbaz.nsfwTemalar, onDegisti: (s) => notifier.sihirbazGuncelle(sihirbaz.kopyala(nsfwTemalar: s)), ), _TekSecimBolumu( baslik: 'Şarkı Açılışı', ikon: Icons.login, renk: FikoRenkler.neonYesil, ogeler: MuzikKataloglari.acilisSecenekleri, secili: sihirbaz.acilis, onSec: (id) => notifier.sihirbazGuncelle(sihirbaz.kopyala(acilis: id)), ), _TekSecimBolumu( baslik: 'Şarkı Kapanışı', ikon: Icons.logout, renk: FikoRenkler.neonYesil, ogeler: MuzikKataloglari.kapanisSecenekleri, secili: sihirbaz.kapanis, onSec: (id) => notifier.sihirbazGuncelle(sihirbaz.kopyala(kapanis: id)), ), if (!sihirbaz.enstrumantalMi) _TekSecimBolumu( baslik: 'Vokal Tempo / Söyleyiş', ikon: Icons.speed, renk: FikoRenkler.neonCyan, ogeler: MuzikKataloglari.vokalPace, secili: sihirbaz.vokalPace, onSec: (id) => notifier.sihirbazGuncelle(sihirbaz.kopyala(vokalPace: id)), ), _TekSecimBolumu( baslik: 'Enerji Eğrisi / Dinamik', ikon: Icons.show_chart, renk: FikoRenkler.neonPembe, ogeler: MuzikKataloglari.enerjiEgrisi, secili: sihirbaz.enerjiEgrisi, onSec: (id) => notifier.sihirbazGuncelle(sihirbaz.kopyala(enerjiEgrisi: id)), ), _YapiBolumu(sihirbaz: sihirbaz, notifier: notifier), _NegatifBolumu(sihirbaz: sihirbaz, notifier: notifier), const SizedBox(height: 12), _SihirbazSlider( etiket: 'Tempo', deger: sihirbaz.bpm.toDouble(), min: 50, max: 220, bolum: 170, gosterim: '${sihirbaz.bpm} BPM', renk: FikoRenkler.neonCyan, onDegisti: (v) => notifier.sihirbazGuncelle(sihirbaz.kopyala(bpm: v.round())), ), _SihirbazSlider( etiket: 'Enerji', deger: sihirbaz.enerji.toDouble(), min: 0, max: 100, bolum: 100, gosterim: '${sihirbaz.enerji} (CFG ${sihirbaz.cfg.toStringAsFixed(1)})', renk: FikoRenkler.neonPembe, onDegisti: (v) => notifier.sihirbazGuncelle(sihirbaz.kopyala(enerji: v.round())), ), _SihirbazSlider( etiket: 'Yaratıcılık', deger: sihirbaz.yaraticilik, min: 0, max: 1.5, bolum: 30, gosterim: '${sihirbaz.yaraticilik.toStringAsFixed(2)} (temp ${sihirbaz.lmTemperature})', renk: FikoRenkler.neonAmber, onDegisti: (v) => notifier.sihirbazGuncelle(sihirbaz.kopyala(yaraticilik: v)), ), const SizedBox(height: 8), TextFormField( initialValue: sihirbaz.ekstraNot, decoration: const InputDecoration( labelText: 'Ekstra not (İngilizce, prompt sonuna eklenir)', hintText: 'catchy chorus, radio-ready mix...', ), maxLines: 2, onChanged: (v) => notifier.sihirbazGuncelle(sihirbaz.kopyala(ekstraNot: v)), ), ], ); } } /// Tek seçimli katalog bölümü (ör. prodüksiyon kalitesi). Boş = seçimsiz. class _TekSecimBolumu extends StatelessWidget { const _TekSecimBolumu({ required this.baslik, required this.ikon, required this.ogeler, required this.secili, required this.onSec, this.renk, }); final String baslik; final IconData ikon; final List ogeler; final String secili; final ValueChanged onSec; final Color? renk; @override Widget build(BuildContext context) { return _Bolum( baslik: baslik, ikon: ikon, renk: renk, cocuk: Wrap( spacing: 5, runSpacing: 5, children: [ for (final o in ogeler) _MiniCip( etiket: o.etiketTr, secili: secili == o.id, renk: renk, // Aynısına tekrar tıklayınca seçim kalkar. onTap: () => onSec(secili == o.id ? '' : o.id), ), ], ), ); } } /// Şarkı yapısı toggle'ları + nakarat tekrarı. class _YapiBolumu extends StatelessWidget { const _YapiBolumu({required this.sihirbaz, required this.notifier}); final SihirbazSecimleri sihirbaz; final MuzikFormNotifier notifier; @override Widget build(BuildContext context) { return _Bolum( baslik: 'Söz Yapısı', ikon: Icons.account_tree, renk: FikoRenkler.neonYesil, cocuk: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Wrap( spacing: 5, runSpacing: 5, children: [ _ToggleCip( etiket: 'Enstrümantal ara (solo)', secili: sihirbaz.enstrumantalAra, onTap: () => notifier.sihirbazGuncelle(sihirbaz.kopyala( enstrumantalAra: !sihirbaz.enstrumantalAra)), ), _ToggleCip( etiket: 'Temiz (radyo edit)', secili: sihirbaz.temizMi, onTap: () => notifier.sihirbazGuncelle( sihirbaz.kopyala(temizMi: !sihirbaz.temizMi)), ), ], ), const SizedBox(height: 8), Row( children: [ const Text('Nakarat tekrarı:', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600)), const SizedBox(width: 10), for (final (id, etiket) in const [ ('az', 'Az'), ('orta', 'Orta'), ('cok', 'Çok'), ]) Padding( padding: const EdgeInsets.only(right: 5), child: _MiniCip( etiket: etiket, secili: sihirbaz.nakaratTekrari == id, renk: FikoRenkler.neonYesil, onTap: () => notifier.sihirbazGuncelle( sihirbaz.kopyala(nakaratTekrari: id)), ), ), ], ), ], ), ); } } /// Negatif prompt: otomatik (mood bazlı) toggle + serbest metin. class _NegatifBolumu extends StatelessWidget { const _NegatifBolumu({required this.sihirbaz, required this.notifier}); final SihirbazSecimleri sihirbaz; final MuzikFormNotifier notifier; @override Widget build(BuildContext context) { return _Bolum( baslik: 'Negatif Prompt', ikon: Icons.block, renk: const Color(0xFFFF5370), baslangictaAcik: false, cocuk: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ _ToggleCip( etiket: 'Mood bazlı otomatik', secili: sihirbaz.otoNegatif, onTap: () => notifier.sihirbazGuncelle( sihirbaz.kopyala(otoNegatif: !sihirbaz.otoNegatif)), ), ], ), const SizedBox(height: 6), TextFormField( initialValue: sihirbaz.negatifPrompt, decoration: const InputDecoration( labelText: 'Ek negatif (İngilizce)', hintText: 'harsh, screaming, off-key...', ), maxLines: 2, onChanged: (v) => notifier .sihirbazGuncelle(sihirbaz.kopyala(negatifPrompt: v)), ), const SizedBox(height: 6), Text( 'Sonuç: ${sihirbaz.negatifDerle()}', maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 10.5, color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.5)), ), ], ), ); } } /// Açma/kapama çipi (onay ikonlu). class _ToggleCip extends StatelessWidget { const _ToggleCip( {required this.etiket, required this.secili, required this.onTap}); final String etiket; final bool secili; final VoidCallback onTap; @override Widget build(BuildContext context) { final tema = Theme.of(context); return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 120), padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 4), decoration: BoxDecoration( color: secili ? FikoRenkler.neonYesil.withValues(alpha: 0.2) : tema.colorScheme.surface, borderRadius: BorderRadius.circular(7), border: Border.all( color: secili ? FikoRenkler.neonYesil : tema.dividerColor, width: secili ? 1.2 : 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(secili ? Icons.check_circle : Icons.circle_outlined, size: 13, color: secili ? FikoRenkler.neonYesil : tema.colorScheme.onSurface.withValues(alpha: 0.4)), const SizedBox(width: 5), Text(etiket, style: TextStyle( fontSize: 11.5, fontWeight: secili ? FontWeight.w700 : FontWeight.w500, color: secili ? FikoRenkler.neonYesil : tema.colorScheme.onSurface)), ], ), ), ); } } class _VokalModuSecici extends StatelessWidget { const _VokalModuSecici({required this.sihirbaz, required this.notifier}); final SihirbazSecimleri sihirbaz; final MuzikFormNotifier notifier; @override Widget build(BuildContext context) { return _Bolum( baslik: 'Vokal', ikon: Icons.mic, cocuk: Wrap( spacing: 5, runSpacing: 5, children: [ for (final m in MuzikKataloglari.vokalModlari) _MiniCip( etiket: m.etiketTr, secili: sihirbaz.vokalModu == m.id, renk: FikoRenkler.neonCyan, onTap: () => notifier.sihirbazGuncelle(sihirbaz.kopyala(vokalModu: m.id)), ), ], ), ); } } class _KatalogBolumu extends StatefulWidget { const _KatalogBolumu({ required this.baslik, required this.ikon, required this.ogeler, required this.secili, required this.onDegisti, this.renk, this.aramali = false, }); final String baslik; final IconData ikon; final List ogeler; final Set secili; final ValueChanged> onDegisti; final Color? renk; final bool aramali; @override State<_KatalogBolumu> createState() => _KatalogBolumuState(); } class _KatalogBolumuState extends State<_KatalogBolumu> { String _arama = ''; @override Widget build(BuildContext context) { final secili = widget.secili; final filtreli = _arama.isEmpty ? widget.ogeler : widget.ogeler .where((o) => o.etiketTr.toLowerCase().contains(_arama) || o.promptEn.toLowerCase().contains(_arama)) .toList(); return _Bolum( baslik: secili.isEmpty ? widget.baslik : '${widget.baslik} (${secili.length})', ikon: widget.ikon, renk: widget.renk, cocuk: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (widget.aramali) Padding( padding: const EdgeInsets.only(bottom: 6), child: SizedBox( height: 30, child: TextField( style: const TextStyle(fontSize: 12), decoration: const InputDecoration( isDense: true, prefixIcon: Icon(Icons.search, size: 15), prefixIconConstraints: BoxConstraints(minWidth: 30, minHeight: 30), hintText: 'Sanatçı ara...', contentPadding: EdgeInsets.symmetric(vertical: 6), ), onChanged: (v) => setState(() => _arama = v.toLowerCase().trim()), ), ), ), Wrap( spacing: 5, runSpacing: 5, children: [ for (final o in filtreli) _MiniCip( etiket: o.etiketTr, secili: secili.contains(o.id), renk: widget.renk, onTap: () { final yeni = Set.from(secili); yeni.contains(o.id) ? yeni.remove(o.id) : yeni.add(o.id); widget.onDegisti(yeni); }, ), ], ), ], ), ); } } /// Kompakt seçim çipi — minimal padding/font (Ant yoğunluğu). class _MiniCip extends StatelessWidget { const _MiniCip({ required this.etiket, required this.secili, required this.onTap, this.renk, }); final String etiket; final bool secili; final VoidCallback onTap; final Color? renk; @override Widget build(BuildContext context) { final tema = Theme.of(context); final vurgu = renk ?? FikoRenkler.neonMor; return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 120), padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 4), decoration: BoxDecoration( color: secili ? vurgu.withValues(alpha: 0.22) : tema.colorScheme.surface, borderRadius: BorderRadius.circular(7), border: Border.all( color: secili ? vurgu : tema.dividerColor, width: secili ? 1.2 : 1, ), ), child: Text( etiket, style: TextStyle( fontSize: 11.5, height: 1.1, fontWeight: secili ? FontWeight.w700 : FontWeight.w500, color: secili ? vurgu : tema.colorScheme.onSurface, ), ), ), ); } } class _EnstrumanBolumu extends StatelessWidget { const _EnstrumanBolumu({required this.sihirbaz, required this.notifier}); final SihirbazSecimleri sihirbaz; final MuzikFormNotifier notifier; @override Widget build(BuildContext context) { final secili = sihirbaz.enstrumanlar; return _Bolum( baslik: secili.isEmpty ? 'Enstrümanlar' : 'Enstrümanlar (${secili.length})', ikon: Icons.piano, baslangictaAcik: false, cocuk: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (final grup in MuzikKataloglari.enstrumanGruplari) ...[ Padding( padding: const EdgeInsets.only(top: 7, bottom: 3), child: Text(grup.ad, style: TextStyle( fontSize: 10.5, fontWeight: FontWeight.w700, color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.55))), ), Wrap( spacing: 5, runSpacing: 5, children: [ for (final o in grup.ogeler) _MiniCip( etiket: o.etiketTr, secili: secili.contains(o.id), renk: FikoRenkler.neonYesil, onTap: () { final yeni = Set.from(secili); yeni.contains(o.id) ? yeni.remove(o.id) : yeni.add(o.id); notifier.sihirbazGuncelle( sihirbaz.kopyala(enstrumanlar: yeni)); }, ), ], ), ], ], ), ); } } class _Bolum extends StatelessWidget { const _Bolum({ required this.baslik, required this.ikon, required this.cocuk, this.baslangictaAcik = true, this.renk, }); final String baslik; final IconData ikon; final Widget cocuk; final bool baslangictaAcik; final Color? renk; @override Widget build(BuildContext context) { final vurgu = renk ?? FikoRenkler.neonMor; return Theme( data: Theme.of(context).copyWith(dividerColor: Colors.transparent), child: ExpansionTile( initiallyExpanded: baslangictaAcik, dense: true, visualDensity: VisualDensity.compact, tilePadding: EdgeInsets.zero, minTileHeight: 34, childrenPadding: const EdgeInsets.only(bottom: 6), leading: Icon(ikon, size: 16, color: vurgu), title: Text(baslik, style: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w700)), children: [Align(alignment: Alignment.centerLeft, child: cocuk)], ), ); } } class _SihirbazSlider extends StatelessWidget { const _SihirbazSlider({ required this.etiket, required this.deger, required this.min, required this.max, required this.bolum, required this.gosterim, required this.renk, required this.onDegisti, }); final String etiket; final double deger; final double min; final double max; final int bolum; final String gosterim; final Color renk; final ValueChanged onDegisti; @override Widget build(BuildContext context) { return Row( children: [ SizedBox( width: 78, child: Text(etiket, style: const TextStyle( fontSize: 12.5, fontWeight: FontWeight.w600))), Expanded( child: SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: renk, thumbColor: renk, overlayColor: renk.withValues(alpha: 0.15), ), child: Slider( value: deger.clamp(min, max), min: min, max: max, divisions: bolum, onChanged: onDegisti, ), ), ), SizedBox( width: 110, child: Text(gosterim, textAlign: TextAlign.right, style: TextStyle(fontSize: 11.5, color: renk)), ), ], ); } }