import os, math, sys, getopt, string
from ..utils import debug_print
import pango
import random
from gtk import gdk
from ..tablemaker import PreferencesTableMaker
import threading
import cairo, gtk, gobject
import matplotlib
import numpy, numpy.fft
import scipy, scipy.interpolate, scipy.optimize
from matplotlib.backends.backend_cairo import RendererCairo
from matplotlib.backends.backend_gtkcairo import FigureCanvasGTKCairo as mpl_Canvas
from matplotlib.backends.backend_gtkcairo import NavigationToolbar2Cairo as mpl_Navbar
import pylab
from PIL import Image
from ..aobject import *
from Canvas import *
from Line import GlancerLine
from Scatter import GlancerScatter
from Legend import GlancerLegend

legend_locs = matplotlib.legend.Legend.codes    
legend_locs_rev = dict((legend_locs[k],k) for k in legend_locs)

class GlancerPlot(AObject) :
    fig = None
    axes = None
    canvas = None

    lines = None
    legend_object = None

    plot_over = False
    legend = True
    title = ''
    xlabel_font = ''

    read_series = True

    scroll_speed = 0.1

    def get_useful_vars(self) :
        return {
                 'axes' : 'mpl Axes',
                 'fig' : 'mpl Fig',
                 'canvas' : 'mpl Canvas' }

    def do_mpl_scroll_event(self, event) :
        '''Handle scrolling ourselves.'''

        if event.inaxes != self.axes :
            return False

        self.axes.set_autoscale_on(False)
        xl = self.axes.get_xlim()
        yl = self.axes.get_ylim()

        ec = (event.xdata, event.ydata)

        # event.step tells direction
        spd = (1+self.scroll_speed) ** (-event.step)

        # unfortunately, this seems to be the only sensible way to
        # get to the modifiers. Phrased oddly, but says do_x if we're
        # not told to only do y, and v.v.
        do_specific = event.guiEvent.state & gtk.gdk.CONTROL_MASK
        do_x = not (do_specific and (event.guiEvent.state & gtk.gdk.SHIFT_MASK))
        do_y = not (do_specific and do_x)

        if do_x :
            self.axes.set_xlim(ec[0] - (ec[0]-xl[0])*spd,
                               ec[0] - (ec[0]-xl[1])*spd)
        if do_y :
            self.axes.set_ylim(ec[1] - (ec[1]-yl[0])*spd,
                               ec[1] - (ec[1]-yl[1])*spd)


        self.queue_draw()
        return True

    _move_from = None
    _move_from_xl = None
    _move_from_yl = None
    def do_mpl_button_press_event(self, event) :
        '''Check button presses.'''

        if event.inaxes != self.axes :
            return False
        
        m_control = event.guiEvent.state & gtk.gdk.CONTROL_MASK

        if event.button == 2 :
            if m_control :
                self.axes.autoscale_view()
                self.axes.set_autoscale_on(True)
                self.queue_draw()
            else :
                self.axes.set_autoscale_on(False)
                self._move_from = (event.x, event.y)
                self._move_from_xl = self.axes.get_xlim()
                self._move_from_yl = self.axes.get_ylim()

            self.queue_draw()
            return True

        return False

    def do_mpl_button_release_event(self, event) :
        '''Check button releases.'''

        if event.button == 2 :
            self._move_from = None
            self._move_from_xl = None
            self._move_from_yl = None

            self.queue_draw()
            return True

        return False

    def do_mpl_motion_notify_event(self, event) :
        '''Check motion notifications.'''

        if event.inaxes != self.axes :
            return False

        do_specific = event.guiEvent.state & gtk.gdk.CONTROL_MASK
        do_x = not (do_specific and (event.guiEvent.state & gtk.gdk.SHIFT_MASK))
        do_y = not (do_specific and do_x)

        if self._move_from is not None :
            dx = (event.x-self._move_from[0])
            dy = (event.y-self._move_from[1])
            l,b,r,t = self.axes.bbox.extents
            el,er = self.axes.get_xlim()
            eb,et = self.axes.get_ylim()
            dx = dx*(er-el)/(r-l)
            dy = dy*(et-eb)/(t-b)

            if do_x :
                self.axes.set_xlim(self._move_from_xl[0]-dx,
                                   self._move_from_xl[1]-dx)
            if do_y :
                self.axes.set_ylim(self._move_from_yl[0]-dy,
                                   self._move_from_yl[1]-dy)

            self.queue_draw()
            return True

    def __init__(self, fig, queue_draw, env=None):
        self.queue_draw = queue_draw
        self.fig = fig
        self.canvas = GlancerCanvas(self.fig)
        self.axes = self.fig.add_subplot(1,1,1)
        self.canvas.mpl_connect('scroll_event', self.do_mpl_scroll_event)
        self.canvas.mpl_connect('button_press_event', self.do_mpl_button_press_event)
        self.canvas.mpl_connect('button_release_event',
                                self.do_mpl_button_release_event)
        self.canvas.mpl_connect('motion_notify_event',
                                self.do_mpl_motion_notify_event)
        self.lines = []

        AObject.__init__(self, "GlancerPlot", env, view_object=False)

    def __del__(self) :
        get_object_dictionary().disconnect(self.sd_chg_conn)
        AObject.__del__(self)

    def do_legend(self, loc = None) :
        if len(self.lines) > 0 and self.legend :
            self.axes.legend(loc=loc)

            if self.legend_object is not None :
                self.legend_object.aes_remove()
                self.legend_object = None
                self.legend_font_holder.remove(self.legend_font_holder.get_child())
            self.legend_object = GlancerLegend(self.axes.legend_,
                                               self.queue_draw, env = self.get_aenv())
            self.legend_font_holder.add(\
                self.legend_object.aes_method_font_button("font",
                                                          "Set legend font"))
            self.legend_font_holder.show_all()

            self.absorb_properties(self.legend_object, as_self = False)

            if loc==None : self.emit_property_change("legend_loc")
        else : self.axes.legend_ = None
        self.canvas.draw()

    def check_legend(self) :
        if len(self.lines) == 0 and self.legend_object :
            self.legend_object.aes_remove()
            self.legend_object = None

    def check_clear(self, force = False) :
        if not self.plot_over or force :
            self.axes.clear()
            for line in self.lines :
                line.self_remove(parent_remove = False)
            self.lines = []
            self.check_legend()
            self.queue_draw()

    def load_series(self, source, series = None, vals = None):
        if source is None :
            mpl_line, = self.axes.plot(series, vals)
            line = GlancerLine(self, line=mpl_line, env=self.get_aenv())
        elif source.source_type() == 'line' :
            line = GlancerLine(self, source=source, axes=self.axes,
                           read_series=self.read_series,
                           env=self.get_aenv())
        else :
            line = GlancerScatter(self, source=source, axes=self.axes,
                           read_series=self.read_series,
                           env=self.get_aenv())

        self.lines.append(line)
        self.absorb_properties(line, as_self = False)

        self.do_legend()

    #PROPERTIES
    def get_aesthete_properties(self):
        return { 'plot_over' : [self.change_plot_over, self.get_plot_over, True],
             'legend' : [self.change_legend, self.get_legend, True],
             'figure_facecolor' : [self.change_figure_facecolor, self.get_figure_facecolor, True],
             'axes_axis_bgcolor' : [self.change_axes_axis_bgcolor, self.get_axes_axis_bgcolor, True],
             'axes_xlabel' : [self.change_axes_xlabel, self.get_axes_xlabel, True],
             'axes_ylabel' : [self.change_axes_ylabel, self.get_axes_ylabel, True],
             'title_font' : [self.change_title_font, self.get_title_font, True],
             'xlabel_font' : [self.change_xlabel_font, self.get_xlabel_font, True],
             'ylabel_font' : [self.change_ylabel_font, self.get_ylabel_font, True],
             'xhide_oom' : [self.change_xhide_oom, self.get_xhide_oom, True],
             'yhide_oom' : [self.change_yhide_oom, self.get_yhide_oom, True],
             'xtick_font' : [self.change_xtick_font, self.get_xtick_font, True],
             'ytick_font' : [self.change_ytick_font, self.get_ytick_font, True],
             'xmultiplier' : [self.change_xmultiplier, self.get_xmultiplier, True],
             'ymultiplier' : [self.change_ymultiplier, self.get_ymultiplier, True],
             'read_series' : [self.change_read_series, self.get_read_series, True],
             'legend_loc' : [self.change_legend_loc, self.get_legend_loc, True],
             'title' : [self.change_title, self.get_title, True] }
    #BEGIN PROPERTIES FUNCTIONS
    def get_xmultiplier(self, val=None) : return 1. if val==None else float(val)
    def get_ymultiplier(self, val=None) : return 1. if val==None else float(val)
    def get_plot_over(self, val=None): return self.plot_over if val==None else (val=='True')
    def get_legend(self, val=None): return self.legend if val==None else (val=='True')
    def get_read_series(self, val=None): return self.read_series if val==None else (val=='True')
    def get_title(self, val=None): return self.title if val==None else val
    def get_legend_loc(self, val=None) :
        return (legend_locs_rev[self.legend_object.get_loc()] if self.legend_object else '')\
        if val==None else val
    def get_axes_axis_bgcolor(self, val=None):
        return mpl_to_tuple(self.axes.get_axis_bgcolor()) \
        if val==None else string_to_float_tup(val)
    def get_figure_facecolor(self, val=None):
        return mpl_to_tuple(self.fig.get_facecolor()) \
        if val==None else string_to_float_tup(val)
    def get_axes_xlabel(self, val=None) : return self.axes.get_xlabel() if val==None else val
    def get_axes_ylabel(self, val=None) : return self.axes.get_ylabel() if val==None else val
    def get_xhide_oom(self, val=None) :
        return False \
        if val==None else (val=='True')
    def get_yhide_oom(self, val=None) :
        return False \
        if val==None else (val=='True')
    def get_xtick_font(self, val=None) :
        tick_props = self.axes.get_xaxis().get_major_ticks()[0].label1.get_fontproperties()
        return mpl_to_font(tick_props) \
        if val==None else val
    def get_ytick_font(self, val=None) :
        tick_props = self.axes.get_yaxis().get_major_ticks()[0].label1.get_fontproperties()
        return mpl_to_font(tick_props) \
        if val==None else val
    def get_xlabel_font(self, val=None) :
        label_props = self.axes.get_xaxis().get_label().get_fontproperties()
        return mpl_to_font(label_props) \
        if val==None else val
    def get_ylabel_font(self, val=None) :
        label_props = self.axes.get_yaxis().get_label().get_fontproperties()
        return mpl_to_font(label_props) \
        if val==None else val
    def get_title_font(self, val=None) :
        label_props = self.axes.title.get_fontproperties()
        return mpl_to_font(label_props) \
        if val==None else val
    def change_legend_loc(self, val) : self.do_legend(loc = val)
    def change_plot_over(self, val): self.plot_over = val
    def change_title(self, val) :
        self.title = val
        self.axes.set_title(self.title, visible = (self.title!=''))
        self.queue_draw()
    def change_xhide_oom(self, val) : 
        self.axes.get_xaxis().major.formatter.hide_oom = val
        self.queue_draw()
    def change_yhide_oom(self, val) : 
        self.axes.get_yaxis().major.formatter.hide_oom = val
        self.queue_draw()
    def change_ytick_font(self, val) : 
        ticks = self.axes.get_yaxis().get_major_ticks()
        for tick in ticks :
            font_to_mpl(tick.label1.get_fontproperties(), val)
        self.queue_draw()
    def change_xtick_font(self, val) : 
        ticks = self.axes.get_xaxis().get_major_ticks()
        for tick in ticks :
            font_to_mpl(tick.label1.get_fontproperties(), val)
        self.queue_draw()
    def change_xlabel_font(self, val) : 
        label_props = self.axes.get_xaxis().get_label().get_fontproperties()
        font_to_mpl(label_props, val)
        self.queue_draw()
    def change_ylabel_font(self, val) : 
        label_props = self.axes.get_yaxis().get_label().get_fontproperties()
        font_to_mpl(label_props, val)
        self.queue_draw()
    def change_title_font(self, val) : 
        label_props = self.axes.title.get_fontproperties()
        font_to_mpl(label_props, val)
        self.queue_draw()
    def change_read_series(self, val) : self.read_series = val
    def change_xmultiplier(self, val=None) : self.axes.xaxis.get_major_formatter().multiplier = val; self.queue_draw()
    def change_ymultiplier(self, val=None) : self.axes.yaxis.get_major_formatter().multiplier = val; self.queue_draw()
    def change_legend(self, val) : self.legend = val; self.do_legend()
    def change_axes_axis_bgcolor(self, val) : self.axes.set_axis_bgcolor(val); self.queue_draw()
    def change_axes_xlabel(self, val) : self.axes.set_xlabel(val); self.queue_draw()
    def change_axes_ylabel(self, val) : self.axes.set_ylabel(val); self.queue_draw()
    def change_figure_facecolor(self, val) : self.fig.set_facecolor(val); self.queue_draw()
    #END PROPERTIES FUNCTIONS

    def replot_all(self) :
        for line in self.lines :
            line.replot()

    def get_method_window(self) :
        #fram = gtk.Frame()
        #fram.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(1, 1, 1))
        win = gtk.VBox()
        who_algn = gtk.Alignment(0.5, 0.5)
        who_algn.set_property("top_padding", 10)
        who_hbox = gtk.HBox(spacing=5)
        who_hbox.pack_start(gtk.image_new_from_stock('aes-glancer-plot2d',
                                                     gtk.ICON_SIZE_BUTTON),
                            False)
        who_hbox.pack_start(gtk.Label("2D Plot"), False)
        who_algn.add(who_hbox)
        win.pack_start(who_algn)

        icon_table = gtk.Table(1, 5)
        win.pack_start(icon_table)

        # Visual Config
        config_butt = gtk.Button()
        config_butt.set_image(gtk.image_new_from_stock(gtk.STOCK_PAGE_SETUP,
                                                       gtk.ICON_SIZE_BUTTON))
        config_butt.set_tooltip_text("Appearance preferences...")
        icon_table.attach(config_butt, 0, 1, 0, 1)
        config_vbox = self.methods_make_visual_config()
        config_vbox.aes_title = "Configure plot appearance"
        config_butt.connect("clicked", lambda o :
                            self.env.action_panel.to_action_panel(config_vbox))

        # Import Config
        legend_amtb = self.aes_method_toggle_button("legend", None,
                                                   preferencable=False)
        legend_amtb.set_image(gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_RIGHT,
                                                    gtk.ICON_SIZE_BUTTON))
        legend_amtb.set_tooltip_text("Toggle legend")
        icon_table.attach(legend_amtb, 1, 2, 0, 1)
        plot_over_amtb = self.aes_method_toggle_button("plot_over", None,
                                                     preferencable=False)
        plot_over_amtb.set_image(gtk.image_new_from_stock(gtk.STOCK_DND_MULTIPLE,
                                                    gtk.ICON_SIZE_BUTTON))
        plot_over_amtb.set_tooltip_text("Toggle overlay of new plots")
        icon_table.attach(plot_over_amtb, 2, 3, 0, 1)

        # From Sim
        sim_hbox = gtk.HBox()
        #sim_cmbo = gtk.ComboBox( get_object_dictionary().get_liststore_by_am('Source') )
        #sim_cllr = gtk.CellRendererText(); sim_cmbo.pack_start(sim_cllr); sim_cllr.props.ellipsize = pango.ELLIPSIZE_END;
        #sim_cmbo.add_attribute(sim_cllr, 'text', 1)
        #self.sim_cmbo = sim_cmbo
        #sim_hbox.pack_start(sim_cmbo)

        clear_butt = gtk.Button()
        clear_butt.set_image(gtk.image_new_from_stock(gtk.STOCK_CLEAR,
                                                      gtk.ICON_SIZE_BUTTON))
        clear_butt.set_tooltip_text("Clear all lines")
        icon_table.attach(clear_butt, 3, 4, 0, 1)
        clear_butt.connect("clicked", lambda o : self.check_clear(force=True))

        replot_butt = gtk.Button()
        replot_butt.set_image(gtk.image_new_from_stock(gtk.STOCK_REFRESH,
                                                       gtk.ICON_SIZE_BUTTON))
        replot_butt.set_tooltip_text("Replot all lines")
        replot_butt.connect("clicked", lambda o : self.replot_all())
        icon_table.attach(replot_butt, 4, 5, 0, 1)

        #fram.add(win)
        win.show_all()

        return win

    def methods_make_visual_config(self) :
        config_vbox = gtk.VBox()

        config_ntbk = gtk.Notebook()

        general_table_maker = PreferencesTableMaker()
        general_table_maker.append_heading("Title")
        general_table_maker.append_row("Text",
            self.aes_method_entry_update("title", "Set"))
        general_table_maker.append_row("Font", self.aes_method_font_button("title_font", "Set title font"))
        general_table_maker.append_heading("Colours")
        general_table_maker.append_row("Face", self.aes_method_colour_button("figure_facecolor", "Set figure colour"))
        general_table_maker.append_row("Axes",self.aes_method_colour_button("axes_axis_bgcolor", "Axes Background Colour"))

        config_tabl = general_table_maker.make_table()
        config_tabl_vbox = gtk.VBox(); config_tabl_vbox.pack_start(config_tabl, False)
        config_ntbk.append_page(config_tabl_vbox, gtk.Label("General"))

        legend_table_maker = PreferencesTableMaker()
        legend_table_maker.append_heading("Geometry")

        legend_position_cmbo = gtk.combo_box_new_text()
        for loc in legend_locs : legend_position_cmbo.append_text(loc)
        self.aes_method_automate_combo_text(legend_position_cmbo, "legend_loc")
        legend_table_maker.append_row("Position", legend_position_cmbo)

        legend_table_maker.append_heading("Appearance")
        self.legend_font_holder = gtk.Frame()
        legend_table_maker.append_row(" Font", self.legend_font_holder)

        config_tabl = legend_table_maker.make_table()
        config_tabl_vbox = gtk.VBox(); config_tabl_vbox.pack_start(config_tabl, False)
        config_ntbk.append_page(config_tabl_vbox, gtk.Label("Legend"))

        axes = { 'x' : "X" , 'y' : "Y" }
        for axis in axes :
            axes_table_maker = PreferencesTableMaker()
            axes_table_maker.append_heading("Labeling")
            axes_table_maker.append_row(" Label",
                self.aes_method_entry_update("axes_"+axis+"label", "Set"))
            axes_table_maker.append_row(" Axis",
                self.aes_method_font_button(axis+"label_font", "Set "+axes[axis]+" axis font"))
            axes_table_maker.append_row(" Tick", self.aes_method_font_button(axis+"tick_font", "Set "+axes[axis]+" axis font"))

            config_tabl = axes_table_maker.make_table()
            config_tabl_vbox = gtk.VBox(); config_tabl_vbox.pack_start(config_tabl, False); 
            config_ntbk.append_page(config_tabl_vbox, gtk.Label(axes[axis]+" Axis"))

        config_vbox.pack_start(config_ntbk)
        config_vbox.show_all()
        return config_vbox
