"""
Cubes
=====

Tools to deal with spectroscopic data cubes.  

Some features in Cubes require additional packages:

   * smoothing - requires agpy_\'s smooth and parallel_map routines
   * `pyregion <git://github.com/astropy/pyregion.git>`_


The 'grunt work' is performed by the :py:mod:`cubes` module


"""
# import parent package
import pyspeckit
from pyspeckit import spectrum
from ..spectrum.units import generate_xarr,SpectroscopicAxis
# import local things
import mapplot
import readers
import time
import numpy as np
from pyspeckit.parallel_map import parallel_map
import types
import copy
import itertools
from pyspeckit.spectrum import history
from astropy.io import fits
import cubes
from astropy import log
from astropy import wcs
from astropy import units
class Cube(spectrum.Spectrum):

    def __init__(self, filename=None, cube=None, xarr=None, xunit=None,
                 errorcube=None, header=None, x0=0, y0=0,
                 maskmap=None,
                 **kwargs):
        """
        A pyspeckit Cube object.  Can be created from a FITS file on disk or
        from an array or a `spectral_cube.SpectralCube` object.  If an array
        is used to insantiate the cube, the `xarr` keyword must be given,
        specifying the X-axis units

        Parameters
        ----------
        filename : str, optional
            The name of a FITS file to open and read from.  Must be 3D
        cube : `np.ndarray`, `spectral_cube.SpectralCube`, or \
               `astropy.units.Quantity`
            The data from which to instantiate a Cube object.  If it is
            an array or an astropy Quantity (which is an array with attached
            units), the X-axis must be specified.  If this is given as a
            SpectralCube object, the X-axis and units should be handled
            automatically.
        xarr : `np.ndarray` or `astropy.units.Quantity`, optional
            The X-axis of the spectra from each cube.  This actually
            corresponds to axis 0, or what we normally refer to as the Z-axis
            of the cube, but it indicates the X-axis in a plot of intensity vs
            wavelength.  The units for this array are specified in the `xunit`
            keyword unless a `~astropy.units.Quantity` is given.
        xunit : str, optional
            The unit of the ``xarr`` array if ``xarr`` is given as a numpy
            array
        errorcube : `np.ndarray`, `spectral_cube.SpectralCube`,\
                    or `~astropy.units.Quantity`, optional
            A cube with the same shape as the input cube providing the 1-sigma
            error for each voxel.  This can be specified more efficiently as an
            error map for most use cases, but that approach has not yet been
            implemented.  However, you can pass a 2D error map to `fiteach`.
        header : `fits.Header` or dict, optional
            The header associated with the data.  Only needed if the cube is
            given as an array or a quantity.
        x0, y0 : int
            The initial spectrum to use.  The `Cube` object can be treated as
            a `pyspeckit.Spectrum` object, with all the associated tools
            (plotter, fitter) using the `set_spectrum` method to select a pixel
            from the cube to plot and fit.  However, it is generally more sensible
            to extract individual spectra and treat them separately using the
            `get_spectrum` method, so these keywords MAY BE DEPRECATED in the
            future.
        maskmap : `np.ndarray`, optional
            A boolean mask map, where ``True`` implies that the data are good.
            This will be used for both plotting using `mapplot` and fitting
            using `fiteach`.

        """

        if filename is not None:
            self.load_fits(filename)
            return
        else:
            if hasattr(cube, 'spectral_axis'):
                # Load from a SpectralCube instance
                self.cube = cube.hdu.data
                if (cube.unit in ('undefined', units.dimensionless_unscaled)
                    and 'BUNIT' in cube._meta):
                    self.unit = cube._meta['BUNIT']
                else:
                    self.unit = cube.unit
                log.debug("Self.unit: {0}".format(self.unit))
                if xarr is None: