#
#coding=utf-8
#Copyright (C) 2015 Barcelona Science
#
#This program is free software; you can redistribute it and/or modify it under the terms of the 
#GNU General Public License as published by the Free Software Foundation; either version 2 of the License, 
#or (at your option) any later version. This program is distributed in the hope that it will be useful, but 
#WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
#PURPOSE. See the GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License along with this program; if not, write to 
#the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
'''
:copyright: Copyright (C) 2015 Barcelona Science
:authors: L.M. de Vries, A. Nardi
:contact: enric.vazquez@idaea.csic.es
:license: GPL General Public License, version 2 or any later version (http://opensource.org/licenses/GPL-2.0)
'''
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import itertools

from PyQt4 import QtGui
from collections import OrderedDict
import copy
from matplotlib.lines import Line2D

known_types = {
    'int': [int],
    'float': [int, float],
    'str': [str, unicode],
    'bool': [bool]
}

class MARKERSERIES():
    filled = "filled set"
    circle = "circle"
    all = "complete set"
    
    name = {}    
    name[filled]=['o', 'v', '8', 's', 'p', '*', '^', '<', '>', 'h', 'H', 'D', 'd']    
    name[circle]=['o']    
    name[all] = []
    for m in Line2D.markers:            
        try:
            if len(m) == 1 and m != ' ':
                name[all].append(m)
        except TypeError:
            pass

class LINESERIES():
    dashed = "dashed"
    solid = "solid"
    alternate = "alternate"
    
    name = {}
    name[dashed]=['--']    
    name[solid]=['-']        
    name[alternate]=['-', '--']    

class CPSETTINGS():
    value_key = "value"
    label_key = "label"
    type_value_key = "type_value"

    title = "title"
    window = "window"
    line_plot = "line_plot"   
    marker_plot = "marker_plot"    
    legend = "legend"
    
    title_show_id = "title_show"    
    title_horizontal_alignment_id = "title_horizontal_alignment"
    title_label_id = "title_label"
    title_font_type_id = "title_font_type"   
    title_font_size_id = "title_font_size"   
    title_font_color_id = "title_font_color"
    title_y_position_id = "title_y_position"
    
    window_title_id = "window_title"
    window_X_size_id = "window_X_size"
    window_Y_size_id = "window_Y_size"
    window_DPI_id = "window_DPI"    
    
    marker_show_id = "marker_show"        
    marker_type_serie_id = "marker_type_serie"    
    marker_color_serie_id = "marker_color_serie"    
    marker_size_id = "marker_size"    
    marker_edge_width_id = "marker_edge_width"
    
    line_show_id = "line_show"
    line_type_serie_id = "line_type_serie"        
    line_color_serie_id = "line_color_serie"    
    line_width_id = "line_width"
      
    legend_show_id = "legend_show"    
    legend_num_columns_automatic_id = "legend_num_columns_automatic"    
    legend_num_columns_id = "legend_num_columns"    
    legend_shadow_id = "legend_shadow"
    legend_markerscale_id = "legend_markerscale"
    legend_fontsize_id = "legend_fontsize"

    default_settings = OrderedDict(
                            {title: {
                                title_show_id:{
                                    value_key: True,
                                    label_key: "Show title",
                                    type_value_key: "bool"
                                    },
                                title_horizontal_alignment_id:{
                                    value_key: "center",
                                    label_key: "Horizontal Alignment",
                                    type_value_key: "str"
                                    },                                        
                                title_label_id:{
                                    value_key: "Plot title",
                                    label_key: "Plot title",
                                    type_value_key: "str"
                                    },
                                title_font_type_id:{
                                    value_key: "Arial",
                                    label_key: "Title Font Type",
                                    type_value_key: "str"
                                    },
                                title_font_size_id:{
                                    value_key: 24.0,
                                    label_key: "Plot title",
                                    type_value_key: "float"
                                    },
                                title_font_color_id:{
                                    value_key: "black",
                                    label_key: "Plot title",
                                    type_value_key: "str"
                                    },
                                title_y_position_id:{
                                    value_key: 1.0,
                                    label_key: "Title y position",
                                    type_value_key: "float"
                                    }   
                                },
                            window:{
                                window_title_id:{
                                    value_key: "Window title",
                                    label_key: "Window title",
                                    type_value_key: "str"
                                    },
                                window_X_size_id:{
                                    value_key: 860,
                                    label_key: "Plot window size",
                                    type_value_key: "float"
                                    },
                                window_Y_size_id:{
                                    value_key: 720,
                                    label_key: "Plot window size",
                                    type_value_key: "float"
                                    },
                                window_DPI_id:{
                                    value_key: 80,
                                    label_key: "DPI",
                                    type_value_key: "float"
                                    }                                 
                                },
                            marker_plot:{
                                marker_show_id:{
                                    value_key: True,
                                    label_key: "Marker show",
                                    type_value_key: "bool"
                                    },                                         
                                marker_type_serie_id:{
                                    value_key: "filled set",
                                    label_key: "Marker type serie ",
                                    type_value_key: "str"
                                    },
                                marker_color_serie_id:{
                                    value_key: "jet",
                                    label_key: "Marker color Serie",
                                    type_value_key: "str"    
                                    },
                                marker_size_id:{
                                    value_key: 10.5,
                                    label_key: "Marker Size",
                                    type_value_key: "float"
                                    },
                                marker_edge_width_id:{
                                    value_key: 0.5,
                                    label_key: "Marker edge width",
                                    type_value_key: "float"
                                    }    
                                },
                            line_plot:{
                                line_show_id:{
                                    value_key: True,
                                    label_key: "Line show",
                                    type_value_key: "bool"
                                    },                                               
                                line_type_serie_id:{
                                    value_key: "solid",
                                    label_key: "Line serie",
                                    type_value_key: "str"
                                    },
                                line_color_serie_id:{
                                    value_key: "jet",
                                    label_key: "Line color Serie",
                                    type_value_key: "str" 
                                    },
                                line_width_id:{
                                    value_key: 1.0,
                                    label_key: "Line width",
                                    type_value_key: "float"
                                    }                      
                                },                           
                            legend:{
                                legend_show_id:{
                                    value_key: True,
                                    label_key: "Show Legend",
                                    type_value_key: "bool"
                                    },
                                legend_num_columns_automatic_id:{
                                    value_key: True,
                                    label_key: "Number of columns automatic",
                                    type_value_key: "bool"
                                    },
                                legend_num_columns_id:{
                                    value_key: 4,
                                    label_key: "Number of columns",
                                    type_value_key: "int"
                                    },
                                legend_shadow_id:{
                                    value_key: False,
                                    label_key: "Shadow",
                                    type_value_key: "bool"
                                    },
                                legend_markerscale_id:{
                                    value_key: 1.0,
                                    label_key: "legend markerscale",
                                    type_value_key: "float"
                                    },
                                legend_fontsize_id:{
                                    value_key: 14.0,
                                    label_key: "legend fontsize",
                                    type_value_key: "float"
                                    }
                                }
                            })

class ChemPlot(QtGui.QDialog):
    '''
    Base class of the hydrochemistry plotting library called chemPLotLib. Provides methods that are used for derived 
    classes that define each plot characteristics.
    '''
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent)
        self.parent = parent        
        self.legendPosition = 'withSpaceForXTicks'        
        self.m_settings = {}
        self.reset_to_default_settings()    
    
    def set_setting(self, family, id_setting, value):
        if family in self.m_settings:
            if id_setting in self.m_settings[family]:
        
                type_value = self.m_settings[family][id_setting][CPSETTINGS.type_value_key]
                if type(value) in known_types[type_value]:              
                    self.m_settings[family][id_setting][CPSETTINGS.value_key] = value
                else:
                    print("Error type value:" + id_setting)
                    print("expected" + str( known_types[type_value]))
                    print("arrived" + str(type(value)))                    
            else:
                print("Setting does not exist:" + id_setting)
        else:
            print("family does not exist:" + family)                
                
        pass
    
    def setting(self, family, id_setting):
        if family in self.m_settings:
            if id_setting in self.m_settings[family]:        
                return self.m_settings[family][id_setting][CPSETTINGS.value_key]        
            else:
                print("Setting does not exist:" + id_setting)
        else:
            print("family does not exist:" + family)
        return None   

    def settings(self):        
        return self.m_settings
            
    def reset_to_default_settings(self):        
        self.m_settings = copy.deepcopy(CPSETTINGS.default_settings)        
    
    def set_settings(self, settings):
        for family in settings:
            for id_setting in settings[family]:
                value = settings[family][id_setting][CPSETTINGS.value_key]
                self.set_setting(family, id_setting, value)                
        
    def set_marker_settings(self):
        pass
    
    def plot_values(self):
        pass
    
    def private_plot(self, x,y,label, curPoint, date_plot = False):
        marker_color = self.marker_scalar_map.to_rgba(curPoint)            
        marker = self.marker_type_cycle.next()
        
        marker_size = self.setting(CPSETTINGS.marker_plot, CPSETTINGS.marker_size_id)
        marker_edge_width = self.setting(CPSETTINGS.marker_plot, CPSETTINGS.marker_edge_width_id)

        line_color = self.line_scalar_map.to_rgba(curPoint) 
        type_line = self.line_type_cycle.next()
        line_width = self.setting(CPSETTINGS.line_plot, CPSETTINGS.line_width_id)
        
        hasToPlotMarker = self.setting(CPSETTINGS.marker_plot, CPSETTINGS.marker_show_id)
        hasToPlotLine = self.setting(CPSETTINGS.line_plot, CPSETTINGS.line_show_id)
        
        if not hasToPlotMarker:
            marker = ""
        if not hasToPlotLine:
            type_line = ""

        if date_plot:
            self.ax.plot_date(x, y, color=line_color, label=label, 
                    ls=type_line, linewidth=line_width,                          
                    marker=marker, markersize=marker_size, markeredgewidth = marker_edge_width, 
                    markerfacecolor = marker_color, markeredgecolor="black",
                    antialiased=True)
        else:
            self.ax.plot(x,y, color=line_color, label=label, 
                    ls=type_line, linewidth=line_width,                          
                    marker=marker, markersize=marker_size, markeredgewidth = marker_edge_width, 
                    markerfacecolor = marker_color, markeredgecolor="black",
                    antialiased=True)
    
    def configure_marker(self, num_values):
        marker_color_serie = self.setting(CPSETTINGS.marker_plot, CPSETTINGS.marker_color_serie_id)
        marker_type_serie = MARKERSERIES.name[self.setting(CPSETTINGS.marker_plot, CPSETTINGS.marker_type_serie_id)]
        
        typeOfCmap = plt.get_cmap(marker_color_serie) 
        cNorm  = colors.Normalize(vmin=0, vmax=num_values)                
        self.marker_scalar_map = cmx.ScalarMappable(norm=cNorm, cmap=typeOfCmap)
        listMarker = marker_type_serie
        self.marker_type_cycle = itertools.cycle(listMarker)
        
        line_color_serie = self.setting(CPSETTINGS.line_plot, CPSETTINGS.line_color_serie_id)
        line_type_serie = LINESERIES.name[self.setting(CPSETTINGS.line_plot, CPSETTINGS.line_type_serie_id)]
        
        typeOfCmap = plt.get_cmap(line_color_serie) 
        cNorm  = colors.Normalize(vmin=0, vmax=num_values)                
        self.line_scalar_map = cmx.ScalarMappable(norm=cNorm, cmap=typeOfCmap)
        listLine = line_type_serie
        self.line_type_cycle = itertools.cycle(listLine)          
            
    def draw(self, num_generated = 0):        
        self.__create()
        self.__show(num_generated)
    
    def save(self, filename, format=None):        
        self.__create()
        self.fig.savefig(filename, format=format)

    def remove(self):
        plt.close(self.fig)
        self.fig.clear()        
        self.fig = None
        self.ax = None
               
    def __create(self):
        self.fig, self.ax = plt.subplots()
        
        # remove button customize from the toolbar
        self.toolbar = self.fig.canvas.toolbar       
        for a in self.toolbar.findChildren(QtGui.QAction):
            if a.text() == 'Customize':
                self.toolbar.removeAction(a)
                break
        
        self.fig.patch.set_facecolor('white')
                
        self.pre_processing()
        if self.setting(CPSETTINGS.title, CPSETTINGS.title_show_id):
            self.add_title()
        self.configure_marker(len(self.labels))
        self.plot_values()
        hasToShowLegend = self.setting(CPSETTINGS.legend, CPSETTINGS.legend_show_id)
        if hasToShowLegend:
            self.add_legend()
        self.post_processing()
    
    def add_title(self):
        self.setting(CPSETTINGS.marker_plot, CPSETTINGS.marker_color_serie_id)
        
        title = self.setting(CPSETTINGS.title, CPSETTINGS.title_label_id)
        title_font_type = self.setting(CPSETTINGS.title, CPSETTINGS.title_font_type_id)

        title_font_size = self.setting(CPSETTINGS.title, CPSETTINGS.title_font_size_id)
        title_font_color = self.setting(CPSETTINGS.title, CPSETTINGS.title_font_color_id)
        
        title_y_position = self.setting(CPSETTINGS.title, CPSETTINGS.title_y_position_id)        
        h_align = self.setting(CPSETTINGS.title, CPSETTINGS.title_horizontal_alignment_id)        

        plt.title(title, fontsize=title_font_size, y=title_y_position, loc=h_align, family=title_font_type, color=title_font_color) 

    def pre_processing(self):
        pass
    
    def post_processing(self):        
        pass
    
    def add_legend(self):
        # Now add the legend with some customizations.
        
        #'best'         : 0, (only implemented for axis legends)
        #'upper right'  : 1,
        #'upper left'   : 2,
        #'lower left'   : 3,
        #'lower right'  : 4,
        #'right'        : 5,
        #'center left'  : 6,
        #'center right' : 7,
        #'lower center' : 8,
        #'upper center' : 9,
        #'center'       : 10,

        numOfCol = self.setting(CPSETTINGS.legend, CPSETTINGS.legend_num_columns_id)
        is_num_legend_columns_automatic = self.setting(CPSETTINGS.legend, CPSETTINGS.legend_num_columns_automatic_id) 
        if is_num_legend_columns_automatic:
            counterCharacter = 0
            for label in self.labels:
                counterCharacter += len(label)
                
            avgCharacter = counterCharacter/len(self.labels)
            maxColumns = 4
            if avgCharacter <= 5:
                maxColumns = 7
            elif avgCharacter <= 6:
                maxColumns = 6
            elif avgCharacter <= 7:
                maxColumns = 5
            else:
                maxColumns = 4
            
            numOfCol = maxColumns

        numOfEntries = len(self.labels)
        if numOfEntries<=numOfCol :
            numOfCol = numOfEntries
        
        numOfRows = int(numOfEntries/numOfCol)+1

        box = self.ax.get_position()
        
        width = box.width
        x0 = box.x0

        if self.legendPosition == 'timePlotLegend':
            fixedLength = 0.08
            maxTop = 0.9
            minBottom = 0.0
            xAxesMargin = 0.1
                
        if self.legendPosition == 'PiperLegend':
            fixedLength = 0.08
            maxTop = 0.9
            minBottom = 0.0
            xAxesMargin = 0.05
            width = 0.9
            x0 = 0.05
        
        if self.legendPosition == 'tightToXAxis':
            fixedLength = 0.08
            maxTop = 0.85
            minBottom = 0.0
            xAxesMargin = 0.0

        ratioSpacePerRow = 0.035

        marginLegend = ratioSpacePerRow*(numOfRows)+fixedLength            
        bottomLegend = minBottom
        topLegend = minBottom+marginLegend
        sizePlotLegend = topLegend-bottomLegend
        
        bottomPlot = minBottom + sizePlotLegend + xAxesMargin
        topPlot = maxTop            
        sizePlotY = topPlot-bottomPlot

        legendCenterDistanceFromXAxes = sizePlotLegend/2 + xAxesMargin
        
        self.ax.set_position([x0, bottomPlot, width,  sizePlotY])
        
        is_legend_shadow = self.setting(CPSETTINGS.legend, CPSETTINGS.legend_shadow_id)
        markerscale = self.setting(CPSETTINGS.legend, CPSETTINGS.legend_markerscale_id) 
        fontsize = self.setting(CPSETTINGS.legend, CPSETTINGS.legend_fontsize_id)  
        
        self.ax.legend(bbox_to_anchor=(0.5, -legendCenterDistanceFromXAxes), loc='upper center',
                        borderaxespad=0. ,  numpoints=1, fancybox=True, shadow=is_legend_shadow, ncol=numOfCol, scatterpoints=1,
                        fontsize = fontsize, markerscale = markerscale).draggable()        

    def __show(self, num_generated):
        window_X_size = self.setting(CPSETTINGS.window, CPSETTINGS.window_X_size_id)
        window_Y_size = self.setting(CPSETTINGS.window, CPSETTINGS.window_Y_size_id)
        DPI = self.setting(CPSETTINGS.window, CPSETTINGS.window_DPI_id)

        self.fig.set_dpi(DPI)        
        figManager = plt.get_current_fig_manager()
        x0_pos = 100 + num_generated*20
        y0_pos = 100 + num_generated*20
        figManager.window.setGeometry(x0_pos, y0_pos, window_X_size, window_Y_size)        
        
        window_title = self.setting(CPSETTINGS.window, CPSETTINGS.window_title_id)
        figManager.window.setWindowTitle(window_title)
        
        plt.show()