import os

from typing import List, Dict

import numpy as np

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.offsetbox import OffsetImage, AnnotationBbox

from sklearn.decomposition import PCA
from sklearn.mixture import GaussianMixture

from cinemr import load_cine_mri_data
from cinemr import hide_axis_ticks

## ================================================ ##
## Exercises 8: PCA analysis of cine-MR image data  ##
## ================================================ ##

## Part 1: Specify directory and load images
##         The loaded images will be a list of
##         2D numpy arrays.
##         Each entry in the list represents one image.
directory = ...
image_arrs = load_cine_mri_data(directory)

## Part 2: Flatten all images and use them
##         to construct a data matrix.
##         Store the shape of the original images
##         in a variable. You will need it later.
## Tip: Use a for-loop to iterate over all images in the list.
##      The flattened images can be appended to another
##      list and this list can be used to
##      create a 2D numpy array, the data matrix X.
#unpacked_images_list = []
#for ...

#X = ...

## Part 3: Print the shape of your data matrix.
## It should have the shape:
## 450x16384
#print(f"Shape of the data matrix X:\n{X.shape}")

## Part 4: Create a PCA model that should retain 2 components
##         Fit the PCA model to and transform your data.
##         Print the shape of your transformed data
##         Does it look reasonable?

#...

## Part 5: Create a figure and plot the transformed data
##         using a scatter plot.
##         Since it is a PCA plot you should remove the
##         numbers from the axes. There is a function
##         for this in the same file as the image loading.
##         Add labels to the two axes to indicate that they
##         are the first and second principal components.
##         Also try to add the relative amount of variance
##         they represent.
##         Look for examples in the lecture slides.
##         What do you think the plot represents?
#ax = plt.figure(figsize=(4,4)).gca()

#...

## Part 6: Create a GaussianMixture (GM) of the transformed data.
##         Ie, the GM will model the data in the two PCA dimensions.
##         Look at the previous plot and use a reasonable number of
##         components for the GM, each component in this case represents
##         one Gaussian function to be "placed on the data" in order
##         to model its distribution.

#...

## Part 7: Sample from your GaussianMixture to generate 10-20 new data points.
##         You can look at the example in the lecture 7 slides on clustering.
##         Plot the original transformed data points and these new
##         generated data points. Use different colors for them.
## Note:   We are only interested in the data points X_gm themselves, not
##         their labels, y_gm, which is also output by the "sample(...)"-method
##         of the GaussianMixture class.

#X_gm, y_gm = ...

#...

## Part 8: Plot the PCA reconstructed images from the GM
##         sampled data on the same plot as the transformed
##         data points.
##         Make sure the data matrix containing the GM samples
##         is called X_gm.

## For each sampled position in PCA space use inverse_transform
## to generate an 128x128 image.
"""
for index in range(X_gm.shape[0]):
    x_gm = X_gm[index,:]

    ## Part 8: Inverse transform the GM data point and reshape
    ##         such that the flat vector is unpacked into
    ##         an image with the same shape as the original images.
    pca_image = ...
    ...

    ## This will plot the generated image at
    ## its position in the PCA plot
    boxstyle = mpatches.BoxStyle("Round", pad=0.2)
    im_f = OffsetImage(pca_image,
                       zoom=0.25,
                       cmap='bone',
                       interpolation='bicubic',
                       #interpolation='nearest',
                       )
    ab = AnnotationBbox(im_f,
                        x_gm,
                        xycoords='data',
                        frameon=True,
                        bboxprops={'boxstyle': boxstyle},
                        zorder=10)
    # NOTE: ax has to exist here for this to work!
    ax.add_artist(ab)
"""

## Part 9: Save or show the last plot containing the data points as well as
##         the reconstructed images
