"""
wrapper to some of the bedops (http://code.google.com/p/bedops/) utilities
"""


import subprocess
import re
from itertools import ifilter
from functools import partial
import numpy as np

from . import parseBED

BEDMAP = "bedmap"


def bedmap(refdata, mapdata, ops=("--echo", "--echo-map"),
           parsePatt="([01])[|](.+)[|](.*)", onlyOverlapping=True):
    """run bedmap on the given files and return an iterator over the results


    bedmap command is: bedmap opts refdata mapdata

    @param refdata: reference intervals. output is with respect to these. I.e.,
            unless there is filtering, output has one line for each refdata line
    @param mapdata: qualifying (e.g, within a range of a refdata element)
            intervals on which to apply operations.
    @param ops: operations (see bedmap help)
    @param parsePatt: a regex object or pattern used to parse the output of bedmap.
              Make this grouped, to get a tuple of groups. Notice that first group
              of this should be "([01])"
    @return: iterator over overapping features  (str)
    """

    cmd = ("bedmap", "--indicator") + tuple(ops) + (refdata, mapdata)
    out = ifilter(bool, subprocess.check_output(cmd).split("\n"))
    if onlyOverlapping:
        out = ifilter(lambda tp: tp[0] == '1', out)
    for line in out:
        match = re.match(parsePatt, line)
        yield match.groups()[1:]


def overlap_as_array(anchor, feat_lst, bin=1, dtype=np.float64,
                     parseFeature=partial(parseBED, use_score=True), respect_strand=True):
    """convert an anchor and a list of overlapping features on a signal array

    anchor: BED5
    feat_list: [>=BED4, ...]
    bin: bin the signal
    dtype:type of array
    use_score: whether to use score from feature. will set to 1.0 if False
    respect_strand : whether to mirror the signal for anchors on the negative strand

    returns a signal array of length anchor[2] - anchor[1]
    """

    def mydiff(v, S=anchor[1]):
        dv = v - S
        if dv < 0:
            return 0
        return dv

    x = np.zeros((anchor[2] - anchor[1],), dtype=dtype)
    if x.shape[0] % bin:
        raise ValueError("bin (%d) not a multiple of track width (%d)" % (bin, x.shape[0]) )

    for ft in filter(bool, feat_lst):
        (ch, s, e, n, v) = parseFeature(ft)
        x[mydiff(s):mydiff(e)] += v
    binned_x = x.reshape((-1, bin)).sum(axis=1)
    if respect_strand:
        if len(anchor) < 6:
            raise ValueError("respect_strand on >BED6 anchors.")
        if anchor[5] == "-":
            for i in range(binned_x.shape[0] / 2):
                binned_x[i], binned_x[-(i + 1)] = binned_x[-(i + 1)], binned_x[i]
    return binned_x


if __name__ == "__main__":
    pass
