import re
import tkinter as tk
from tkinter import filedialog, messagebox
import PyPDF2
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import threading
import os
import sys
from konlpy.tag import Okt
from collections import Counter
import platform
import pyhwp
import python-docx

class WordCloudGenerator:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("WordCloud Generator")
        
        # 한글 형태소 분석기 초기화
        try:
            self.okt = Okt()
        except Exception as e:
            messagebox.showerror("초기화 오류", f"형태소 분석기 초기화 실패: {str(e)}\nKoNLPy가 설치되어 있는지 확인해주세요.")
            self.root.quit()
            sys.exit(1)
            
        self.setup_stopwords()
        self.setup_font()
        self.setup_gui()

    def setup_font(self):
        """시스템에 따른 한글 폰트 설정"""
        system = platform.system()
        
        if system == 'Windows':
            self.font_path = 'C:/Windows/Fonts/malgun.ttf'  
            if not os.path.exists(self.font_path):
                self.font_path = 'C:/Windows/Fonts/gulim.ttc'  
        elif system == 'Darwin':
            self.font_path = '/System/Library/Fonts/AppleSDGothicNeo.ttc'
        else:
            self.font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
            
        if not os.path.exists(self.font_path):
            messagebox.showwarning("폰트 경고", 
                                 "기본 한글 폰트를 찾을 수 없습니다.\n"
                                 "워드클라우드 생성 시 한글이 제대로 표시되지 않을 수 있습니다.")
            self.font_path = None

    def setup_stopwords(self):
        """한글과 영어 불용어 설정"""
        self.korean_stop_words = {
            '있', '하', '것', '들', '그', '되', '수', '이', '보', '않', '없', '나',
            '사람', '주', '아니', '등', '같', '우리', '때', '년', '가', '한', '지',
            '대하', '오', '말', '일', '그렇', '위하', '때문', '그것', '두', '말하',
            '알', '그러나', '받', '못하', '일', '그런', '또', '문제', '더', '사회',
            '많', '그리고', '좋', '크', '따르', '중', '나오', '가지', '씨', '시키',
            '만들', '지금', '생각하', '그러', '속', '하나', '집', '살', '모르',
            '적', '월', '데', '자신', '안', '어떤', '내', '경우', '명', '생각',
            '시간', '그녀', '다시', '이런', '앞', '보이', '번', '나', '다른',
            '어떻', '여자', '개', '전', '들', '사실', '이렇', '점', '싶', '말',
            '정도', '좀', '원', '잘', '통하', '소리', '놓'
        }

        self.english_stop_words = {
            'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you',
            "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself',
            'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her',
            'hers', 'herself', 'it', "it's", 'its', 'itself', 'they', 'them',
            'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom',
            'this', 'that', "that'll", 'these', 'those', 'am', 'is', 'are',
            'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having',
            'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if',
            'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for',
            'with', 'about', 'against', 'between', 'into', 'through', 'during',
            'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down',
            'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further',
            'then', 'once'
        }

    def setup_gui(self):
        """GUI 요소를 설정합니다."""
        self.frame = tk.Frame(self.root)
        self.frame.pack(padx=60, pady=25)

        self.status_label = tk.Label(self.frame, text="파일을 선택해주세요")
        self.status_label.pack(pady=5)

        self.length_frame = tk.Frame(self.frame)
        self.length_frame.pack(pady=5)
        tk.Label(self.length_frame, text="최소 글자 수:").pack(side=tk.LEFT)
        self.min_length_var = tk.StringVar(value="2")
        self.min_length_entry = tk.Entry(self.length_frame, textvariable=self.min_length_var, width=5)
        self.min_length_entry.pack(side=tk.LEFT, padx=5)

        self.max_words_frame = tk.Frame(self.frame)
        self.max_words_frame.pack(pady=5)
        tk.Label(self.max_words_frame, text="최대 단어 수:").pack(side=tk.LEFT)
        self.max_words_var = tk.StringVar(value="50")
        self.max_words_entry = tk.Entry(self.max_words_frame, textvariable=self.max_words_var, width=5)
        self.max_words_entry.pack(side=tk.LEFT, padx=5)

        self.load_button = tk.Button(
            self.frame, 
            text="PDF, 텍스트, HWP, Word 파일 불러오기", 
            command=self.process_file_async
        )
        self.load_button.pack(pady=10)

    def extract_text_from_pdf(self, pdf_path):
        """PDF 파일에서 텍스트를 추출합니다."""
        text = ""
        try:
            with open(pdf_path, 'rb') as file:
                reader = PyPDF2.PdfReader(file)
                for page in reader.pages:
                    text += page.extract_text() or ""  # None 방지
        except Exception as e:
            raise Exception(f"PDF 파일 읽기 오류: {str(e)}")
        return text

    def extract_text_from_txt(self, txt_path):
        """텍스트 파일에서 텍스트를 추출합니다."""
        encodings = ['utf-8', 'cp949', 'euc-kr']
        for encoding in encodings:
            try:
                with open(txt_path, 'r', encoding=encoding) as file:
                    return file.read()
            except UnicodeDecodeError:
                continue
        raise Exception("텍스트 파일의 인코딩을 확인할 수 없습니다.")

    def extract_text_from_hwp(self, hwp_path):
        """HWP 파일에서 텍스트를 추출합니다."""
        try:
            # pyhwp를 사용하여 HWP 파일에서 텍스트를 추출합니다.
            doc = pyhwp.HWPDocument(hwp_path)
            text = ""
            for section in doc.bodytext:
                text += section.text.replace('\r\n', '\n') + '\n'
            return text.strip()
        except Exception as e:
            raise Exception(f"HWP 파일 읽기 오류: {str(e)}")

    def extract_text_from_docx(self, docx_path):
        """DOCX 파일에서 텍스트를 추출합니다."""
        try:
            doc = docx.Document(docx_path)
            text = '\n'.join(paragraph.text for paragraph in doc.paragraphs)
            return text
        except Exception as e:
            raise Exception(f"DOCX 파일 읽기 오류: {str(e)}")

    def process_text(self, text):
        """텍스트를 전처리하고 단어 빈도수를 계산합니다."""
        try:
            min_length = max(2, int(self.min_length_var.get()))
            nouns = self.okt.nouns(text)
            words = [word for word in nouns if len(word) >= min_length and word not in self.korean_stop_words]

            english_words = re.findall(r'\b[a-zA-Z]+\b', text)
            words.extend([word.lower() for word in english_words if len(word) >= min_length and word.lower() not in self.english_stop_words])

            word_freq = Counter(words)
            return dict(word_freq)
        except Exception as e:
            raise Exception(f"텍스트 처리 중 오류 발생: {str(e)}")

    def generate_wordcloud(self, word_freq):
        """워드클라우드를 생성하고 표시합니다."""
        max_words = 50
        try:
            max_words = max(1, int(self.max_words_var.get()))
            if not word_freq:
                raise Exception("처리할 텍스트가 충분하지 않습니다.")

            wordcloud = WordCloud(
                font_path=self.font_path,
                width=1200,
                height=800,
                background_color='white',
                max_words=max_words,
                min_font_size=10,
                max_font_size=150,
                random_state=42
            ).generate_from_frequencies(word_freq)

            self.root.after(0, self.show_wordcloud, wordcloud)
        except Exception as e:
            raise Exception(f"워드클라우드 생성 중 오류 발생: {str(e)}")

    def show_wordcloud(self, wordcloud):
        """워드클라우드를 표시합니다."""
        plt.figure(figsize=(15, 10))
        plt.imshow(wordcloud, interpolation='bilinear')
        plt.axis('off')
        plt.show()

    def process_file_async(self):
        """파일 처리를 비동기적으로 실행합니다."""
        file_path = filedialog.askopenfilename(
            filetypes=[
                ("지원되는 파일", "*.pdf;*.txt;*.hwp;*.docx"),
                ("PDF 파일", "*.pdf"),
                ("텍스트 파일", "*.txt"),
                ("HWP 파일", "*.hwp"),
                ("Word 파일", "*.docx")
            ]
        )
        
        if not file_path:
            return

        self.status_label.config(text="파일 처리 중...")
        self.load_button.config(state=tk.DISABLED)

        def process():
            try:
                if file_path.lower().endswith('.pdf'):
                    text = self.extract_text_from_pdf(file_path)
                elif file_path.lower().endswith('.txt'):
                    text = self.extract_text_from_txt(file_path)
                elif file_path.lower().endswith('.hwp'):
                    text = self.extract_text_from_hwp(file_path)
                elif file_path.lower().endswith('.docx'):
                    text = self.extract_text_from_docx(file_path)
                else:
                    raise Exception("지원하지 않는 파일 형식입니다.")

                word_freq = self.process_text(text)
                self.generate_wordcloud(word_freq)

                self.status_label.config(text="처리 완료")
            except Exception as e:
                messagebox.showerror("오류", str(e))
                self.status_label.config(text="오류 발생")
            finally:
                self.load_button.config(state=tk.NORMAL)

        threading.Thread(target=process, daemon=True).start()

    def run(self):
        """애플리케이션을 실행합니다."""
        self.root.mainloop()

if __name__ == "__main__":
    app = WordCloudGenerator()
    app.run() 해당 코드 실행에 문제가 있습니다. cmd창에 오류 메세지와 함께 바로 종료됩니다.