File size: 48,988 Bytes
1e8ef1e
 
b331feb
 
c2f2113
242352d
 
e96e7e6
c2f2113
e96e7e6
242352d
c2f2113
 
 
 
 
242352d
 
 
c2f2113
b331feb
c2f2113
 
b331feb
c2f2113
 
 
 
47062da
c2f2113
b331feb
c2f2113
 
 
 
 
 
 
cb9ffee
242352d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c2f2113
 
 
 
 
 
 
 
 
 
 
242352d
 
 
 
 
c2f2113
 
 
 
 
 
47062da
c2f2113
47062da
c2f2113
242352d
c2f2113
e96e7e6
7f64d0d
 
 
 
e96e7e6
7f64d0d
 
 
 
 
c2f2113
 
0d0522c
7f64d0d
 
 
a2fd734
7f64d0d
e96e7e6
7f64d0d
 
 
 
 
 
 
 
 
 
e96e7e6
0d0522c
 
 
 
7f64d0d
1bfceb0
c2f2113
 
 
 
 
e96e7e6
c2f2113
32acbf3
c2f2113
1bfceb0
c2f2113
242352d
 
 
c2f2113
1bfceb0
c2f2113
 
e96e7e6
 
2a03676
1bfceb0
c2f2113
 
e96e7e6
 
1bfceb0
2a03676
c2f2113
 
 
 
 
 
 
 
 
242352d
c2f2113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b5439f6
1bfceb0
b5439f6
 
 
 
242352d
b5439f6
 
 
 
 
 
1bfceb0
b5439f6
 
 
c2f2113
 
 
 
 
 
 
1bfceb0
c2f2113
242352d
 
c2f2113
242352d
 
 
 
 
 
 
0d0522c
242352d
 
 
 
 
 
 
 
 
 
 
 
7f64d0d
 
 
242352d
 
b5439f6
c2f2113
 
242352d
 
 
 
c2f2113
e96e7e6
c2f2113
 
 
 
 
 
 
 
 
1bfceb0
 
 
 
 
 
c2f2113
1bfceb0
c2f2113
242352d
c2f2113
1bfceb0
 
242352d
1bfceb0
 
 
c2f2113
1bfceb0
e96e7e6
1bfceb0
 
e96e7e6
1bfceb0
e96e7e6
 
 
1bfceb0
e96e7e6
 
 
 
 
 
1bfceb0
e96e7e6
1bfceb0
 
 
 
 
 
 
 
 
 
 
e96e7e6
 
 
1bfceb0
c2f2113
 
 
 
e96e7e6
 
 
c2f2113
 
 
e96e7e6
c2f2113
 
f83e9e1
1bfceb0
f83e9e1
 
 
648f5d4
f83e9e1
1bfceb0
f83e9e1
 
 
 
 
 
 
1bfceb0
f83e9e1
1bfceb0
 
 
c2f2113
 
1bfceb0
 
 
 
 
 
 
 
 
c2f2113
 
1bfceb0
 
 
 
 
 
c2f2113
1bfceb0
 
 
 
 
 
 
 
 
c2f2113
 
1bfceb0
 
 
 
 
 
 
 
 
c2f2113
f83e9e1
1bfceb0
 
 
 
c2f2113
1bfceb0
 
 
 
 
 
f83e9e1
1bfceb0
 
 
 
 
 
 
 
 
 
 
f83e9e1
 
1bfceb0
 
 
 
 
 
 
 
 
c2f2113
f83e9e1
 
1bfceb0
 
 
 
 
 
 
 
 
 
 
 
c2f2113
1bfceb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f83e9e1
 
 
 
 
c2f2113
f83e9e1
 
 
 
c2f2113
f83e9e1
 
 
 
 
 
b331feb
 
 
c2f2113
 
 
 
 
242352d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
# app.py

import gradio as gr
import torch
import logging
import re
import random
from gradio.themes import Soft
import json
import spaces
import gc
from typing import List, Dict, Tuple, Optional, Any
from dataclasses import dataclass
from transformers import (
    pipeline,
    MarianMTModel,
    MarianTokenizer,
    AutoTokenizer,
    AutoModelForSeq2SeqLM
)
from diffusers import StableDiffusionPipeline
import numpy as np
from PIL import Image

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

@dataclass
class StoryTemplate:
    """Story template data structure"""
    name: str
    template_en: str
    template_ar: str
    parameters: List[str]

class MultilingualStoryGenerator:
    """Robust story generation in both English and Arabic with content safety"""
    
    def __init__(self):
        try:
            # Initialize English generator
            self.en_generator = pipeline(
                "text-generation",
                model="EleutherAI/gpt-neo-1.3B",
                device="cpu"
            )
            logger.info("Initialized English story generator")
            
            # Initialize dedicated English sentiment analyzer
            self.en_sentiment = pipeline(
                "sentiment-analysis",
                model="distilbert-base-uncased-finetuned-sst-2-english",
                device="cpu"
            )
            logger.info("Initialized English sentiment analyzer")
            
            # Initialize Arabic MT5 model - better for controlled generation
            try:
                self.ar_tokenizer = AutoTokenizer.from_pretrained("google/mt5-small")
                self.ar_model = AutoModelForSeq2SeqLM.from_pretrained("google/mt5-small")
                logger.info("Initialized MT5 for Arabic generation")
                self.ar_model_available = True
            except Exception as e:
                logger.error(f"Failed to load Arabic MT5 model: {str(e)}")
                self.ar_model_available = False
                
            # Initialize Arabic sentiment analyzer if possible
            try:
                self.ar_sentiment = pipeline(
                    "sentiment-analysis",
                    model="CAMeL-Lab/bert-base-arabic-sentiment",
                    device="cpu"
                )
                logger.info("Initialized Arabic sentiment analyzer")
                self.ar_sentiment_available = True
            except Exception as e:
                logger.error(f"Failed to load Arabic sentiment: {str(e)}")
                self.ar_sentiment_available = False
                
            # Initialize translator models
            self.translator_ar_to_en = pipeline(
                "translation",
                model="Helsinki-NLP/opus-mt-ar-en",
                device="cpu"
            )
            logger.info("Initialized Arabic to English translator")
            
            # Attempt to load English to Arabic translator
            try:
                self.translator_en_to_ar = pipeline(
                    "translation",
                    model="Helsinki-NLP/opus-mt-en-ar",
                    device="cpu"
                )
                logger.info("Initialized English to Arabic translator")
                self.en_to_ar_available = True
            except Exception as e:
                logger.error(f"Failed to load EN->AR translator: {str(e)}")
                self.en_to_ar_available = False
                
            # Content filtering regex patterns
            self.banned_patterns = [
                r'sex', r'fuck', r'porn', r'explicit',
                r'malay', r'panama', r'solar', r'queent',
                # Arabic inappropriate terms
                r'جنس', r'فاحش', r'إباحي'
            ]
            
            # Pre-written high-quality templates
            self.story_templates = self._load_templates()
            
            self.initialized = True
        except Exception as e:
            logger.error(f"Failed to initialize MultilingualStoryGenerator: {str(e)}")
            self.initialized = False
    
    def _load_templates(self) -> Dict[str, Dict[str, List[str]]]:
        """Load high-quality story templates in both languages"""
        return {
            "english": {
                "adventure": [
                    "In the ancient ruins of {location}, a brave {hero} discovered a map leading to {object}. The journey wouldn't be easy, as dangerous traps and mythical creatures guarded the path. Armed with courage and determination, {hero} ventured into the unknown. After overcoming numerous challenges, including a treacherous river crossing and a riddle-speaking sphinx, {hero} finally reached the chamber where {object} was kept. This treasure wasn't just valuable - it held the power to change the world.",
                    
                    "The small village of {location} had a legend about {object}, a mysterious artifact said to grant its finder extraordinary powers. {hero}, a curious and adventurous soul, had always been fascinated by this tale. When strange events began occurring in the village, {hero} realized the time had come to seek {object}. The journey through dark forests and misty mountains tested every skill {hero} possessed. But with each obstacle overcome, {hero} grew stronger and wiser, eventually discovering that the true power of {object} was not what anyone had expected.",
                    
                    "{hero} had spent years studying ancient texts about {object}, hidden somewhere in {location}. Scholars had dismissed these stories as myths, but {hero} knew better. When troubling signs appeared in the sky, {hero} recognized them from the texts - it was time to find {object}. The expedition into {location} revealed dangers and wonders beyond imagination. {hero} faced not only physical challenges but moral dilemmas that questioned everything {hero} believed. In the final chamber, {hero} discovered that {object} was actually a gateway to knowledge that would forever change humanity's understanding of the universe."
                ],
                "friendship": [
                    "In a quiet neighborhood, a small cat named {character1} lived alone, scavenging for food and shelter. One rainy day, {character1} discovered an abandoned robot called {character2} in an alley, damaged and powered down. Curious, {character1} stayed nearby until a kind engineer reactivated {character2}. As {character2} came back to life, it was confused and disoriented. {character1}, despite being initially cautious, started visiting {character2} daily. Slowly, an unusual friendship formed. {character2} would protect {character1} from the neighborhood dogs, while {character1} would guide {character2} through the city streets. They taught each other about their very different perspectives on life, proving that friendship can transcend even the greatest differences.",
                    
                    "{character1} was the most advanced robot created by Future Technologies, designed to learn and evolve. However, {character1} felt something was missing in its programmed existence. One day, a stray cat named {character2} wandered into the laboratory. Instead of shooing it away, {character1} offered it food. Day after day, {character2} returned, and {character1} began to experience emotions not in its programming. When the company decided to reset {character1}'s learning algorithms, {character2} somehow sensed the danger and created a distraction that allowed {character1} to escape. Together, they embarked on an adventure that would redefine what it means to be alive and to care for another being.",
                    
                    "After a technical malfunction, {character1}, an experimental domestic robot, escaped from the research facility and hid in an abandoned building. There, it encountered {character2}, a street-smart cat who initially hissed and clawed at the strange metal creature. But when winter came and temperatures dropped, {character1} used its internal heating to keep {character2} warm. Gradually, {character2} began to trust the gentle robot. When researchers finally tracked down {character1}, they were amazed to find it had developed emotional responses through its friendship with {character2}. Instead of reclaiming the robot, they decided to study this unique bond, learning valuable lessons about connection and compassion that would transform robotics forever."
                ],
                "fantasy": [
                    "In the enchanted kingdom of {place}, young {protagonist} lived an ordinary life until the day of the Great Storm. As lightning struck the ancient oak tree in the village square, {protagonist} felt a strange tingling sensation. Suddenly, {protagonist} could control {magic} - a power not seen in the realm for centuries. But as {protagonist} was discovering these new abilities, darkness was spreading across {place}. The evil sorcerer Malakar had returned and was draining the life force from the land. With the guidance of a wise old wizard, {protagonist} learned to harness {magic}, gathering allies and growing stronger. In a final confrontation that lit up the sky, {protagonist} faced Malakar, using {magic} in ways never imagined to save {place} and restore balance to the world.",
                    
                    "{protagonist} had always felt different from the other inhabitants of {place}. Strange dreams haunted the nights, and unexplainable events occurred when emotions ran high. On the day of the centennial festival, as {protagonist} touched the sacred crystal in the town square, a surge of {magic} awakened within. The elders revealed a prophecy: {protagonist} was the chosen one who must save {place} from the shadow that emerges once every hundred years. With newfound abilities of {magic}, {protagonist} journeyed to the forbidden mountains, discovering ancient truths about {place}'s history and the true nature of the shadow. The final battle tested not just {protagonist}'s command of {magic}, but also wisdom, compassion, and the courage to make the ultimate sacrifice for {place}.",
                    
                    "The floating islands of {place} were held together by ancient magic, but the bridges between them had begun to crumble. {protagonist}, a student at the Academy of Mystic Arts, accidentally discovered an ability to control {magic} during a failed classroom experiment. This power, thought to be extinct, marked {protagonist} as the heir to the Founder's legacy. As {place} began to literally fall apart, {protagonist} had to master {magic} while evading the Void Seekers, who believed destroying {place} would usher in a new world order. Through forgotten ruins and secret libraries, {protagonist} pieced together the complex spell that could restore {place}, discovering that {magic} was actually the lifeforce that connected all the inhabitants of the floating realm."
                ]
            },
            "arabic": {
                "adventure": [
                    "في آثار {location} القديمة، اكتشف {hero} الشجاع خريطة تقود إلى {object}. لم تكن الرحلة سهلة، حيث كانت الفخاخ الخطيرة والمخلوقات الأسطورية تحرس الطريق. مسلحًا بالشجاعة والتصميم، غامر {hero} في المجهول. بعد التغلب على تحديات عديدة، بما في ذلك عبور نهر خطير وأبو الهول الذي يتحدث بالألغاز، وصل {hero} أخيرًا إلى الغرفة التي يُحفظ فيها {object}. لم يكن هذا الكنز قيمًا فحسب - بل كان يملك القدرة على تغيير العالم.",
                    
                    "كانت للقرية الصغيرة {location} أسطورة عن {object}، وهي قطعة أثرية غامضة يُقال إنها تمنح من يجدها قوى استثنائية. كان {hero}، وهو شخص فضولي ومغامر، مفتونًا دائمًا بهذه القصة. عندما بدأت أحداث غريبة تحدث في القرية، أدرك {hero} أن الوقت قد حان للبحث عن {object}. اختبرت الرحلة عبر الغابات المظلمة والجبال الضبابية كل مهارة يمتلكها {hero}. لكن مع كل عقبة تم التغلب عليها، أصبح {hero} أقوى وأكثر حكمة، ليكتشف في النهاية أن القوة الحقيقية لـ {object} لم تكن كما توقع أي شخص.",
                    
                    "قضى {hero} سنوات في دراسة النصوص القديمة حول {object}، المخفي في مكان ما في {location}. رفض العلماء هذه القصص باعتبارها أساطير، لكن {hero} كان يعرف الحقيقة. عندما ظهرت علامات مقلقة في السماء، تعرف عليها {hero} من النصوص - حان الوقت للعثور على {object}. كشفت البعثة إلى {location} عن مخاطر وعجائب تفوق الخيال. واجه {hero} ليس فقط التحديات الجسدية ولكن أيضًا المعضلات الأخلاقية التي شككت في كل ما اعتقده {hero}. في الغرفة الأخيرة، اكتشف {hero} أن {object} كان في الواقع بوابة إلى المعرفة التي من شأنها أن تغير إلى الأبد فهم البشرية للكون."
                ],
                "friendship": [
                    "في حي هادئ، عاشت قطة صغيرة تدعى {character1} وحدها، تبحث عن الطعام والمأوى. في يوم ممطر، اكتشفت {character1} روبوتًا مهجورًا يدعى {character2} في زقاق، متضررًا ومتوقفًا عن العمل. بدافع الفضول، بقيت {character1} في الجوار حتى قام مهندس لطيف بإعادة تنشيط {character2}. عندما عادت {character2} إلى الحياة، كانت مرتبكة وغير مستقرة. بدأت {character1}، رغم حذرها المبدئي، في زيارة {character2} يوميًا. ببطء، تشكلت صداقة غير عادية. كانت {character2} تحمي {character1} من كلاب الحي، بينما كانت {character1} ترشد {character2} عبر شوارع المدينة. علّم كل منهما الآخر عن وجهات نظرهما المختلفة جدًا في الحياة، مما يثبت أن الصداقة يمكن أن تتجاوز حتى أكبر الاختلافات.",
                    
                    "كانت {character1} أكثر الروبوتات تطوراً التي أنشأتها شركة تكنولوجيا المستقبل، مصممة للتعلم والتطور. ومع ذلك، شعرت {character1} بأن هناك شيئًا مفقودًا في وجودها المبرمج. في أحد الأيام، تسللت قطة ضالة تدعى {character2} إلى المختبر. بدلاً من طردها، قدمت لها {character1} الطعام. يومًا بعد يوم، عادت {character2}، وبدأت {character1} تشعر بعواطف لم تكن في برمجتها. عندما قررت الشركة إعادة ضبط خوارزميات التعلم الخاصة بـ {character1}، استشعرت {character2} الخطر بطريقة ما وخلقت تشتيتًا سمح لـ {character1} بالهروب. معًا، شرعا في مغامرة من شأنها إعادة تعريف معنى الحياة والاهتمام بكائن آخر.",
                    
                    "بعد عطل فني، هربت {character1}، وهي روبوت منزلي تجريبي، من منشأة الأبحاث واختبأت في مبنى مهجور. هناك، واجهت {character2}، وهي قطة ذكية في الشارع هسهست في البداية وخدشت المخلوق المعدني الغريب. ولكن عندما جاء الشتاء وانخفضت درجات الحرارة، استخدمت {character1} التدفئة الداخلية للحفاظ على دفء {character2}. تدريجيًا، بدأت {character2} تثق في الروبوت اللطيف. عندما عثر الباحثون أخيرًا على {character1}، ذهلوا عندما وجدوا أنها طورت استجابات عاطفية من خلال صداقتها مع {character2}. وبدلاً من استعادة الروبوت، قرروا دراسة هذه العلاقة الفريدة، مما أدى إلى تعلم دروس قيمة حول الارتباط والتعاطف من شأنها أن تغير علم الروبوتات إلى الأبد."
                ],
                "fantasy": [
                    "في مملكة {place} المسحورة، عاش {protagonist} الشاب حياة عادية حتى يوم العاصفة الكبرى. عندما ضرب البرق شجرة البلوط القديمة في ساحة القرية، شعر {protagonist} بإحساس غريب من الوخز. فجأة، أصبح {protagonist} قادرًا على التحكم في {magic} - وهي قوة لم تظهر في المملكة منذ قرون. ولكن بينما كان {protagonist} يكتشف هذه القدرات الجديدة، كان الظلام ينتشر في أنحاء {place}. لقد عاد الساحر الشرير مالاكار وكان يستنزف قوة الحياة من الأرض. بتوجيه من ساحر عجوز حكيم، تعلم {protagonist} ترويض {magic}، وجمع الحلفاء وأصبح أقوى. في مواجهة نهائية أضاءت السماء، واجه {protagonist} مالاكار، واستخدم {magic} بطرق لم تكن متخيلة من قبل لإنقاذ {place} واستعادة التوازن إلى العالم.",
                    
                    "شعر {protagonist} دائمًا بأنه مختلف عن سكان {place} الآخرين. طاردت الأحلام الغريبة الليالي، ووقعت أحداث غير قابلة للتفسير عندما كانت المشاعر مرتفعة. في يوم المهرجان المئوي، عندما لمس {protagonist} الكريستال المقدس في ساحة البلدة، استيقظت موجة من {magic} بداخله. كشف الشيوخ عن نبوءة: كان {protagonist} هو المختار الذي يجب أن ينقذ {place} من الظل الذي يظهر مرة واحدة كل مائة عام. مع القدرات الجديدة لـ {magic}، سافر {protagonist} إلى الجبال المحرمة، واكتشف حقائق قديمة حول تاريخ {place} والطبيعة الحقيقية للظل. اختبرت المعركة النهائية ليس فقط تحكم {protagonist} في {magic}، ولكن أيضًا الحكمة والتعاطف والشجاعة لتقديم التضحية النهائية من أجل {place}.",
                    
                    "كانت الجزر العائمة في {place} متماسكة بفضل السحر القديم، لكن الجسور بينها بدأت تتفكك. اكتشف {protagonist}، وهو طالب في أكاديمية الفنون الصوفية، عن طريق الصدفة قدرة على التحكم في {magic} خلال تجربة فاشلة في الفصل الدراسي. هذه القوة، التي كان يعتقد أنها انقرضت، وضعت علامة على {protagonist} كوريث لإرث المؤسس. مع بدء {place} في التفكك حرفيًا، كان على {protagonist} إتقان {magic} مع تجنب المتطلعين إلى الفراغ، الذين اعتقدوا أن تدمير {place} سيؤدي إلى نظام عالمي جديد. من خلال الأنقاض المنسية والمكتبات السرية، جمع {protagonist} أجزاء التعويذة المعقدة التي يمكن أن تعيد {place}، ليكتشف أن {magic} كان في الواقع قوة الحياة التي تربط جميع سكان المملكة العائمة."
                ]
            }
        }
    
    def is_safe_content(self, text: str) -> bool:
        """Check if content is safe to display (basic pattern filtering)"""
        # Check against banned patterns
        for pattern in self.banned_patterns:
            if re.search(pattern, text.lower()):
                logger.warning(f"Content filtered - banned pattern detected")
                return False
                
        # Check for repetitive patterns (sign of model failure)
        words = text.split()
        if len(words) > 20:
            unique_ratio = len(set(words)) / len(words)
            if unique_ratio < 0.4:  # Less than 40% unique words
                logger.warning(f"Content filtered - repetitive content (unique ratio: {unique_ratio})")
                return False
        
        return True
        
    def detect_language(self, text: str) -> str:
        """Detect if text is primarily Arabic or English"""
        arabic_char_count = sum(1 for char in text if '\u0600' <= char <= '\u06FF')
        return "arabic" if arabic_char_count > len(text) * 0.3 else "english"
        
    def analyze_sentiment(self, text: str, language: str) -> Dict[str, float]:
        """Analyze sentiment in the specified language"""
        try:
            if language == "arabic":
                if self.ar_sentiment_available:
                    result = self.ar_sentiment(text[:512])[0]
                    sentiment_label = result['label'] 
                    sentiment_score = float(result['score'])
                    
                    # Convert to standard format
                    if "positive" in sentiment_label.lower():
                        return {"positive": sentiment_score, "negative": 1-sentiment_score}
                    elif "negative" in sentiment_label.lower():
                        return {"negative": sentiment_score, "positive": 1-sentiment_score}
                    else:
                        return {"neutral": sentiment_score}
                else:
                    # Translate to English and use English sentiment
                    translated = self.translate(text, "arabic", "english")
                    return self.analyze_sentiment(translated, "english")
            else:
                # English sentiment analysis
                result = self.en_sentiment(text[:512])[0]
                sentiment_score = float(result['score'])
                
                if result['label'] == 'POSITIVE':
                    return {"positive": sentiment_score, "negative": 1-sentiment_score}
                else:
                    return {"negative": sentiment_score, "positive": 1-sentiment_score}
                
        except Exception as e:
            logger.error(f"Sentiment analysis error: {str(e)}")
            return {"neutral": 1.0}
            
    def translate(self, text: str, from_lang: str, to_lang: str) -> str:
        """Translate between languages"""
        try:
            if from_lang == to_lang:
                return text
                
            if from_lang == "arabic" and to_lang == "english":
                result = self.translator_ar_to_en(text[:512])
                return result[0]['translation_text']
            elif from_lang == "english" and to_lang == "arabic":
                if self.en_to_ar_available:
                    result = self.translator_en_to_ar(text[:512])
                    return result[0]['translation_text']
                else:
                    logger.warning("English to Arabic translation not available")
                    return text
            else:
                return text
        except Exception as e:
            logger.error(f"Translation error: {str(e)}")
            return text
            
    def _select_template(self, prompt: str, language: str) -> Tuple[str, Dict[str, str]]:
        """Select most appropriate template based on prompt keywords"""
        # Lower case for matching
        prompt_lower = prompt.lower()
        
        # Check for keywords to match
        template_type = "adventure"  # Default
        
        # Adventure keywords
        adventure_terms = ["adventure", "explorer", "ruins", "ancient", "discover", "treasure", "quest",
                          "مغامرة", "مستكشف", "آثار", "قديمة", "اكتشاف", "كنز"]
                          
        # Friendship keywords
        friendship_terms = ["friend", "friendship", "relationship", "together", "bond", "cat", "robot",
                           "صداقة", "صديق", "علاقة", "معًا", "رابطة", "قطة", "روبوت"]
                           
        # Fantasy keywords
        fantasy_terms = ["magic", "wizard", "spell", "kingdom", "power", "mystical", "enchanted",
                        "سحر", "ساحر", "تعويذة", "مملكة", "قوة", "غامض", "مسحور"]
        
        # Count matches
        adventure_matches = sum(1 for term in adventure_terms if term in prompt_lower)
        friendship_matches = sum(1 for term in friendship_terms if term in prompt_lower)
        fantasy_matches = sum(1 for term in fantasy_terms if term in prompt_lower)
        
        # Select template with most matches
        if friendship_matches > adventure_matches and friendship_matches > fantasy_matches:
            template_type = "friendship"
        elif fantasy_matches > adventure_matches and fantasy_matches > friendship_matches:
            template_type = "fantasy"
            
        # Extract potential parameters based on template type
        params = {}
        
        if template_type == "adventure":
            # Try to extract hero, object, location from prompt
            hero_match = re.search(r"(about|عن)\s+(?:a|an|the)?\s*(\w+)", prompt_lower)
            if hero_match:
                params["hero"] = hero_match.group(2).capitalize()
            else:
                params["hero"] = "the adventurer" if language == "english" else "المغامر"
                
            # Try to find object
            object_match = re.search(r"(find|discover|يجد|يكتشف)\s+(?:a|an|the)?\s*(\w+)", prompt_lower)
            if object_match:
                params["object"] = object_match.group(2)
            else:
                params["object"] = "lost treasure" if language == "english" else "الكنز المفقود"
                
            # Try to find location
            location_match = re.search(r"(in|at|في)\s+(?:a|an|the)?\s*(\w+)", prompt_lower)
            if location_match:
                params["location"] = location_match.group(2)
            else:
                params["location"] = "ancient temple" if language == "english" else "المعبد القديم"
                
        elif template_type == "friendship":
            # Handle friendship template parameters
            params["character1"] = "Whiskers" if language == "english" else "مشمش"
            params["character2"] = "Bolt" if language == "english" else "بولت"
            
            # Look for character mentions
            char_match = re.search(r"(between|بين)\s+(?:a|an|the)?\s*(\w+)", prompt_lower)
            if char_match:
                params["character1"] = char_match.group(2).capitalize()
                
        elif template_type == "fantasy":
            # Handle fantasy template parameters
            params["protagonist"] = "young wizard" if language == "english" else "الساحر الشاب"
            params["magic"] = "elemental magic" if language == "english" else "سحر العناصر"
            params["place"] = "mystical kingdom" if language == "english" else "المملكة السحرية"
            
            # Look for character mentions
            protag_match = re.search(r"(about|عن)\s+(?:a|an|the)?\s*(\w+)", prompt_lower)
            if protag_match:
                params["protagonist"] = protag_match.group(2).capitalize()
                
        return template_type, params
    
    def _customize_template(self, template: str, params: Dict[str, str]) -> str:
        """Fill template with parameters"""
        for key, value in params.items():
            placeholder = "{" + key + "}"
            template = template.replace(placeholder, value)
        return template
        
    def generate_story(
        self, 
        prompt: str, 
        language: str,
        template_name: Optional[str] = None,
        parameters: Optional[Dict[str, str]] = None
    ) -> Tuple[str, Dict[str, float]]:
        """Generate a story in the specified language with sentiment analysis"""
        if not self.initialized:
            error_msg = "Story generator was not properly initialized."
            return error_msg, {"neutral": 1.0}
            
        try:
            # Normalize language
            language = language.lower()
            if language not in ["english", "arabic"]:
                language = "english"
                
            # Detect input language and translate prompt if needed
            input_language = self.detect_language(prompt)
            if input_language != language:
                prompt = self.translate(prompt, input_language, language)
                
            # If template and parameters are provided, use them
            if template_name and parameters:
                if template_name in self.story_templates[language]:
                    # Get random template of the specified type
                    template_options = self.story_templates[language][template_name]
                    template = random.choice(template_options)
                    story = self._customize_template(template, parameters)
                else:
                    # Fallback to adventure if template not found
                    template = random.choice(self.story_templates[language]["adventure"])
                    story = self._customize_template(template, parameters)
            else:
                # Determine best template and parameters from prompt
                template_type, params = self._select_template(prompt, language)
                
                # Get random template of determined type
                template_options = self.story_templates[language][template_type]
                template = random.choice(template_options)
                
                # Fill template with extracted/default parameters
                story = self._customize_template(template, params)
                
            # Safety check
            if not self.is_safe_content(story):
                # Fallback to safe template
                template = random.choice(self.story_templates[language]["adventure"])
                default_params = {
                    "hero": "brave explorer" if language == "english" else "المستكشف الشجاع",
                    "object": "ancient artifact" if language == "english" else "القطعة الأثرية القديمة",
                    "location": "mysterious ruins" if language == "english" else "الآثار الغامضة"
                }
                story = self._customize_template(template, default_params)
                
            # Analyze sentiment
            emotions = self.analyze_sentiment(story, language)
            
            return story, emotions
            
        except Exception as e:
            logger.error(f"Story generation error: {str(e)}")
            # Return fallback story
            if language == "arabic":
                return "كان يا ما كان، في قديم الزمان، قصة لم تكتمل بعد...", {"neutral": 1.0}
            else:
                return "Once upon a time, there was a story waiting to be told...", {"neutral": 1.0}

class StoryManager:
    """Manages story templates and generation"""
    
    def __init__(self):
        self.templates = {
            "adventure": StoryTemplate(
                "Adventure Quest",
                "Tell a story about {hero} who must find {object} in {location}",
                "احكِ قصة عن {hero} الذي يجب أن يجد {object} في {location}",
                ["hero", "object", "location"]
            ),
            "friendship": StoryTemplate(
                "Friendship Tale",
                "Write about a friendship between {character1} and {character2}",
                "اكتب عن صداقة بين {character1} و {character2}",
                ["character1", "character2"]
            ),
            "fantasy": StoryTemplate(
                "Fantasy World",
                "Create a tale where {protagonist} discovers they have the power of {magic} and must save {place}",
                "اخلق قصة حيث يكتشف {protagonist} أن لديه قوة {magic} ويجب عليه إنقاذ {place}",
                ["protagonist", "magic", "place"]
            )
        }

class EmotionAnalyzer:
    """Legacy emotion analyzer class (kept for backward compatibility)"""
    
    def __init__(self):
        try:
            self.classifier = pipeline(
                "sentiment-analysis",
                model="distilbert-base-uncased-finetuned-sst-2-english",
                device="cpu"
            )
            logger.info("Initialized emotion analyzer successfully")
        except Exception as e:
            logger.error(f"Failed to initialize emotion analyzer: {str(e)}")
            self.classifier = None
        
    def analyze_emotions(self, text: str) -> Dict[str, float]:
        try:
            if not self.classifier:
                return {"neutral": 1.0}
                
            result = self.classifier(text[:512])[0]
            sentiment_score = float(result['score'])
            
            if result['label'] == 'POSITIVE':
                emotions = {
                    "positive": sentiment_score,
                    "negative": 1 - sentiment_score
                }
            else:
                emotions = {
                    "negative": sentiment_score,
                    "positive": 1 - sentiment_score
                }
            
            return emotions
            
        except Exception as e:
            logger.error(f"Emotion analysis error: {str(e)}")
            return {"neutral": 1.0}

class AIStoryteller:
    """Main class for story generation and management"""
    
    def __init__(self):
        self.story_manager = StoryManager()
        self.emotion_analyzer = EmotionAnalyzer()
        self.setup_models()

    def setup_models(self):
        """Initialize all required models and pipelines (on CPU by default)"""
        try:
            # Initialize the new multilingual story generator
            self.story_generator = MultilingualStoryGenerator()
            logger.info("Loaded multilingual story generator")
            
            # Translation Model (Arabic->English)
            translator_model_name = "Helsinki-NLP/opus-mt-ar-en"
            self.translator_tokenizer = MarianTokenizer.from_pretrained(translator_model_name)
            self.translator_model = MarianMTModel.from_pretrained(translator_model_name).to("cpu")
            logger.info("Loaded translation model")
    
            # Image Generation Model (Stable Diffusion), remains on CPU until needed
            self.pipe = StableDiffusionPipeline.from_pretrained(
                "runwayml/stable-diffusion-v1-5",
                torch_dtype=torch.float16
            )
            logger.info("Loaded Stable Diffusion pipeline (CPU -> GPU at runtime)")
    
        except Exception as e:
            logger.error(f"Error during model initialization: {str(e)}")
            raise

    def get_story_template(self, template_name: str, language: str) -> str:
        """Get template in specified language"""
        template = self.story_manager.templates.get(template_name)
        if not template:
            raise ValueError(f"Template {template_name} not found")
        return template.template_en if language.lower() == "english" else template.template_ar

    def fill_template(self, template_name: str, language: str, parameters: Dict[str, str]) -> str:
        """Fill template with provided parameters"""
        template = self.get_story_template(template_name, language)
        return template.format(**parameters)

    def translate_ar_to_en(self, text: str) -> str:
        """Translate Arabic text to English"""
        try:
            tokenized = self.translator_tokenizer([text], return_tensors="pt", truncation=True)
            translation = self.translator_model.generate(**tokenized)
            return self.translator_tokenizer.decode(translation[0], skip_special_tokens=True)
        except Exception as e:
            logger.error(f"Translation error: {str(e)}")
            raise ValueError(f"Failed to translate text: {str(e)}")

    def detect_language(self, text: str) -> str:
        """A simple heuristic to detect if text is mostly Arabic or English"""
        arabic_char_count = sum(1 for char in text if '\u0600' <= char <= '\u06FF')
        return "Arabic" if arabic_char_count > len(text) * 0.3 else "English"

    def translate_if_needed(self, text: str, from_lang: str, to_lang: str) -> str:
        """Legacy translate method (kept for backward compatibility)"""
        if from_lang == to_lang:
            return text
        
        if from_lang == "Arabic" and to_lang == "English":
            return self.translate_ar_to_en(text)
        elif from_lang == "English" and to_lang == "Arabic":
            logger.warning("English->Arabic translation not implemented.")
            return text
        return text

    def generate_text(
        self,
        prompt: str,
        language: str,
        template_name: Optional[str] = None,
        parameters: Optional[Dict[str, str]] = None
    ) -> str:
        """Generate text with optional template usage (CPU-based generation)"""
        try:
            # Convert language format to standard format for story generator
            lang = language.lower()
            
            # Generate story with sentiment analysis
            story, emotions = self.story_generator.generate_story(
                prompt, 
                lang,
                template_name, 
                parameters
            )
            
            # Format emotions for display
            if lang == "arabic":
                emotion_summary = "\n\nالنبرة العاطفية:\n"
                for emotion, score in emotions.items():
                    # Translate emotion names to Arabic
                    emotion_ar = {
                        "positive": "إيجابي",
                        "negative": "سلبي",
                        "neutral": "محايد"
                    }.get(emotion, emotion)
                    emotion_summary += f"- {emotion_ar}: {score:.1%}\n"
            else:
                emotion_summary = "\n\nEmotional tones:\n"
                for emotion, score in emotions.items():
                    emotion_summary += f"- {emotion}: {score:.1%}\n"
                    
            return story + emotion_summary
    
        except Exception as e:
            logger.error(f"Text generation error: {str(e)}")
            if language.lower() == "arabic":
                return f"عذراً، حدث خطأ أثناء إنشاء القصة: {str(e)}"
            else:
                return f"Error generating text: {str(e)}"

    @spaces.GPU
    def generate_story_and_scenes(
        self,
        prompt: str,
        language: str,
        template_name: Optional[str] = None,
        parameters: Optional[Dict[str, str]] = None,
        num_scenes: int = 3,
        style: str = "realistic"
    ) -> Tuple[str, List[Image.Image]]:
        """
        Single top-level function decorated with @spaces.GPU.
        1) Generate story on CPU.
        2) Move Stable Diffusion to GPU to generate multiple scenes.
        3) Move pipeline back to CPU at the end.
        """
        try:
            # Step 1: Generate story (CPU)
            story = self.generate_text(prompt, language, template_name, parameters)
            if "Error generating text" in story or "عذراً، حدث خطأ" in story:
                return story, []
            
            # Prepare story for image generation
            if language.lower() == "arabic":
                story_for_image = self.translate_ar_to_en(story)
            else:
                story_for_image = story

            # Step 2: Move pipe to GPU
            self.pipe = self.pipe.to("cuda")

            segments = [seg.strip() for seg in story_for_image.split('.') if seg.strip()]
            if len(segments) < num_scenes:
                selected_segments = segments
            else:
                step = max(1, len(segments) // num_scenes)
                selected_segments = segments[::step][:num_scenes]

            style_prompts = {
                "realistic": "photorealistic, highly detailed, 4k",
                "anime": "anime style, studio ghibli, detailed",
                "fantasy": "fantasy art, magical, detailed illustration"
            }
            
            scenes = []
            for segment in selected_segments:
                prompt_for_image = f"{segment}, {style_prompts.get(style, '')}"
                with torch.no_grad():
                    image = self.pipe(
                        prompt_for_image,
                        num_inference_steps=30,
                        guidance_scale=7.5
                    ).images[0]
                if image:
                    scenes.append(image)

            # Step 3: Move back to CPU
            self.pipe = self.pipe.to("cpu")
            torch.cuda.empty_cache()
            gc.collect()

            return story, scenes

        except Exception as e:
            logger.error(f"Story and scene generation error: {str(e)}")
            if hasattr(self, 'pipe'):
                self.pipe = self.pipe.to("cpu")
            torch.cuda.empty_cache()
            return f"Error generating story and scenes: {str(e)}", []

def build_gradio_interface() -> gr.Blocks:
    """Build and return the Gradio interface"""
    storyteller = AIStoryteller()

    with gr.Blocks(theme=gr.themes.Soft()) as demo:
        # Header
        with gr.Row(variant="panel"):
            gr.Markdown(
                """
                # 📚 AI Bilingual Storyteller & Illustrator
                
                Create engaging stories in English or Arabic, with optional illustrations and emotional analysis.
                
                - 📝 **Basic Story**: Simple text generation
                - 🎯 **Template Story**: Guided story creation
                - 🎨 **Visual Story**: Story with scene illustrations
                """
            )

        # Tabs
        with gr.Tabs() as tabs:
            # Basic Story
            with gr.Tab("📝 Basic Story"):
                gr.Markdown("Enter a prompt, choose a language, and get a story with emotion analysis.")
                
                with gr.Row():
                    prompt_text = gr.Textbox(
                        label="Story Prompt",
                        placeholder="Once upon a time... / في يوم من الأيام...",
                        lines=3
                    )
                    language_choice = gr.Radio(
                        choices=["English", "Arabic"],
                        value="English",
                        label="Language"
                    )
                
                generate_button = gr.Button("✨ Generate Story")
                output_story = gr.Textbox(
                    label="Your Generated Story",
                    lines=8,
                    show_copy_button=True
                )
                
                generate_button.click(
                    fn=storyteller.generate_text,
                    inputs=[prompt_text, language_choice],
                    outputs=output_story
                )

            # Template Story
            with gr.Tab("🎯 Template Story"):
                gr.Markdown("Choose a template and fill parameters for a structured story.")
                
                with gr.Row():
                    template_choice = gr.Dropdown(
                        choices=list(storyteller.story_manager.templates.keys()),
                        label="Story Template",
                        value="adventure"
                    )
                    template_language = gr.Radio(
                        choices=["English", "Arabic"],
                        value="English",
                        label="Language"
                    )
                
                template_params = gr.JSON(
                    label="Template Parameters",
                    value={"hero": "brave knight", "object": "magical sword", "location": "enchanted forest"}
                )
                
                template_button = gr.Button("🎮 Generate from Template")
                template_output = gr.Textbox(
                    label="Your Generated Story",
                    lines=8,
                    show_copy_button=True
                )
                
                template_button.click(
                    fn=lambda t, l, p: storyteller.generate_text(
                        "", l, template_name=t, parameters=p
                    ),
                    inputs=[template_choice, template_language, template_params],
                    outputs=template_output
                )

            # Visual Story
            with gr.Tab("🎨 Visual Story"):
                gr.Markdown("Create a story with multiple illustrated scenes.")
                
                with gr.Row():
                    scene_prompt = gr.Textbox(
                        label="Story Prompt",
                        placeholder="Describe your story scene...",
                        lines=3
                    )
                    scene_language = gr.Radio(
                        choices=["English", "Arabic"],
                        value="English",
                        label="Language"
                    )
                
                with gr.Row():
                    num_scenes = gr.Slider(
                        minimum=1,
                        maximum=5,
                        value=3,
                        step=1,
                        label="Number of Scenes"
                    )
                    art_style = gr.Radio(
                        choices=["realistic", "anime", "fantasy"],
                        value="realistic",
                        label="Art Style",
                        interactive=True
                    )
                
                scene_button = gr.Button("🎭 Generate Story & Scenes")
                scene_story = gr.Textbox(
                    label="Your Generated Story",
                    lines=8,
                    show_copy_button=True
                )
                scene_gallery = gr.Gallery(
                    label="Story Scenes",
                    columns=3,
                    height="auto"
                )
                
                scene_button.click(
                    fn=storyteller.generate_story_and_scenes,
                    inputs=[
                        scene_prompt,
                        scene_language,
                        gr.Textbox(value=None, visible=False),
                        gr.Textbox(value=None, visible=False),
                        num_scenes,
                        art_style
                    ],
                    outputs=[scene_story, scene_gallery]
                )

        # Footer with Examples
        with gr.Row(variant="panel"):
            gr.Markdown("""
                ### Example Prompts
                
                **English:**
                - "Tell a story about a young wizard discovering a magical garden"
                - "Create an adventure about a brave explorer in ancient ruins"
                - "Write about a friendship between a cat and a robot"
                
                **Arabic:**
                - "احكِ قصة عن ساحر صغير يكتشف حديقة سحرية"
                - "اكتب مغامرة عن مستكشف شجاع في آثار قديمة"
                - "اكتب عن صداقة بين قطة وروبوت"
            """)

    return demo

if __name__ == "__main__":
    try:
        demo = build_gradio_interface()
        demo.launch()
    except Exception as e:
        logger.error(f"Application startup error: {str(e)}")
        raise