import json
import h5py
import sys
# sys.path.append("C:/Users/iuliia/recoUI/serv")
from traj import *
from reco import *
from seqData import SequenceDataFrame
from service import ReconstructionService


class ReconstructionApp():

    def __init__(self, name, digit, shift):
        super().__init__()
        self.name = name  # type seq (linear|non|epi|radial)
        # print("self.name", self.name)
        self.digit = digit  # 2|3 d
        self.phase_shift = shift  # bool

    def start_reconstruction(self, path_raw_data, path_np_data_json, path_order_json):
        try:
            service = ReconstructionService()

            nX, nY, nZ, d_scans, nContr, spoil, ETL, N_TE, phi_wing, N_wings = self.read_consts(path_np_data_json)
            # print(nX, nY, nZ, d_scans, nContr, spoil)
            service.path_raw_data = path_raw_data

            raw_data = service.read_mat_file()
            sequence_data = SequenceDataFrame()
            sequence_data.read_raw_data(raw_data)
            sequence_data.read_dots(int(nX))
            sequence_name = self.name  # Получаем введенное имя последовательности

            # print(sequence_name, path_order_json)

            if sequence_name == 'nonlinear_decart(tse)' or sequence_name == 'radial_propeller':
                print(path_order_json)

                k_space_order = self.read_order_from_json(path_order_json)
                print(f"Загружен порядок k-space: {k_space_order}")

                self.k_space_order = k_space_order

            arr_mat = np.array(sequence_data.mat_df['dot'])
            # print("len_arr_raw_data: ", len(arr_mat))

            # print(sequence_name)
            digit = self.digit  # Получаем 2d или 3d
            # print("digit:", self.digit)

            if not path_raw_data or not path_np_data_json:
                raise ValueError("Пожалуйста, выберите файлы и введите данные перед началом реконструкции.")

            else:
                json_data = path_np_data_json

                if digit == '2d':
                    # print("@ @ 2d @ @")
                    self.reconstruct_2d(sequence_name, arr_mat, json_data)
                elif digit == '3d':
                    # print("@ @ 3d @ @")
                    self.reconstruct_3d(sequence_name, arr_mat, json_data)

        except ValueError as ve:
            raise ValueError(str(ve))

        except NotImplementedError as nie:
            raise NotImplementedError(str(nie))

        except Exception as e:
            raise RuntimeError(f"Произошла ошибка в процессе реконструкции: {e}")

    def read_order_from_json(self, path_order_json):
        with open(path_order_json, 'r') as f:
            data = json.load(f)

        # Извлечение массива из ключа "k_space_order"
        k_space_order = data.get("k_space_order", [])
        return k_space_order

    def read_mat_file(self, path):
        with h5py.File(path, 'r') as f:
            key = list(f.keys())[0]

        with h5py.File(path, 'r') as f:
            data = f[key][:]

        data = np.array(data)

        return data

    def get_value_or_default(self, json_obj, key, default_value=1):
        return int(json_obj.get(key, default_value))

    def read_consts(self, path):
        with open(path, 'r') as file:
            data = json.load(file)

        # d_scans = int(data['D_scans'])
        d_scans = self.get_value_or_default(data, "D_scans", 1)
        Np = int(data['Np'])
        Nf = int(data['Nf'])
        sl_nb = int(data['sl_nb'])
        # nContrast = int(data['contrasts'])
        nContrast = self.get_value_or_default(data, "contrasts", 1)
        # spoil = float(data['RF_spoil'])
        spoil = self.get_value_or_default(data, "RF_spoil", 0)

        ETL = self.get_value_or_default(data, 'ETL', 0)  # TSE
        N_TE = self.get_value_or_default(data, 'N_TE', 0)  # TSE

        phi_wing = self.get_value_or_default(data, "phi_wing", 0)  # radial
        N_wings = self.get_value_or_default(data, "N_wings", 0)  # radial

        return Np, Nf, sl_nb, d_scans, nContrast, spoil, ETL, N_TE, phi_wing, N_wings

    def read_consts_3d(self, path):
        with open(path, 'r') as file:
            data = json.load(file)

        d_scans = self.get_value_or_default(data, "D_scans", 1)
        # d_scans = int(data['D_scans'])
        Np = int(data['Np'])
        Nf = int(data['Nf'])
        # sl_nb = self.get_value_or_default(data, "Nss", 1)
        sl_nb = int(data['Nss'])
        if sl_nb % 2 == 1:
            sl_nb -= 1
        # nContrast = int(data['contrasts'])
        nContrast = self.get_value_or_default(data, "contrasts", 1)
        # spoil = float(data['RF_spoil'])
        spoil = self.get_value_or_default(data, "RF_spoil", 0)

        return Np, Nf, sl_nb, d_scans, nContrast, spoil

    def reconstruct_2d(self, sequence_name, mat_data, json_data):
        try:
            nX, nY, nZ, d_scans, nContr, spoil, ETL, N_TE, phi_wing, N_wings = self.read_consts(json_data)
            # print(spoil)

            if self.phase_shift == False:
                spoil = 0

            '''
                all_data = k_space[x, y, z, contrast, coil] - собрано в правильном порядке
            '''
            if sequence_name == "linear_decart":
                # print("ll")
                all_data = gather_data_along_trajectory(mat_data, nX, nY, nZ, nContr)
                # print("all_data")

                save_ffft(all_data, spoil)

            elif sequence_name == "nonlinear_decart(tse)":
                all_data = gather_data_along_trajectory_nonlinear(mat_data, self.k_space_order, nX, nY, nZ, nContr)
                save_ffft(all_data, spoil)

            elif sequence_name == "linear_epi":
                all_data = gather_data_along_trajectory_linear_epi(mat_data, nX, nY, nZ, nContr)
                save_ffft(all_data, spoil)

            elif sequence_name == "radial_propeller":
                print("RADIAL")
                all_data = gather_data_along_trajectory_radial(mat_data, self.k_space_order, nX, nY, nZ, phi_wing,
                                                               N_wings)
                save_ffft(all_data, spoil)

            else:
                raise NotImplementedError("Для данного типа ИП еще не добавлена функция.")
                # QMessageBox.critical(self, "Ошибка: для данного типа ИП еще не добавлена функция")
        except ValueError as ve:
            raise ValueError(str(ve))

        except NotImplementedError as nie:
            raise NotImplementedError(str(nie))

        except Exception as e:
            raise RuntimeError(f"Произошла ошибка в процессе реконструкции: {e}")

    def reconstruct_3d(self, sequence_name, mat_data, json_data):
        try:
            nX, nY, nZ, d_scans, nContr, spoil = self.read_consts_3d(json_data)  # Nss = nSlice

            if self.phase_shift == False:
                spoil = 0

            '''
                all_data = [x, y, z, contrast, coil]
            '''

            if sequence_name == "linear_decart":
                all_data = gather_data_along_trajectory(mat_data, nX, nY, nZ, nContr)
                save_ffft_3d(all_data, spoil)

            elif sequence_name == "nonlinear_decart(tse)":
                all_data = gather_data_along_trajectory_nonlinear(mat_data, self.k_space_order, nX, nY, nZ, nContr)
                save_ffft_3d(all_data, spoil)

            else:
                raise NotImplementedError(
                    "Для данного типа ИП еще не добавлена функция.")  # TODO уже можно добавлять функцию, так как она работает
                # QMessageBox.critical(self, "Ошибка: для данного типа ИП еще не добавлена функция")

        except ValueError as ve:
            raise ValueError(str(ve))

        except NotImplementedError as nie:
            raise NotImplementedError(str(nie))

        except Exception as e:
            raise RuntimeError(f"Произошла ошибка в процессе реконструкции: {e}")
