# -*implants -*-
"""

Functions for creating retinal implants

"""
import numpy as np
import logging

from pulse2percept import utils

SUPPORTED_IMPLANT_TYPES = ['epiretinal', 'subretinal']

class Electrode(object):

    def __init__(self, etype, radius, x_center, y_center, height=0, name=None):
        """Create an electrode on the retina

        This function creates a disk electrode of type `etype` and places it
        on the retina at location (`xs`, `ys`) in microns. The electrode has
        radius `radius` (microns) and sits a distance `height` away from the
        retinal surface.
        The coordinate system is anchored around the fovea at (0, 0).

        Parameters
        ----------
        etype : str
            Electrode type, {'epiretinal', 'subretinal'}
        radius : float
            The radius of the electrode (in microns).
        x_center : float
            The x coordinate of the electrode center (in microns) from the
            fovea.
        y_center : float
            The y location of the electrode (in microns) from the fovea
        height : float
            The height of the electrode from the retinal surface:

            - epiretinal array: distance to the ganglion layer
            - subretinal array: distance to the bipolar layer
        name : string
            Electrode name

        """
        assert radius >= 0
        assert height >= 0

        if etype.lower() not in SUPPORTED_IMPLANT_TYPES:
            e_s = "Acceptable values for `etype` are: "
            e_s += ", ".join(SUPPORTED_IMPLANT_TYPES) + "."
            raise ValueError(e_s)

        self.etype = etype.lower()
        self.radius = radius
        self.x_center = x_center
        self.y_center = y_center
        self.name = name
        self.height = height

    def __str__(self):
        info_s = "Electrode(%s, r=%.2f um, " % (self.etype, self.radius)
        info_s += "(x,y) = (%.2f, %.2f) um, " % (self.x_center, self.y_center)
        info_s += "h=%.2f um, n=%s" % (self.height, self.name)
        return info_s

    def get_height(self):
        """Returns the electrode-retina distance

        For epiretinal electrodes, this returns the distance to the ganglion
        cell layer.
        For subretinal electrodes, this returns the distance to the bipolar
        layer.
        """
        if self.etype == 'epiretinal':
            return self.h_ofl
        elif self.etype == 'subretinal':
            return self.h_inl
        else:
            raise ValueError("Unknown `etype`: " + self.etype)

    def set_height(self, height):
        """Sets the electrode-to-retina distance

        This function sets the electrode-to-retina distance according to
        `height`. For an epiretinal device, we calculate the distance to
        the ganglion cell layer (layer thickness depends on retinal location).
        For a subretinal device, we calculate the distance to the bipolar
        layer (layer thickness again depends on retinal location).

        Estimates of layer thickness based on:
        LoDuca et al. Am J. Ophthalmology 2011
        Thickness Mapping of Retinal Layers by Spectral Domain Optical
        Coherence Tomography
        Note that this is for normal retinal, so may overestimate thickness.
        Thickness from their paper (averaged across quadrants):
            0-600 um radius (from fovea):

            - Layer 1. (Nerve fiber layer) = 4
            - Layer 2. (Ganglion cell bodies + inner plexiform) = 56
            - Layer 3. (Bipolar bodies, inner nuclear layer) = 23

          600-1550 um radius:

            - Layer 1. 34
            - Layer 2. 87
            - Layer 3. 37.5

          1550-3000 um radius:
            - Layer 1. 45.5
            - Layer 2. 58.2
            - Layer 3. 30.75

        We place our ganglion axon surface on the inner side of the nerve fiber
        layer.
        We place our bipolar surface 1/2 way through the inner nuclear layer.
        So for an epiretinal array the bipolar layer is L1 + L2 + 0.5 * L3.

        """
        fovdist = np.sqrt(self.x_center ** 2 + self.y_center ** 2)
        if fovdist <= 600:
            # Layer thicknesses given for 0-600 um distance (from fovea)
            th_ofl = 4.0  # nerve fiber layer
            th_gc = 56.0  # ganglion cell bodies + inner nuclear layer
            th_bp = 23.0  # bipolar bodies + inner nuclear layer
        elif fovdist <= 1550:
            # Layer thicknesses given for 600-1550 um distance (from fovea)
            th_ofl = 34.0
            th_gc = 87.0
            th_bp = 37.5
        else:
            # Layer thicknesses given for 1550-3000 um distance (from fovea)
            th_ofl = 45.5
            th_gc = 58.2
            th_bp = 30.75
            if fovdist > 3000:
                e_s = "Distance to fovea=%.0f > 3000 um, " % fovdist
                e_s += "assuming same layer thicknesses as for 1550-3000 um "
                e_s += "distance."
                logging.getLogger(__name__).warning(e_s)

        if self.etype == 'epiretinal':
            # This is simply the electrode-retina distance
            self.h_ofl = height

            # All the way through the ganglion cell layer, inner plexiform
            # layer, and halfway through the inner nuclear layer
            self.h_inl = height + th_ofl + th_gc + 0.5 * th_bp
        elif self.etype == 'subretinal':
            # Starting from the outer plexiform layer, go halfway through the
            # inner nuclear layer
            self.h_inl = height + 0.5 * th_bp

            # Starting from the outer plexiform layer, all the way through the
            # inner nuclear layer, inner plexiform layer, and ganglion cell
            # layer
            self.h_ofl = height + th_bp + th_gc + th_ofl
        else:
            raise ValueError("Unknown `etype`: " + self.etype)
    height = property(get_height, set_height)

    def current_spread(self, xg, yg, layer, alpha=14000, n=1.69):
        """

        The current spread due to a current pulse through an electrode,
        reflecting the fall-off of the current as a function of distance from
        the electrode center. This can be calculated for any layer in the
        retina.
        Based on equation 2 in Nanduri et al [1].

        Parameters
        ----------
        xg : array
            x-coordinates of the retinal grid
        yg : array
            y-coordinates of the retinal grid
        layer: str
            Layer for which to calculate the current spread:

            - 'OFL': optic fiber layer, ganglion axons
            - 'INL': inner nuclear layer, containing the bipolars
        alpha : float
            A constant to do with the spatial fall-off.

        n : float
            A constant to do with the spatial fall-off (Default: 1.69, based
            on Ahuja et al. [2]  An In Vitro Model of a Retinal Prosthesis.
            Ashish K. Ahuja, Matthew R. Behrend, Masako Kuroda, Mark S.
            Humayun, and James D. Weiland (2008). IEEE Trans Biomed Eng 55.

        """
        r = np.sqrt((xg - self.x_center) ** 2 + (yg - self.y_center) ** 2)
        # current values on the retina due to array being above the retinal
        # surface
        if 'OFL' in layer:  # optic fiber layer, ganglion axons
            h = np.ones(r.shape) * self.h_ofl
            # actual distance from the electrode edge
            d = ((r - self.radius)**2 + self.h_ofl**2)**.5
        elif 'INL' in layer:  # inner nuclear layer, containing the bipolars
            h = np.ones(r.shape) * self.h_inl
            d = ((r - self.radius)**2 + self.h_inl**2)**.5
        else:
            s = "Layer %s not found. Acceptable values for `layer` are " \
                "'OFL' or 'INL'." % layer
            raise ValueError(s)
        cspread = (alpha / (alpha + h ** n))
        cspread[r > self.radius] = (alpha
                                   / (alpha + d[r > self.radius] ** n))

        return cspread

    def receptive_field(self, xg, yg, rftype='square', size=None):
        """An electrode's receptive field

        Parameters
        ----------
        xg : array_like
            Array of all x coordinates
        yg : array_like
            Array of all y coordinates
        rftype : {'square', 'gaussian'}
            The type of receptive field.
            - 'square': A simple square box receptive field with side length
                        `size`.
            - 'gaussian': A Gaussian receptive field where the weight drops off
                          as a function of distance from the electrode center.
                          The standard deviation of the Gaussian is `size`.
        size : float, optional
            Parameter describing the size of the receptive field. For square
            receptive fields, this corresponds to the side length of the
            square.
            For Gaussian receptive fields, this corresponds to the standard
            deviation of the Gaussian.
            Default: Twice the electrode radius.
        """
        if size is None:
            size = 2 * self.radius

        if rftype == 'square':
            # Create a map of the retina for each electrode
            # where it's 1 under the electrode, 0 elsewhere
            rf = np.zeros(xg.shape).astype(np.float32)
            ind = np.where((xg > self.x_center - (size / 2.0))
                           & (xg < self.x_center + (size / 2.0))
                           & (yg > self.y_center - (size / 2.0))
                           & (yg < self.y_center + (size / 2.0)))
            rf[ind] = 1.0
        elif rftype == 'gaussian':
            # Create a map of the retina where the weight drops of as a
            # function of distance from the electrode center
            dist = (xg - self.x_center) ** 2 + (yg - self.y_center) ** 2
            rf = np.exp(-dist / (2 * size ** 2))
            rf /= np.sum(rf)
        else:
            e_s = "Acceptable values for `rftype` are 'square' or 'gaussian'"
            raise ValueError(e_s)

        return rf

class ElectrodeArray(object):

    def __init__(self, etype, radii, xs, ys, hs=0, names=None, eye='RE'):
        """Create an ElectrodeArray on the retina
        This function creates an electrode array of type `etype` and places it
        on the retina. Lists should specify, for each electrode, its size
        (`radii`), location on the retina (`xs` and `ys`), distance to the
        retina (height, `hs`), and a string identifier (`names`, optional).
        Array location should be given in microns, where the fovea is located
        at (0, 0).
        Single electrodes in the array can be addressed by index (integer)
        or name.
        Parameters
        ----------
        radii : array_like
            List of electrode radii.
        xs : array_like
            List of x-coordinates for the center of the electrodes (microns).
        ys : array_like
            List of y-coordinates for the center of the electrodes (microns).
        hs : float | array_like, optional, default: 0
            List of electrode heights (distance from the retinal surface).
        names : array_like, optional, default: None
            List of names (string identifiers) for each eletrode.
        eye : {'LE', 'RE'}, optional, default: 'RE'
            Eye in which array is implanted.

        Examples
        --------
        A single epiretinal electrode called 'A1', with radius 100um, sitting
        at retinal location (0, 0), 10um away from the retina:
        >>> from pulse2percept import implants
        >>> implant0 = implants.ElectrodeArray('epiretinal', 100, 0, 0, hs=10,
        ...                                    names='A1')

        Get access to the electrode with name 'A1' in the first array:
        >>> my_electrode = implant0['A1']

        An array with two electrodes of size 100um, one sitting at
        (-100, -100), the other sitting at (0, 0), with 0 distance from the
        retina, of type 'subretinal':
        >>> implant1 = implants.ElectrodeArray('subretinal', [100, 100],
        ...                                    [-100, 0], [-100, 0], hs=[0, 0])
        """
        self.etype = etype
        self.eye = eye
        self.electrodes = []
        self.num_electrodes = 0
        self.add_electrodes(radii, xs, ys, hs, names)

    def __str__(self):
        return "ElectrodeArray(%s, num_electrodes=%d)" % (self.etype,
                                                          self.num_electrodes)

    def add_electrode(self, electrode):
        """Adds an electrode to an ElectrodeArray object
        This function adds a single electrode to an existing ElectrodeArray
        object. The electrode must have the same type as the array
        (see implants.SUPPORTED_IMPLANT_TYPES).
        Parameters
        ----------
        electrode : implants.Electrode
            An electrode object specifying type, size, and location of the
            electrode on the retina.
        """
        if not isinstance(electrode, Electrode):
            raise TypeError("`electrode` must be of type retina.Electrode.")

        if electrode.etype != self.etype:
            e_s = "Added electrode must be of same type as the existing"
            e_s = "array (%s)." % self.etype
            raise ValueError(e_s)

        self.num_electrodes += 1
        self.electrodes.append(electrode)

    def add_electrodes(self, radii, xs, ys, hs=0, names=None):
        """Adds electrodes to an ElectrodeArray object
        This function adds one or more electrodes to an existing ElectrodeArray
        object. Lists should specify, for each electrode to be added, the size
        (`radii`), location on the retina (`xs` and `ys`), distance to the
        retina (height, `hs`), and a string identifier (`names`, optional).
        Array location should be given in microns, where the fovea is located
        at (0, 0).
        Single electrodes in the array can be addressed by index (integer)
        or name.

        Parameters
        ----------
        radii : array_like
            List of electrode radii.
        xs : array_like
            List of x-coordinates for the center of the electrodes (microns).
        ys : array_like
            List of y-coordinates for the center of the electrodes (microns).
        hs : float | array_like, optional, default: 0
            List of electrode heights (distance from the retinal surface).
        names : array_like, optional, default: None
            List of names (string identifiers) for each eletrode.

        Examples
        --------
        Adding a single electrode of radius 50um sitting at (0, 0) to an
        existing ElectrodeArray object:
        >>> implant = ElectrodeArray('epiretinal', 100, 100, 100)
        >>> implant.add_electrodes(50, 0, 0)
        """
        # Make it so the method can accept either floats, lists, or
        # numpy arrays, and `zip` works regardless.
        radii = np.array([radii], dtype=np.float32).flatten()
        xs = np.array([xs], dtype=np.float32).flatten()
        ys = np.array([ys], dtype=np.float32).flatten()
        names = np.array([names], dtype=np.str).flatten()

        if isinstance(hs, list):
            hs = np.array(hs).flatten()
        else:
            # All electrodes have the same height
            hs = np.ones_like(radii) * hs

        assert radii.size == xs.size == ys.size == hs.size

        if names.size != radii.size:
            # If not every electrode has a name, replace with None's
            names = np.array([None] * radii.size)

        for r, x, y, h, n in zip(radii, xs, ys, hs, names):
            self.add_electrode(Electrode(self.etype, r, x, y, h, n))

    def __iter__(self):
        return iter(self.electrodes)

    def __getitem__(self, item):
        """Return the electrode specified by `item`
        Parameters
        ----------
        item : int|string
            If `item` is an integer, returns the `item`-th electrode in the
            array. If `item` is a string, returns the electrode with string
            identifier `item`.
        """
        try:
            # Is `item` an integer?
            return self.electrodes[item]
        except (IndexError, TypeError):
            # If `item` is a valid string identifier, return valid index.
            # Else return None
            try:
                return self.electrodes[self.get_index(item)]
            except (IndexError, TypeError):
                return None

    def get_index(self, name):
        """Returns the index of an electrode called `name`
        This function searches the electrode array for an electrode with
        string identifier `name`. If found, the index of that electrode is
        returned, else None.
        Parameters
        ----------
        name : str
            An electrode name (string identifier).
        Returns
        -------
        A valid electrode index or None.
        """
        # Is `name` a valid electrode name?
        # Iterate through electrodes to find a matching name. Shuffle list
        # to reduce time complexity of average lookup.
        for idx, el in utils.traverse_randomly(enumerate(self.electrodes)):
            if el.name == name:
                return idx

        # Worst case O(n): name could not be found.
        return None

    def get_eye(self):
        return self._eye

    def set_eye(self, eye):
        if eye.lower() in ['r', 're', 'right']:
            self._eye = 'RE'
        elif eye.lower() in ['l', 'le', 'left']:
            self._eye = 'LE'
        else:
            raise ValueError("Unknown eye '%s'. Choose from 'LE', 'RE'.")

    eye = property(get_eye, set_eye)

class ArgusI(ElectrodeArray):

    def __init__(self, x_center=0, y_center=0, h=0, rot=0, eye='RE',
                 use_legacy_names=False):
        """Create an ArgusI array on the retina
        This function creates an ArgusI array and places it on the retina
        such that the center of the array is located at
        [`x_center`, `y_center`] (microns) and the array is rotated by
        rotation angle `rot` (radians).
        The array is oriented in the visual field as shown in Fig. 1 of
        Horsager et al. (2009); that is, if placed in (0,0), the top two
        rows will lie in the lower retina (upper visual field):
        .. raw:: html
          <pre>
            y       A1 B1 C1 D1                     260 520 260 520
            ^       A2 B2 C2 D2   where electrode   520 260 520 260
            |       A3 B3 C3 D3   diameters are:    260 520 260 520
            -->x    A4 B4 C4 D4                     520 260 520 260
          </pre>
        Electrode order is: A1, B1, C1, D1, A2, B2, ..., D4.
        If `use_legacy_names` is True, electrode order is: L6, L2, M8, M4, ...
        An electrode can be addressed by index (integer) or name.

        Parameters
        ----------
        x_center : float, optional, default: 0
            x coordinate of the array center (um)
        y_center : float, optional, default: 0
            y coordinate of the array center (um)
        h : float || array_like, optional, default: 0
            Distance of the array to the retinal surface (um). Either a list
            with 16 entries or a scalar.
        rot : float, optional, default: 0
            Rotation angle of the array (rad). Positive values denote
            counter-clock-wise (CCW) rotations in the retinal coordinate
            system.
        eye : {'LE', 'RE'}, optional, default: 'RE'
            Eye in which array is implanted.

        Examples
        --------
        Create an ArgusI array centered on the fovea, at 100um distance from
        the retina:
        >>> from pulse2percept import implants
        >>> argus = implants.ArgusI(x_center=0, y_center=0, h=100, rot=0)

        Get access to electrode 'B1':
        >>> my_electrode = argus['B1']
        """
        # Alternating electrode sizes, arranged in checkerboard pattern
        r_arr = np.array([260, 520, 260, 520]) / 2.0
        r_arr = np.concatenate((r_arr, r_arr[::-1], r_arr, r_arr[::-1]),
                               axis=0)

        # Set left/right eye
        self.eye = eye

        # In older papers, Argus I electrodes go by L and M
        self.old_names = names = ['L6', 'L2', 'M8', 'M4',
                                  'L5', 'L1', 'M7', 'M3',
                                  'L8', 'L4', 'M6', 'M2',
                                  'L7', 'L3', 'M5', 'M1']
        # In newer papers, they go by A-D: A1, B1, C1, D1, A1, B2, ..., D4
        # Shortcut: Use `chr` to go from int to char
        self.new_names = [chr(i) + str(j) for j in range(1, 5)
                          for i in range(65, 69)]

        if use_legacy_names:
            names = self.old_names
        else:
            names = self.new_names

        if isinstance(h, list):
            h_arr = np.array(h).flatten()
            if h_arr.size != len(r_arr):
                e_s = "If `h` is a list, it must have 16 entries."
                raise ValueError(e_s)
        else:
            # All electrodes have the same height
            h_arr = np.ones_like(r_arr) * h

        # Equally spaced electrodes: n_rows x n_cols = 16
        e_spacing = 800  # um
        n_cols = 4  # number of electrodes horizontally (same vertically)
        x_arr = np.arange(n_cols) * e_spacing - (n_cols / 2 - 0.5) * e_spacing
        if self.eye == 'LE':
            # Left eye: Need to invert x coordinates and rotation angle
            x_arr = x_arr[::-1]
        x_arr, y_arr = np.meshgrid(x_arr, x_arr, sparse=False)

        # Rotation matrix
        R = np.array([np.cos(rot), -np.sin(rot),
                      np.sin(rot), np.cos(rot)]).reshape((2, 2))

        # Set the x, y location of the tack
        if self.eye == 'RE':
            self.tack = np.matmul(R, [-(n_cols / 2 + 0.5) * e_spacing, 0])
        else:
            self.tack = np.matmul(R, [(n_cols / 2 + 0.5) * e_spacing, 0])
        self.tack = tuple(self.tack + [x_center, y_center])

        # Rotate the array
        xy = np.vstack((x_arr.flatten(), y_arr.flatten()))
        xy = np.matmul(R, xy)
        x_arr = xy[0, :]
        y_arr = xy[1, :]

        # Apply offset
        x_arr += x_center
        y_arr += y_center

        self.etype = 'epiretinal'
        self.num_electrodes = 0
        self.electrodes = []
        for r, x, y, h, n in zip(r_arr, x_arr, y_arr, h_arr, names):
            self.add_electrode(Electrode(self.etype, r, x, y, h, n))

    def __str__(self):
        return "ArgusI(%s, num_electrodes=%d)" % (self.etype,
                                                  self.num_electrodes)

    def get_old_name(self, new_name):
        """Look up the legacy name of a standard-named Argus I electrode"""
        return self.old_names[self.new_names.index(new_name)]

    def get_new_name(self, old_name):
        """Look up the standard name of a legacy-named Argus I electrode"""
        return self.new_names[self.old_names.index(old_name)]

class ArgusII(ElectrodeArray):

    def __init__(self, x_center=0, y_center=0, h=0, rot=0, eye='RE'):
        """Create an ArgusII array on the retina
        This function creates an ArgusII array and places it on the retina
        such that the center of the array is located at
        [`x_center`, `y_center`] (microns) and the array is rotated by
        rotation angle `rot` (radians).
        The array is oriented upright in the visual field, such that an
        array with center (0,0) has the top three rows lie in the lower
        retina (upper visual field), as shown below:
        .. raw:: html
          <pre>
                    A1 A2 A3 A4 A5 A6 A7 A8 A9 A10
            y       B1 B2 B3 B4 B5 B6 B7 B8 B9 B10
            ^       C1 C2 C3 C4 C5 C6 C7 C8 C9 C10
            |       D1 D2 D3 D4 D5 D6 D7 D8 D9 D10
            -->x    E1 E2 E3 E4 E5 E6 E7 E8 E9 E10
                    F1 F2 F3 F4 F5 F6 F7 F8 F9 F10
          </pre>
        Electrode order is: A1, A2, ..., A10, B1, B2, ..., F10.
        An electrode can be addressed by index (integer) or name.

        Parameters
        ----------
        x_center : float
            x coordinate of the array center (um)
        y_center : float
            y coordinate of the array center (um)
        h : float || array_like
            Distance of the array to the retinal surface (um). Either a list
            with 60 entries or a scalar.
        rot : float
            Rotation angle of the array (rad). Positive values denote
            counter-clock-wise (CCW) rotations in the retinal coordinate
            system.
        eye : {'LE', 'RE'}, optional, default: 'RE'
            Eye in which array is implanted.

        Examples
        --------
        Create an ArgusII array centered on the fovea, at 100um distance from
        the retina:
        >>> from pulse2percept import implants
        >>> argus = implants.ArgusII(x_center=0, y_center=0, h=100, rot=0)

        Get access to electrode 'E7':
        >>> my_electrode = argus['E7']
        """
        # Electrodes are 200um in diameter
        r_arr = np.ones(60) * 100.0

        # Set left/right eye
        self.eye = eye

        # Standard ArgusII names: A1, A2, ..., A10, B1, ..., F10
        names = [chr(i) + str(j) for i in range(65, 71) for j in range(1, 11)]

        if isinstance(h, list):
            h_arr = np.array(h).flatten()
            if h_arr.size != len(r_arr):
                e_s = "If `h` is a list, it must have 60 entries."
                raise ValueError(e_s)
        else:
            # All electrodes have the same height
            h_arr = np.ones_like(r_arr) * h

        # Equally spaced electrodes: n_rows x n_cols = 60
        e_spacing = 525  # um
        n_cols = 10  # number of electrodes horizontally
        n_rows = 6  # number of electrodes vertically
        x_arr = np.arange(n_cols) * e_spacing - (n_cols / 2 - 0.5) * e_spacing
        if self.eye == 'LE':
            # Left eye: Need to invert x coordinates and rotation angle
            x_arr = x_arr[::-1]
        y_arr = np.arange(n_rows) * e_spacing - (n_rows / 2 - 0.5) * e_spacing
        x_arr, y_arr = np.meshgrid(x_arr, y_arr, sparse=False)

        # Rotation matrix
        rotmat = np.array([np.cos(rot), -np.sin(rot),
                           np.sin(rot), np.cos(rot)]).reshape((2, 2))

        # Set the x, y location of the tack
        if self.eye == 'RE':
            self.tack = np.matmul(rotmat, [-(n_cols / 2 + 0.5) * e_spacing, 0])
        else:
            self.tack = np.matmul(rotmat, [(n_cols / 2 + 0.5) * e_spacing, 0])
        self.tack = tuple(self.tack + [x_center, y_center])

        # Rotate the array
        xy = np.vstack((x_arr.flatten(), y_arr.flatten()))
        xy = np.matmul(rotmat, xy)
        x_arr = xy[0, :]
        y_arr = xy[1, :]

        # Apply offset
        x_arr += x_center
        y_arr += y_center

        self.etype = 'epiretinal'
        self.num_electrodes = 0
        self.electrodes = []
        for r, x, y, h, n in zip(r_arr, x_arr, y_arr, h_arr, names):
            self.add_electrode(Electrode(self.etype, r, x, y, h, n))

    def __str__(self):
        return "ArgusII(%s, num_electrodes=%d)" % (self.etype,
                                                   self.num_electrodes)

class AlphaIMS(ElectrodeArray):

    def __init__(self, x_center=0, y_center=0, h=0, rot=0, eye='RE'):
        """Create an Alpha IMS array on the retina and place it on the retina
        such that the center of the array is located at [`x_center`, `y_center`]
        (microns) and the array is rotated by rotation angle `rot` (radians).
        The array is oriented upright in the visual field, such that an array
        with center (0,0) has the top three rows lie in the lower retina
        (upper visual field), as shown below:

        Parameters
        ----------
        x_center : float
            x coordinate of the array center (um)
        y_center : float
            y coordinate of the array center (um)
        h : float || array_like
            Distance of the array to the retinal surface (um). Either a list
            with 60 entries or a scalar.
        rot : float
            Rotation angle of the array (rad). Positive values denote
            counter-clock-wise (CCW) rotations in the retinal coordinate
            system.
        eye : {'LE', 'RE'}, optional, default: 'RE'
            Eye in which array is implanted.
        """

        self.eye = eye
        self.etype = 'subretinal'

        # Electrode spacing, radius
        e_spacing = 72  # um
        elec_radius = 50
        # number of electrodes horizontally, vertically, and total
        n_cols = 37
        n_rows = 37
        n_elecs = n_cols * n_rows

        # TODO: look up naming convention
        names = np.ones(n_elecs)

        # array containing electrode radii (uniform)
        r_arr = np.full(shape=n_elecs, fill_value=elec_radius)

        # array of electrode heights (uniform)
        h_arr = np.ones_like(r_arr) * h

        # arrays of x and y coordinates
        x_arr = np.arange(n_cols) * e_spacing - (n_cols / 2 - 0.5) * e_spacing
        if self.eye == 'LE':
            # Left eye: Need to invert x coordinates and rotation angle
            x_arr = x_arr[::-1]
        y_arr = np.arange(n_rows) * e_spacing - (n_rows / 2 - 0.5) * e_spacing
        x_arr, y_arr = np.meshgrid(x_arr, y_arr, sparse=False)

        # Rotation matrix
        rotmat = np.array([np.cos(rot), -np.sin(rot),
                           np.sin(rot), np.cos(rot)]).reshape((2, 2))

        # Set the x, y location of the tack
        if self.eye == 'RE':
            self.tack = np.matmul(rotmat, [-(n_cols / 2 + 0.5) * e_spacing, 0])
        else:
            self.tack = np.matmul(rotmat, [(n_cols / 2 + 0.5) * e_spacing, 0])
        self.tack = tuple(self.tack + [x_center, y_center])

        # Rotate the array
        xy = np.vstack((x_arr.flatten(), y_arr.flatten()))
        xy = np.matmul(rotmat, xy)
        x_arr = xy[0, :]
        y_arr = xy[1, :]

        # Apply offset
        x_arr += x_center
        y_arr += y_center

        # add all electrodes
        self.num_electrodes = 0
        self.electrodes = []
        for r, x, y, h, n in zip(r_arr, x_arr, y_arr, h_arr, names):
            self.add_electrode(Electrode(self.etype, r, x, y, h, n))

    def __str__(self):
        return "AlphaIMS(%s, num_electrodes=%d)" % (self.etype,
                                                    self.num_electrodes)


#!/usr/bin/env python

#everything is needed to perform the script and maybe something else
from numpy import *
from scipy import *
from scipy import integrate
from scipy.interpolate import interp1d
import pyfits
import os
import sys
import string
import shutil
import math
import glob
import s3 #import metadata
from s3.utilities import *  #import definitions
from time import strftime, sleep
import time
from pylab import *
from scipy.optimize import curve_fit
# pre-set plot parameters, resolution untouched since it is not needed (default=80 dpi)
from pylab import rcParams
rcParams['figure.figsize'] = 11, 8
rcParams['figure.subplot.top'] = 0.95
rcParams['figure.subplot.right'] = 0.90
rcParams['figure.subplot.left'] = 0.11
###########################################
pypath = os.path.expandvars('$HOME')           # it copies login.cl if it is not in the same dir
if not os.path.isfile('login.cl'):
    shutil.copyfile(pypath+'/iraf/login.cl','login.cl')
###########################################

################### for the help ##################
from optparse import OptionParser

description = " K-correction loop for flux calibrated spectra. List of files are accepted "
usage = "%prog "
if __name__ == "__main__":
    parser = OptionParser(usage=usage, description=description, version="%prog " + str(s3.__version__))
    parser.add_option("-v", "--verbose",dest="verbose",\
                  action="store_true",default=False,
                  help='Print tasks description')
    parser.add_option("-s", "--sleep",dest="sleepc", action="store", type="float", default=None,
                  help='Change the sleep time between cycles. Default is 1s (good for 4GB of RAM or greater), the lower your RAM, the higher it should be.')
    parser.add_option("-r", "--redshifterr",dest="redshifterr", action="store", type="float" ,default=None,
                  help='Change the default error on your redshift (+/- 0.005) to estimate the K-correction errors')
    option,args = parser.parse_args()

###### moved here because OptionParser --version conflicts with pyraf version########
#what we need from iraf
from pyraf import iraf

########### option to change the python sleep function between cycles #########
if option.sleepc == None:
    _sleepc = 1
else:
    _sleepc = option.sleepc

if option.redshifterr == None:
    _redshifterr = 0.005
else:
    _redshifterr = option.redshifterr
################ internal description #############

h="######################################################################\n"+\
  "#########  SuperNova Algorithm for K-correction Evaluation  ##########\n"+\
  "##################     S.N.A.K.E. (loop version)      ################\n"+\
  "##########          C. Inserra  v1.1.0 29/10/2015          ###########\n"+\
  "######################################################################\n"+\
  " K-correction based on the formula m(x) = M(y) + DM + K(y,x)\n"+\
  " BE SURE that the spectra are flux calibrated  \n"+ \
  " If you use this code and find it useful, please give a thought \n"+ \
  " to cite it. \n"+ \
  " The reference is Inserra et al. 2015, ApJ submitted \n"+\
  "######################################################################\n"

print h

#the path where the metatabs dat are
filterdir=s3.__path__[0]+'/metadata/' # To set the directory

# cleaning process
os.system('rm -rf sn.txt')
os.system('rm -rf sn.fits')
os.system('rm -rf sn_xbbody.txt')
os.system('rm -rf sn_dez_xbbody.txt')
os.system('rm -rf sn_dez_xbbody_err1.txt')
os.system('rm -rf sn_dez_xbbody_err2.txt')
os.system('rm -rf bbody_sn_dez_fit.dat')
os.system('rm -rf bbody_sn_dez_fit_err1.dat')
os.system('rm -rf bbody_sn_dez_fit_err2.dat')
os.system('rm -rf bbody_sn_dez_fit.fits')
os.system('rm -rf bbody_sn_dez_fit_err1.fits')
os.system('rm -rf bbody_sn_dez_fit_err2.fits')
os.system('rm -rf bbody_sn_fit.fits')
os.system('rm -rf bbody_sn_fit.dat')
os.system('rm -rf sn_dez.txt')
os.system('rm -rf sn_dez_err1.txt')
os.system('rm -rf sn_dez_err2.txt')
os.system('rm -rf sn_dez.fits')
os.system('rm -rf sn_dez_err1.fits')
os.system('rm -rf sn_dez_err2.fits')
os.system('rm -rf sn_dez_dered.fits')
os.system('rm -rf sn_dez_dered_err1.fits')
os.system('rm -rf sn_dez_dered_err2.fits')
os.system('rm -rf sn_dez_dered.txt')
os.system('rm -rf sn_dez_dered_err1.txt')
os.system('rm -rf sn_dez_dered_err2.txt')
os.system('rm -rf sn_galdered_dez.fits')
os.system('rm -rf sn_galdered_dez_err1.fits')
os.system('rm -rf sn_galdered_dez_err2.fits')
os.system('rm -rf sn_galdered.fits')
os.system('rm -rf bsn_combo_dez.fits')
os.system('rm -rf bsn_combo_dez_err1.fits')
os.system('rm -rf bsn_combo_dez_err2.fits')
os.system('rm -rf bsn_combo_dez.txt')
os.system('rm -rf bsn_combo_dez_err1.txt')
os.system('rm -rf bsn_combo_dez_err2.txt')
os.system('rm -rf bsn_combo.fits')
os.system('rm -rf bsn_combo.txt')

#######################################################
# Variable definitions
#######################################################

question = raw_input('Do you have a list of spectra ? ([yes]/no) ')
if not question:
    question = 'yes'

if question == 'yes' or question == 'y' or question == 'Y' or question == 'Yes' or question == 'YES':
	files = raw_input('List ? [e.g. list, list.txt, list.dat] ')
	lcf = open(files,'r')
	riga = lcf.readlines()             # lista di righe intere
	lcf.close()
	snlist = []
	for line in riga:
		p = line.split()
		snlist.append(p[0])
else:
	files = raw_input('List the spectra to use (space separated list): ')
	snlist = string.split(files)

print ''
questionred = raw_input('Do you have a list of redshifts ? ([yes]/no) ')
if not questionred:
    questionred = 'yes'

if questionred == 'yes' or questionred == 'y' or questionred == 'Y' or questionred == 'Yes' or questionred == 'YES':
	files = raw_input('List ? [e.g. list, list.txt, list.dat] ')
	lcf = open(files,'r')
	riga = lcf.readlines()             # lista di righe intere
	lcf.close()
	z = []
	for line in riga:
		p = line.split()
		z.append(p[0])
else:
	red = raw_input('List the redshifts you want to use (space separated list) or the single redshift that will be used for all the spectra: ')
	redshift = string.split(red)
	if len(redshift) != len(snlist):
		if len(redshift) == 1:
			z = redshift * len(snlist)
	else:
		z = redshift

print ''
print '#################################'
print '#   Available filters and ID    #'
print '#-------------------------------#'
print '# BESSEL:  U  B  V  R  I        #'
print '# SLOAN:   us gs rs is zs       #'
print '# UV:  NUV  FUV  uw2  um2  uw1  #'
print '# NIR:     J  H  K              #'
print '#################################'
print ''
questionfilobs = raw_input('Do you have a list of observed filters ? ([yes]/no) ')
if not questionfilobs:
    questionfilobs = 'yes'

if questionfilobs == 'yes' or questionfilobs == 'y' or questionfilobs == 'Y' or questionfilobs == 'Yes' or questionfilobs == 'YES':
	files = raw_input('List ? [e.g. list, list.txt, list.dat] ')
	lcf = open(files,'r')
	riga = lcf.readlines()             # lista di righe intere
	lcf.close()
	fobs = []
	for line in riga:
		p = line.split()
		fobs.append(p[0])
else:
	folist = raw_input('List the observed filters you want to use (space separated list) or the observed filter that will be used for all the spectra: ')
	folist_1 = string.split(folist)
	if len(folist_1) != len(snlist):
		if len(folist_1) == 1:
			fobs = folist_1 * len(snlist)
	else:
		fobs = folist_1
print ''
questionfilrest = raw_input('Do you have a list of rest-frame filters ? ([yes]/no) ')
if not questionfilrest:
    questionfilrest = 'yes'

if questionfilrest == 'yes' or questionfilrest == 'y' or questionfilrest == 'Y' or questionfilrest == 'Yes' or questionfilrest == 'YES':
	files = raw_input('List ? [e.g. list, list.txt, list.dat] ')
	lcf = open(files,'r')
	riga = lcf.readlines()             # lista di righe intere
	lcf.close()
	frest = []
	for line in riga:
		p = line.split()
		frest.append(p[0])
else:
	frlist = raw_input('List the rest-frame filters you want to use (space separated list) or the rest-frame filter that will be used for all the spectra: ')
	frlist_1 = string.split(frlist)
	if len(frlist_1) != len(snlist):
		if len(frlist_1) == 1:
			frest = frlist_1 * len(snlist)
	else:
		frest = frlist_1

print ''
print '######################################################################'
print 'Please, bear in mind that the true K-correction should be evaluated   '
print 'without reddening. As a consequence in the cases of K-coorection on   '
print 'hybrid spectra reddening will not be evaluated.'
print '######################################################################'

print ''
_ebvg = raw_input('Galactic E(B-V) [0.0] ? ')
if not _ebvg:
    ebvg = 0.0
else:
    ebvg = float(_ebvg)
print ''

_ebvh = raw_input('Host E(B-V) [0.0] ? ')
if not _ebvh:
    ebvh = 0.0
else:
    ebvh = float(_ebvh)
print ''

questionplot = raw_input('Do you want to see your results plotted against redshift ? ([yes]/no) ')
if not questionplot:
    questionplot = 'yes'

###### Arrays definition ##########
length = shape(snlist)[0]
kcorr = array(zeros(length))
kcorr_e = array(zeros(length))
method = [None] * len(snlist)
btemp = [None] * len(snlist)
anguncov = [None] * len(snlist)
uncovside = [None] * len(snlist)
redlist = [None] * len(snlist)

##########################
### Creating a txt file
#########################
Tnow = int(strftime("%H%M%S"))
Tnowd = int(strftime("%d%m%Y"))
kcf = "Kcorrection_%.0i_%.0i.txt" % (Tnowd,Tnow)
filekc = open(kcf,"w")
filekc.write("# K-corrections from observed filter to rest-frame filter \n")
filekc.write("# Galactic E(B-V)=%g, host E(B-V)=%g \n" % (ebvg,ebvh))
filekc.write("# File\tRedshift\tObs-filter\tRest-filter\tK-corr\terror\tSNAKE mode\t Blackbody Temperature\t Angstroms uncovered in the wavelength region\n\n")

now = time.time()
ii = 0
while ii != len(snlist):
	_snname = snlist[ii]
	#### it recognizes automatically the extension of your file and convert to fits
	fileName, fileExtension = os.path.splitext(_snname)
	if fileExtension == '.txt' or fileExtension == '.dat' or fileExtension == '.asci' or fileExtension == '.ascii':
		iraf.rspec(_snname,fileName+'.fits',flux='no',dtype='interp')
		snname = fileName+'.fits'
	else:
		snname = _snname

	redshift = float(z[ii])
	redserrspace1 = float(redshift)+_redshifterr
	redserrspace2 = float(redshift)-_redshifterr
	filter1 = fobs[ii]
	filter2 = frest[ii]
	print '\033[1mSpectrum number\033[0m ', 1+ii
	print 'Spectrum = ', snname, ' & redshift = ', redshift,'+/-',_redshifterr
 	logterm = 2.5*log10(1+float(redshift))
	sn = snname

	############################# Safety loop to check again if you have everything removed and avoid errors in the programme ###################
	filetoremove = ['sn.txt','sn.fits','sn_xbbody.txt','sn_dez_xbbody.txt','sn_dez_xbbody_err1.txt','sn_dez_xbbody_err2.txt','bbody_sn_dez_fit.dat','bbody_sn_dez_fit_err1.dat','bbody_sn_dez_fit_err2.dat','bbody_sn_dez_fit.fits', \
					'bbody_sn_dez_fit_err1.fits','bbody_sn_dez_fit_err2.fits','bbody_sn_fit.fits','bbody_sn_fit.dat','sn_dez.txt','sn_dez_err1.txt','sn_dez_err2.txt','sn_dez.fits','sn_dez_err1.fits','sn_dez_err2.fits','sn_dez_dered.fits', \
					'sn_dez_dered_err1.fits','sn_dez_dered_err2.fits','sn_dez_dered.txt','sn_dez_dered_err1.txt','sn_dez_dered_err2.txt','sn_galdered_dez.fits','sn_galdered_dez_err1.fits','sn_galdered_dez_err2.fits','sn_galdered.fits', \
					'bsn_combo_dez.fits','bsn_combo_dez_err1.fits','bsn_combo_dez_err2.fits','bsn_combo_dez.txt','bsn_combo_dez_err1.txt','bsn_combo_dez_err2.txt','bsn_combo.fits','bsn_combo.txt']
	jj = 0
	while jj != len(filetoremove):
		if os.path.exists(filetoremove[jj]):
			print ''
			print "######################################################################"
			print "Sorry, I am going too fast for your computer RAM, I need to rest for a bit..."
			print "######################################################################"
			print ''
			for i in xrange(5,0,-1):
				time.sleep(1)
    			sys.stdout.write(str(i)+' ')
    			sys.stdout.flush()
			if os.path.exists(filetoremove[jj]):
				print ''
				print "######################################################################"
				print "Ooops, that is kind of embarassing, apparently there is this file "+filetoremove[jj]+" that is delaying my job. May I ask you to assist me and remove it?"
				for i in xrange(10,0,-1):
					time.sleep(1)
    				sys.stdout.write(str(i)+' ')
    				sys.stdout.flush()

				print "######################################################################"
				print ''
		jj = jj + 1
	#########################################################################################################

	#######################################################
	# Filter1 and its definitions
	#######################################################
	lcf = open(filterdir+filter1+'.txt','r')      # defintion of the file
	riga = lcf.readlines()             # list of lines
	riga1 = riga[4:len(riga)]  #list of lines where the wave and transmission are stored
	lcf.close()
	zp_ef = float(riga[0]) #zero point in energy flux (erg/cm^2/s)
	zp_ef_err = zp_ef * 1.0075
	filter_ew = riga[1] #equivalent width of the filter
	peak_wave = float(riga[2]) #peak wavelength of the filter
	system = riga[3] # system used: vega or ab
	wavefilter, transmission= [], []
	for line in riga1:
	    p = line.split()
	    wavefilter.append(float(p[0]))
	    transmission.append(float(p[1]))

	wavefilterv = array(wavefilter)
	transmissionv = array(transmission)
	fil_obs_min= min(wavefilterv)
	fil_obs_max= int(max(wavefilterv)) #integer is needed for a sharper cut-off
	#############################################################

	#######################################################
	# Filter2 and its definitions
	#######################################################
	lcfr = open(filterdir+filter2+'.txt','r')      # defintion of the file
	rigar = lcfr.readlines()             # list of lines
	rigar1 = rigar[4:len(rigar)]  #list of lines where the wave and transmission are stored
	lcfr.close()
	zp_ef_rest = float(rigar[0]) #zero point in energy flux (erg/cm^2/s)
	zp_ef_rest_err = zp_ef_rest * 1.0075
	filter_ew_rest = rigar[1] #equivalent width of the filter
	peak_wave_rest = float(rigar[2]) #peak wavelength of the filter
	system_rest = rigar[3] # system used: vega or ab
	wavefilter_rest, transmission_rest= [], []
	for line in rigar1:
	    p = line.split()
	    wavefilter_rest.append(float(p[0]))
	    transmission_rest.append(float(p[1]))

	wavefilter_restv = array(wavefilter_rest)
	transmission_restv = array(transmission_rest)
	fil_rest_min= min(wavefilter_restv)
	fil_rest_max= int(max(wavefilter_restv)) #integer is needed for a sharper cut-off
	#############################################################

	spec = sn + "[*,1,1]"            # generally multidimension
	iraf.imcopy(sn+'[*,1,1]','sn.fits',verbose='no')             # to create a onedimension fit to use during the script
	# redshift correction without absorption
	print '\033[34m*** correcting the spectrum for redshift without Milky Way absorption *** \033[0m'
	try:
		iraf.dopcor('sn.fits','sn_dez.fits', redshift=redshift, isveloc='no', flux='no',factor=3)
		iraf.dopcor('sn.fits','sn_dez_err1.fits', redshift=redserrspace1, isveloc='no', flux='no',factor=3)
		iraf.dopcor('sn.fits','sn_dez_err2.fits', redshift=redserrspace2, isveloc='no', flux='no',factor=3)
	except:
		print ' WARNING: Problem to redshift the spectrum'

	if ebvh == 0.0:
		# galaxy and host reddening correction
		print '\033[31m'+'*** correcting spectrum for galactic reddening ***\033[0m'
		ebv = ebvg+ebvh
		print '\033[31m Total E(B-V) = galactic E(B-V) = \033[0m',ebv
		try:
			iraf.unlearn("deredden")
			iraf.dered('sn_dez.fits',"sn_dez_dered.fits", value=ebv, R=3.1, type='E(B-V)',overrid='yes',uncorre='no')
			iraf.dered('sn_dez_err1.fits',"sn_dez_dered_err1.fits", value=ebv, R=3.1, type='E(B-V)',overrid='yes',uncorre='no')
			iraf.dered('sn_dez_err2.fits',"sn_dez_dered_err2.fits", value=ebv, R=3.1, type='E(B-V)',overrid='yes',uncorre='no')
		except:
				print 'WARNING: it is not possible to correct the spectrum for salactic reddenning '
				try:
					iraf.unlearn("scopy")
					iraf.scopy('sn_dez.fits',"sn_dez_dered.fits", w1='INDEF', w2='INDEF',format='multispec')
					iraf.scopy('sn_dez_err1.fits',"sn_dez_dered_err1.fits", w1='INDEF', w2='INDEF',format='multispec')
					iraf.scopy('sn_dez_err2.fits',"sn_dez_dered_err2.fits", w1='INDEF', w2='INDEF',format='multispec')
				except:
					print 'WARNING: problem to copy the spectrum or with the spectrum fits format'
	else:
		#Galaxy reddening correction
		print '\033[31m'+'*** correcting spectrum for galactic reddening ***\033[0m'
		try:
			iraf.dered(sn + '[*,1,1]',"sn_galdered.fits", value=ebvg, R=3.1, type='E(B-V)')
		except:
			print ' WARNING: it is not possible to correct the spectrum for salactic reddenning '
			try:
				iraf.scopy(sn + '[*,1,1]',"sn_galdered.fits", w1='INDEF', w2='INDEF',format='multispec')
			except:
				print ' WARNING: a problem is appeared to copy the spectrum, problems with the spectrum fits format'
		print '\033[34m*** correcting the spectrum for redshift ***\033[0m'
		try:
			iraf.dopcor('sn_galdered.fits','sn_galdered_dez.fits', redshift=redshift, isveloc='no', flux='no')
			iraf.dopcor('sn_galdered.fits','sn_galdered_dez_err1.fits', redshift=redserrspace1, isveloc='no', flux='no',factor=3)
			iraf.dopcor('sn_galdered.fits','sn_galdered_dez_err2.fits', redshift=redserrspace2, isveloc='no', flux='no',factor=3)
		except:
			print ' WARNING: Problem to redshift the spectrum'
		# host reddening correction
		print '\033[31m*** correcting spectrum for host reddening ***\033[0m'
		ebv = ebvg+ebvh
		print '\033[31m NOW total E(B-V) = ',ebv
		iraf.hedit("sn_galdered_dez.fits", 'DEREDDEN', add='no', addonly='no', delete='yes', verify ='no')
		iraf.hedit("sn_galdered_dez_err1.fits", 'DEREDDEN', add='no', addonly='no', delete='yes', verify ='no', show='no')
		iraf.hedit("sn_galdered_dez_err2.fits", 'DEREDDEN', add='no', addonly='no', delete='yes', verify ='no', show='no')
		iraf.dered('sn_galdered_dez.fits',"sn_dez_dered.fits", value=ebvh, R=3.1, type='E(B-V)',overrid='yes')
		iraf.dered('sn_galdered_dez_err1.fits',"sn_dez_dered_err1.fits", value=ebvh, R=3.1, type='E(B-V)',overrid='yes')
		iraf.dered('sn_galdered_dez_err2.fits',"sn_dez_dered_err2.fits", value=ebvh, R=3.1, type='E(B-V)',overrid='yes')
		print '\033[0m'

	#################################################################
	#### Preparation for wavelength check
	#################################################################

	spectrum=iraf.wspec("sn.fits","sn_xbbody.txt", header='no')
	lcf = open('sn_xbbody.txt','r')
	riga = lcf.readlines()
	lcf.close()
	wave,flux= [],[]
	for line in riga:
	    p = line.split()
	    wave.append(float(p[0]))
	    flux.append(float(p[1]))

	wavev = array(wave)
	fluxv = array(flux)
	waveobs_min= min(wavev)
	waveobs_max= max(wavev)

	spectrum=iraf.wspec("sn_dez_dered.fits","sn_dez_xbbody.txt", header='no')
	lcf = open('sn_dez_xbbody.txt','r')
	riga = lcf.readlines()
	lcf.close()
	wavedez,fluxdez= [],[]
	for line in riga:
		p = line.split()
		wavedez.append(float(p[0]))
		fluxdez.append(float(p[1]))

	wavedezv = array(wavedez)
	fluxdezv = array(fluxdez)
	waverest_min= min(wavedezv)
	waverest_max= max(wavedezv)

	spectrum=iraf.wspec("sn_dez_err1.fits","sn_dez_xbbody_err1.txt", header='no')
	lcf = open('sn_dez_xbbody_err1.txt','r')
	riga = lcf.readlines()
	lcf.close()
	wave,flux= [],[]
	for line in riga:
	    p = line.split()
	    wave.append(float(p[0]))
	    flux.append(float(p[1]))

	wavedezv_err1 = array(wave)
	fluxdezv_err1 = array(flux)

	spectrum=iraf.wspec("sn_dez_err2.fits","sn_dez_xbbody_err2.txt", header='no')
	lcf = open('sn_dez_xbbody_err1.txt','r')
	riga = lcf.readlines()
	lcf.close()
	wave,flux= [],[]
	for line in riga:
	    p = line.split()
	    wave.append(float(p[0]))
	    flux.append(float(p[1]))

	wavedezv_err2 = array(wave)
	fluxdezv_err2 = array(flux)

	################################
	### Define the different cases for K-correction
	################################
	split = 0 # splitting value

	if ((waveobs_min-fil_obs_min) > 50) or ((fil_obs_max-waveobs_max) > 50):
	    print ''
	    if (waveobs_min-fil_obs_min) > 50:
	        print waveobs_min-fil_obs_min,' Angstrom not covered by the observed spectrum in the blue'
	        anguncov[ii] = waveobs_min-fil_obs_min
	        uncovside[ii] = 'Blue'
	    if (fil_obs_max-waveobs_max) > 50:
	        print fil_obs_max-waveobs_max,' Angstrom not covered by the observed spectrum in the red'
	        anguncov[ii] = fil_obs_max-waveobs_max
	        uncovside[ii] = 'Red'
	        ############################################
	        # Prevent small exceptions for blue bands or the extreme of the NIR
	        ############################################
	    if filter1 != 'U' or filter1 != 'u' or filter1 != 'K' or filter1 != 'uvw1' or filter1 != 'uvw2' or filter1 != 'uvm2' or filter1 != 'NUV' or filter1 != 'FUV':

		    ###############################
		    ### BBody evaluation of the observed spectrum
		    ###############################
		    BBparams, covar = curve_fit(bbody,wavev,fluxv,p0=(10000,1E-16)) #intial guess
		    T= BBparams[0]
		    Area = BBparams[1]
		    print '\nBlackbody temperature observed spectrum = %.0f +\- %.0f K\n' % (T,np.sqrt(covar[0,0]))
		    bbt = 'BBobs = %.0f +\- %.0f K' % (T,np.sqrt(covar[0,0]))
		    outputname = "bbody_sn_fit.dat" #% T
		    file = open(outputname,"w")
		    file.write("# Blackbody temperature = %.0f +\- %.0f K\n" % (T,np.sqrt(covar[0,0])))
		    w,f = [],[]
		    for wav in range(900,26000):
		        file.write("%g\t%g\n" % (wav,bbody(wav,T,Area)))
		        w.append(wav)
		        f.append(bbody(wav,T,Area))

		    iraf.rspec('bbody_sn_fit.dat','bbody_sn_fit.fits', title='bbodyfit',flux='no',dtype='interp',crval1=900,cdelt1=1)
		    iraf.scombine('bbody_sn_fit.fits,sn.fits,sn.fits,sn.fits', 'bsn_combo.fits',combine='median')

		    iraf.wspec('bsn_combo.fits','bsn_combo.txt',header='no')

		    print '#######################################'
		    print '\033[4mSince now you are working with an hybrid spectrum+blackbody, if you want additional information, please use the normal version.\033[0m '
		    print '#######################################'

		    lcf = open('bsn_combo.txt','r')
		    riga = lcf.readlines()
		    lcf.close()
		    wave,flux= [],[]
		    for line in riga:
		        p = line.split()
		        if float(line.split()[0]) >= fil_obs_min and float(line.split()[0]) <= fil_obs_max: #to match the spectrum wavelegnths to those of the filter
		            wave.append(float(p[0]))
		            flux.append(float(p[1]))

		    wavev = array(wave)
		    fluxv = array(flux)
		    wavesp_min= min(wavev)
		    wavesp_max= int(max(wavev)) #needed to avoid problems with interp1d

		    # interpolating the two responses to match the length and sampling coverage
		    conf = conv(wavev,fluxv,wavefilterv,transmissionv,wavesp_min,wavesp_max,fil_obs_min,fil_obs_max)

		    ##################################
		    ### Evaluating the magnitudes
		    ##################################

		    flux_obs = max(integrate.cumtrapz(conf[0],conf[1])) # using trapezoidal rule to integrate
		    flux_obs_err = flux_obs * (1+(anguncov[ii]-50)*0.0001)

		    iraf.dopcor('bsn_combo.fits','bsn_combo_dez.fits', redshift=redshift, isveloc='no', flux='no',factor=3)
		    iraf.dopcor('bsn_combo.fits','bsn_combo_dez_err1.fits', redshift=redserrspace1, isveloc='no', flux='no',factor=3)
		    iraf.dopcor('bsn_combo.fits','bsn_combo_dez_err2.fits', redshift=redserrspace2, isveloc='no', flux='no',factor=3)
		    iraf.wspec('bsn_combo_dez.fits','bsn_combo_dez.txt',header='no')
		    iraf.wspec('bsn_combo_dez_err1.fits','bsn_combo_dez_err1.txt',header='no')
		    iraf.wspec('bsn_combo_dez_err2.fits','bsn_combo_dez_err2.txt',header='no')

		    lcf = open('bsn_combo_dez.txt','r')
		    riga = lcf.readlines()
		    lcf.close()
		    rwave,rflux= [],[]
		    for line in riga:
		        p = line.split()
		        if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
		            rwave.append(float(p[0]))
		            rflux.append(float(p[1]))

		    wave_dezv = array(rwave)
		    flux_dezv = array(rflux)
		    wavesp_dez_min= min(wave_dezv)
		    wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

		    # interpolating the two responses in the rest wavelength to match the length and sampling coverage
		    conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)
		    flux_rest = max(integrate.cumtrapz(conf[0],conf[1]))

		    ###### doing that again for the errors  ############################################################
		    lcf = open('bsn_combo_dez_err1.txt','r')
		    riga = lcf.readlines()
		    lcf.close()
		    rwave,rflux= [],[]
		    for line in riga:
		        p = line.split()
		        if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
		            rwave.append(float(p[0]))
		            rflux.append(float(p[1]))

		    wave_dezv = array(rwave)
		    flux_dezv = array(rflux)
		    wavesp_dez_min= min(wave_dezv)
		    wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

		    conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)

		    flux_rest_err1 = max(integrate.cumtrapz(conf[0],conf[1]))

		    lcf = open('bsn_combo_dez_err2.txt','r')
		    riga = lcf.readlines()
		    lcf.close()
		    rwave,rflux= [],[]
		    for line in riga:
		        p = line.split()
		        if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
		            rwave.append(float(p[0]))
		            rflux.append(float(p[1]))

		    wave_dezv = array(rwave)
		    flux_dezv = array(rflux)
		    wavesp_dez_min= min(wave_dezv)
		    wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

		    conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)

		    flux_rest_err2 = max(integrate.cumtrapz(conf[0],conf[1]))
        	##################################################################################################
		    kcorrrest_bb_wor=-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest)) +logterm
		    kcorrrest_bb_wor_err1=-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest_err1/zp_ef_rest)) +(2.5*log10(1+redserrspace1))
		    kcorrrest_bb_wor_err2=-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest_err2/zp_ef_rest)) +(2.5*log10(1+redserrspace2))
		    kcorrerror_bb_wor=(abs(kcorrrest_bb_wor_err1 - kcorrrest_bb_wor) + abs(kcorrrest_bb_wor_err2 - kcorrrest_bb_wor))/2
		    kcorrerrfilt_obs = abs((-2.5*log10(flux_obs/zp_ef_err) - (-2.5*log10(flux_rest/zp_ef_rest)))-(-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest))))
		    kcorrerrfilt_rest = abs((-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest_err)))-(-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest))))
		    Kcorrerr_bb = abs((-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest)))-(-2.5*log10(flux_obs_err/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest))))
		    kcorrerr = sqrt((kcorrerror_bb_wor**2 + kcorrerrfilt_obs**2 + kcorrerrfilt_rest**2 + Kcorrerr_bb**2)/4)

		    kcorr[ii] = kcorrrest_bb_wor
		    kcorr_e[ii] = kcorrerr
		    method[ii] = 'Hybrid obs_spec_BB'
		    btemp[ii] = bbt
		    split = 1

	    elif filter1 == 'U' or filter1 == 'u' or filter1 == 'uvw1' or filter1 == 'uvw2' or filter1 == 'uvm2' or filter1 == 'NUV' or filter1 == 'FUV':
		    print waveobs_min-fil_obs_min,' Angstrom not covered by the observed spectrum in the blue'
		    kcorr[ii] = 0.0
		    kcorr_e[ii] = 0.0
		    method[ii] = 'None'
		    btemp[ii] = 'None'
		    anguncov[ii] = waveobs_min-fil_obs_min
		    uncovside[ii] = 'Blue'
	    elif filter1 == 'K':
		    print fil_obs_max-waveobs_max,' Angstrom not covered by the observed spectrum in the red'
		    kcorr[ii] = 0.0
		    kcorr_e[ii] = 0.0
		    method[ii] = 'None'
		    btemp[ii] = 'None'
		    anguncov[ii] = fil_obs_max-waveobs_max
		    uncovside[ii] = 'Red'

	if split == 0:
		if ((waverest_min-fil_rest_min) > 50) or ((fil_rest_max-waverest_max) > 50):
			print ''
			if (waverest_min-fil_rest_min) > 50:
				print waverest_min-fil_rest_min,' Angstrom not covered by the restframe spectrum in the blue'
				anguncov[ii] = waverest_min-fil_rest_min
				uncovside[ii] = 'Blue'
			elif (fil_rest_max-waverest_max) > 50:
				print fil_rest_max-waverest_max,' Angstrom not covered by the restframe spectrum in the red'
				anguncov[ii] = fil_rest_max-waverest_max
				uncovside[ii] = 'Red'

			print ''
			BBparams, covar = curve_fit(bbody,wavedezv,fluxdezv,p0=(10000,1E-16))
			T= BBparams[0]
			Area = BBparams[1]
			print '\nBlackbody temperature rest spectrum = %.0f +\- %.0f K\n' % (T,np.sqrt(covar[0,0]))
			bbt = 'BBrest = %.0f +\- %.0f K' % (T,np.sqrt(covar[0,0]))
			outputname = "bbody_sn_dez_fit.dat" #% T
			file = open(outputname,"w")
			file.write("# Blackbody temperature = %.0f +\- %.0f K\n" % (T,np.sqrt(covar[0,0])))
			w,f = [],[]
			for wav in range(900,26000):
				file.write("%g\t%g\n" % (wav,bbody(wav,T,Area)))
				w.append(wav)
				f.append(bbody(wav,T,Area))

			######### BBody for the error spectra ################################################
			BBparams, covar = curve_fit(bbody,wavedezv_err1,fluxdezv_err1,p0=(10000,1E-16)) #initial guess
			T= BBparams[0]
			Area = BBparams[1]
			outputname = "bbody_sn_dez_fit_err1.dat" #% T
			file = open(outputname,"w")
			file.write("# Blackbody temperature = %.0f +\- %.0f K\n" % (T,np.sqrt(covar[0,0])))
			w,f = [],[]
			for wav in range(900,24005):
			   file.write("%g\t%g\n" % (wav,bbody(wav,T,Area)))
			   w.append(wav)
			   f.append(bbody(wav,T,Area))

			BBparams, covar = curve_fit(bbody,wavedezv_err2,fluxdezv_err2,p0=(10000,1E-16)) #initial guess
			T= BBparams[0]
			Area = BBparams[1]
			outputname = "bbody_sn_dez_fit_err2.dat" #% T
			file = open(outputname,"w")
			file.write("# Blackbody temperature = %.0f +\- %.0f K\n" % (T,np.sqrt(covar[0,0])))
			w,f = [],[]
			for wav in range(900,24005):
			   file.write("%g\t%g\n" % (wav,bbody(wav,T,Area)))
			   w.append(wav)
			   f.append(bbody(wav,T,Area))
			########################################################################################
			iraf.rspec('bbody_sn_dez_fit.dat','bbody_sn_dez_fit.fits', title='bbodyfit',flux='no',dtype='interp',crval1=900,cdelt1=1)
			iraf.rspec('bbody_sn_dez_fit_err1.dat','bbody_sn_dez_fit_err1.fits', title='bbodyfit',flux='no',dtype='interp',crval1=900,cdelt1=1)
			iraf.rspec('bbody_sn_dez_fit_err2.dat','bbody_sn_dez_fit_err2.fits', title='bbodyfit',flux='no',dtype='interp',crval1=900,cdelt1=1)
			iraf.scombine('bbody_sn_dez_fit.fits,sn_dez.fits,sn_dez.fits,sn_dez.fits', 'bsn_combo_dez.fits',combine='median')
			iraf.scombine('bbody_sn_dez_fit_err1.fits,sn_dez_err1.fits,sn_dez_err1.fits,sn_dez_err1.fits', 'bsn_combo_dez_err1.fits',combine='median')
			iraf.scombine('bbody_sn_dez_fit_err2.fits,sn_dez_err2.fits,sn_dez_err2.fits,sn_dez_err2.fits', 'bsn_combo_dez_err2.fits',combine='median')
			iraf.wspec('bsn_combo_dez.fits','bsn_combo_dez.txt',header='no')
			iraf.wspec('bsn_combo_dez_err1.fits','bsn_combo_dez_err1.txt',header='no')
			iraf.wspec('bsn_combo_dez_err2.fits','bsn_combo_dez_err2.txt',header='no')

			print '#######################################'
			print ' Since now you are using a rest-frame spectrum based on a combination of SN+Blackbody '
			print '#######################################'

			iraf.wspec("sn.fits","sn.txt", header='no')
			lcf = open('sn.txt','r')
			riga = lcf.readlines()
			lcf.close()
			wave,flux= [],[]
			for line in riga:
			    p = line.split()
			    if float(line.split()[0]) >= fil_obs_min and float(line.split()[0]) <= fil_obs_max: #to match the spectrum wavelegnths to those of the filter
			        wave.append(float(p[0]))
			        flux.append(float(p[1]))

			wavev = array(wave)
			fluxv = array(flux)
			wavesp_min= min(wavev)
			wavesp_max= int(max(wavev)) #needed to avoid problems with interp1d

			# interpolating the two responses to match the length and sampling coverage
			conf = conv(wavev,fluxv,wavefilterv,transmissionv,wavesp_min,wavesp_max,fil_obs_min,fil_obs_max)
			flux_obs = max(integrate.cumtrapz(conf[0],conf[1])) # using trapezoidal rule to integrate

			lcf = open('bsn_combo_dez.txt','r')
			riga = lcf.readlines()
			lcf.close()
			rwave,rflux= [],[]
			for line in riga:
				p = line.split()
				if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
					rwave.append(float(p[0]))
					rflux.append(float(p[1]))

			wave_dezv = array(rwave)
			flux_dezv = array(rflux)
			wavesp_dez_min= min(wave_dezv)
			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

			# interpolating the two responses in the rest wavelength to match the length and sampling coverage
			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)

			flux_rest = max(integrate.cumtrapz(conf[0],conf[1]))
			flux_rest_err = flux_rest * (1+(anguncov[ii]-50)*0.0001)

			############ Errors #########################################################################
			lcf = open('bsn_combo_dez_err1.txt','r')
			riga = lcf.readlines()
			lcf.close()
			rwave,rflux= [],[]
			for line in riga:
			    p = line.split()
			    if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
			        rwave.append(float(p[0]))
			        rflux.append(float(p[1]))

			wave_dezv = array(rwave)
			flux_dezv = array(rflux)
			wavesp_dez_min= min(wave_dezv)
			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)

			flux_rest_err1 = max(integrate.cumtrapz(conf[0],conf[1]))

			lcf = open('bsn_combo_dez_err2.txt','r')
			riga = lcf.readlines()
			lcf.close()
			rwave,rflux= [],[]
			for line in riga:
			    p = line.split()
			    if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
			        rwave.append(float(p[0]))
			        rflux.append(float(p[1]))

			wave_dezv = array(rwave)
			flux_dezv = array(rflux)
			wavesp_dez_min= min(wave_dezv)
			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)
			flux_rest_err2 = max(integrate.cumtrapz(conf[0],conf[1]))
 			#############################################################################################

			kcorrrest_bb_wor=-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest))+logterm
			kcorrrest_bb_wor_err1=-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest_err1/zp_ef_rest))+(2.5*log10(1+redserrspace1))
			kcorrrest_bb_wor_err2=-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest_err2/zp_ef_rest))+(2.5*log10(1+redserrspace2))
			kcorrerror_bb_wor=(abs(kcorrrest_bb_wor_err1 - kcorrrest_bb_wor) + abs(kcorrrest_bb_wor_err2 - kcorrrest_bb_wor))/2
			kcorrerrfilt_obs = abs((-2.5*log10(flux_obs/zp_ef_err) - (-2.5*log10(flux_rest/zp_ef_rest)))-(-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest))))
			kcorrerrfilt_rest = abs((-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest_err)))-(-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest))))
			Kcorrerr_bb = abs((-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest)) +(2.5*log10(1+redshift)))-(-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest_err/zp_ef_rest)) +(2.5*log10(1+redshift))))
			kcorrerr = sqrt((kcorrerror_bb_wor**2 + kcorrerrfilt_obs**2 + kcorrerrfilt_rest**2 + Kcorrerr_bb**2)/4)

			kcorr[ii] = kcorrrest_bb_wor
			kcorr_e[ii] = kcorrerr
			method[ii] = 'Hybrid rest_spec_BB'
			btemp[ii] = bbt
		else:
			iraf.wspec("sn.fits","sn.txt", header='no')
			lcf = open('sn.txt','r')
			riga = lcf.readlines()
			lcf.close()
			wave,flux= [],[]
			for line in riga:
			    p = line.split()
			    if float(line.split()[0]) >= fil_obs_min and float(line.split()[0]) <= fil_obs_max: #to match the spectrum wavelegnths to those of the filter
			        wave.append(float(p[0]))
			        flux.append(float(p[1]))

			wavev = array(wave)
			fluxv = array(flux)
			wavesp_min= min(wavev)
			wavesp_max= int(max(wavev)) #needed to avoid problems with interp1d
			# interpolating the two responses to match the length and sampling coverage

			conf = conv(wavev,fluxv,wavefilterv,transmissionv,wavesp_min,wavesp_max,fil_obs_min,fil_obs_max)

			##################################
			### Evaluating the magnitudes
			##################################
			flux_obs = max(integrate.cumtrapz(conf[0],conf[1])) # using trapezoidal rule to integrate
			phot_filtobs_sn = -2.5*log10(flux_obs/zp_ef)

			iraf.wspec("sn_dez_dered.fits","sn_dez_dered.txt", header='no')
			lcf = open('sn_dez_dered.txt','r')
			riga = lcf.readlines()
			lcf.close()
			rwave,rflux= [],[]
			for line in riga:
			    p = line.split()
			    if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
			        rwave.append(float(p[0]))
			        rflux.append(float(p[1]))

			wave_dezv = array(rwave)
			flux_dezv = array(rflux)
			wavesp_dez_min= min(wave_dezv)
			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

			# interpolating the two responses in the rest wavelength to match the length and sampling coverage
			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)
			flux_rest = max(integrate.cumtrapz(conf[0],conf[1]))

			phot_filtrest_sn_dez_dered=-2.5*log10(flux_rest/zp_ef_rest)
 			#######    Errors  ######################################################################################
 			iraf.wspec("sn_dez_dered_err1.fits","sn_dez_dered_err1.txt", header='no')
 			lcf = open('sn_dez_dered_err1.txt','r')
 			riga = lcf.readlines()
 			lcf.close()
 			rwave,rflux= [],[]
 			for line in riga:
 			    p = line.split()
 			    if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
 			        rwave.append(float(p[0]))
 			        rflux.append(float(p[1]))

 			wave_dezv = array(rwave)
 			flux_dezv = array(rflux)
 			wavesp_dez_min= min(wave_dezv)
 			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

 			# interpolating the two responses in the rest wavelength to match the length and sampling coverage
 			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)
 			flux_rest_err1 = max(integrate.cumtrapz(conf[0],conf[1]))

 			iraf.wspec("sn_dez_dered_err2.fits","sn_dez_dered_err2.txt", header='no')
 			lcf = open('sn_dez_dered_err2.txt','r')
 			riga = lcf.readlines()
 			lcf.close()
 			rwave,rflux= [],[]
 			for line in riga:
 			    p = line.split()
 			    if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
 			        rwave.append(float(p[0]))
 			        rflux.append(float(p[1]))

 			wave_dezv = array(rwave)
 			flux_dezv = array(rflux)
 			wavesp_dez_min= min(wave_dezv)
 			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

 			# interpolating the two responses in the rest wavelength to match the length and sampling coverage
 			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)
 			flux_rest_err2 = max(integrate.cumtrapz(conf[0],conf[1]))
 			######################################################################################################
 			phot_filtrest_sn_dez_dered_err1=-2.5*log10(flux_rest_err1/zp_ef_rest)
 			phot_filtrest_sn_dez_dered_err2=-2.5*log10(flux_rest_err2/zp_ef_rest)

			iraf.wspec("sn_dez.fits","sn_dez.txt", header='no')
			lcf = open('sn_dez.txt','r')
			riga = lcf.readlines()
			lcf.close()
			rwave,rflux= [],[]
			for line in riga:
			    p = line.split()
			    if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
			        rwave.append(float(p[0]))
			        rflux.append(float(p[1]))

			wave_dezv = array(rwave)
			flux_dezv = array(rflux)
			wavesp_dez_min= min(wave_dezv)
			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

			# interpolating the two responses in the rest wavelength to match the length and sampling coverage
			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)
			flux_rest = max(integrate.cumtrapz(conf[0],conf[1]))

			phot_filtrest_sn_dez=-2.5*log10(flux_rest/zp_ef_rest)

			##################### Errors ######################################################################
			iraf.wspec("sn_dez_err1.fits","sn_dez_err1.txt", header='no')
			lcf = open('sn_dez_err1.txt','r')
			riga = lcf.readlines()
			lcf.close()
			rwave,rflux= [],[]
			for line in riga:
			    p = line.split()
			    if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
			        rwave.append(float(p[0]))
			        rflux.append(float(p[1]))

			wave_dezv = array(rwave)
			flux_dezv = array(rflux)
			wavesp_dez_min= min(wave_dezv)
			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

			# interpolating the two responses in the rest wavelength to match the length and sampling coverage
			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)
			flux_rest_err1 = max(integrate.cumtrapz(conf[0],conf[1]))

			iraf.wspec("sn_dez_err2.fits","sn_dez_err2.txt", header='no')
			lcf = open('sn_dez_err2.txt','r')
			riga = lcf.readlines()
			lcf.close()
			rwave,rflux= [],[]
			for line in riga:
			    p = line.split()
			    if float(line.split()[0]) >= fil_rest_min and float(line.split()[0]) <= fil_rest_max:
			        rwave.append(float(p[0]))
			        rflux.append(float(p[1]))

			wave_dezv = array(rwave)
			flux_dezv = array(rflux)
			wavesp_dez_min= min(wave_dezv)
			wavesp_dez_max= int(max(wave_dezv)) #needed to avoid problems with interp1d

			# interpolating the two responses in the rest wavelength to match the length and sampling coverage
			conf = conv(wave_dezv,flux_dezv,wavefilter_restv,transmission_restv,wavesp_dez_min,wavesp_dez_max,fil_rest_min,fil_rest_max)
			flux_rest_err2 = max(integrate.cumtrapz(conf[0],conf[1]))
			####################################################################################################
			phot_filtrest_sn_dez_err1=-2.5*log10(flux_rest_err1/zp_ef_rest)
			phot_filtrest_sn_dez_err2=-2.5*log10(flux_rest_err2/zp_ef_rest)

			##################################
			### K-correction evaluation
			##################################

			kcorrrest_wr=(float(phot_filtobs_sn)-float(phot_filtrest_sn_dez_dered))+logterm
			kcorrrest_wr_err1=(float(phot_filtobs_sn)-float(phot_filtrest_sn_dez_dered_err1))+(2.5*log10(1+redserrspace1))
			kcorrrest_wr_err2=(float(phot_filtobs_sn)-float(phot_filtrest_sn_dez_dered_err2))+(2.5*log10(1+redserrspace2))
			kcorrerror_wr=(abs(kcorrrest_wr_err1 - kcorrrest_wr) + abs(kcorrrest_wr_err2 - kcorrrest_wr))/2

			kcorrrest_wor=(float(phot_filtobs_sn)-float(phot_filtrest_sn_dez))+logterm
			kcorrrest_wor_err1=(float(phot_filtobs_sn)-float(phot_filtrest_sn_dez_err1))+(2.5*log10(1+redserrspace1))
			kcorrrest_wor_err2=(float(phot_filtobs_sn)-float(phot_filtrest_sn_dez_err2))+(2.5*log10(1+redserrspace2))
			kcorrerror_wor=(abs(kcorrrest_wor_err1 - kcorrrest_wor) + abs(kcorrrest_wor_err2 - kcorrrest_wor))/2

			kcorrerrfilt_obs = abs((-2.5*log10(flux_obs/zp_ef_err) - (-2.5*log10(flux_rest/zp_ef_rest)))-(-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest))))
			kcorrerrfilt_rest = abs((-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest_err)))-(-2.5*log10(flux_obs/zp_ef) - (-2.5*log10(flux_rest/zp_ef_rest))))

			kcorrerr_wor = sqrt((kcorrerror_wor**2 + kcorrerrfilt_obs**2 + kcorrerrfilt_rest**2)/3)
			kcorrerr_wr = sqrt((kcorrerror_wr**2 + kcorrerrfilt_obs**2 + kcorrerrfilt_rest**2)/3)

			kcorr[ii] = kcorrrest_wr
			kcorr_e[ii] = kcorrerr_wr
			method[ii] = 'specTOspec'
			btemp[ii] = 'None'
			anguncov[ii] = 0.0
			uncovside[ii] = 'None'

	# cleaning process (unnecessary)
	os.system('rm -rf sn.txt')
	os.system('rm -rf sn.fits')
	os.system('rm -rf sn_xbbody.txt')
	os.system('rm -rf sn_dez_xbbody.txt')
	os.system('rm -rf sn_dez_xbbody_err1.txt')
	os.system('rm -rf sn_dez_xbbody_err2.txt')
	os.system('rm -rf bbody_sn_dez_fit.dat')
	os.system('rm -rf bbody_sn_dez_fit_err1.dat')
	os.system('rm -rf bbody_sn_dez_fit_err2.dat')
	os.system('rm -rf bbody_sn_dez_fit.fits')
	os.system('rm -rf bbody_sn_dez_fit_err1.fits')
	os.system('rm -rf bbody_sn_dez_fit_err2.fits')
	os.system('rm -rf bbody_sn_fit.fits')
	os.system('rm -rf bbody_sn_fit.dat')
	os.system('rm -rf sn_dez.txt')
	os.system('rm -rf sn_dez_err1.txt')
	os.system('rm -rf sn_dez_err2.txt')
	os.system('rm -rf sn_dez.fits')
	os.system('rm -rf sn_dez_err1.fits')
	os.system('rm -rf sn_dez_err2.fits')
	os.system('rm -rf sn_dez_dered.fits')
	os.system('rm -rf sn_dez_dered_err1.fits')
	os.system('rm -rf sn_dez_dered_err2.fits')
	os.system('rm -rf sn_dez_dered.txt')
	os.system('rm -rf sn_dez_dered_err1.txt')
	os.system('rm -rf sn_dez_dered_err2.txt')
	os.system('rm -rf sn_galdered_dez.fits')
	os.system('rm -rf sn_galdered_dez_err1.fits')
	os.system('rm -rf sn_galdered_dez_err2.fits')
	os.system('rm -rf sn_galdered.fits')
	os.system('rm -rf bsn_combo_dez.fits')
	os.system('rm -rf bsn_combo_dez_err1.fits')
	os.system('rm -rf bsn_combo_dez_err2.fits')
	os.system('rm -rf bsn_combo_dez.txt')
	os.system('rm -rf bsn_combo_dez_err1.txt')
	os.system('rm -rf bsn_combo_dez_err2.txt')
	os.system('rm -rf bsn_combo.fits')
	os.system('rm -rf bsn_combo.txt')
	sleep(_sleepc) #to avoid missing files and correction due to a combo of two different spectra (a.k.a. the code arrives at the right step before the system remove the file)
	##########################
	### Adding values to the txt file
	#########################
	redlist[ii] = redshift
	filekc.write(snname)
	filekc.write("\t")
	filekc.write("%s+/-%s" % (redshift,_redshifterr))
	filekc.write("\t\t")
	filekc.write(filter1)
	filekc.write("\t\t")
	filekc.write(filter2)
	filekc.write("\t\t")
	filekc.write("%0.3f" % (kcorr[ii]))
	filekc.write("\t\t")
	filekc.write("+/-%0.3f" % (kcorr_e[ii]))
	filekc.write("\t\t")
	filekc.write(method[ii])
	filekc.write("\t\t")
	filekc.write(btemp[ii])
	filekc.write("\t\t")
	filekc.write("%s" % (anguncov[ii]))
	filekc.write("\t")
	filekc.write(uncovside[ii])
	filekc.write("\n")
	#ax = subplot(111)
	if questionplot == 'yes':
		ax = axes([0.1, 0.1, 0.65, 0.82])
		plot(9999,9999,color='b',marker='o',markeredgecolor='k',ls='None')
		plot(9999,9999,color='g',marker='d',markeredgecolor='k',ls='None')
		plot(9999,9999,color='r',marker='s',markeredgecolor='k',ls='None')
		reds,redo,redr,kcors,kcoro,kcor1 = [],[],[],[],[],[]
		if method[ii] == 'specTOspec':
			reds.append(redshift)
			kcors.append(kcorr[ii])
			plot(reds,kcors,'bo',ms=12)
		if method[ii] == 'Hybrid rest_spec_BB':
			redr.append(redshift)
			kcor1.append(kcorr[ii])
			plot(redr,kcor1,'gd',ms=12)
		if method[ii] == 'Hybrid obs_spec_BB':
			redo.append(redshift)
			kcoro.append(kcorr[ii])
			plot(redo,kcoro,'rs',ms=12)
	ii = ii + 1

then = time.time()
time = then -now
################
### plotting commands
##################
if questionplot == 'yes':
	xl = [float(min(redlist))-0.07,float(max(redlist))+0.07]
	yl = [min(kcorr)-0.3,max(kcorr)+0.3]
	legend(('specTOspec', 'Hybrid rest specBB', 'Hybrid obs specBB'), numpoints=1,bbox_to_anchor=(1.01, 1), loc=2, borderaxespad=0.)
	ltext = gca().get_legend().get_texts()
	setp(ltext[0], fontsize = 14, color = 'b')
	setp(ltext[1], fontsize = 14, color = 'g')
	setp(ltext[2], fontsize = 14, color = 'r')
	xlim(xl[0],xl[1])
	ylim(yl[0],yl[1])
	title('From various observed filters to various rest filters')
	xlabel('Redshift',size=18)
	ylabel('K-correction',size=18)
	ax.minorticks_on()
	show()
####################
##### writing legend on the file
####################
filekc.write("\n# ----------------------------------------------------------------------------------\n")
filekc.write("# Legend for SNAKE mode:\n")
filekc.write("# specTOspec        \t--> K-correction computed with original spectrum in observed  \n#\t\t\t\t\t\tand rest frame\n")
filekc.write("# Hybrid rest_spec_BB\t--> K-correction computed with original spectrum in observed  \n#\t\t\t\t\t\tframe and SN+Bbody hybrid in rest frame\n")
filekc.write("# Hybrid obs_spec_BB \t--> K-correction computed with SN+bbody spectrum in observed  \n#\t\t\t\t\t\tand rest frame\n")
filekc.write("# ----------------------------------------------------------------------------------\n")

sltime = _sleepc*len(snlist)
print '######################################################################'
print ''
print ' Evaluation done in %.0is, of which %.0is to take a nap to let rest your Random Access Memory (RAM) ' % (time,sltime)
print ''
print ' \033[46mList of K-corrections\033[0m ' , kcorr
print ' \033[44mVersion used\033[0m ' , method
print ''
print ' A text file has been created ===> Kcorrection_%.0i_%.0i.txt ' % (Tnowd,Tnow)


"""
Tools for aperture photometry with non native bg/error methods

This function serves to ease the computation of photometric magnitudes
and errors using PhotUtils by replicating DAOPHOT's photometry and
error methods.  The formula for DAOPHOT's error is:

err = sqrt (Poisson_noise / epadu + area * stdev**2 + area**2 * stdev**2 / nsky)

Which gives a magnitude error:

mag_err = 1.0857 * err / flux

Where epadu is electrons per ADU (gain), area is the photometric
aperture area, stdev is the uncertainty in the sky measurement
and nsky is the sky annulus area.  To get the uncertainty in the sky
we must use a custom background tool, which also enables computation of
the mean and median of the sky as well (more robust statistics).
All the stats are sigma clipped.  These are calculated by the
functions in aperture_stats_tbl.

.. note::
    Currently, the background computations will fully include a pixel that has ANY overlap with the background aperture
    (the annulus). This is to simplify the computation of the median, as a weighted median is nontrivial, and slower.
    Copied from https://grit.stsci.edu/HLA/software/blob/master/HLApipeline/HLApipe/scripts/photometry_tools.py
Authors
-------
    - Varun Bajaj, January 2018

Use
---

::

    from photometry_tools import iraf_style_photometry
    phot_aps = CircularAperture((sources['xcentroid'], sources['ycentroid']),r=10.)
    bg_aps = CircularAnnulus((sources['xcentroid'], sources['ycentroid']), r_in=13., r_out=15.)

Simplest call:

::

    photometry_tbl = iraf_style_photometry(phot_aps, bg_aps, data)

Pass in pixelwise error and set background to mean

::

    photometry_tbl = iraf_style_photometry(phot_aps, bg_aps, data, error_array=data_err, bg_method='mean')

Can also set the gain (if image units are DN)

::

    photometry_tbl = iraf_style_photometry(phot_aps, bg_aps, data, epadu=2.5)

Classes and Functions
---------------------
"""
import numpy as np
from astropy.table import Table
from drizzlepac.hlautils.background_median import aperture_stats_tbl
from photutils import aperture_photometry

def iraf_style_photometry(phot_apertures, bg_apertures, data, photflam, photplam, error_array=None,
                          bg_method='mode', epadu=1.0):
    """
    Computes photometry with PhotUtils apertures, with IRAF formulae

    Parameters
    ----------
    phot_apertures : photutils PixelAperture object (or subclass)
        The PhotUtils apertures object to compute the photometry. i.e. the object returned via CirularAperture.

    bg_apertures : photutils PixelAperture object (or subclass)
        The phoutils aperture object to measure the background in. i.e. the object returned via CircularAnnulus.

    data : array
        The data for the image to be measured.

    photflam : float
        inverse sensitivity, in ergs/cm2/angstrom/electron

    photplam : float
        Pivot wavelength, in angstroms

    error_array : array
        (Optional) The array of pixelwise error of the data.  If none, the Poisson noise term in the error computation
        will just be the square root of the flux/epadu. If not none, the aperture_sum_err column output by
        aperture_photometry (divided by epadu) will be used as the Poisson noise term.

    bg_method: string
        {'mean', 'median', 'mode'}, optional. The statistic used to calculate the background. All measurements are
        sigma clipped. Default value is 'mode'. NOTE: From DAOPHOT, mode = 3 * median - 2 * mean.

    epadu : float
        (optional) Gain in electrons per adu (only use if image units aren't e-). Default value is 1.0

    Returns
    -------
        An astropy Table with columns as follows:
        X-Center Y-Center RA DEC ID MagAp1 MagErrAp1 MagAp2 MagErrAp2 MSkyAp2 StdevAp2 FluxAp2 CI Flags
    """
    if bg_method not in ['mean', 'median', 'mode']:
        raise ValueError('Invalid background method, choose either \
                          mean, median, or mode')
    phot = aperture_photometry(data, phot_apertures, error=error_array)
    bg_phot = aperture_stats_tbl(data, bg_apertures, sigma_clip=True)
    names = ['X-Center', 'Y-Center', 'ID']
    x, y = phot_apertures[0].positions.T
    final_stacked = np.stack([x, y, phot["id"].data], axis=1)
    # n_aper = 0
    name_list = 'Flux', 'FluxErr', 'Mag', 'MagErr'
    for aper_string  in ['Ap1', 'Ap2']:
        for col_name in name_list:
            names.append("{}{}".format(col_name,aper_string))

    # for item in list(phot.keys()):
    #     if item.startswith("aperture_sum_") and not item.startswith("aperture_sum_err_"):
    #         aper_size_arcsec = phot_apertures[n_aper].r * platescale
    #         for name in name_list:
    #             names.append("{}_{:.2f}".format(name, aper_size_arcsec))
    #         n_aper += 1
    for aperCtr in range(0, 2):
        ap_area = phot_apertures[aperCtr].area
        bg_method_name = 'aperture_{}'.format(bg_method)

        # NOTE background subtraction below commented out 8/14/19
        flux = phot['aperture_sum_{}'.format(aperCtr)]  # - bg_phot[bg_method_name] * ap_area

        # Need to use variance of the sources
        # for Poisson noise term in error computation.
        #
        # This means error needs to be squared.
        # If no error_array error = flux ** .5

        if error_array is not None:
            flux_error = compute_phot_error(phot['aperture_sum_err_{}'.format(aperCtr)] ** 2.0, bg_phot, bg_method,
                                            ap_area, epadu)
        else:
            flux_error = compute_phot_error(flux, bg_phot, bg_method, ap_area, epadu)

        mag = convert_flux_to_abmag(flux, photflam, photplam)

        # NOTE: Magnitude error calculation comes from computing d(ABMAG)/d(flux).
        # See https://iraf.net/forum/viewtopic.php?showtopic=83932 for details.
        mag_err = 1.0857 * flux_error / flux

        # Build the final data table
        stacked = np.stack([flux, flux_error, mag, mag_err], axis=1)
        final_stacked = np.concatenate([final_stacked, stacked], axis=1)

    # Build final output table
    final_tbl = Table(data=final_stacked, names=names,
                      dtype=[np.float64, np.float64, np.int64, np.float64, np.float64, np.float64, np.float64,
                             np.float64, np.float64, np.float64, np.float64])

    # add sky and std dev columns from background calculation subroutine
    final_tbl.add_column(bg_phot[bg_method_name])
    final_tbl.rename_column(bg_method_name, 'MSkyAp2')
    final_tbl.add_column(bg_phot['aperture_std'])
    final_tbl.rename_column('aperture_std', 'StdevAp2')

    return final_tbl

def compute_phot_error(flux_variance, bg_phot, bg_method, ap_area, epadu=1.0):
    """Computes the flux errors using the DAOPHOT style computation

    Parameters
    ----------
    flux_variance : array
        flux values

    bg_phot : array
        background brightness values.

    bg_method : string
        background method

    ap_area : array
        the area of the aperture in square pixels

    epadu : float
        (optional) Gain in electrons per adu (only use if image units aren't e-). Default value is 1.0

    Returns
    -------
    flux_error : array
        an array of flux errors
    """

    bg_variance_terms = (ap_area * bg_phot['aperture_std'] ** 2.) * (1. + ap_area/bg_phot['aperture_area'])
    variance = flux_variance / epadu + bg_variance_terms
    flux_error = variance ** .5
    return flux_error

def convert_flux_to_abmag(in_flux, photflam, photplam):
    """converts flux (in units of electrons/sec) to ABMAG

    Parameters
    ----------
    in_flux : astropy.table.column.Column object
        flux values to convert to ABMAG, in electrons/second

    photflam : float
        inverse sensitivity, in ergs/cm2/angstrom/electron

    photplam : float
        pivot wavelength, in angstroms

    Returns
    -------
    abmag : astropy.table.column.Column object
        input flux values converted to ABMAG
    """

    # convert flux from units of electrons/second to ergs/cm2/angstrom/second
    f_lambda = in_flux * photflam

    # Convert f_lambda to STMAG
    stmag = -2.5 * np.log10(f_lambda) - 21.10

    # Convert STMAG to ABMAG
    abmag =  stmag - 5.0 * np.log10(photplam) + 18.6921

    return abmag