""" implement the TimedeltaIndex """
from datetime import datetime
import warnings

import numpy as np

from pandas._libs import (
    NaT, Timedelta, index as libindex, join as libjoin, lib)
import pandas.compat as compat
from pandas.util._decorators import Appender, Substitution

from pandas.core.dtypes.common import (
    _TD_DTYPE, ensure_int64, is_float, is_integer, is_list_like, is_scalar,
    is_timedelta64_dtype, is_timedelta64_ns_dtype, pandas_dtype)
import pandas.core.dtypes.concat as _concat
from pandas.core.dtypes.missing import isna

from pandas.core.accessor import delegate_names
from pandas.core.arrays import datetimelike as dtl
from pandas.core.arrays.timedeltas import TimedeltaArray, _is_convertible_to_td
from pandas.core.base import _shared_docs
import pandas.core.common as com
from pandas.core.indexes.base import Index, _index_shared_docs
from pandas.core.indexes.datetimelike import (
    DatetimeIndexOpsMixin, DatetimelikeDelegateMixin, maybe_unwrap_index,
    wrap_arithmetic_op)
from pandas.core.indexes.numeric import Int64Index
from pandas.core.ops import get_op_result_name

from pandas.tseries.frequencies import to_offset


def _make_wrapped_arith_op(opname):

    meth = getattr(TimedeltaArray, opname)

    def method(self, other):
        result = meth(self._data, maybe_unwrap_index(other))
        return wrap_arithmetic_op(self, other, result)

    method.__name__ = opname
    return method


class TimedeltaDelegateMixin(DatetimelikeDelegateMixin):
    # Most attrs are dispatched via datetimelike_{ops,methods}
    # Some are "raw" methods, the result is not not re-boxed in an Index
    # We also have a few "extra" attrs, which may or may not be raw,
    # which we we dont' want to expose in the .dt accessor.
    _delegate_class = TimedeltaArray
    _delegated_properties = (TimedeltaArray._datetimelike_ops + [
        'components',
    ])
    _delegated_methods = TimedeltaArray._datetimelike_methods + [
        '_box_values',
    ]
    _raw_properties = {
        'components',
    }
    _raw_methods = {
        'to_pytimedelta',
    }


@delegate_names(TimedeltaArray,
                TimedeltaDelegateMixin._delegated_properties,
                typ="property")
@delegate_names(TimedeltaArray,
                TimedeltaDelegateMixin._delegated_methods,
                typ="method", overwrite=False)
class TimedeltaIndex(DatetimeIndexOpsMixin, dtl.TimelikeOps, Int64Index,
                     TimedeltaDelegateMixin):
    """
    Immutable ndarray of timedelta64 data, represented internally as int64, and
    which can be boxed to timedelta objects

    Parameters
    ----------
    data  : array-like (1-dimensional), optional
        Optional timedelta-like data to construct index with
    unit : unit of the arg (D,h,m,s,ms,us,ns) denote the unit, optional
        which is an integer/float number
    freq : string or pandas offset object, optional
        One of pandas date offset strings or corresponding objects. The string
        'infer' can be passed in order to set the frequency of the index as the
        inferred frequency upon creation
    copy  : bool
        Make a copy of input ndarray
    start : starting value, timedelta-like, optional
        If data is None, start is used as the start point in generating regular
        timedelta data.

        .. deprecated:: 0.24.0

    periods  : int, optional, > 0
        Number of periods to generate, if generating index. Takes precedence
        over end argument

        .. deprecated:: 0.24.0

    end : end time, timedelta-like, optional
        If periods is none, generated index will extend to first conforming
        time on or just past end argument

        .. deprecated:: 0.24. 0

    closed : string or None, default None
        Make the interval closed with respect to the given frequency to
        the 'left', 'right', or both sides (None)

        .. deprecated:: 0.24. 0

    name : object
        Name to be stored in the index

    Attributes
    ----------
    days
    seconds
    microseconds
    nanoseconds
    components
    inferred_freq

    Methods
    -------
    to_pytimedelta
    to_series
    round
    floor
    ceil
    to_frame

    See Also
    ---------
    Index : The base pandas Index type.
    Timedelta : Represents a duration between two dates or times.
    DatetimeIndex : Index of datetime64 data.
    PeriodIndex : Index of Period data.
    timedelta_range : Create a fixed-frequency TimedeltaIndex.

    Notes
    -----
    To learn more about the frequency strings, please see `this link
    <http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

    Creating a TimedeltaIndex based on `start`, `periods`, and `end` has
    been deprecated in favor of :func:`timedelta_range`.
    """

    _typ = 'timedeltaindex'
    _join_precedence = 10

    def _join_i8_wrapper(joinf, **kwargs):
        return DatetimeIndexOpsMixin._join_i8_wrapper(
            joinf, dtype='m8[ns]', **kwargs)

    _inner_indexer = _join_i8_wrapper(libjoin.inner_join_indexer_int64)
    _outer_indexer = _join_i8_wrapper(libjoin.outer_join_indexer_int64)
    _left_indexer = _join_i8_wrapper(libjoin.left_join_indexer_int64)
    _left_indexer_unique = _join_i8_wrapper(
        libjoin.left_join_indexer_unique_int64, with_indexers=False)

    _engine_type = libindex.TimedeltaEngine

    _comparables = ['name', 'freq']
    _attributes = ['name', 'freq']
    _is_numeric_dtype = True
    _infer_as_myclass = True

    _freq = None

    _box_func = TimedeltaArray._box_func
    _bool_ops = TimedeltaArray._bool_ops
    _object_ops = TimedeltaArray._object_ops
    _field_ops = TimedeltaArray._field_ops
    _datetimelike_ops = TimedeltaArray._datetimelike_ops
    _datetimelike_methods = TimedeltaArray._datetimelike_methods
    _other_ops = TimedeltaArray._other_ops

    # -------------------------------------------------------------------
    # Constructors

    def __new__(cls, data=None, unit=None, freq=None, start=None, end=None,
                periods=None, closed=None, dtype=_TD_DTYPE, copy=False,
                name=None, verify_integrity=None):

        if verify_integrity is not None:
            warnings.warn("The 'verify_integrity' argument is deprecated, "
                          "will be removed in a future version.",
                          FutureWarning, stacklevel=2)
        else:
            verify_integrity = True

        if data is None:
            freq, freq_infer = dtl.maybe_infer_freq(freq)
            warnings.warn("Creating a TimedeltaIndex by passing range "
                          "endpoints is deprecated.  Use "
                          "`pandas.timedelta_range` instead.",
                          FutureWarning, stacklevel=2)
            result = TimedeltaArray._generate_range(start, end, periods, freq,
                                                    closed=closed)
            return cls._simple_new(result._data, freq=freq, name=name)

        if is_scalar(data):
            raise TypeError('{cls}() must be called with a '
                            'collection of some kind, {data} was passed'
                            .format(cls=cls.__name__, data=repr(data)))

        if isinstance(data, TimedeltaArray):
            if copy:
                data = data.copy()
            return cls._simple_new(data, name=name, freq=freq)

        if (isinstance(data, TimedeltaIndex) and
                freq is None and name is None):
            if copy:
                return data.copy()
            else:
                return data._
shallow_copy()

        # - Cases checked above al
l return/raise before reaching here - #

        tdarr = TimedeltaArray._from_sequence(data, freq=freq, unit=unit,
                                              dtype=dtype, copy=copy)
        return cls._simple_new(tdarr._data, freq=tdarr.freq, name=name)

    @classmethod
    def _simple_new(cls, values, name=None, freq=None, dtype=_TD_DTYPE):
        # `dtype` is passed by _shallow_copy in corner cases, should always
        #  be timedelta64[ns] if present
        if not isinstance(values, TimedeltaArray):
            values = Tim