import sys
import os
import py7zr
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QFileDialog, 
                             QLabel, QProgressBar, QLineEdit, QMessageBox, QTabWidget, QGroupBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtCore import QUrl

class CompressThread(QThread):
    progress = pyqtSignal(int)
    finished = pyqtSignal(str, int, int)

    def __init__(self, files, folders, output_path):
        super().__init__()
        self.files = files
        self.folders = folders
        self.output_path = output_path
        self.total_files = []
        self.total_size = 0

    def collect_files_from_folder(self, folder_path):
        files = []
        for root, _, filenames in os.walk(folder_path):
            for filename in filenames:
                file_path = os.path.join(root, filename)
                files.append((file_path, os.path.relpath(file_path, os.path.dirname(folder_path))))
                self.total_size += os.path.getsize(file_path)
        return files

    def run(self):
        # Dosyaları hazırla
        self.total_files = [(f, os.path.basename(f)) for f in self.files]
        self.total_size = sum(os.path.getsize(f) for f in self.files)

        # Klasörleri işle
        for folder in self.folders:
            self.total_files.extend(self.collect_files_from_folder(folder))
        
        # En yüksek sıkıştırma ayarları ile 7z dosyası oluştur
        filters = [
            {'id': py7zr.FILTER_LZMA2, 'preset': 9 | py7zr.PRESET_EXTREME},
        ]

        with py7zr.SevenZipFile(self.output_path, 'w', filters=filters) as archive:
            for i, (file_path, arc_name) in enumerate(self.total_files):
                archive.write(file_path, arc_name)
                self.progress.emit(int((i + 1) / len(self.total_files) * 100))

        compressed_size = os.path.getsize(self.output_path)
        self.finished.emit(self.output_path, self.total_size, compressed_size)

class ExtractThread(QThread):
    progress = pyqtSignal(int)
    finished = pyqtSignal()

    def __init__(self, archive_path, output_dir):
        super().__init__()
        self.archive_path = archive_path
        self.output_dir = output_dir

    def run(self):
        with py7zr.SevenZipFile(self.archive_path, 'r') as archive:
            total_files = len(archive.files)
            for i, _ in enumerate(archive.extractall(path=self.output_dir)):
                self.progress.emit(int((i + 1) / total_files * 100))
        self.finished.emit()

class FileCompressorApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Gelişmiş 7zip Sıkıştırma ve Açma Uygulaması')
        self.setGeometry(300, 300, 600, 500)

        layout = QVBoxLayout()

        # Sekme widget'ı
        self.tab_widget = QTabWidget()
        self.compress_tab = QWidget()
        self.extract_tab = QWidget()
        self.tab_widget.addTab(self.compress_tab, "Sıkıştır")
        self.tab_widget.addTab(self.extract_tab, "Aç")

        # Sıkıştırma sekmesi
        compress_layout = QVBoxLayout()

        # Dosya seçim grubu
        file_group = QGroupBox("Dosya Seçimi")
        file_layout = QVBoxLayout()
        
        self.select_files_button = QPushButton('Dosya Seç (Çoklu)')
        self.select_files_button.clicked.connect(self.selectFiles)
        file_layout.addWidget(self.select_files_button)
        
        self.file_label = QLabel('Seçilen dosya sayısı: 0')
        file_layout.addWidget(self.file_label)
        
        file_group.setLayout(file_layout)
        compress_layout.addWidget(file_group)

        # Klasör seçim grubu
        folder_group = QGroupBox("Klasör Seçimi")
        folder_layout = QVBoxLayout()
        
        folder_buttons_layout = QHBoxLayout()
        self.select_folder_button = QPushButton('Tek Klasör Seç')
        self.select_folder_button.clicked.connect(self.selectFolder)
        folder_buttons_layout.addWidget(self.select_folder_button)
        
        self.select_folders_button = QPushButton('Çoklu Klasör Seç')
        self.select_folders_button.clicked.connect(self.selectFolders)
        folder_buttons_layout.addWidget(self.select_folders_button)
        
        folder_layout.addLayout(folder_buttons_layout)
        
        self.folder_label = QLabel('Seçilen klasör sayısı: 0')
        folder_layout.addWidget(self.folder_label)
        
        folder_group.setLayout(folder_layout)
        compress_layout.addWidget(folder_group)

        self.compress_button = QPushButton('Sıkıştır')
        self.compress_button.clicked.connect(self.compressFiles)
        self.compress_button.setEnabled(False)
        compress_layout.addWidget(self.compress_button)

        self.compress_tab.setLayout(compress_layout)

        # Açma sekmesi
        extract_layout = QVBoxLayout()

        self.select_archive_button = QPushButton('Arşiv Seç')
        self.select_archive_button.clicked.connect(self.selectArchive)
        extract_layout.addWidget(self.select_archive_button)

        self.archive_label = QLabel('Seçilen arşiv: ')
        extract_layout.addWidget(self.archive_label)

        self.extract_button = QPushButton('Aç')
        self.extract_button.clicked.connect(self.extractFiles)
        self.extract_button.setEnabled(False)
        extract_layout.addWidget(self.extract_button)

        self.extract_tab.setLayout(extract_layout)

        layout.addWidget(self.tab_widget)

        self.progress_bar = QProgressBar()
        layout.addWidget(self.progress_bar)

        self.status_label = QLabel('')
        layout.addWidget(self.status_label)

        self.result_label = QLabel('')
        layout.addWidget(self.result_label)

        self.about_button = QPushButton('Hakkında')
        self.about_button.clicked.connect(self.openAboutPage)
        layout.addWidget(self.about_button)

        self.setLayout(layout)

        self.files = []
        self.folders = []
        self.archive_path = ''

    def selectFiles(self):
        file_dialog = QFileDialog()
        files, _ = file_dialog.getOpenFileNames(self, "Dosyaları Seç", "", "Tüm Dosyalar (*)")
        if files:
            self.files.extend(files)
            self.updateSelectionLabels()
            self.updateCompressButton()

    def selectFolder(self):
        folder = QFileDialog.getExistingDirectory(self, "Klasör Seç")
        if folder:
            self.folders.append(folder)
            self.updateSelectionLabels()
            self.updateCompressButton()

    def selectFolders(self):
        # Not: Qt doğrudan çoklu klasör seçimini desteklemez
        # Bu yüzden tek klasör seçimini tekrarlı olarak kullanacağız
        while True:
            folder = QFileDialog.getExistingDirectory(self, "Klasör Seç (Bitirmek için İptal'e basın)")
            if not folder:
                break
            self.folders.append(folder)
            self.updateSelectionLabels()
            self.updateCompressButton()

    def updateSelectionLabels(self):
        total_size = sum(os.path.getsize(f) for f in self.files)
        for folder in self.folders:
            for root, _, files in os.walk(folder):
                for file in files:
                    total_size += os.path.getsize(os.path.join(root, file))

        self.file_label.setText(f'Seçilen dosya sayısı: {len(self.files)}\n'
                              f'Toplam dosya boyutu: {self.format_size(total_size)}')
        self.folder_label.setText(f'Seçilen klasör sayısı: {len(self.folders)}')

    def updateCompressButton(self):
        self.compress_button.setEnabled(bool(self.files) or bool(self.folders))

    def selectArchive(self):
        file_dialog = QFileDialog()
        archive_path, _ = file_dialog.getOpenFileName(self, "Arşiv Seç", "", "7zip Dosyaları (*.7z)")
        if archive_path:
            self.archive_path = archive_path
            self.archive_label.setText(f'Seçilen arşiv: {os.path.basename(self.archive_path)}')
            self.extract_button.setEnabled(True)

    def compressFiles(self):
        if not self.files and not self.folders:
            return

        output_path, _ = QFileDialog.getSaveFileName(self, "Sıkıştırılmış Dosyayı Kaydet", "", "7zip Dosyaları (*.7z)")
        if not output_path:
            return

        if not output_path.endswith('.7z'):
            output_path += '.7z'

        self.compress_thread = CompressThread(self.files, self.folders, output_path)
        self.compress_thread.progress.connect(self.updateProgress)
        self.compress_thread.finished.connect(self.compressionFinished)
        
        self.disableUI()
        self.status_label.setText('Sıkıştırma işlemi devam ediyor... Bu işlem uzun sürebilir.')
        
        self.compress_thread.start()

    def extractFiles(self):
        if not self.archive_path:
            return

        output_dir = QFileDialog.getExistingDirectory(self, "Çıkarılacak Klasörü Seç")
        if not output_dir:
            return

        self.extract_thread = ExtractThread(self.archive_path, output_dir)
        self.extract_thread.progress.connect(self.updateProgress)
        self.extract_thread.finished.connect(self.extractionFinished)
        
        self.disableUI()
        self.status_label.setText('Dosyalar çıkarılıyor... Bu işlem uzun sürebilir.')
        
        self.extract_thread.start()

    def disableUI(self):
        self.select_files_button.setEnabled(False)
        self.select_folder_button.setEnabled(False)
        self.select_folders_button.setEnabled(False)
        self.compress_button.setEnabled(False)
        self.select_archive_button.setEnabled(False)
        self.extract_button.setEnabled(False)

    def enableUI(self):
        self.select_files_button.setEnabled(True)
        self.select_folder_button.setEnabled(True)
        self.select_folders_button.setEnabled(True)
        self.compress_button.setEnabled(bool(self.files) or bool(self.folders))
        self.select_archive_button.setEnabled(True)
        self.extract_button.setEnabled(bool(self.archive_path))

    def updateProgress(self, value):
        self.progress_bar.setValue(value)

    def compressionFinished(self, output_path, original_size, compressed_size):
        self.enableUI()
        self.status_label.setText('Sıkıştırma işlemi tamamlandı!')
        self.progress_bar.setValue(100)

        compression_ratio = (1 - compressed_size / original_size) * 100
        self.result_label.setText(f'Orijinal boyut: {self.format_size(original_size)}\n'
                                 f'Sıkıştırılmış boyut: {self.format_size(compressed_size)}\n'
                                 f'Sıkıştırma oranı: {compression_ratio:.2f}%')

    def extractionFinished(self):
        self.enableUI()
        self.status_label.setText('Dosyalar başarıyla çıkarıldı!')
        self.progress_bar.setValue(100)

    @staticmethod
    def format_size(size):
        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
            if size < 1024.0:
                return f"{size:.2f} {unit}"
            size /= 1024.0

    def openAboutPage(self):
        QDesktopServices.openUrl(QUrl("https://ettunc.com"))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = FileCompressorApp()
    ex.show()
    sys.exit(app.exec_())
