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

import warnings
from six import StringIO, BytesIO
from astropy.table import Table
from astropy.io import fits
from astropy import coordinates
from astropy import units as u
from ..query import BaseQuery
from ..utils import commons
from ..utils import async_to_sync
from ..exceptions import InvalidQueryError, NoResultsWarning
from . import conf

__all__ = ['Heasarc', 'HeasarcClass']


@async_to_sync
class HeasarcClass(BaseQuery):

    """
    HEASARC query class.

    For a list of available HEASARC mission tables, visit:
        https://heasarc.gsfc.nasa.gov/cgi-bin/W3Browse/w3catindex.pl
    """

    URL = conf.server
    TIMEOUT = conf.timeout
    coord_systems = ['fk5', 'fk4', 'equatorial', 'galactic']

    def query_async(self, request_payload, cache=True, url=None):
        """
        Submit a query based on a given request_payload. This allows detailed
        control of the query to be submitted.
        """

        if url is None:
            url = conf.server

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

    def query_mission_list(self, cache=True, get_query_payload=False):
        """
        Returns a list of all available mission tables with descriptions
        """
        request_payload = self._args_to_payload(
            entry='none',
            mission='xxx',
            displaymode='BatchDisplay'
        )

        if get_query_payload:
            return request_payload

        # Parse the results specially (it's ascii format, not fits)
        response = self.query_async(
            request_payload,
            url=conf.server,
            cache=cache
        )
        data_str = response.text
        data_str = data_str.replace('Table xxx does not seem to exist!\n\n\n\nAvailable tables:\n', '')
        table = Table.read(data_str, format='ascii.fixed_width_two_line',
                           delimiter='+', header_start=1, position_line=2,
                           data_start=3, data_end=-1)
        return table

    def query_mission_cols(self, mission, cache=True, get_query_payload=False,
                           **kwargs):
        """
        Returns a list containing the names of columns that can be returned for
        a given mission table. By default all column names are returned.

        Parameters
        ----------
        mission : str
            Mission table (short name) to search from
        fields : str, optional
            Return format for columns from the server available options:
            * Standard      : Return default table columns
            * All (default) : Return all table columns
            * <custom>      : User defined csv list of columns to be returned
        All other parameters have no effect
        """

        response = self.query_region_async(position='0.0 0.0', mission=mission,
                                           radius='361 degree', cache=cache,
                                           get_query_payload=get_query_payload,
                                           resultsmax=1,
                                           fields='All')

        # Return payload if requested
        if get_query_payload:
            return response

        return self._parse_result(response).colnames

    def query_object_async(self, object_name, mission,
                           cache=True, get_query_payload=False,
                           **kwargs):
        """
        Query around a specific object within a given mission catalog

        Parameters
        ----------
        object_name : str
            Object to query around. To set search radius use the 'radius'
            parameter.
        mission : str
            Mission table to search from
        **kwargs :
            see `~astroquery.heasarc.HeasarcClass._args_to_payload` for list
            of additional parameters that can be used to refine search query
        """
        request_payload = self._args_to_payload(
            mission=mission,
            entry=object_name,
            **kwargs
        )

        # Return payload if requested
        if get_query_payload:
            return request_payload

        return self.query_async(request_payload, cache=cache)

    def query_region_async(self, position, mission, radius,
                           cache=True, get_query_payload=False,
                           **kwargs):
        """
        Query around specific set of coordinates within a given mission
        catalog. Method first converts the supplied coordinates into the FK5
        reference frame and searches for sources from there. Because of this,
        returned offset coordinates may look different than the ones supplied.

        Parameters
        ----------
        position : `astropy.coordinates` or str
            The position around which to search. It may be specified as a
            string in which case it is resolved using online services or as
            the appropriate `astropy.coordinates` object. ICRS coordinates
            may also be entered as a string.
            (adapted from nrao module)
        mission : str
            Mission table to search from
        radius :
            Astropy Quantity object, or a string that can be parsed into one.
            e.g., '1 degree' or 1*u.degree.
        **kwargs :
            see `~astroquery.heasarc.HeasarcClass._args_to_payload` for list
            of additional parameters that can be used to refine search query
        """
        # Convert the coordinates to FK5
        c = commons.parse_coordinates(position).transform_to(coordinates.FK5)
        kwargs['coordsys'] = 'fk5'
        kwargs['equinox'] = 2000

        # Generate the request
        request_payload = self._args_to_payload(
            mission=mission,
            entry="{},{}".format(c.ra.degree, c.dec.degree),
            radius=u.Quantity(radius),
            **kwargs
        )

        # Return payload if requested
        if get_query_payload:
            return request_payload

        # Submit the request
        return self.query_async(request_payload, cache=cache)

    def _old_w3query_fallback(self, content):
        # old w3query (such as that used in ISDC) return very strange fits, with all ints

        f = fits.open(BytesIO(content))

        for c in f[1].columns:
            if c.disp is not None:
                c.format = c.disp
            else: