# Licensed under a 3-clause BSD style license - see LICENSE.rst


from collections import OrderedDict
import warnings
from io import BytesIO

from astropy.table import QTable, MaskedColumn
from astropy.time import Time
from astropy.io.votable import parse
import astropy.units as u
from astropy.coordinates import SkyCoord, Angle
from astropy.utils.exceptions import AstropyUserWarning

from astroquery.exceptions import NoResultsWarning
from astroquery.query import BaseQuery
from astroquery.utils import async_to_sync, commons

from . import conf

__all__ = ['Miriade', 'MiriadeClass', 'Skybot', 'SkybotClass']


@async_to_sync
class MiriadeClass(BaseQuery):
    """
    A class for querying the
    `IMCCE/Miriade <http://vo.imcce.fr/webservices/miriade/>`_ service.
    """

    _query_uri = None  # uri used in query
    _get_raw_response = False

    @property
    def uri(self):
        """
        URI used in query to service.
        """
        return self._query_uri

    def get_ephemerides_async(self, targetname, *, objtype='asteroid',
                              epoch=None, epoch_step='1d', epoch_nsteps=1,
                              location=500, coordtype=1,
                              timescale='UTC',
                              planetary_theory='INPOP',
                              ephtype=1,
                              refplane='equator',
                              elements='ASTORB',
                              radial_velocity=False,
                              get_query_payload=False,
                              get_raw_response=False, cache=True):
        """
        Query the
        `IMCCE Miriade <http://vo.imcce.fr/webservices/miriade/>`_
        `ephemcc <http://vo.imcce.fr/webservices/miriade/?ephemcc>`_
        service.


        Parameters
        ----------

        targetname : str
            Name of the target to be queried.

        objtype : str, optional
            Type of the object to be queried. Available are: ``'asteroid'``,
            ``'comet'``, ``'dwarf planet'``, ``'planet'``, ``'satellite'``.
            Default: ``'asteroid'``

        epoch : `~astropy.time.Time` object, float, str,``None``, optional
            Start epoch of the query. If a float is provided, it is
            expected to be a Julian Date; if a str is provided, it is
            expected to be an iso date of the form
            ``'YYYY-MM-DD HH-MM-SS'``. If ``None`` is provided, the
            current date and time are used as epoch. Default: ``None``

        epoch_step : str, optional
            Step size for ephemerides calculation. Must consist of a decimal
            number followed by a single character: (d)ays, (h)ours,
            (m)inutes or (s)econds. Default: ``'1d'``

        epoch_nsteps : int, optional
            Number of increments of ``epoch_step`` starting from ``epoch``
            for which ephemerides are calculated. Maximum number of steps
            is 5000. Default: 1

        location : str, optional
            Location of the observer on Earth as a code or a set of
            coordinates. See the
            `Miriade manual <http://vo.imcce.fr/webservices/miriade/?documentation#field_7>`_
            for details. Default: geocentric location (``'500'``)

        coordtype : int, optional
            Type of coordinates to be calculated: ``1``: spherical, ``2``:
            rectangular, ``3``: local coordinates (azimuth and elevation),
            ``4``: hour angle coordinates, ``5``: dedicated to observation,
            ``6``: dedicated to AO observation. Default: ``1``

        timescale : str, optional
            The time scale used in the computation of the ephemerides:
            ``'UTC'`` or ``'TT'``. Default: ``'UTC'``

        planetary_theory : str, optional
            Planetary ephemerides set to be utilized in the calculations:
            ``'INPOP'``, ``'DE405'``, ``'DE406'``. Default: ``'INPOP'``

        ephtype : int, optional
            Type of ephemerides to be calculated: ``1``: astrometric J2000,
            ``2``: apparent of the date, ``3``: mean of the date,
            ``4``: mean J2000, Default: ``1``

        refplane : str, optional
            Reference plane: ``'equator'`` or ``'ecliptic'``. Default:
            ``'equator'``

        elements : str, optional
            Set of osculating elements to be used in the calculations:
            ``'ASTORB'`` or ``'MPCORB'``. Default: ``'ASTORB'``

        radial_velocity : bool, optional
            Calculate additional information on the target's radial velocity.
            Default: ``False``

        get_query_payload : bool, optional
            When set to ``True`` the method returns the HTTP request
            parameters as a dict, default: ``False``

        get_raw_response : bool, optional
            Return raw data as obtained by Miriade without parsing the
            data into a table, default: ``False``

        cache : bool, optional
            If ``True`` the query will be cached. Default: ``True``


        Notes
        -----

        The following parameters can be queried using this function. Note
        that different ``coordtype`` setting provide different sets of
        parameters; number in parentheses denote which ``coordtype``
        settings include the parameters.

        +------------------+-----------------------------------------------+
        | Column Name      | Definition                                    |
        +==================+===============================================+
        | ``target``       | Target name (str, 1, 2, 3, 4, 5, 6 )          |
        +------------------+-----------------------------------------------+
        | ``epoch``        | Ephemerides epoch (JD, float, 1, 2, 3, 4, 5,  |
        |                  | 6)                                            |
        +------------------+-----------------------------------------------+
        | ``RA``           | Target RA at ``ephtype`` (deg, float, 1)      |
        +------------------+-----------------------------------------------+
        | ``DEC``          | Target declination at ``ephtype`` (deg,       |
        |                  | float, 1, 4, 5)                               |
        +------------------+-----------------------------------------------+
        | ``RAJ2000``      | Target RA at J2000 (deg, float, 5, 6)         |
        +------------------+-----------------------------------------------+
        | ``DECJ2000``     | Target declination at J2000 (deg, float, 5, 6)|
        +------------------+-----------------------------------------------+
        | ``AZ``           | Target azimuth (deg, float, 3, 5)             |
        +------------------+-----------------------------------------------+
        | ``EL``           | Target elevation (deg, float, 3, 5)           |
        +------------------+-----------------------------------------------+
        | ``delta``        | Distance from observer (au, float, 1, 2, 3,   |
        |                  | 4, 5, 6)                                      |
        +------------------+-----------------------------------------------+
        | ``delta_rate``   | Rate in observer distance (km/s, float,       |
        |                  | 1, 5, 6)                                      |
        +------------------+-----------------------------------------------+
        | ``V``            | Apparent visual magnitude (mag, float, 1, 2,  |
        |                  | 3, 4, 5, 6)                                   |
        +------------------+-----------------------------------------------+
        | ``alpha``        | Solar phase angle (deg, 1, 2, 3, 4, 5, 6)     |
        +------------------+-----------------------------------------------+
        | ``elong``        | Solar elongation angle (deg, 1, 2, 3, 4, 5, 6)|
        +------------------+-----------------------------------------------+
        | ``RAcosD_rate``  | Rate of motion in RA * cos(DEC) (arcsec/min,  |
        |                  | float, 1, 5, 6)                               |
        +------------------+-----------------------------------------------+
        | ``DEC_rate``     | Rate of motion in DEC (arcsec/min, float, 1,  |
        |                  | 5, 6)                                         |
        +------------------+-----------------------------------------------+
        | ``x``            | X position state vector (au, float, 2)        |
        +------------------+-----------------------------------------------+
        | ``y``            | Y position state vector (au, float, 2)        |
        +------------------+-----------------------------------------------+
        | ``z``            | Z position state vector (au, float, 2)        |
        +------------------+-----------------------------------------------+
        | ``vx``           | X velocity state vector (au/d, float, 2)      |
        +------------------+-----------------------------------------------+
        | ``vy``           | Y velocity state vector (au/d, float, 2)      |
        +------------------+-----------------------------------------------+
        | ``vz``           | Z velocity state vector (au/d, float, 2)      |
        +------------------+-----------------------------------------------+
        | ``rv``           | Radial velocity (km/s, float, 2)              |
        +------------------+-----------------------------------------------+
        | ``heldist``      | Target heliocentric distance (au, float, 2,   |
        |                  | 5, 6)                                         |
        +------------------+-----------------------------------------------+
        | ``x_h``          | X heliocentric position vector (au, float, 2) |
        +------------------+-----------------------------------------------+
        | ``y_h``          | Y heliocentric position vector (au, float, 2) |
        +------------------+-----------------------------------------------+
        | ``z_h``          | Z heliocentric position vector (au, float, 2) |
        +------------------+-----------------------------------------------+
        | ``vx_h``         | X heliocentric vel. vector (au/d, float, 2)   |
        +------------------+-----------------------------------------------+
        | ``vy_h``         | Y heliocentric vel. vector (au/d, float, 2)   |
        +------------------+-----------------------------------------------+
        | ``vz_h``         | Z heliocentric vel. vector (au/d, float, 2)   |
        +------------------+-----------------------------------------------+
        | ``hourangle``    | Target hour angle (deg, float, 4, 5)          |
        +------------------+-----------------------------------------------+
        | ``siderealtime`` | Local sidereal time (hr, float, 5, 6)         |
        +------------------+-----------------------------------------------+
        | ``refraction``   | Atmospheric refraction (arcsec, float, 5, 6)  |
        +------------------+-----------------------------------------------+
        | ``airmass``      | Target airmass (float, 5, 6)                  |
        +------------------+-----------------------------------------------+
        | ``posunc``       | Positional uncertainty (arcsec, float, 5, 6)  |
        +------------------+-----------------------------------------------+


        Examples
        --------

        >>> from astroquery.imcce import Miriade
        >>> from astropy.time import Time
        >>> epoch = Time('2019-01-01', format='iso')
        >>> Miriade.get_ephemerides('3552', epoch=epoch)  # doctest: +SKIP
        <Table masked=True length=1>
           target          epoch                 RA         ...  DEC_rate   delta_rate
                             d                  deg         ... arcs / min    km / s
          bytes20         float64             float64       ...  float64     float64
        ----------- -------------------- ------------------ ... ---------- ------------
        Don Quixote            2458484.5 16.105294999999998 ...   -0.25244   31.4752734
        """

        URL = conf.ephemcc_server
        TIMEOUT = conf.timeout

        if isinstance(epoch, (int, float)):
            epoch = Time(epoch, format='jd')
        elif isinstance(epoch, str):
            epoch = Time(epoch, format='iso')
        elif epoch is None:
            epoch = Time.now()

        request_payload = OrderedDict([
            ('-name', targetname),
            ('-type', objtype[0].upper()+objtype[1:]),
            ('-ep', str(epoch.jd)),
            ('-step', epoch_step),
            ('-nbd', epoch_nsteps),
            ('-observer', location),
            ('-output', '--jul'),
            ('-tscale', timescale),
            ('-theory', planetary_theory),
            ('-teph', ephtype),
            ('-tcoor', coordtype),
            ('-rplane', {'equator': 1, 'ecliptic': 2}[refplane]),
            ('-oscelem', elements),
            ('-mime', 'votable')])

        if radial_velocity:
            request_payload['-output'] += ',--rv'

        if get_query_payload:
            return request_payload

        # query and parse
        response = self._request('GET', URL, params=request_payload,
                                 timeout=TIMEOUT, cache=cache)
        self._query_uri = response.url

        self._get_raw_response = get_raw_response

        return response

    def _parse_result(self, response, *, verbose=None):
        """
        Parser for Miriade request results
        """

        response_txt = response.text

        if self._get_raw_response:
            return response_txt

        # intercept error messages
        for line in response_txt.split('\n'):
            if 'name="QUERY_STATUS" value="ERROR"' in line:
                errmsg = line[line.find('ERROR:>')+9:
                              line.find('</vot:INFO>')]
                raise RuntimeError(errmsg)

        # convert votable to table
        commons.suppress_vo_warnings()
        voraw = BytesIO(response.content)
        votable = parse(voraw)
        data = votable.get_first_table().to_table()

        # modify table columns
        data['epoch'].unit = u.d

        if 'ra' in data.columns:
            data['ra'] = Angle(data['ra'], unit=u.hourangle).deg*u.deg
            data.rename_column('ra', 'RA')

        if 'dec' in data.columns:
            data['dec'] = Angle(data['dec'], unit=u.deg).deg*u.deg
            data.rename_column('dec', 'DEC')

        if 'raJ2000' in data.columns and 'decJ2000' in data.columns:
            data['raJ2000'] = Angle(
                data['raJ2000'], unit=u.hourangle).deg*u.deg
            data['decJ2000'] = Angle(data['decJ2000'], unit=u.deg).deg*u.deg
            data.rename_column('raJ2000', 'RAJ2000')
            data.rename_column('decJ2000', 'DECJ2000')

        if all([p in data.columns for p in ['xp', 'yp', 'zp']]):
            data.rename_column('xp', 'vx')
            data.rename_column('yp', 'vy')
            data.rename_column('zp', 'vz')
            if all([str(data[p].unit) == 'au/day'
                    for p in ['vx', 'vy', 'vz']]):
                data['vx'].unit = u.au/u.day
                data['vy'].unit = u.au/u.day
                data['vz'].unit = u.au/u.day

        if all([p in data.columns for p in ['xh', 'yh', 'zh']]):
            data.rename_column('xh', 'x_h')
            data.rename_column('yh', 'y_h')
            data.rename_column('zh', 'z_h')

        if all([p in data.columns for p in ['xhp', 'yhp', 'zhp']]):
            data.rename_column('xhp', 'vx_h')
            data.rename_column('yhp', 'vy_h')
            data.rename_column('zhp', 'vz_h')
            if all([str(data[p].unit) == 'au/day'
                    for p in ['vx_h', 'vy_h', 'vz_h']]):
                data['vx_h'].unit = u.au/u.day
                data['vy_h'].unit = u.au/u.day
                data['vz_h'].unit = u.au/u.day

        if 'distance' in data.columns:
            data.rename_column('distance', 'delta')

        if 'obsdistance' in data.columns:
            data.rename_column('obsdistance', 'delta')

        if 'heliodistance' in data.columns:
            data.rename_column('heliodistance', 'heldist')

        if 'azimut' in data.columns and 'elevation' in data.columns:
            data['azimut'] = Angle(data['azimut'], unit=u.deg).deg * u.deg
            data['elevation'] = Angle(
                data['elevation'], unit=u.deg).deg * u.deg
            data.rename_column('azimut', 'AZ')
            data.rename_column('elevation', 'EL')

        if 'mv' in data.columns:
            data.rename_column('mv', 'V')
            data['V'].unit = u.mag

        if 'phase' in data.columns:
            data.rename_column('phase', 'alpha')

        if 'elongation' in data.columns:
            data.rename_column('elongation', 'elong')

        if 'dracosdec' in data.columns:
            data.rename_column('dracosdec', 'RAcosD_rate')

        if 'ddec' in data.columns:
            data.rename_column('ddec', 'DEC_rate')

        if 'dist_dot' in data.columns:
            data.rename_column('dist_dot', 'delta_rate')

        if 'lst' in data.columns:
            data.rename_column('lst', 'siderealtime')

        if 'hourangle' in data.columns:
            data['hourangle'] = Angle(data['hourangle'],
                                      unit=u.hourangle).deg * u.deg

        if 'aeu' in data.columns:
            data.rename_column('aeu', 'posunc')

        return data


Miriade = MiriadeClass()


@async_to_sync
class SkybotClass(BaseQuery):
    """A class for querying the `IMCCE SkyBoT
    <https://vo.imcce.fr/webservices/skybot/>`_ service.
    """
    _uri = None  # query uri
    _get_raw_response = False

    @property
    def uri(self):
        """
        URI used in query to service.

        Examples
        --------

        >>> from astroquery.imcce import Skybot
        >>> from astropy.coordinates import SkyCoord
        >>> import astropy.units as u
        >>> from astropy.time import Time
        >>> field = SkyCoord(1*u.deg, 1*u.deg)
        >>> epoch = Time('2019-05-29 21:42', format='iso')
        >>> skybot = Skybot()
        >>> obj = skybot.cone_search(field, 0.1*u.deg, epoch) # doctest: +SKIP
        >>> skybot.uri # doctest: +SKIP
        'https://ssp.imcce.fr/webservices/skybot/skybotconesearch_query.php?-ra=1.0&-dec=1.0&-rd=0.1&-ep=2458633.404166667&-loc=500&-filter=120.0&-objFilter=111&-refsys=EQJ2000&-output=all&-mime=text'
        """
        return self._uri

    def cone_search_async(self,
                          coo,
                          rad,
                          epoch,
                          *,
                          location='500',
                          position_error=120,
                          find_planets=True,
                          find_asteroids=True,
                          find_comets=True,
                          get_query_payload=False,
                          get_raw_response=False,
                          cache=True):
        """
        This method queries the IMCCE
        `SkyBoT <https://ssp.imcce.fr/webservices/skybot/api/conesearch>`_
        cone search service and produces a `~astropy.table.QTable` object
        containing all Solar System bodies that might be in the cone
        defined by the cone center coordinates and epoch provided.

        Parameters
        ----------
        coo : `~astropy.coordinates.SkyCoord` object or tuple
            Center coordinates of the search cone in ICRS coordinates. If
            provided as tuple, the input is excepted as (right ascension
            in degrees, declination in degrees).
        rad : `~astropy.units.Quantity` object or float
            Radius of the search cone. If no units are provided (input as
            float), degrees are assumed. The maximum search radius is 10
            degrees; if this
            maximum radius is exceeded, it will be clipped and a warning
            will be provided to the user.
        epoch : `~astropy.time.Time` object, float, or string
            Epoch of search process in UT. If provided as float, it is
            interpreted as Julian Date, if provided as string, it is
            interpreted as date in the form ``'YYYY-MM-DD HH-MM-SS'``.
        location : int or str, optional
            Location of the observer on Earth as defined in the official
            `list of IAU codes
            <https://www.minorplanetcenter.net/iau/lists/ObsCodes.html>`_.
            Default: geocentric location (``'500'``)
        position_error : `~astropy.units.Quantity` or float, optional
            Maximum positional error for targets to be queried. If no
            unit is provided, arcseconds are assumed. Maximum positional
            error is 120 arcseconds, larger values are clipped and warning
            will be provided to the user. Default: 120 arcseconds
        find_planets : boolean, optional
            If ``True``, planets will be included in the search. Default:
            ``True``
        find_asteroids : boolean, optional
            If ``True``, asteroids will be included in the search. Default:
            ``True``
        find_comets : boolean, optional
            If ``True``, comets will be included in the search. Default:
            ``True``
        get_query_payload : boolean, optional
            Returns the query payload only and performs no query.
            Default: ``False``
        get_raw_response : boolean, optional
            Returns the raw response as provided by the IMCCE server instead
            of the parsed output. Default: ``False``
        cache : boolean, optional
            Cache this specfific query so it might be retrieved faster in
            the future. Default: ``True``

        Notes
        -----
        The following parameters are queried from the SkyBoT service:

        +------------------+-----------------------------------------------+
        | Column Name      | Definition                                    |
        +==================+===============================================+
        | ``'Number'``     | Target Number (``-1`` if none provided, int)  |
        +------------------+-----------------------------------------------+
        | ``'Name'``       | Target Name (str)                             |
        +------------------+-----------------------------------------------+
        | ``'RA'``         | Target RA (J2000, deg, float)                 |
        +------------------+-----------------------------------------------+
        | ``'DEC'``        | Target declination (J2000, deg, float)        |
        +------------------+-----------------------------------------------+
        | ``'Type'``       | Target dynamical/physical type (str)          |
        +------------------+-----------------------------------------------+
        | ``'V'``          | Target apparent brightness (V-band, mag,      |
        |                  | float)                                        |
        +------------------+-----------------------------------------------+
        | ``'posunc'``     | Positional uncertainty (arcsec, float)        |
        +------------------+-----------------------------------------------+
        | ``'centerdist'`` | Angular distance of target from cone center   |
        |                  | (arcsec, float)                               |
        +------------------+-----------------------------------------------+
        | ``'RA_rate'``    | RA rate of motion (arcsec/hr, float)          |
        +------------------+-----------------------------------------------+
        | ``'DEC_rate'``   | Declination rate of motion (arcsec/hr, float) |
        +------------------+-----------------------------------------------+
        | ``'geodist'``    | Geocentric distance of target (au, float)     |
        +------------------+-----------------------------------------------+
        | ``'heliodist'``  | Heliocentric distance of target (au, float)   |
        +------------------+-----------------------------------------------+
        | ``'alpha'``      | Solar phase angle (deg, float)                |
        +------------------+-----------------------------------------------+
        | ``'elong'``      | Solar elongation angle (deg, float)           |
        +------------------+-----------------------------------------------+
        | ``'x'``          | Target equatorial vector x (au, float)        |
        +------------------+-----------------------------------------------+
        | ``'y'``          | Target equatorial vector y (au, float)        |
        +------------------+-----------------------------------------------+
        | ``'z'``          | Target equatorial vector z (au, float)        |
        +------------------+-----------------------------------------------+
        | ``'vx'``         | Target velocity vector x (au/d, float)        |
        +------------------+-----------------------------------------------+
        | ``'vy'``         | Target velocity vector y (au/d, float)        |
        +------------------+-----------------------------------------------+
        | ``'vz'``         | Target velocity vector z (au/d, float)        |
        +------------------+-----------------------------------------------+
        | ``'epoch'``      | Ephemerides epoch (JD, float)                 |
        +------------------+-----------------------------------------------+

        Examples
        --------
        >>> from astroquery.imcce import Skybot
        >>> from astropy.coordinates import SkyCoord
        >>> from astropy.time import Time
        >>> import astropy.units as u
        >>> field = SkyCoord(1*u.deg, 1*u.deg)
        >>> epoch = Time('2019-05-29 21:42', format='iso')
        >>> Skybot.cone_search(field, 0.1*u.deg, epoch)  # doctest: +SKIP
        <QTable length=2>
        Number    Name           RA         ...      vy          vz       epoch
                                deg         ...    AU / d      AU / d       d
        int64     str9        float64       ...   float64     float64    float64
        ------ --------- ------------------ ... ----------- ----------- ---------
        180969 2005 MM39 1.0019566666666666 ...  0.00977568 0.003022634 2458630.0
        107804 2001 FV58 1.0765258333333332 ... 0.006551369 0.003846177 2458630.0
        """

        URL = conf.skybot_server
        TIMEOUT = conf.timeout

        # check for types and units
        if not isinstance(coo, SkyCoord):
            coo = SkyCoord(ra=coo[0]*u.degree,
                           dec=coo[1]*u.degree, frame='icrs')
        if isinstance(rad, u.Quantity):
            rad = Angle(rad.value, unit=rad.unit)
        if not isinstance(rad, u.Quantity):
            rad = Angle(rad, unit=u.degree)
        if rad > Angle(10, unit=u.degree):
            rad = Angle(10, unit=u.degree)
            warnings.warn('search cone radius set to maximum: 10 deg',
                          UserWarning)
        if isinstance(epoch, (int, float)):
            epoch = Time(epoch, format='jd')
        elif isinstance(epoch, str):
            epoch = Time(epoch, format='iso')
        if isinstance(position_error, u.Quantity):
            position_error = Angle(position_error.value,
                                   unit=position_error.unit)
        if not isinstance(position_error, u.Quantity):
            position_error = Angle(position_error, unit=u.arcsec)
        if position_error > Angle(120, unit=u.arcsec):
            position_error = Angle(120, unit=u.arcsec)
            warnings.warn('positional error set to maximum: 120 arcsec',
                          UserWarning)

        # assemble payload
        request_payload = {'-ra': coo.ra.deg,
                           '-dec': coo.dec.deg,
                           '-rd': rad.deg,
                           '-ep': str(epoch.jd),
                           '-loc': str(location),
                           '-filter': position_error.arcsec,
                           '-objFilter':
                           str(int(find_asteroids))
                           + str(int(find_planets))
                           + str(int(find_comets)),
                           '-refsys': 'EQJ2000',
                           '-output': 'all',
                           '-mime': 'votable'}

        # check for diagnostic flags
        if get_query_payload:
            return request_payload

        self._get_raw_response = get_raw_response

        response = self._request(method='GET', url=URL,
                                 params=request_payload,
                                 timeout=TIMEOUT, cache=cache)

        response.raise_for_status()

        self._uri = response.url

        return response

    def _parse_result(self, response, *, verbose=False):
        """
        internal wrapper to parse queries.
        """

        if self._get_raw_response:
            return response.text

        with warnings.catch_warnings():
            # We deal with RA/DEC manually
            warnings.filterwarnings("ignore", category=AstropyUserWarning,
                                    message=r"column ra|(column de) has a unit but is kept as a MaskedColumn")
            results = QTable.read(BytesIO(response.content), format='votable')

        if len(results) == 0:
            warnings.warn("No objects were found with the query constraints.", NoResultsWarning)
            return results

        # convert coordinates to degrees
        coo = SkyCoord(ra=results['ra'], dec=results['de'],
                       unit=(u.hourangle, u.deg), frame='icrs')
        results['ra'] = coo.ra.deg
        results['ra'].unit = u.deg
        results['de'] = coo.dec.deg
        results['de'].unit = u.deg

        colnames = results.columns[:]
        for fieldname in colnames:
            # apply field name change
            results.rename_column(fieldname, conf.field_names[fieldname])

        # convert object numbers to int
        # unnumbered asteroids return as non numeric values ('-')
        # this is treated as defaulting to 0, and masking the entry
        unnumbered_mask = [not str(x).isdigit() for x in results['Number']]
        numbers = [int(x) if str(x).isdigit()
                   else 0
                   for x in results['Number']]
        asteroid_number_col = MaskedColumn(numbers, name='Number',
                                           mask=unnumbered_mask)

        results.replace_column('Number', asteroid_number_col)

        return results


Skybot = SkybotClass()
