File size: 26,461 Bytes
6d3a484
 
629ea8d
bdb403f
6d3a484
bdb403f
6d3a484
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bdb403f
 
 
 
 
 
 
a76e273
 
bdb403f
 
 
 
 
6d3a484
3bdf777
bdb403f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a76e273
3bdf777
 
bdb403f
 
3bdf777
bdb403f
3bdf777
bdb403f
 
 
3bdf777
 
 
 
bdb403f
3bdf777
 
bdb403f
3bdf777
bdb403f
 
 
 
 
 
3bdf777
 
 
a76e273
bdb403f
629ea8d
 
 
 
6d3a484
629ea8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6d3a484
bdb403f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6d3a484
bdb403f
 
 
6d3a484
 
 
bdb403f
 
 
6d3a484
bdb403f
 
 
 
6d3a484
 
bdb403f
 
6d3a484
 
 
 
 
 
 
 
 
 
 
 
 
bdb403f
 
 
 
6d3a484
 
bdb403f
 
 
 
6d3a484
bdb403f
6d3a484
bdb403f
 
 
 
 
 
6d3a484
 
 
 
bdb403f
6d3a484
bdb403f
6d3a484
 
 
bdb403f
 
a76e273
bdb403f
 
6d3a484
bdb403f
 
 
 
 
 
6d3a484
 
 
bdb403f
 
6d3a484
 
 
 
bdb403f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ed7a2c2
ff7bc08
bdb403f
3bdf777
bdb403f
 
 
 
 
 
 
3bdf777
 
bdb403f
 
 
3bdf777
bdb403f
 
 
 
 
 
3bdf777
 
bdb403f
ff7bc08
 
bdb403f
3bdf777
ff7bc08
bdb403f
3bdf777
6d3a484
bdb403f
 
 
 
a76e273
6d3a484
bdb403f
6d3a484
bdb403f
 
 
 
 
6d3a484
bdb403f
6d3a484
bdb403f
a76e273
bdb403f
 
 
 
 
 
 
 
6d3a484
3bdf777
6d3a484
bdb403f
 
 
6d3a484
bdb403f
6d3a484
bdb403f
6d3a484
bdb403f
 
 
 
 
 
 
3bdf777
a76e273
6d3a484
bdb403f
 
 
 
 
 
 
 
6d3a484
bdb403f
 
3bdf777
bdb403f
3bdf777
bdb403f
 
6d3a484
bdb403f
 
 
 
6d3a484
bdb403f
6d3a484
bdb403f
a76e273
bdb403f
 
 
a76e273
bdb403f
 
6d3a484
bdb403f
 
 
 
 
3bdf777
bdb403f
a76e273
bdb403f
 
 
 
3bdf777
bdb403f
6d3a484
 
 
 
bdb403f
 
 
 
ff7bc08
bdb403f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6d3a484
bdb403f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
import datetime
import re
from calendar import monthrange

class SajuCalculator:
    def __init__(self):
        # 천간 (하늘줄기)
        self.heavenly_stems = ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계']
        
        # 지지 (땅가지)
        self.earthly_branches = ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해']
        
        # 오행
        self.five_elements = {
            '갑': '목', '을': '목', '병': '화', '정': '화', '무': '토',
            '기': '토', '경': '금', '신': '금', '임': '수', '계': '수',
            '자': '수', '축': '토', '인': '목', '묘': '목', '진': '토',
            '사': '화', '오': '화', '미': '토', '신': '금', '유': '금',
            '술': '토', '해': '수'
        }
        
        # 십신
        self.ten_gods = {
            '목': {'목': '비견', '화': '식신', '토': '편재', '금': '편관', '수': '편인'},
            '화': {'목': '편인', '화': '비견', '토': '식신', '금': '편재', '수': '편관'},
            '토': {'목': '편관', '화': '편인', '토': '비견', '금': '식신', '수': '편재'},
            '금': {'목': '편재', '화': '편관', '토': '편인', '금': '비견', '수': '식신'},
            '수': {'목': '식신', '화': '편재', '토': '편관', '금': '편인', '수': '비견'}
        }
        
        # 지장간 (지지 안에 숨어있는 천간들)
        self.hidden_stems = {
            '자': ['계'], '축': ['기', '계', '신'], '인': ['갑', '병', '무'], '묘': ['을'],
            '진': ['무', '을', '계'], '사': ['병', '무', '경'], '오': ['정', '기'], '미': ['기', '정', '을'],
            '신': ['경', '임', '무'], '유': ['신'], '술': ['무', '신', '정'], '해': ['임', '갑']
        }
        
        # 12운성
        self.twelve_phases = ['장생', '목욕', '관대', '건록', '제왕', '쇠', '병', '사', '묘', '절', '태', '양']
        
        # 절기 정보 (월별 대략적인 날짜 - 실제로는 년도별로 다름)
        self.seasonal_divisions = {
            1: {'소한': 6, '대한': 21},
            2: {'입춘': 4, '우수': 19},
            3: {'경칩': 6, '춘분': 21},
            4: {'청명': 5, '곡우': 20},
            5: {'입하': 6, '소만': 21},
            6: {'망종': 6, '하지': 21},
            7: {'소서': 7, '대서': 23},
            8: {'입추': 8, '처서': 23},
            9: {'백로': 8, '추분': 23},
            10: {'한로': 8, '상강': 24},
            11: {'입동': 7, '소설': 22},
            12: {'대설': 7, '동지': 22}
        }
        
        # 지역별 시간 보정 (서울 기준 분 단위)
        self.location_offsets = {
            '서울': 0, '서울특별시': 0, '경기': 0, '경기도': 0, '인천': 0, '인천광역시': 0,
            '강원': +12, '강원도': +12, '춘천': +12, '원주': +8, '강릉': +20,
            '충북': -8, '충청북도': -8, '청주': -8, '충주': 0,
            '충남': -16, '충청남도': -16, '대전': -12, '대전광역시': -12, '천안': -12,
            '전북': -20, '전라북도': -20, '전주': -20, '군산': -24,
            '전남': -24, '전라남도': -24, '광주': -20, '광주광역시': -20, '목포': -32, '여수': -16,
            '경북': +8, '경상북도': +8, '대구': +4, '대구광역시': +4, '포항': +20, '경주': +16,
            '경남': -4, '경상남도': -4, '부산': +12, '부산광역시': +12, '울산': +16, '울산광역시': +16, '창원': +4, '마산': +4, '진주': -8,
            '제주': -20, '제주도': -20, '제주시': -20, '서귀포': -20
        }

    def get_location_offset(self, location):
        """출생지에 따른 시간 보정값 반환 (분 단위)"""
        if not location:
            return 0
        
        location = location.strip()
        
        # 정확한 매칭 시도
        if location in self.location_offsets:
            return self.location_offsets[location]
        
        # 부분 매칭 시도
        for key, offset in self.location_offsets.items():
            if key in location or location in key:
                return offset
        
        return 0  # 기본값 (서울 기준)

    def parse_date(self, date_str):
        """날짜 파싱"""
        numbers = re.findall(r'\d+', str(date_str))
        
        if not numbers:
            raise ValueError("날짜에서 숫자를 찾을 수 없습니다")
        
        if len(numbers) == 1:
            num_str = numbers[0]
            if len(num_str) == 8:  # YYYYMMDD
                year = int(num_str[:4])
                month = int(num_str[4:6])
                day = int(num_str[6:8])
            elif len(num_str) == 6:  # YYMMDD
                year_part = int(num_str[:2])
                year = 1900 + year_part if year_part > 30 else 2000 + year_part
                month = int(num_str[2:4])
                day = int(num_str[4:6])
            else:
                raise ValueError(f"날짜 형식을 인식할 수 없습니다: {num_str}")
        elif len(numbers) >= 3:
            year = int(numbers[0])
            month = int(numbers[1])
            day = int(numbers[2])
            
            if year < 100:
                year = 1900 + year if year > 30 else 2000 + year
        else:
            raise ValueError("날짜 정보가 부족합니다")
        
        return year, month, day

    def parse_time(self, time_str):
        """시간 파싱"""
        if not time_str:
            return 12, 0
        
        numbers = re.findall(r'\d+', str(time_str))
        
        if not numbers:
            return 12, 0
        
        if len(numbers) == 1:
            time_num = numbers[0]
            if len(time_num) == 4:  # HHMM
                hour = int(time_num[:2])
                minute = int(time_num[2:4])
            elif len(time_num) == 3:  # HMM
                hour = int(time_num[0])
                minute = int(time_num[1:3])
            else:  # H or HH
                hour = int(time_num)
                minute = 0
        else:
            hour = int(numbers[0])
            minute = int(numbers[1]) if len(numbers) > 1 else 0
        
        hour = hour % 24
        return hour, minute

    def get_seasonal_month(self, year, month, day):
        """절기를 고려한 월주 계산"""
        # 간단한 절기 계산 (정확하지 않지만 대략적)
        seasonal_month = month
        
        # 입춘(2월 4일경) 기준으로 월 조정
        if month == 1:
            seasonal_month = 12  # 12월 (축월)
        elif month == 2:
            if day < 4:  # 입춘 전
                seasonal_month = 12
            else:  # 입춘 후
                seasonal_month = 1  # 1월 (인월)
        else:
            seasonal_month = month - 1
        
        return seasonal_month

    def get_ganzhi(self, year, month, day, hour):
        """간지 계산 (절기 고려)"""
        base_date = datetime.datetime(1900, 1, 1)
        target_date = datetime.datetime(year, month, day)
        
        days_diff = (target_date - base_date).days
        
        # 연주 계산
        year_stem_index = (year - 1900 + 6) % 10
        year_branch_index = (year - 1900 + 0) % 12
        
        # 월주 계산 (절기 고려)
        seasonal_month = self.get_seasonal_month(year, month, day)
        month_branch_index = (seasonal_month + 1) % 12  # 인월부터 시작
        month_stem_index = (year_stem_index * 2 + seasonal_month) % 10
        
        # 일주 계산
        day_stem_index = (days_diff + 10) % 10
        day_branch_index = (days_diff + 2) % 12
        
        # 시주 계산
        hour_branch_index = ((hour + 1) // 2) % 12
        hour_stem_index = (day_stem_index * 2 + hour_branch_index) % 10
        
        return {
            'year': (self.heavenly_stems[year_stem_index], self.earthly_branches[year_branch_index]),
            'month': (self.heavenly_stems[month_stem_index], self.earthly_branches[month_branch_index]),
            'day': (self.heavenly_stems[day_stem_index], self.earthly_branches[day_branch_index]),
            'hour': (self.heavenly_stems[hour_stem_index], self.earthly_branches[hour_branch_index])
        }

    def analyze_elements(self, ganzhi):
        """오행 분석 (지장간 포함)"""
        element_count = {'목': 0, '화': 0, '토': 0, '금': 0, '수': 0}
        
        # 천간 오행 (가중치 1.0)
        for pillar in ganzhi.values():
            stem_element = self.five_elements[pillar[0]]
            element_count[stem_element] += 1.0
        
        # 지지 오행 (가중치 0.8)
        for pillar in ganzhi.values():
            branch_element = self.five_elements[pillar[1]]
            element_count[branch_element] += 0.8
        
        # 지장간 오행 (가중치 0.3)
        for pillar in ganzhi.values():
            hidden = self.hidden_stems[pillar[1]]
            for stem in hidden:
                stem_element = self.five_elements[stem]
                element_count[stem_element] += 0.3 / len(hidden)
        
        return element_count

    def get_ten_gods_analysis(self, ganzhi):
        """십신 분석 (지장간 포함)"""
        day_stem = ganzhi['day'][0]
        day_element = self.five_elements[day_stem]
        
        analysis = {}
        for pillar_name, pillar in ganzhi.items():
            stem_element = self.five_elements[pillar[0]]
            branch_element = self.five_elements[pillar[1]]
            
            stem_relation = self.ten_gods[day_element][stem_element]
            branch_relation = self.ten_gods[day_element][branch_element]
            
            # 지장간 십신
            hidden_relations = []
            for hidden_stem in self.hidden_stems[pillar[1]]:
                hidden_element = self.five_elements[hidden_stem]
                hidden_relation = self.ten_gods[day_element][hidden_element]
                hidden_relations.append(f"{hidden_stem}({hidden_relation})")
            
            analysis[pillar_name] = {
                'stem_relation': stem_relation,
                'branch_relation': branch_relation,
                'hidden_relations': hidden_relations
            }
        
        return analysis

def get_element_personality(element):
    """오행별 성격 특성"""
    personalities = {
        '목': "창의적이고 성장 지향적이며, 유연하고 협력적인 성격을 가지고 있습니다.",
        '화': "열정적이고 활동적이며, 밝고 사교적인 성격을 가지고 있습니다.",
        '토': "안정적이고 신중하며, 포용력이 있고 책임감이 강한 성격을 가지고 있습니다.",
        '금': "원칙적이고 정의로우며, 결단력이 있고 리더십이 강한 성격을 가지고 있습니다.",
        '수': "지혜롭고 적응력이 있으며, 깊이 있고 신중한 성격을 가지고 있습니다."
    }
    return personalities.get(element, "균형 잡힌 성격을 가지고 있습니다.")

def get_element_balance_advice(elements):
    """오행 균형에 따른 조언"""
    max_element = max(elements, key=elements.get)
    min_element = min(elements, key=elements.get)
    
    advice = f"현재 {max_element}가 가장 강하고 {min_element}가 가장 약합니다. "
    
    if elements[max_element] - elements[min_element] > 2:
        advice += f"{min_element}를 보강하고 {max_element}의 기운을 조절하는 것이 좋겠습니다."
    else:
        advice += "전체적으로 균형이 잘 잡혀 있는 편입니다."
    
    return advice

def format_saju_result(calculator, ganzhi, elements, ten_gods, birth_info):
    """사주 결과 포맷팅 - 전문적인 표 형태로 출력"""
    
    # 오행별 색상 설정
    element_colors = {
        '목': '#28a745',  # 녹색
        '화': '#dc3545',  # 빨강
        '토': '#ffc107',  # 노랑
        '금': '#6c757d',  # 회색
        '수': '#007bff'   # 파랑
    }
    
    def get_colored_element(char, element):
        color = element_colors.get(element, '#000000')
        return f'<span style="color: {color}; font-weight: bold;">{char}</span>'
    
    def get_colored_ganzhi(stem, branch):
        stem_element = calculator.five_elements[stem]
        branch_element = calculator.five_elements[branch]
        colored_stem = get_colored_element(stem, stem_element)
        colored_branch = get_colored_element(branch, branch_element)
        return f'{colored_stem}{colored_branch}'
    
    # 시간 보정 정보
    time_correction_info = ""
    if birth_info['location_offset'] != 0:
        sign = "+" if birth_info['location_offset'] > 0 else ""
        time_correction_info = f"""
### ⏰ 출생지 시간 보정
- **입력 시간**: {birth_info['original_time']}
- **보정 시간**: {birth_info['corrected_time']} ({birth_info['birth_place']} 기준 {sign}{birth_info['location_offset']}분 보정)
"""
    
    birth_datetime = birth_info['birth_datetime']
    gender = birth_info['gender'] 
    birth_place = birth_info['birth_place']
    corrected_time = birth_info['corrected_time']
    
    result = f"""
# 🔮 사주명리 만세력 분석결과

## 📋 기본정보
- **생년월일**: {birth_datetime.strftime('%Y년 %m월 %d일')}
- **출생시간**: {corrected_time} ({birth_place})
- **성별**: {gender}
{time_correction_info}

## 🏛️ 사주(四柱) 만세력표

<div style="text-align: center; font-size: 16px;">

| 구분 | **시주(時柱)** | **일주(日柱)** | **월주(月柱)** | **연주(年柱)** |
|:---:|:---:|:---:|:---:|:---:|
| **천간** | {get_colored_element(ganzhi['hour'][0], calculator.five_elements[ganzhi['hour'][0]])} | {get_colored_element(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])} | {get_colored_element(ganzhi['month'][0], calculator.five_elements[ganzhi['month'][0]])} | {get_colored_element(ganzhi['year'][0], calculator.five_elements[ganzhi['year'][0]])} |
| **십신** | {ten_gods['hour']['stem_relation']} | {ten_gods['day']['stem_relation']} | {ten_gods['month']['stem_relation']} | {ten_gods['year']['stem_relation']} |
| **지지** | {get_colored_element(ganzhi['hour'][1], calculator.five_elements[ganzhi['hour'][1]])} | {get_colored_element(ganzhi['day'][1], calculator.five_elements[ganzhi['day'][1]])} | {get_colored_element(ganzhi['month'][1], calculator.five_elements[ganzhi['month'][1]])} | {get_colored_element(ganzhi['year'][1], calculator.five_elements[ganzhi['year'][1]])} |
| **십신** | {ten_gods['hour']['branch_relation']} | {ten_gods['day']['branch_relation']} | {ten_gods['month']['branch_relation']} | {ten_gods['year']['branch_relation']} |
| **간지** | **{get_colored_ganzhi(ganzhi['hour'][0], ganzhi['hour'][1])}** | **{get_colored_ganzhi(ganzhi['day'][0], ganzhi['day'][1])}** | **{get_colored_ganzhi(ganzhi['month'][0], ganzhi['month'][1])}** | **{get_colored_ganzhi(ganzhi['year'][0], ganzhi['year'][1])}** |
| **지장간** | {"·".join(calculator.hidden_stems[ganzhi['hour'][1]])} | {"·".join(calculator.hidden_stems[ganzhi['day'][1]])} | {"·".join(calculator.hidden_stems[ganzhi['month'][1]])} | {"·".join(calculator.hidden_stems[ganzhi['year'][1]])} |

</div>

**💫 사주 구성**
- **일간(日干)**: {get_colored_element(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])} - 본인을 나타내는 핵심 요소
- **월령(月令)**: {get_colored_element(ganzhi['month'][1], calculator.five_elements[ganzhi['month'][1]])} - 계절의 기운과 성장 환경

## 🌟 오행(五行) 분석

<div style="text-align: center;">

| 오행 | 천간+지지+지장간 | 비율 | 상태 | 특징 |
|:---:|:---:|:---:|:---:|:---:|
| {get_colored_element('목', '목')} | {elements['목']:.1f}점 | {(elements['목']/sum(elements.values())*100):.1f}% | {'매우강함' if elements['목'] >= 3 else '강함' if elements['목'] >= 2 else '보통' if elements['목'] >= 1 else '약함'} | 성장, 창의, 인자함 |
| {get_colored_element('화', '화')} | {elements['화']:.1f}점 | {(elements['화']/sum(elements.values())*100):.1f}% | {'매우강함' if elements['화'] >= 3 else '강함' if elements['화'] >= 2 else '보통' if elements['화'] >= 1 else '약함'} | 열정, 활동, 표현력 |
| {get_colored_element('토', '토')} | {elements['토']:.1f}점 | {(elements['토']/sum(elements.values())*100):.1f}% | {'매우강함' if elements['토'] >= 3 else '강함' if elements['토'] >= 2 else '보통' if elements['토'] >= 1 else '약함'} | 안정, 포용, 신뢰 |
| {get_colored_element('금', '금')} | {elements['금']:.1f}점 | {(elements['금']/sum(elements.values())*100):.1f}% | {'매우강함' if elements['금'] >= 3 else '강함' if elements['금'] >= 2 else '보통' if elements['금'] >= 1 else '약함'} | 의지, 결단, 정의 |
| {get_colored_element('수', '수')} | {elements['수']:.1f}점 | {(elements['수']/sum(elements.values())*100):.1f}% | {'매우강함' if elements['수'] >= 3 else '강함' if elements['수'] >= 2 else '보통' if elements['수'] >= 1 else '약함'} | 지혜, 유연, 깊이 |

</div>

### 오행 균형 분석
"""
    
    max_element = max(elements, key=elements.get)
    min_element = min(elements, key=elements.get)
    
    result += f"- **가장 강한 오행**: {get_colored_element(max_element, max_element)} ({elements[max_element]:.1f}점)\n"
    result += f"- **가장 약한 오행**: {get_colored_element(min_element, min_element)} ({elements[min_element]:.1f}점)\n"

    result += """
## 🎭 십신(十神) 세부 분석

<div style="text-align: center;">

| 구분 | **시주** | **일주** | **월주** | **연주** |
|:---:|:---:|:---:|:---:|:---:|"""

    result += f"""
| **천간 십신** | {ten_gods['hour']['stem_relation']} | {ten_gods['day']['stem_relation']} | {ten_gods['month']['stem_relation']} | {ten_gods['year']['stem_relation']} |
| **지지 십신** | {ten_gods['hour']['branch_relation']} | {ten_gods['day']['branch_relation']} | {ten_gods['month']['branch_relation']} | {ten_gods['year']['branch_relation']} |
| **지장간** | {' '.join(ten_gods['hour']['hidden_relations'])} | {' '.join(ten_gods['day']['hidden_relations'])} | {' '.join(ten_gods['month']['hidden_relations'])} | {' '.join(ten_gods['year']['hidden_relations'])} |

</div>

## 💡 종합 해석

### 일간 분석
- **일간**: {get_colored_element(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])} ({calculator.five_elements[ganzhi['day'][0]]}행)
- **성격**: {get_element_personality(calculator.five_elements[ganzhi['day'][0]])}

### 오행 조화 분석
{get_element_balance_advice(elements)}

### 십신 구성 특징
- **관성(편관/정관)**: {"있음" if any("관" in str(ten_gods[p]['stem_relation']) or "관" in str(ten_gods[p]['branch_relation']) for p in ten_gods) else "없음"} - 리더십, 책임감
- **재성(편재/정재)**: {"있음" if any("재" in str(ten_gods[p]['stem_relation']) or "재" in str(ten_gods[p]['branch_relation']) for p in ten_gods) else "없음"} - 재물운, 실용성
- **인성(편인/정인)**: {"있음" if any("인" in str(ten_gods[p]['stem_relation']) or "인" in str(ten_gods[p]['branch_relation']) for p in ten_gods) else "없음"} - 학습력, 지혜
- **식상(식신/상관)**: {"있음" if any("식" in str(ten_gods[p]['stem_relation']) or "상" in str(ten_gods[p]['branch_relation']) for p in ten_gods) else "없음"} - 창의력, 표현력

---

### 📌 주의사항
- 본 분석은 절기를 고려한 전통 사주명리학 원리에 따른 것입니다
- 지장간과 십신 관계를 포함한 종합적 분석입니다
- 정확한 운세 해석을 위해서는 전문가 상담을 권장합니다

*분석 일시: {datetime.datetime.now().strftime('%Y년 %m월 %d일 %H시 %M분')}*
"""
    
    return result

def calculate_saju(birth_date, birth_time, gender, birth_place):
    """사주 계산 메인 함수"""
    # SajuCalculator 인스턴스 생성
    calculator = SajuCalculator()
    
    try:
        # 입력 검증
        if not birth_date:
            return "❌ 생년월일을 입력해주세요."
        
        # 날짜 파싱
        year, month, day = calculator.parse_date(birth_date)
        
        # 시간 파싱
        hour, minute = calculator.parse_time(birth_time)
        
        # 출생지에 따른 시간 보정
        location_offset = calculator.get_location_offset(birth_place)
        
        # 보정된 시간 계산
        corrected_minute = minute + location_offset
        corrected_hour = hour
        
        # 분이 60을 넘거나 0 미만인 경우 시간 조정
        if corrected_minute >= 60:
            corrected_hour += corrected_minute // 60
            corrected_minute = corrected_minute % 60
        elif corrected_minute < 0:
            corrected_hour -= (-corrected_minute - 1) // 60 + 1
            corrected_minute = 60 + (corrected_minute % 60)
        
        # 시간이 24를 넘거나 0 미만인 경우 조정
        corrected_hour = corrected_hour % 24
        
        # 날짜 유효성 검사
        if year < 1900 or year > 2100:
            return f"❌ 연도는 1900~2100 사이여야 합니다. 입력된 연도: {year}"
        if month < 1 or month > 12:
            return f"❌ 월은 1~12 사이여야 합니다. 입력된 월: {month}"
        if day < 1 or day > 31:
            return f"❌ 일은 1~31 사이여야 합니다. 입력된 일: {day}"
        
        # datetime 객체 생성 (보정된 시간 사용)
        birth_datetime = datetime.datetime(year, month, day, corrected_hour, corrected_minute)
        original_time = f"{hour:02d}:{minute:02d}"
        corrected_time = f"{corrected_hour:02d}:{corrected_minute:02d}"
        
        # 출생 정보 딕셔너리
        birth_info = {
            'birth_datetime': birth_datetime,
            'gender': gender,
            'birth_place': birth_place,
            'original_time': original_time,
            'corrected_time': corrected_time,
            'location_offset': location_offset
        }
        
        # 간지 계산 (절기 고려)
        ganzhi = calculator.get_ganzhi(year, month, day, corrected_hour)
        
        # 오행 분석 (지장간 포함)
        elements = calculator.analyze_elements(ganzhi)
        
        # 십신 분석 (지장간 포함)
        ten_gods = calculator.get_ten_gods_analysis(ganzhi)
        
        # 결과 포맷팅
        result = format_saju_result(calculator, ganzhi, elements, ten_gods, birth_info)
        
        return result
        
    except ValueError as ve:
        return f"❌ 입력 오류: {str(ve)}\n\n💡 입력 예시:\n- 생년월일: 19851015, 1985-10-15\n- 시간: 1430, 14:30"
    
    except Exception as e:
        return f"❌ 계산 중 오류가 발생했습니다: {str(e)}"

def create_interface():
    """Gradio 인터페이스 생성"""
    with gr.Blocks(title="🔮 전문 사주명리 만세력 시스템") as demo:
        gr.HTML("""
        <div style="text-align: center; padding: 20px;">
            <h1>🔮 전문 사주명리학 만세력 분석 시스템</h1>
            <p><strong>절기 고려 · 지장간 분석 · 출생지 시간 보정</strong></p>
            <p>생년월일시와 출생지 정보를 입력하시면 전문적인 만세력을 분석해드립니다.</p>
        </div>
        """)
        
        with gr.Row():
            with gr.Column(scale=1):
                gr.HTML("<h3>📝 정보 입력</h3>")
                
                birth_date = gr.Textbox(
                    label="생년월일",
                    placeholder="19851015 또는 1985-10-15",
                    info="8자리 숫자 또는 구분자 포함하여 입력 (양력 기준)"
                )
                
                birth_time = gr.Textbox(
                    label="태어난 시간 (선택사항)",
                    placeholder="1430 또는 14:30",
                    info="정확한 시간을 입력하세요. 비우면 정오(12시)로 설정됩니다."
                )
                
                gender = gr.Radio(
                    choices=["남", "여"],
                    label="성별",
                    value="남"
                )
                
                birth_place = gr.Textbox(
                    label="출생지",
                    placeholder="서울특별시",
                    info="정확한 출생지를 입력하면 시간 보정이 적용됩니다"
                )
                
                calculate_btn = gr.Button(
                    "🔮 전문 만세력 분석하기",
                    variant="primary"
                )
        
        with gr.Row():
            with gr.Column():
                result_output = gr.Markdown(
                    label="분석 결과",
                    value="👆 위의 정보를 입력하고 '전문 만세력 분석하기' 버튼을 클릭하세요."
                )
        
        # 이벤트 연결
        calculate_btn.click(
            fn=calculate_saju,
            inputs=[birth_date, birth_time, gender, birth_place],
            outputs=result_output
        )
        
        gr.HTML("""
        <div style="text-align: center; padding: 20px; margin-top: 30px; border-top: 1px solid #eee;">
            <h4>🔍 시스템 특징</h4>
            <div style="display: flex; justify-content: center; gap: 30px; flex-wrap: wrap;">
                <div>✅ <strong>절기 고려</strong><br>정확한 월주 계산</div>
                <div>✅ <strong>지장간 분석</strong><br>지지 안 숨은 천간</div>
                <div>✅ <strong>십신 세분화</strong><br>천간/지지/지장간별</div>
                <div>✅ <strong>시간 보정</strong><br>전국 지역별 적용</div>
                <div>✅ <strong>오행 가중치</strong><br>천간/지지/지장간</div>
            </div>
            <p style="margin-top: 15px;"><small>※ 본 시스템은 전통 사주명리학을 기반으로 하며, 참고용으로만 활용해주시기 바랍니다.</small></p>
        </div>
        """)
    
    return demo

if __name__ == "__main__":
    demo = create_interface()
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=True
    )