# Licensed under a 3-clause BSD style license - see LICENSE.rst
# This file connects the readers/writers to the astropy.table.Table class

import functools
import os.path

from astropy.utils.misc import NOT_OVERWRITING_MSG
from astropy.table import Table
import astropy.io.registry as io_registry

__all__ = ['PANDAS_FMTS']

# Astropy users normally expect to not have an index, so default to turn
# off writing the index.  This structure allows for astropy-specific
# customization of all options.
PANDAS_FMTS = {'csv': {'read': {},
                       'write': {'index': False}},
               'fwf': {'read': {}},  # No writer
               'html': {'read': {},
                        'write': {'index': False}},
               'json': {'read': {},
                        'write': {}}}

PANDAS_PREFIX = 'pandas.'

# Imports for reading HTML
_IMPORTS = False
_HAS_BS4 = False
_HAS_LXML = False
_HAS_HTML5LIB = False


def import_html_libs():
    """Try importing dependencies for reading HTML.

    This is copied from pandas.io.html
    """
    # import things we need
    # but make this done on a first use basis

    global _IMPORTS
    if _IMPORTS:
        return

    global _HAS_BS4, _HAS_LXML, _HAS_HTML5LIB

    from astropy.utils.compat.optional_deps import (
        HAS_BS4 as _HAS_BS4,
        HAS_LXML as _HAS_LXML,
        HAS_HTML5LIB as _HAS_HTML5LIB
    )
    _IMPORTS = True


def _pandas_read(fmt, filespec, **kwargs):
    """Provide io Table connector to read table using pandas.

    """
    try:
        import pandas
    except ImportError:
        raise ImportError('pandas must be installed to use pandas table reader')

    pandas_fmt = fmt[len(PANDAS_PREFIX):]  # chop the 'pandas.' in front
    read_func = getattr(pandas, 'read_' + pandas_fmt)

    # Get defaults and then override with user-supplied values
    read_kwargs = PANDAS_FMTS[pandas_fmt]['read'].copy()
    read_kwargs.update(kwargs)

    # Special case: pandas defaults to HTML lxml for reading, but does not attempt
    # to fall back to bs4 + html5lib.  So do that now for convenience if user has
    # not specifically selected a flavor.  If things go wrong the pandas exception
    # with instruction to install a library will come up.
    if pandas_fmt == 'html' and 'flavor' not in kwargs:
        import_html_libs()
        if (not _HAS_LXML and _HAS_HTML5LIB and _HAS_BS4):
            read_kwargs['flavor'] = 'bs4'

    df = read_func(filespec, **read_kwargs)

    # Special case for HTML
    if pandas_fmt == 'html':
        df = df[0]

    return Table.from_pandas(df)


def _pandas_write(fmt, tbl, filespec, overwrite=False, **kwargs):
    """Provide io Table connector to write table using pandas.

    """
    pandas_fmt = fmt[len(PANDAS_PREFIX):]  # chop the 'pandas.' in front

    # Get defaults and then override with user-supplied values
    write_kwargs = PANDAS_FMTS[pandas_fmt]['write'].copy()
    write_kwargs.update(kwargs)

    df = tbl.to_pandas()
    write_method = getattr(df, 'to_' + pandas_fmt)

    if not overwrite:
        try:  # filespec is not always a path-like
            exists = os.path.exists(filespec)
        except TypeError:  # skip invalid arguments
            pass
        else:
            if exists:  # only error if file already exists
                raise OSError(NOT_OVERWRITING_MSG.format(filespec))

    return write_method(filespec, **write_kwargs)


for pandas_fmt, defaults in PANDAS_FMTS.items():
    fmt = PANDAS_PREFIX + pandas_fmt  # Full format specifier

    if 'read' in defaults:
        func = functools.partial(_pandas_read, fmt)
        io_registry.register_reader(fmt, Table, func)

    if 'write' in defaults:
        func = functools.partial(_pandas_write, fmt)
        io_registry.register_writer(fmt, Table, func)
