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

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':  # macOS
            self.font_path = '/System/Library/Fonts/AppleSDGothicNeo.ttc'  # Apple SD Gothic Neo
        else:  # Linux
            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 또는 텍스트 파일 불러오기", 
            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()
        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 process_text(self, text):
        """텍스트를 전처리하고 단어 빈도수를 계산합니다."""
        try:
            # 최소 글자 수 가져오기
            try:
                min_length = max(2, int(self.min_length_var.get()))
            except ValueError:
                min_length = 2

            # 한글 형태소 분석
            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:
            # 최대 단어 수 가져오기
            try:
                max_words = max(1, int(self.max_words_var.get()))
            except ValueError:
                max_words = 50  # 기본값
            
            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"),
                ("PDF 파일", "*.pdf"),
                ("텍스트 파일", "*.txt")
            ]
        )
        
        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)
                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() 해당 코드에서 hwp 포맷의 문서를 불러와서 텍스트 빈도를 검출하는 작업을 수행할 수 있도록 하고자 합니다. hwp5txt를 윈도우환경에서 적용하여 작업을 진행하려고 합니다. 이를 위한 방법과 개선된 코드를 제시해주십시오