import sys
import socket
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QPushButton, QTextEdit, QVBoxLayout, QWidget, QProgressBar
from PyQt5.QtCore import QThread, pyqtSignal, QObject
from concurrent.futures import ThreadPoolExecutor, as_completed

class Worker(QObject):
    progress = pyqtSignal(int)
    finished = pyqtSignal()
    log_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.file_path = None
        self.executor = None

    def run(self):
        if not self.file_path:
            self.log_signal.emit("No file selected!")
            return

        self.log_signal.emit("Processing file...")
        output_file = self.file_path.rsplit('.', 1)[0] + '_ips.csv'
        futures = []
        code_number_to_future = {}
        results = []
        total_lines = 0

        # Create a new ThreadPoolExecutor instance for each run
        self.executor = ThreadPoolExecutor(max_workers=10)

        try:
            with open(self.file_path, 'r') as infile:
                lines = infile.readlines()
                total_lines = len(lines)
                self.progress.emit(0)

                # Step 1: Resolve base IP addresses
                for line in lines:
                    code_number = line.strip()
                    if code_number.isdigit():
                        base_hostname = f'pth{code_number}'
                        future = self.executor.submit(self.get_ip_from_hostname, base_hostname)
                        futures.append(future)
                        code_number_to_future[future] = code_number
                    else:
                        self.log_signal.emit(f'Invalid code number: {code_number}')
                        results.extend([(code_number, f'N/A.{octet}') for octet in [60, 61, 62]])

            completed_futures = 0
            unique_ips = set()  # To track unique IP addresses
            for future in as_completed(futures):
                code_number = code_number_to_future[future]
                base_ip = future.result()
                if base_ip:
                    # Generate IP addresses with last octet 60, 61, and 62
                    for last_octet in [60, 61, 62]:
                        ip_address = f'{base_ip.rsplit(".", 1)[0]}.{last_octet}'
                        if ip_address not in unique_ips:
                            results.append((code_number, ip_address))
                            unique_ips.add(ip_address)
                else:
                    # Handle case where base IP is not resolved
                    for last_octet in [60, 61, 62]:
                        ip_address = f'N/A.{last_octet}'
                        if (code_number, ip_address) not in results:
                            results.append((code_number, ip_address))

                self.log_signal.emit(f'Processed {code_number}: {base_ip if base_ip else "N/A"}')
                completed_futures += 1
                self.progress.emit(int((completed_futures / len(futures)) * 100))  # Update progress as a percentage

            # Final progress update to ensure it reaches 100%
            self.progress.emit(100)

            # Sort results by code_number
            results.sort(key=lambda x: x[0])

            with open(output_file, 'w') as outfile:
                for code_number, ip_address in results:
                    outfile.write(f'{code_number},{ip_address}\n')

            self.log_signal.emit(f"Processing completed. Results saved to: {output_file}")

        except Exception as e:
            self.log_signal.emit(f"Error occurred: {str(e)}")

        finally:
            if self.executor:
                self.executor.shutdown(wait=True)
            self.finished.emit()

    def get_ip_from_hostname(self, hostname):
        """Get the IP address for a given hostname."""
        try:
            return socket.gethostbyname(hostname)
        except socket.gaierror:
            return None

class IPResolver(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('IP Resolver')
        self.setGeometry(100, 100, 600, 400)

        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout()
        central_widget.setLayout(layout)

        self.log_area = QTextEdit()
        self.log_area.setReadOnly(True)
        layout.addWidget(self.log_area)

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

        self.btn_load = QPushButton('Load File')
        self.btn_load.clicked.connect(self.load_file)
        layout.addWidget(self.btn_load)

        self.worker_thread = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.worker_thread)

        self.worker.progress.connect(self.update_progress)
        self.worker.finished.connect(self.on_worker_finished)
        self.worker.log_signal.connect(self.log)
        
        self.worker_thread.started.connect(self.worker.run)

        self.file_path = None

    def load_file(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "Select Input File", "", "Text Files (*.txt);;All Files (*)")
        if file_name:
            self.file_path = file_name
            self.log(f"Selected file: {file_name}")
            self.start_worker()

    def start_worker(self):
        if self.file_path:
            self.worker.file_path = self.file_path
            self.worker_thread.start()

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

    def on_worker_finished(self):
        self.log("Processing finished.")
        self.worker_thread.quit()
        self.worker_thread.wait()

    def log(self, message):
        self.log_area.append(message)

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


change option load file or can pasting text box for load data and make retrive button