import numpy as np
import tkinter as tk
from tkinter import ttk
from multiprocessing import Pool
import time

def multiply_matrices_sequential(matrix_a, matrix_b):
    return np.dot(matrix_a, matrix_b)

def multiply_matrices_parallel(args):
    row, matrix_a, matrix_b, cols = args
    return [sum(matrix_a[row, k] * matrix_b[k, col] for k in range(cols)) for col in range(cols)]

def multiply_matrices(matrix_size, parallel=False, num_threads=None):
    matrix_a = np.random.randint(1, 10, size=(matrix_size, matrix_size))  # Генерация целых чисел от 1 до 10
    matrix_b = np.random.randint(1, 10, size=(matrix_size, matrix_size))

    if parallel:
        start_time = time.time()
        args = [(row, matrix_a, matrix_b, matrix_size) for row in range(matrix_size)]

        with Pool(num_threads) as pool:
            result = np.array(pool.map(multiply_matrices_parallel, args))

        end_time = time.time()
    else:
        start_time = time.time()
        result = multiply_matrices_sequential(matrix_a, matrix_b)
        end_time = time.time()

    execution_time = end_time - start_time

    return matrix_a, matrix_b, result, execution_time

def run_multiplication():
    matrix_size = int(matrix_size_entry.get())
    algorithm_choice = algorithm_var.get()
    parallel = True if algorithm_choice == 'Параелльный' else False
    num_threads = int(threads_entry.get()) if parallel else None

    matrix_a, matrix_b, result, execution_time = multiply_matrices(matrix_size, parallel, num_threads)

    result_text.config(state=tk.NORMAL)
    result_text.delete("1.0", tk.END)
    result_text.insert(tk.END, f"Результат:\n{result}\n\nВремя: {execution_time:.6f} секунд")
    result_text.config(state=tk.DISABLED)

    matrix_a_text.config(state=tk.NORMAL)
    matrix_a_text.delete("1.0", tk.END)
    matrix_a_text.insert(tk.END, f"Матрица A:\n{matrix_a}")
    matrix_a_text.config(state=tk.DISABLED)

    matrix_b_text.config(state=tk.NORMAL)
    matrix_b_text.delete("1.0", tk.END)
    matrix_b_text.insert(tk.END, f"Матрица B:\n{matrix_b}")
    matrix_b_text.config(state=tk.DISABLED)

# GUI setup
root = tk.Tk()
root.title("Matrix Multiplication")

# Matrix Size
matrix_size_label = ttk.Label(root, text="Размер матрицы:")
matrix_size_label.grid(row=0, column=0, padx=5, pady=5)
matrix_size_entry = ttk.Entry(root)
matrix_size_entry.grid(row=0, column=1, padx=5, pady=5)
matrix_size_entry.insert(0, "")

# Algorithm Choice
algorithm_label = ttk.Label(root, text="Алгоритм:")
algorithm_label.grid(row=1, column=0, padx=5, pady=5)
algorithm_var = tk.StringVar(value="Обычный")
algorithm_combobox = ttk.Combobox(root, textvariable=algorithm_var, values=["Обычный", "Паралелльный"])
algorithm_combobox.grid(row=1, column=1, padx=5, pady=5)

# Threads (only for parallel algorithm)
threads_label = ttk.Label(root, text="Потоки:")
threads_label.grid(row=2, column=0, padx=5, pady=5)
threads_entry = ttk.Entry(root)
threads_entry.grid(row=2, column=1, padx=5, pady=5)
threads_entry.insert(0, "4")
#threads_entry.state(['readonly'])  # make it read-only initially

# Run Button
run_button = ttk.Button(root, text="Начать", command=run_multiplication)
run_button.grid(row=3, column=0, columnspan=2, pady=10)

# Result Text
result_text = tk.Text(root, height=15, width=50, state=tk.DISABLED)
result_text.grid(row=4, column=0, columnspan=2, padx=10, pady=10)

# Generated Matrix A
matrix_a_text = tk.Text(root, height=10, width=25, state=tk.DISABLED)
matrix_a_text.grid(row=5, column=0, padx=10, pady=5)

# Generated Matrix B
matrix_b_text = tk.Text(root, height=10, width=25, state=tk.DISABLED)
matrix_b_text.grid(row=5, column=1, padx=10, pady=5)

root.mainloop()