import datetime as dt
import logging


def match_data_ocb(ocb, dat_dtime, idat=0, max_tol=600, min_sectors=7,
                   rcent_dev=8.0, max_r=23.0, min_r=10.0, min_j=0.15):
    """Matches data records with OCB records, locating the closest values
    within a specified tolerance

    Parameters
    ----------
    ocb : (OCBoundary)
        Class containing the open-close field line boundary data
    dat_dtime : (list or numpy array of datetime objects)
        Times where data exists
    idat : (int)
        Current data index (default=0)
    max_tol : (int)
        maximum seconds between OCB and data record in sec (default=600)
    min_sectors : (int)
        Minimum number of MLT sectors required for good OCB. (default=7)
    rcent_dev : (float)
        Maximum number of degrees between the new centre and the AACGM pole
        (default=8.0)
    max_r : (float)
        Maximum radius for open-closed field line boundary in degrees.
        (default=23.0)
    min_r : (float)
        Minimum radius for open-closed field line boundary in degrees
        (default=10.0)
    min_j : (float)
        Minimum unitless current magnitude scale difference (default=0.15)

    Returns
    ---------
    idat : (int or NoneType)
        Data index for match value.  None if all of the data have been searched.

    Notes
    --------
    Updates OCBoundary.rec_ind for matched value.  None if all of the
    boundaries have been searched.
    """

    dat_records = len(dat_dtime)

    # Ensure that the indices are good
    if idat >= dat_records:
        return idat
    if ocb.rec_ind >= ocb.records:
        return idat

    # Get the first reliable circle boundary estimate if none was provided
    if ocb.rec_ind < 0:
        ocb.get_next_good_ocb_ind(min_sectors=min_sectors, rcent_dev=rcent_dev,
                                  max_r=max_r, min_r=min_r, min_j=min_j)
        if ocb.rec_ind >= ocb.records:
            estr = "unable to find a good OCB record in "
            estr = "{:s}{:s}".format(estr, ocb.filename)
            logging.error(estr)
            return idat
        else:
            estr = "found first good OCB record at "
            estr = "{:s}{:}".format(estr, ocb.dtime[ocb.rec_ind])
            logging.info(estr)

        # Cycle past data occuring before the specified OC boundary point
        first_ocb = ocb.dtime[ocb.rec_ind] - dt.timedelta(seconds=max_tol)
        while dat_dtime[idat] < first_ocb:
            idat += 1

            if idat >= dat_records:
                logging.error("no input data close enough to first record")
                return None

    # If the times match, return
    if ocb.dtime[ocb.rec_ind] == dat_dtime[idat]:
        return idat

    # If the times don't match, cycle through both datasets until they do
    while idat < dat_records and ocb.rec_ind < ocb.records:
        # Increase the OCB index until one lies within the desired boundary
        sdiff = (ocb.dtime[ocb.rec_ind] - dat_dtime[idat]).total_seconds()

        if sdiff < -max_tol:
            # Cycle to the next OCB value since the lowest vorticity value
            # is in the future
            ocb.get_next_good_ocb_ind(min_sectors=min_sectors,
                                      rcent_dev=rcent_dev, max_r=max_r,
                                      min_r=min_r, min_j=min_j)
        elif sdiff > max_tol:
            # Cycle to the next vorticity value if no OCB values were close
            # enough to grid this one
            estr = "no OCB data available within [{:d} s] of".format(max_tol)
            estr = "{:s} input measurement at ".format(estr)
            estr = "{:s}[{:}]".format(estr, dat_dtime[idat])
            logging.info(estr)
            idat += 1
        else:
            # Make sure this is the OCB value closest to the input record
            last_sdiff = sdiff
            last_iocb = ocb.rec_ind
            ocb.get_next_good_ocb_ind(min_sectors=min_sectors,
                                      rcent_dev=rcent_dev, max_r=max_r,
                                      min_r=min_r, min_j=min_j)

            if ocb.rec_ind < ocb.records:
                sdiff = (ocb.dtime[ocb.rec_ind] -
                         dat_dtime[idat]).total_seconds()

                while abs(sdiff) < abs(last_sdiff):
                    last_sdiff = sdiff
                    last_iocb = ocb.rec_ind
                    ocb.get_next_good_ocb_ind(min_sectors=min_sectors,
                                              rcent_dev=rcent_dev, max_r=max_r,
                                              min_r=min_r, min_j=min_j)
                    if ocb.rec_ind < ocb.records:
                        sdiff = (ocb.dtime[ocb.rec_ind] -
                                 dat_dtime[idat]).total_seconds()

            sdiff = last_sdiff
            ocb.rec_ind = last_iocb

            # Return the requested indices
            return idat

    # Return from the last loop
    return idat