# 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
from pathlib import Path

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

__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
    from astropy.utils.compat.optional_deps import HAS_HTML5LIB as _HAS_HTML5LIB
    from astropy.utils.compat.optional_deps import HAS_LXML as _HAS_LXML

    _IMPORTS = True


def _pandas_read(fmt, filespec, **kwargs):
    """Provide io Table connector to read table using pandas."""
    if not HAS_PANDAS:
        raise ModuleNotFoundError("pandas must be installed to use pandas table reader")
    import pandas as pd

    pandas_fmt = fmt[len(PANDAS_PREFIX) :]  # chop the 'pandas.' in front
    read_func = getattr(pd, "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 = Path(filespec).exists()
        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)
