""" This module is for visualising the results obtained by different experiments. Functions in this module
    are designed to create the required type of visulaisations that are hard coded and serve the need of
    project however the code can be reused for your purpose of creating a different graph/visulaization

    matplotlib is used for the purpose of visualization
"""

from matplotlib import pyplot as plt
from operator import itemgetter

def show_l_inf_plot(kind='',dim='',method='',spectral_radius='',l_inf_values=[],s=10,folder='test'):
    """ This function generates the graph depicting how the infinity norm of error is decreasing with the number of iterations
        with x-axis containing number of iterations and y-axis the error in x, which is ||X-X*||_inf
    """
    plt.scatter(x = range(0,len(l_inf_values)),y = l_inf_values,s=s,c='#0303fc')
    plt.title(f'Kind : {kind}\nDimension : {dim}\nMethod : {method}\nSpectral radius : {spectral_radius}')
    plt.xlabel('Iteration Number')
    plt.ylabel('||X-X*||_inf')

    # A shortname for the iterative method used for storing the plot in a file using this short name
    shortMethod = "".join(list(map(itemgetter(0),method.split(' '))))
    # plt.show()
    # plt.gcf() gets the current figure on the canvas maintained by the python matplotlib package
    # this is needed as the canvas used by the library is modified by other calls as the package
    # only maintains a single state-machine and thus resulting in multiple plots overlapped while
    # calling savefig. So, the solution to this problem is to get the current figure and operate and close
    # the figure rather than operating on the whole canvas which can be modified by other calls to the
    # package.
    fig = plt.gcf()
    # bbox_inches is needed to export the complete graph without cropping the large axis titles and names
    fig.savefig(folder+'/'+kind+'_'+str(dim)+'_'+shortMethod+'_linf',bbox_inches='tight')
    # As we are operating on a figure object from matplotlib package we need to close once we are done with
    # exporting the figure if not the next figure/plot is drawn on same figure object creating unwarranted
    # overlaps of multiple plots. However, as a feature we can choose not to close if we need multiple plots
    # to be overlapped.
    plt.close(fig)

def show_iterations_plot(kind='',dim='',y=[],iteration_values=[],s=1,color='blue',folder='test'):

    """ This is a summary of multiple error vs number of iteration plots on different iterative methods
        It plots a horizontal bar histogram with length of the bar depicting the number of iterations needed
        for the corresponding iterative method to converge until the tolerance value chosen. This gives us a
        good comparision of how each iterative method performs with respect to other methods.
    """

    plt.barh(y=y[::-1],width=iteration_values[::-1],color=color)
    # the 0.9, 1.1 quantities are arbitrary and only chosen so that the graph scale on x-axis is fine-grained
    # clearly depicting the small differences in the number of iterations.
    plt.xlim([min(iteration_values)*0.9,max(iteration_values)*1.1])
    for index,value in enumerate(iteration_values[::-1]):
        plt.text(value,index,str(round(value,6))) # Rounding off the value to 6 digits, useless here though
    plt.title(f'Kind : {kind}\nDimension : {dim}\n')
    plt.xlabel('Number of Iterations')
    plt.ylabel('Method')
    # plt.show()
    fig = plt.gcf()
    fig.savefig(folder+'/'+kind+'_'+str(dim)+'_iter',bbox_inches='tight')
    plt.close(fig)
    
def show_spectral_radius_plot(kind='',dim='',y=[],spectral_radius_values=[],s=1,color='blue',folder='test'):

    """ This is a summary plot comparing the spectral radius of iteration matrix of different iterative methods.
        It plots a horizontal bar histogram with length of the bar depicting the spectral radius of the iterative matrix
        of different iterative methods. This gives us a good comparision on spectral radius of iteration matrices
        generated by different iterative methods.
    """

    plt.barh(y=y[::-1],width=spectral_radius_values[::-1],color=color)
    width = max(spectral_radius_values) - min(spectral_radius_values)
    # xlim is set in such a way that the differences in the spectral radius can be seen clearly by limiting the x-axis
    # to the fine-grained values near to the min and max of the spectral radius values to be plotted.
    plt.xlim([min(spectral_radius_values)-width/2,max(spectral_radius_values)+width/2])
    for index,value in enumerate(spectral_radius_values[::-1]):
        plt.text(value,index,str(round(value,8)))
    plt.title(f'Kind : {kind}\nDimension : {dim}\n')
    plt.ylabel('Method')
    plt.xlabel('Spectral Radius')
    # plt.show()
    fig = plt.gcf()
    fig.savefig(folder+'/'+kind+'_'+str(dim)+'_spec',bbox_inches='tight')
    plt.close(fig)