"""
Module to do integration tests of plots, checking that the outputs look right.
"""
from cis.cis_main import plot_cmd
from cis.parse import parse_args
from cis.test.integration_test_data import *
from cis.test.integration.base_integration_test import BaseIntegrationTest
from cis.data_io.products.AProduct import ProductPluginException
from cis.exceptions import InvalidDimensionError
from nose.tools import raises
from unittest import skip

import shutil
import logging

import matplotlib.testing.compare as mcompare
import matplotlib.pyplot as plt

# Whether to display matplotlib output to the screen.
_DISPLAY_FIGURES = False

# plt.switch_backend('agg')


class VisualTest(BaseIntegrationTest):

    _DEFAULT_IMAGE_TOLERANCE = 0.2

    def id(self):
        # Just use the module, class and method name - the root can change depending on where it is run from
        return ".".join(super(VisualTest, self).id().split('.')[-3:])

    def setUp(self):
        # Set force overwrite in case working files are still present
        os.environ['CIS_FORCE_OVERWRITE'] = "True"
        # Make sure we have no unclosed plots from previous tests before
        # generating this one.
        if _DISPLAY_FIGURES:
            plt.close('all')

    def tearDown(self):
        # Pop off the environemnt variable
        os.environ.pop('CIS_FORCE_OVERWRITE')
        # If a plotting test bombs out it can leave the current figure
        # in an odd state, so we make sure it's been disposed of.
        if _DISPLAY_FIGURES:
            plt.close('all')

    def check_graphic(self, tol=_DEFAULT_IMAGE_TOLERANCE):
        """Checks the CRC matches for the current matplotlib.pyplot figure, and closes the figure."""

        test_id = self.id()

        figure = plt.gcf()

        try:
            expected_fname = os.path.join(os.path.dirname(__file__),
                                          'reference', 'visual_tests',
                                          test_id + '.png')

            if not os.path.isdir(os.path.dirname(expected_fname)):
                os.makedirs(os.path.dirname(expected_fname))

            #: The path where the images generated by the tests should go.
            image_output_directory = os.path.join(os.path.dirname(__file__),
                                                  'result_image_comparison')
            if not os.access(image_output_directory, os.W_OK):
                if not os.access(os.getcwd(), os.W_OK):
                    raise IOError('Write access to a local disk is required '
                                  'to run image tests.  Run the tests from a '
                                  'current working directory you have write '
                                  'access to to avoid this issue.')
                else:
                    image_output_directory = os.path.join(os.getcwd(), 'result_image_comparison')
            result_fname = os.path.join(image_output_directory, test_id + '.png')

            if not os.path.isdir(os.path.dirname(result_fname)):
                # Handle race-condition where the directories are
                # created sometime between the check above and the
                # creation attempt below.
                try:
                    os.makedirs(os.path.dirname(result_fname))
                except OSError as err:
                    # Don't care about "File exists"
                    if err.errno != 17:
                        raise

            # Output filename if the test output a file itself (using -o)
            output_fname = os.path.join(os.path.dirname(__file__), test_id+'.png')
            # If the test created an output file itself then move that to the results folder, otherwise create an output
            if os.path.exists(output_fname):
                shutil.move(os.path.join(os.path.dirname(__file__), test_id+'.png'), result_fname)
            else:
                figure.savefig(result_fname)

            if not os.path.exists(expected_fname):
                logging.warn('Created image for test %s' % test_id)
                shutil.copy2(result_fname, expected_fname)

            try:
                err = mcompare.compare_images(expected_fname, result_fname, tol=tol)
            except ValueError:
                failed_name = mcompare.make_test_filename(result_fname, 'failed-diff')
                shutil.copy2(os.path.join(os.path.dirname(__file__),
                                          'reference', 'kitten.png'), failed_name)
                err = "Images differ in size and so are not comparable"

            if _DISPLAY_FIGURES:
                if err:
                    print(('Image comparison would have failed. Message: %s' % err))
                plt.show()
            else:
                assert not err, 'Image comparison failed. Message: %s' % err

        finally:
            plt.close()


class TestPlotVisual(VisualTest):

    def test_iris_comparative_scatter(self):
        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":color=green,itemstyle=^,itemwidth=40",
                     "snow:" + escape_colons(valid_2d_filename), "--type", "comparativescatter",
                     "--logx", "--logy", "--output", self.id() + ".png"]

        main_arguments = parse_args(arguments)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_contour(self):
        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":cmap=RdBu", "--type", "contour",
                     "--xlabel", "Overidden X Label", "--title", "Overidded Title",
                    "--height", "5", "--width", "5", "--xmin", "15", "--xmax", "45", "--xstep", "20", "--cbarorient",
                     "vertical", "--fontsize", "10", "--output", self.id() + ".png"]

        main_arguments = parse_args(arguments)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_contourf(self):
        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename), "--type", "contourf",
                     "--ylabel", "Overidden Y Label", "--height", "5", "--width", "10", "--ymin", "15", "--ymax", "45",
                     "--ystep", "10", "--nocolourbar", "--output", self.id() + ".png"]

        main_arguments = parse_args(arguments)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_heatmap(self):
        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":cmap=RdBu", "--type", "heatmap",
                     "--ylabel", "Overidden Y", "--title", "OveriddenTitle",
                     "--height", "3.5", "--width", "3.5", "--vmax", "0.000135", "--fontsize", "10", "--output",
                     self.id() + ".png"]

        main_arguments = parse_args(arguments)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_heatmap_force_minus_180_to_180(self):
        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":cmap=RdBu", "--type", "heatmap",
                     "--ylabel", "Overidden Y", "--title", "OveriddenTitle", "--xmin", "-180", "--xmax", "180",
                     "--height", "3.5", "--width", "3.5", "--vmax", "0.000135", "--fontsize", "10", "--output",
                     self.id() + ".png"]

        main_arguments = parse_args(arguments)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_histogram(self):
        opts = "--xbins 5 --ymin 1 --logy --output".split() + [self.id() + ".png"]
        arguments = ["plot", "rain:" + escape_colons(valid_1d_filename) + ":color=red,itemstyle=step,label=overridenlabel",
                    "snow:" + escape_colons(valid_1d_filename) + ":color=green,itemstyle=step", "--type", "histogram"] + opts

        main_arguments = parse_args(arguments)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_histogram2d(self):
        opts = "--cmap RdBu --ylabel overiddeny --title overiddentitle --xmin 0.000002 --xmax 0.000006 " \
               "--ybins 20 --output ".split() + [self.id() + ".png"]
        arguments = ["plot", "rain:" + escape_colons(valid_1d_filename), 
                     "snow:" + escape_colons(valid_1d_filename), "--type", "histogram2d"] + opts

        main_arguments = parse_args(arguments)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_many_lines(self):
        opts = "--xlabel overiddenxlabel --ylabel overiddenylabel --width 10 --logy --grid " \
               "--ymin 0.00000000001 --xmax 50 --output".split() + [self.id() + ".png"]
        arguments = ["plot", "rain:" + escape_colons(valid_1d_filename) + ":color=red,itemwidth=5",
                     "snow:" + escape_colons(valid_1d_filename) + ":itemstyle=dashed,itemwidth=5,label=overiddenlabel2"] + opts

        main_arguments = parse_args(arguments)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_one_line(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--xlabel overiddenxlabel --title overiddentitle --height 5 --width 10 --logx" \
               " --grid".split()
        arguments = ["plot", "rain:" + escape_colons(valid_1d_filename) + ":itemstyle=dashed,label=overiddenlabel,itemwidth=4"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_one_line_with_step(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--ystep 0.00001 --xstep 25".split()
        arguments = ["plot", "rain:" + escape_colons(valid_1d_filename) ]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_scatter(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type scatter --title overiddentitle --fontsize 10 --width 7 --ymin=-0.0005" \
               " --ymax 0.0005".split()
        arguments = ["plot", "rain:" + escape_colons(valid_1d_filename) + ":itemstyle=^,itemwidth=30"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_scatter2d(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type scatter2d --fontsize 7 --cbarorient vertical --grid --ymin 0 --ymax 90 --vmin 0" \
               " --cbarorient vertical".split()
        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":cmap=RdBu",
                     "snow:" + escape_colons(valid_2d_filename) + ":label=snowlabel,cmap=RdBu"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_comparative_scatter(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = " --type comparativescatter --xlabel overiddenx --ylabel overiddeny --title overiddentitle --fontsize 7" \
               " --height 5 --width 5 --xmin 0 --xmax 1 --ymin 0 --ymax 0.5 --grid".split()
        arguments = ["plot", "AOT_440:" + escape_colons(valid_aeronet_filename) + ":color=red,itemstyle=s,itemwidth=40",
                     "AOT_870:" + escape_colons(valid_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_taylor_diagram(self):
        output_file_opt = ["--output", self.id() + ".png"]
        # You can change the fontsize, height, width and title, but the labels and limits are ignored
        opts = " --type taylor --bias color --xlabel overiddenx --title overiddentitle --fontsize 17" \
               " --height 15 --width 15 --xmin 0 --xmax 1 --ymin 0 --ymax 0.5 --grid".split()
        arguments = ["plot", "AOT_440:" + escape_colons(valid_aeronet_filename),
                     "AOT_870:" + escape_colons(valid_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_contour(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type contour --ylabel overiddenylabel --title overiddentitle --vmin 0 --cbarorient vertical --grid" \
               " --xaxis Latitude --yaxis Height".split()
        arguments = ["plot", "RVOD_liq_water_content:" + escape_colons(valid_cloudsat_RVOD_file) + ":cmap=RdBu"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_contourf(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type contourf --xlabel overiddenxlabel --fontsize 15 --height 10 --ymin 0 --ymax 10000" \
               " --vmin 0 --cbarorient horizontal --grid --xaxis Latitude --yaxis Height".split()
        arguments = ["plot", "RVOD_liq_water_content:" + escape_colons(valid_cloudsat_RVOD_file) + ":cmap=RdBu,itemwidth=4"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_histogram(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type histogram --xlabel overiddenx --fontsize 10 --height 10 --width 10 --xmin 0 --xmax 1.5" \
               " --xbins 20 --grid".split()
        arguments = ["plot", "AOT_440:" + escape_colons(valid_aeronet_filename) + ":itemstyle=step"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_histogram_bin_width(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = " --type histogram --xbins 3".split()
        arguments = ["plot", 
                     "AOT_440:" + escape_colons(valid_aeronet_filename) + ":itemstyle=step",
                     "AOT_870:" + escape_colons(valid_aeronet_filename) + ":itemstyle=step"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_histogram2d(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type histogram2d --xlabel overridenx --ylabel overiddeny --cbarlabel overiddencbarlabel " \
               "--title overiddentitle --fontsize 7 --width 10 --xmin 0 --xmax 2 --xbins 20 --ymin 0 --ymax 1.5" \
               " --ybins 20 --vmin 60 --vmax 480 --vstep 30 --cbarorient vertical --grid".split()
        arguments = ["plot", 
                     "AOT_440:" + escape_colons(valid_aeronet_filename), 
                     "AOT_870:" + escape_colons(valid_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_histogram2d_doesnt_plot_coastlines(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type histogram2d".split()
        arguments = ["plot", 
                     "RVOD_liq_water_content:" + escape_colons(valid_cloudsat_RVOD_file), 
                     "Height:" + escape_colons(valid_cloudsat_RVOD_file)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_many_scatter_points(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type scatter --ylabel overiddenylabel --ymin 0.1 --ymax 1 --logy".split()
        arguments = ["plot", "AOT_440:" + escape_colons(valid_aeronet_filename),
                     "AOT_870:" + escape_colons(valid_aeronet_filename) + ":itemstyle=x,itemwidth=20"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_many_scatter_points_given_color(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type scatter --ylabel overiddenylabel --ymin 0.1 --ymax 1 --logy".split()
        arguments = ["plot", "AOT_440:" + escape_colons(valid_aeronet_filename),
                     "AOT_870:" + escape_colons(valid_aeronet_filename) + ":color=blue,itemstyle=x,itemwidth=4"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_many_lines(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type line --ylabel overiddenylabel --ymin 0.1 --ymax 1 --logy".split()
        arguments = ["plot", "AOT_440:" + escape_colons(valid_aeronet_filename) + ":color=green,itemstyle=dotted,itemwidth=2",
                     "AOT_870:" + escape_colons(valid_aeronet_filename) + ":itemstyle=dashed,itemwidth=2",
                     "AOT_1020:" + escape_colons(valid_aeronet_filename) + ":color=red,itemwidth=2"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_one_line(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type line --xlabel overiddenx --title overiddentitle --fontsize 7 --height 7" \
               " --ymin 0.1 --ymax 1 --logy".split()
        arguments = ["plot", "AOT_440:" + escape_colons(valid_aeronet_filename) + ":itemstyle=dashed,label=overiddenlabel,itemwidth=4"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_scatter(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type scatter --xlabel overiddenx --ylabel overiddeny --title overiddentitle" \
               " --fontsize 15 --height 5 --width 6 --logy --grid".split()
        arguments = ["plot", "AOT_440:" + escape_colons(valid_aeronet_filename) + ":itemwidth=2"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_scatter2d(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type scatter2d --title overiddentitle --vmax 175 --logy --xaxis Latitude" \
               " --yaxis Height".split()
        arguments = ["plot", "RVOD_liq_water_content:" + escape_colons(valid_cloudsat_RVOD_file) + ":cmap=RdBu,itemwidth=20"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_other_longitude_wrapping_0_360(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.test.util.mock import make_regular_2d_ungridded_data
        from cis.plotting.formatted_plot import Plotter

        data = make_regular_2d_ungridded_data(lon_dim_length=90, lon_min=5., lon_max=325., lat_min=-30, lat_max=30)

        Plotter([data], xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                layer_opts=[{'itemwidth': 50}], ymin=-90, ymax=90)

        self.check_graphic()

    def test_other_longitude_wrapping_0_360_forced_minus_180_to_180(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.test.util.mock import make_regular_2d_ungridded_data
        from cis.plotting.formatted_plot import Plotter

        data = make_regular_2d_ungridded_data(lon_dim_length=90, lon_min=5., lon_max=325., lat_min=-30, lat_max=30)

        Plotter([data], xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                layer_opts=[{'itemwidth': 50}], ymin=-90, ymax=90, xmin=-180, xmax=180)

        self.check_graphic()

    def test_other_longitude_wrapping_minus_180_180(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.plotting.formatted_plot import Plotter
        from cis.test.util.mock import make_regular_2d_ungridded_data

        data = make_regular_2d_ungridded_data(lat_dim_length=2, lon_dim_length=90, lon_min=-175., lon_max=145.)

        Plotter([data], xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                layer_opts=[{'itemwidth': 50}], ymin=-90, ymax=90)

        self.check_graphic()

    @skip("Known failure")
    def test_other_longitude_wrapping_minus_180_180_forced_0_to_360(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.plotting.formatted_plot import Plotter
        from cis.test.util.mock import make_regular_2d_ungridded_data

        data = make_regular_2d_ungridded_data(lat_dim_length=2, lon_dim_length=90, lon_min=-175., lon_max=145.)

        Plotter([data], xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                layer_opts=[{'itemwidth': 50}], ymin=-90, ymax=90, xmin=0, xmax=360)

        self.check_graphic()

    def test_bluemarble_minus_180_180(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.plotting.formatted_plot import Plotter
        from cis.test.util.mock import make_regular_2d_ungridded_data

        data = make_regular_2d_ungridded_data(lat_dim_length=2, lon_dim_length=90, lon_min=-175., lon_max=145.)

        Plotter([data], xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                layer_opts=[{'itemwidth': 50}], ymin=-90, ymax=90, nasabluemarble=True)

        self.check_graphic()

    def test_bluemarble_0_360(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.test.util.mock import make_regular_2d_ungridded_data
        from cis.plotting.formatted_plot import Plotter

        data = make_regular_2d_ungridded_data(lon_dim_length=90, lon_min=5., lon_max=325., lat_min=-30, lat_max=30)

        Plotter([data], xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                layer_opts=[{'itemwidth': 50}], ymin=-90, ymax=90, nasabluemarble=True)

        self.check_graphic()

    def test_coastline_color(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.test.util.mock import make_regular_2d_ungridded_data
        from cis.plotting.formatted_plot import Plotter

        data = make_regular_2d_ungridded_data(lon_dim_length=90, lon_min=5., lon_max=325., lat_min=-30, lat_max=30)

        Plotter([data], xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                ymin=-90, ymax=90, coastlinescolour='red', layer_opts=[{'itemwidth': 50}])

        self.check_graphic()

    @skip("I can't make this plot 0-360 whatever I try...")
    def test_other_longitude_wrapping_multiple_ranges_forced_0_to_360(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.plotting.formatted_plot import Plotter
        from cis.test.util.mock import make_regular_2d_ungridded_data

        datas = [make_regular_2d_ungridded_data(lat_dim_length=2, lon_dim_length=90, lon_min=-175., lon_max=145.),
                 make_regular_2d_ungridded_data(lon_dim_length=90, lon_min=5., lon_max=325., lat_min=30, lat_max=60)]

        Plotter(datas, xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                ymin=-90, ymax=90, xmin=-180, xmax=180,
                layer_opts=[{'label': '-180to180', 'itemwidth': 50}, {'label': '0to360', 'itemwidth': 50}])

        self.check_graphic()

    def test_other_longitude_wrapping_multiple_ranges_forced_minus_180_to_180(self):
        """
        Test that ungridded data which crosses the dateline gets plotted correctly
        """
        from cis.plotting.formatted_plot import Plotter
        from cis.test.util.mock import make_regular_2d_ungridded_data

        datas = [make_regular_2d_ungridded_data(lat_dim_length=2, lon_dim_length=90, lon_min=-175., lon_max=145.),
                 make_regular_2d_ungridded_data(lon_dim_length=90, lon_min=5., lon_max=325., lat_min=30, lat_max=60)]

        Plotter(datas, xaxis='longitude', yaxis='latitude', width=8, height=6, cbarscale=None,
                ymin=-90, ymax=90, xmin=-180, xmax=180,
                layer_opts=[{'label': '-180to180', 'itemwidth': 50}, {'label': '0to360', 'itemwidth': 50}])

        self.check_graphic()

    def test_iris_contour_over_heatmap(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--width 20 --height 15 --cbarscale 0.5".split()

        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":type=heatmap,cbarorient=vertical",
                     "solarupclear:" + escape_colons(valid_2d_filename) + ":type=contour,color=white,contlevels=[1,10,25,50,175],cbarorient=vertical"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_contour_over_heatmap_binary_cmap(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--xmin -180 --xmax 180 --width 20 --height 15 --cbarscale 0.5".split()

        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":type=heatmap,cmap=binary,cbarorient=vertical",
                     "solarupclear:" + escape_colons(valid_2d_filename) + ":type=contour,cmap=jet,contlevels=[1,10,25,50,175],cbarorient=vertical"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    @skip("For some reason matplotlib doesn't respect vmin... But you can just set contlevels instead.")
    def test_transparent_contour_over_bluemarble(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type contourf --xmin -180 --xmax 180 --width 20 --height 15 --cbarscale 0.5" \
               " --nasabluemarble".split()

        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":cmap=Reds,alpha=0.5,vmin=0.000075"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    @skip("The wrapping here isn't working for some reason")
    def test_filled_contour_over_scatter2d(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--type contourf --width 20 --height 15 --xaxis longitude --yaxis latitude " \
               " --ymin 0 --ymax 90".split()

        arguments = ["plot", "GGALT:" + escape_colons(valid_NCAR_NetCDF_RAF_filename) + ":type=scatter2d,itemwidth=20",
                     "solarupclear:" + escape_colons(valid_2d_filename) + ":contlevels=[0,10,20,30,40,50,100],alpha=0.7,"
                                              "contlabel=true"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    @skip("The wrapping here isn't working for some reason")
    def test_filled_contour_over_scatter2d_with_cmin(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--width 20 --height 15 --xaxis longitude --yaxis latitude --xmin -180" \
               " --xmax -90 --ymin 0 --ymax 90 --nasabluemarble".split()

        arguments = ["plot", "GGALT:" + escape_colons(valid_NCAR_NetCDF_RAF_filename) + ":type=scatter2d,itemwidth=20",
                     "solarupclear:" + escape_colons(valid_2d_filename) + ":type=contourf,contlevels=[40,50,100],alpha=0.3,contlabel=true,"
                                              "cmap=Reds"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_scatter2d_over_contour(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--xlabel overiddenxlabel --height 10 --width 12 --xmin 0 --xmax 200 --xstep 10" \
               " --cbarorient horizontal --ymin 0 --ymax 90 --vmin 0 --cbarorient horizontal".split() + \
               ["--title=Overlay test"]
        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":type=contour" ,
                     "snow:" + escape_colons(valid_2d_filename) + ":type=scatter2d,itemstyle=^,label=snowlabel,itemwidth=3"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_iris_scatter2d_overlay(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = "--xlabel overiddenxlabel --height 10 --width 12 --xmin 0 --xmax 200 --xstep 10" \
               " --cbarorient horizontal --ymin 0 --ymax 90 --vmin 0 --cbarorient horizontal".split() + \
               ["--title=Overlay test"]
        arguments = ["plot", "rain:" + escape_colons(valid_2d_filename) + ":type=heatmap" ,
                     "snow:" + escape_colons(valid_2d_filename) + ":type=scatter2d,itemstyle=^,label=snowlabel,itemwidth=3"]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_aerosol_cci_default_axes(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = []

        arguments = ["plot", valid_aerosol_cci_variable + ":" + escape_colons(valid_aerosol_cci_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_polar_projection(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = ["--projection=NorthPolarStereo"]

        arguments = ["plot", valid_aerosol_cci_variable + ":" + escape_colons(valid_aerosol_cci_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_mercator_projection(self):
        output_file_opt = ["--output", self.id() + ".png"]
        # These limits are ignored
        opts = ["--projection=Mercator", "--xmin=-90", "--xmax=90", "--nasabluemarble"]

        arguments = ["plot", valid_aerosol_cci_variable + ":" + escape_colons(valid_aerosol_cci_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_setting_xrange_using_datetimes(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = ["--xmin=2003-08-01",  "--xmax=2003-12-01T12:30:12"]

        arguments = ["plot", valid_aeronet_variable + ":" + escape_colons(valid_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_aeronet_default_axes(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = []

        arguments = ["plot", valid_aeronet_variable + ":" + escape_colons(valid_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_multiple_time_series_default_axes(self):
        # JASCIS-231
        output_file_opt = ["--output", self.id() + ".png"]
        opts = []

        # This guesses the axes based on the first datagroup, and then the second one based on the shape.
        arguments = ["plot", valid_aeronet_variable + ":" + escape_colons(another_valid_aeronet_filename),
                     'AOT_440:' + escape_colons(aggregated_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_multiple_time_series_incompatible_axes(self):
        # JASCIS-231
        output_file_opt = ["--output", self.id() + ".png"]
        opts = ['--type=line']

        # The aggregated data has some guessed axis labels (x and y are lon and lat respectively) which don't
        #  correspond to the other aeronet file, the plotter should ignore these though since they are scalar coords.
        arguments = ["plot", 'AOT_440_std_dev:' + escape_colons(aggregated_aeronet_filename),
                     valid_aeronet_variable + ":" + escape_colons(another_valid_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_multiple_time_series_default_axes_files_with_named_xaxis(self):
        # JASCIS-231
        output_file_opt = ["--output", self.id() + ".png"]
        opts = ['--xaxis=time']

        arguments = ["plot", 'AOT_440_std_dev:' + escape_colons(aggregated_aeronet_filename),
                     valid_aeronet_variable + ":" + escape_colons(another_valid_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    def test_plotting_heatmap_of_aggregated_ungridded_data(self):
        output_file_opt = ["--output", self.id() + ".png"]
        # opts = ['--xmin=-0.5', '--xmax=360']
        opts = []

        arguments = ["plot", 'AOD550:' + escape_colons(make_pathname('aggregated_aerosol_cci.nc'))]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()

    @raises(ProductPluginException)
    def test_aeronet_multiple_variable_plots(self):
        output_file_opt = ["--output", self.id() + ".png"]
        opts = []

        # Plotting doesn't support multiple variables
        arguments = ["plot", "AOT_532,AOT_551" + ":" + escape_colons(valid_aeronet_filename)]

        main_arguments = parse_args(arguments + opts + output_file_opt)
        plot_cmd(main_arguments)

        self.check_graphic()


class TestPlotAPIVisual(VisualTest):

    def test_implicit_comparative_scatter(self):
        from cis.test.util.mock import make_regular_2d_ungridded_data
        from cis.data_io.ungridded_data import UngriddedDataList

        d = UngriddedDataList([make_regular_2d_ungridded_data(), make_regular_2d_ungridded_data(data_offset=2)])
        # This is needed to setup the coord shapes unfortunately...
        _ = d[0].data
        _ = d[1].data
        d[0].metadata._name = 'snow'
        d[1].plot(xaxis=d[0])

        self.check_graphic()

    def test_explicit_comparative_scatter(self):
        from cis.test.util.mock import make_regular_2d_ungridded_data
        from cis.data_io.ungridded_data import UngriddedDataList

        d = UngriddedDataList([make_regular_2d_ungridded_data(), make_regular_2d_ungridded_data(data_offset=2)])
        # This is needed to setup the coord shapes unfortunately...
        # TODO: Fix this in the Coord somewhere
        _ = d[0].data
        _ = d[1].data
        d[0].metadata._name = 'snow'
        d.plot(how='comparativescatter')

        self.check_graphic()

    def test_iris_multiple_scatter(self):
        from cis.test.util.mock import make_mock_cube
        from cis.data_io.gridded_data import GriddedDataList

        # This only works with one dimensional gridded data
        d = GriddedDataList([make_mock_cube(lat_dim_length=0), make_mock_cube(lat_dim_length=0, data_offset=2)])
        d[0].var_name = 'snow'
        d[1].var_name = 'rain'

        # Will default to line plots
        d.plot()

        self.check_graphic()

    def test_iris_comparative_scatter(self):
        from cis.test.util.mock import make_mock_cube
        from cis.data_io.gridded_data import GriddedDataList

        d = GriddedDataList([make_mock_cube(), make_mock_cube(data_offset=2)])
        d[0].var_name = 'snow'
        d[1].var_name = 'rain'

        d.plot(how='comparativescatter')

        self.check_graphic()

    def test_heatmap(self):
        from cis.test.util.mock import make_mock_cube
        from cis.data_io.gridded_data import GriddedData

        d = GriddedData.make_from_cube(make_mock_cube())
        d.plot()

        self.check_graphic()

    def test_contour_over_bluemarble(self):
        from cis.test.util.mock import make_mock_cube
        from cis.data_io.gridded_data import GriddedData

        d = GriddedData.make_from_cube(make_mock_cube())
        ax = d.plot(how='contour')
        ax.bluemarble()

        self.check_graphic()

    def test_multiple_line(self):
        from cis.test.util.mock import make_dummy_ungridded_data_time_series
        from cis.data_io.ungridded_data import UngriddedDataList

        d = UngriddedDataList([make_dummy_ungridded_data_time_series(), make_dummy_ungridded_data_time_series()])
        _ = d[0].data
        d[1].data += 2.0
        d[1].metadata._name = 'snow'

        d.plot(how='line')

        self.check_graphic()

    def test_scatter2d_over_bluemarble(self):
        from cis.test.util.mock import make_regular_2d_ungridded_data

        d = make_regular_2d_ungridded_data()
        ax = d.plot(how='scatter2d')

        ax.bluemarble()

        self.check_graphic()

    def test_scatter2d_over_bluemarble_explicit_axis(self):
        from cis.test.util.mock import make_regular_2d_ungridded_data

        d = make_regular_2d_ungridded_data()
        d.lat.axis = ''
        d.lon.axis=''
        ax = d.plot(how='scatter2d', xaxis='longitude', yaxis='latitude')

        ax.bluemarble()

        self.check_graphic()

    def test_scatter2d_over_bluemarble_coord_axis(self):
        from cis.test.util.mock import make_regular_2d_ungridded_data

        d = make_regular_2d_ungridded_data()
        d.lat.axis = ''
        d.lon.axis=''
        ax = d.plot(how='scatter2d', xaxis=d.lon, yaxis=d.lat)

        ax.bluemarble()

        self.check_graphic()

    def test_mpl_kwargs(self):
        from cis.test.util.mock import make_dummy_ungridded_data_time_series

        d = make_dummy_ungridded_data_time_series()

        d.plot(how='line', c='purple', itemstyle='dashed')

        self.check_graphic()

    def test_layer_opts(self):
        from cis.test.util.mock import make_dummy_ungridded_data_time_series
        from cis.data_io.ungridded_data import UngriddedDataList

        d = UngriddedDataList([make_dummy_ungridded_data_time_series(),
                               make_dummy_ungridded_data_time_series()])
        _ = d[0].data
        d[1].data += 2.0
        d[1].metadata._name = 'snow'

        d.plot(how='line', layer_opts=[dict(c='yellow', itemstyle='dotted'),
                                       dict(c='purple', itemstyle='dashed')])

        self.check_graphic()

    def test_histogram(self):
        from cis.test.util.mock import make_regular_2d_ungridded_data

        d = make_regular_2d_ungridded_data()
        d.plot(how='histogram')

        self.check_graphic()

    def test_histogram_2d(self):
        from cis.test.util.mock import make_mock_cube
        from cis.data_io.gridded_data import GriddedDataList

        d = GriddedDataList([make_mock_cube(), make_mock_cube(data_offset=2)])
        d[0].var_name = 'snow'
        d[1].var_name = 'rain'

        d.plot(how='histogram2d')

        self.check_graphic()

    def test_invalid_args(self):
        from cis.test.util.mock import make_regular_2d_ungridded_data
        from nose.tools import assert_raises

        d = make_regular_2d_ungridded_data()

        with assert_raises(AttributeError):
            d.plot(how='histogram', contnlevels=5)

        #TODO: There must be more of these

    def test_taylor_diagram_gridded(self):
        from cis.test.util.mock import make_mock_cube
        from cis.data_io.gridded_data import GriddedDataList

        d = GriddedDataList([make_mock_cube(), make_mock_cube(data_offset=2)])
        d[0].var_name = 'snow'
        d[1].var_name = 'rain'

        d.plot(how='taylor')

        self.check_graphic()

    def test_orographic_projection(self):
        from cis import read_data
        import cartopy.crs as ccrs

        d = read_data(valid_aerosol_cci_filename, valid_aerosol_cci_variable)

        ax = plt.subplot(1, 1, 1, projection=ccrs.Orthographic(0, 90))

        ax = d.plot(ax=ax)

        ax.set_global()

        self.check_graphic()

    def test_polar_projection(self):
        from cis import read_data
        import cartopy.crs as ccrs

        d = read_data(valid_aerosol_cci_filename, valid_aerosol_cci_variable)

        ax = d.plot(projection=ccrs.NorthPolarStereo())

        self.check_graphic()

    def test_mercator_projection(self):
        from cis import read_data
        import cartopy.crs as ccrs

        d = read_data(valid_aerosol_cci_filename, valid_aerosol_cci_variable)

        ax = d.plot(projection=ccrs.Mercator())

        ax.bluemarble()

        self.check_graphic()

    def test_can_specify_yaxis_altitude(self):
        from cis import read_data
        d = read_data(valid_GASSP_aeroplane_filename, valid_GASSP_aeroplane_variable)
        d.plot(yaxis='altitude')

        self.check_graphic()

    def test_log_colorbar_scale(self):
        from cis import read_data
        d = read_data(valid_aerosol_cci_filename, valid_aerosol_cci_variable)
        d.plot(logv=True)

        self.check_graphic()

