import numpy as np
from sklearn import preprocessing

def netNormExtended(v, nbr_of_sub, nbr_of_regions, nbr_of_views):
    nbr_of_feat = int((np.square(nbr_of_regions) - nbr_of_regions) / 2)

    def scale_mtx(mt):
        shape = mt.shape
        min_max_scaler = preprocessing.MinMaxScaler()
        temp = mt.flatten().reshape(-1, 1)
        temp = min_max_scaler.fit_transform(temp)
        return temp.reshape(shape[0], shape[1])

    def minmax_sc(x):
        min_max_scaler = preprocessing.MinMaxScaler()
        x = min_max_scaler.fit_transform(x)
        return x

    def upper_triangular():
        All_subj = np.zeros((nbr_of_sub, len(v), nbr_of_feat))
        for i in range(len(v)):
            for j in range (nbr_of_sub):

                subj_x = v[i, j, :, :]
                subj_x = np.reshape(subj_x, (nbr_of_regions, nbr_of_regions))
                subj_x = minmax_sc(subj_x)
                subj_x = subj_x[np.triu_indices(nbr_of_regions, k = 1)]
                subj_x = np.reshape(subj_x, (1, 1, nbr_of_feat))
                All_subj[j, i, :] = subj_x

        return All_subj

    def distances_inter(All_subj):
        theta = 0
        distance_vector = np.zeros(1)
        distance_vector_final = np.zeros(1)
        x = All_subj
        for i in range(nbr_of_feat): #par rapport ll number of ROIs
            ROI_i = x[:, :, i]
            ROI_i = np.reshape(ROI_i, (nbr_of_sub, nbr_of_views)) #1,3
            for j in range(nbr_of_sub):
                subj_j = ROI_i[j:j+1, :]
                subj_j = np.reshape(subj_j, (1, nbr_of_views))
                for k in range(nbr_of_sub):
                    if k != j:
                        subj_k = ROI_i[k:k+1, :]
                        subj_k = np.reshape(subj_k, (1, nbr_of_views))

                        for l in range(nbr_of_views):
                            if l ==0:
                                distance_euclidienne_sub_j_sub_k = np.square(subj_k[:, l:l+1] - subj_j[:, l:l+1])
                            else:
                                distance_euclidienne_sub_j_sub_k = distance_euclidienne_sub_j_sub_k + np.square(subj_k[:, l:l+1] - subj_j[:, l:l+1])

                            theta +=1
                if j ==0:
                    distance_vector = np.sqrt(distance_euclidienne_sub_j_sub_k)
                else:
                    distance_vector = np.concatenate((distance_vector, np.sqrt(distance_euclidienne_sub_j_sub_k)), axis=0)

            if i ==0:
                distance_vector_final = distance_vector
            else:
                distance_vector_final = np.concatenate((distance_vector_final, distance_vector), axis=1)

        print(theta)
        return distance_vector_final


    def minimum_distances(distance_vector_final):
        x = distance_vector_final
        general_minimum = 0
        for i in range(nbr_of_feat):
            for j in range(nbr_of_sub):
                minimum_sub = x[j:j+1, i:i+1]
                minimum_sub = float(minimum_sub)
                for k in range(nbr_of_sub):
                    if k != j:
                        local_sub = x[k:k+1, i:i+1]
                        local_sub = float(local_sub)
                        if local_sub < minimum_sub:
                            general_minimum = k
                            general_minimum = np.array(general_minimum)
            if i == 0:
                final_general_minimum = np.array(general_minimum)
            else:
                final_general_minimum = np.vstack((final_general_minimum, general_minimum))

        final_general_minimum = np.transpose(final_general_minimum)

        return final_general_minimum

    def new_tensor(final_general_minimum, All_subj):
        y = All_subj
        x = final_general_minimum
        for i in range(nbr_of_feat):
            optimal_subj = x[:, i:i+1]
            optimal_subj = np.reshape(optimal_subj, (1))
            optimal_subj = int(optimal_subj)
            if i ==0:
                final_new_tensor = y[optimal_subj: optimal_subj+1, :, i:i+1]
            else:
                final_new_tensor = np.concatenate((final_new_tensor, y[optimal_subj: optimal_subj+1, :, i:i+1]), axis=2)

        return final_new_tensor


    def make_sym_matrix(nbr_of_regions, feature_vector):

        nbr_of_regions = nbr_of_regions
        feature_vector = feature_vector
        my_matrix = np.zeros([nbr_of_regions,nbr_of_regions], dtype=np.double)

        my_matrix[np.triu_indices(nbr_of_regions, k=1)] = feature_vector
        my_matrix = my_matrix + my_matrix.T
        my_matrix[np.diag_indices(nbr_of_regions)] = 0

        return my_matrix

    def re_make_tensor(final_new_tensor, nbr_of_regions):
        x =final_new_tensor
        x = np.reshape(x, (nbr_of_views, nbr_of_feat))
        for i in range (nbr_of_views):
            view_x = x[i, :]
            view_x = np.reshape(view_x, (1, nbr_of_feat))
            view_x = make_sym_matrix(nbr_of_regions, view_x)
            view_x = np.reshape(view_x, (1, nbr_of_regions, nbr_of_regions))
            if i ==0:
                tensor_for_snf = view_x
            else:
                tensor_for_snf = np.concatenate((tensor_for_snf, view_x), axis=0)
        return tensor_for_snf

    def create_list(tensor_for_snf):
        x =tensor_for_snf
        for i in range(nbr_of_views):
            view = x[i, :, :]
            view = np.reshape(view, (nbr_of_regions, nbr_of_regions))
            list = [view]
            if i ==0:
                list_final = list
            else:
                list_final = list_final +list
        return list_final

    def cross_subjects_cbt(fused_network, nbr_of_exemples):
        final_cbt = np.zeros((nbr_of_exemples, nbr_of_feat))
        x = fused_network
        x = x[np.triu_indices(nbr_of_regions, k=1)]
        x = np.reshape(x, (1, nbr_of_feat))
        for i in range(nbr_of_exemples):
            final_cbt[i, :] = x


        return final_cbt



    Upp_trig = upper_triangular()
    Dis_int = distances_inter(Upp_trig)
    Min_dis = minimum_distances(Dis_int)
    New_ten = new_tensor(Min_dis, Upp_trig)
    Re_ten = re_make_tensor(New_ten, nbr_of_regions)
    return Re_ten
import numpy as np
from sklearn import preprocessing

def netNormExtended(v, nbr_of_sub, nbr_of_regions, nbr_of_views):
    nbr_of_feat = int((np.square(nbr_of_regions) - nbr_of_regions) / 2)

    def scale_mtx(mt):
        shape = mt.shape
        min_max_scaler = preprocessing.MinMaxScaler()
        temp = mt.flatten().reshape(-1, 1)
        temp = min_max_scaler.fit_transform(temp)
        return temp.reshape(shape[0], shape[1])

    def minmax_sc(x):
        min_max_scaler = preprocessing.MinMaxScaler()
        x = min_max_scaler.fit_transform(x)
        return x

    def upper_triangular():
        All_subj = np.zeros((nbr_of_sub, len(v), nbr_of_feat))
        for i in range(len(v)):
            for j in range (nbr_of_sub):

                subj_x = v[i, j, :, :]
                subj_x = np.reshape(subj_x, (nbr_of_regions, nbr_of_regions))
                subj_x = minmax_sc(subj_x)
                subj_x = subj_x[np.triu_indices(nbr_of_regions, k = 1)]
                subj_x = np.reshape(subj_x, (1, 1, nbr_of_feat))
                All_subj[j, i, :] = subj_x

        return All_subj

    def distances_inter(All_subj):
        theta = 0
        distance_vector = np.zeros(1)
        distance_vector_final = np.zeros(1)
        x = All_subj
        for i in range(nbr_of_feat): #par rapport ll number of ROIs
            ROI_i = x[:, :, i]
            ROI_i = np.reshape(ROI_i, (nbr_of_sub, nbr_of_views)) #1,3
            for j in range(nbr_of_sub):
                subj_j = ROI_i[j:j+1, :]
                subj_j = np.reshape(subj_j, (1, nbr_of_views))
                for k in range(nbr_of_sub):
                    if k != j:
                        subj_k = ROI_i[k:k+1, :]
                        subj_k = np.reshape(subj_k, (1, nbr_of_views))

                        for l in range(nbr_of_views):
                            if l ==0:
                                distance_euclidienne_sub_j_sub_k = np.square(subj_k[:, l:l+1] - subj_j[:, l:l+1])
                            else:
                                distance_euclidienne_sub_j_sub_k = distance_euclidienne_sub_j_sub_k + np.square(subj_k[:, l:l+1] - subj_j[:, l:l+1])

                            theta +=1
                if j ==0:
                    distance_vector = np.sqrt(distance_euclidienne_sub_j_sub_k)
                else:
                    distance_vector = np.concatenate((distance_vector, np.sqrt(distance_euclidienne_sub_j_sub_k)), axis=0)

            if i ==0:
                distance_vector_final = distance_vector
            else:
                distance_vector_final = np.concatenate((distance_vector_final, distance_vector), axis=1)

        print(theta)
        return distance_vector_final


    def minimum_distances(distance_vector_final):
        x = distance_vector_final
        general_minimum = 0
        for i in range(nbr_of_feat):
            for j in range(nbr_of_sub):
                minimum_sub = x[j:j+1, i:i+1]
                minimum_sub = float(minimum_sub)
                for k in range(nbr_of_sub):
                    if k != j:
                        local_sub = x[k:k+1, i:i+1]
                        local_sub = float(local_sub)
                        if local_sub < minimum_sub:
                            general_minimum = k
                            general_minimum = np.array(general_minimum)
            if i == 0:
                final_general_minimum = np.array(general_minimum)
            else:
                final_general_minimum = np.vstack((final_general_minimum, general_minimum))

        final_general_minimum = np.transpose(final_general_minimum)

        return final_general_minimum

    def new_tensor(final_general_minimum, All_subj):
        y = All_subj
        x = final_general_minimum
        for i in range(nbr_of_feat):
            optimal_subj = x[:, i:i+1]
            optimal_subj = np.reshape(optimal_subj, (1))
            optimal_subj = int(optimal_subj)
            if i ==0:
                final_new_tensor = y[optimal_subj: optimal_subj+1, :, i:i+1]
            else:
                final_new_tensor = np.concatenate((final_new_tensor, y[optimal_subj: optimal_subj+1, :, i:i+1]), axis=2)

        return final_new_tensor


    def make_sym_matrix(nbr_of_regions, feature_vector):

        nbr_of_regions = nbr_of_regions
        feature_vector = feature_vector
        my_matrix = np.zeros([nbr_of_regions,nbr_of_regions], dtype=np.double)

        my_matrix[np.triu_indices(nbr_of_regions, k=1)] = feature_vector
        my_matrix = my_matrix + my_matrix.T
        my_matrix[np.diag_indices(nbr_of_regions)] = 0

        return my_matrix

    def re_make_tensor(final_new_tensor, nbr_of_regions):
        x =final_new_tensor
        x = np.reshape(x, (nbr_of_views, nbr_of_feat))
        for i in range (nbr_of_views):
            view_x = x[i, :]
            view_x = np.reshape(view_x, (1, nbr_of_feat))
            view_x = make_sym_matrix(nbr_of_regions, view_x)
            view_x = np.reshape(view_x, (1, nbr_of_regions, nbr_of_regions))
            if i ==0:
                tensor_for_snf = view_x
            else:
                tensor_for_snf = np.concatenate((tensor_for_snf, view_x), axis=0)
        return tensor_for_snf

    def create_list(tensor_for_snf):
        x =tensor_for_snf
        for i in range(nbr_of_views):
            view = x[i, :, :]
            view = np.reshape(view, (nbr_of_regions, nbr_of_regions))
            list = [view]
            if i ==0:
                list_final = list
            else:
                list_final = list_final +list
        return list_final

    def cross_subjects_cbt(fused_network, nbr_of_exemples):
        final_cbt = np.zeros((nbr_of_exemples, nbr_of_feat))
        x = fused_network
        x = x[np.triu_indices(nbr_of_regions, k=1)]
        x = np.reshape(x, (1, nbr_of_feat))
        for i in range(nbr_of_exemples):
            final_cbt[i, :] = x


        return final_cbt



    Upp_trig = upper_triangular()
    Dis_int = distances_inter(Upp_trig)
    Min_dis = minimum_distances(Dis_int)
    New_ten = new_tensor(Min_dis, Upp_trig)
    Re_ten = re_make_tensor(New_ten, nbr_of_regions)
    return Re_ten
#par rapport ll number of ROIs #1,3