#!/usr/bin/env python
"""Test suite for :py:mod:`plastid.readers.bed`

    :py:class:`BED_Reader`
        Reads BED files to SegmentChain objects

See http://genome.ucsc.edu/FAQ/FAQformat.html
"""
import functools
import warnings
import pandas as pd
import os

from csv import QUOTE_NONE
from nose.plugins.attrib import attr
from plastid.util.services.mini2to3 import cStringIO
from plastid.genomics.roitools import SegmentChain, GenomicSegment, Transcript
from plastid.readers.bed import BED_Reader
from nose.tools import assert_equal, assert_true, assert_dict_equal, assert_greater_equal

from plastid.test.ref_files import RPATH, REF_FILES

warnings.simplefilter("ignore", DeprecationWarning)

TXBED = os.path.join(RPATH, REF_FILES["100transcripts_bed"])
CDSBED = os.path.join(RPATH, REF_FILES["100cds_bed"])

from plastid.test.data.annotations.py100cds import control_cds
from plastid.test.data.annotations.py100transcripts import control_transcripts


#===============================================================================
# INDEX: test suites
#===============================================================================

@attr(test="unit")
class TestBED():
    """Test case for BED input/output"""

    @classmethod
    def setUpClass(cls):
        cls.header = _BED_HEADER
        cls.data = {}
        cls.extracol_data = {}
        bed_df = pd.read_table(
            cStringIO.StringIO(_BED12_DATA), header=None, sep="\t", index_col=None
        )
        extra_df = pd.read_table(
            cStringIO.StringIO(_EXTRA_COLS), header=0, sep="\t", index_col=None
        )
        cls.big_df = pd.concat([bed_df, extra_df], axis=1)

        for n in (3, 4, 5, 6, 8, 9, 12):
            cls.data[n] = cls.get_bed_subset(cls.header, n, 0)
            cls.extracol_data[n] = cls.get_bed_subset(cls.header, n, 4)

    @classmethod
    def get_bed_subset(cls, header, bed_cols, extra_cols=0):
        buf = cStringIO.StringIO()
        columns = cls.big_df.columns[list(range(bed_cols)) + list(range(12, 12 + extra_cols))]
        cls.big_df.to_csv(
            buf, columns=columns, sep="\t", index=False, header=False, quoting=QUOTE_NONE
        )  #,float_format="%.8f")
        return buf.getvalue()

    @staticmethod
    def check_equal(found, expected, msg=None):
        if msg is not None:
            assert_equal(found, expected, msg)
        else:
            assert_equal(found, expected)

    def open_from_str(self):
        for expected, found in zip(control_transcripts, BED_Reader(TXBED, return_type=Transcript)):
            assert_equal(expected, found)

    def open_from_multi_str(self):
        for expected, found in zip(control_transcripts + control_cds,
                                   BED_Reader(TXBED, CDSBED, return_type=Transcript)):
            assert_equal(expected, found)

    def open_from_fh(self):
        with open(TXBED) as fh:
            for expected, found in zip(control_transcripts, BED_Reader(fh, return_type=Transcript)):
                assert_equal(expected, found)

    def open_from_multi_fh(self):
        with open(TXBED) as fh:
            with open(CDSBED) as fh2:
                for expected, found in zip(control_transcripts + control_cds,
                                           BED_Reader(fh, fh2, return_type=Transcript)):
                    assert_equal(expected, found)

    def test_bed_import_3to12plus4_columns_with_formatters(self):
        names = [
            ("numcol", int),
            ("floatcol", float),
            ("strcol", str),
            ("attrcol", str),
        ]

        tx_reader = functools.partial(BED_Reader, return_type=Transcript, extra_columns=names)
        seg_reader = functools.partial(BED_Reader, return_type=SegmentChain, extra_columns=names)
        tests = [
            (seg_reader, _TEST_SEGMENTCHAINS, "reader_segmentchain"),
            (tx_reader, _TEST_TRANSCRIPTS, "reader_transcript"),
        ]
        for reader_fn, known_set, name in tests:
            for n, data_str in sorted(self.extracol_data.items()):
                c = 0
                for (test_ivc, known_ivc) in zip(reader_fn(cStringIO.StringIO(data_str)),
                                                 known_set):
                    for x in range(4):
                        colname = names[x][0]
                        assert_true(
                            colname in test_ivc.attr,
                            "Column name '%s' not found in attr dict (%s BED columns)" % (x, n)
                        )
                        assert_equal(test_ivc.attr[colname], self.big_df.iloc[c, 12 + x])

                    # columns: chrom, start, end
                    if n >= 3:
                        # no strand info, so we need to test iv.start, iv.end, iv.chrom
                        err_msg = "%s failed endpoint equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.spanning_segment.start, test_ivc.spanning_segment.start, err_msg
                        yield self.check_equal, known_ivc.spanning_segment.end, test_ivc.spanning_segment.end, err_msg
                        yield self.check_equal, known_ivc.spanning_segment.chrom, test_ivc.spanning_segment.chrom, err_msg
                    # column: name
                    if n >= 4:
                        err_msg = "%s failed name equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr["ID"], test_ivc.attr["ID"], err_msg
                    # column: score
                    if n >= 5:
                        err_msg = "%s failed score equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr.get(
                            "score", 0
                        ), test_ivc.attr["score"], err_msg
                    # column : strand
                    if n >= 6:
                        err_msg = "%s failed strand equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.spanning_segment.strand, test_ivc.spanning_segment.strand
                    # column: color
                    if n >= 9:
                        err_msg = "%s failed color equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr.get(
                            "color", "#000000"
                        ), test_ivc.attr["color"], err_msg
                    # columns: exon/block info
                    if n == 12:
                        err_msg = "%s failed block equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        for iv1, iv2 in zip(known_ivc, test_ivc):
                            assert_equal(iv1, iv2, err_msg)
                        err_msg = "%s failed position set on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.get_position_set(
                        ), test_ivc.get_position_set(), err_msg

                    c += 1

                yield self.check_equal, c, len(
                    known_set
                ), "Not all intervals loaded! Expected %s, found %s." % (len(known_set), c)

    def test_bed_import_3to12plus4_columns_with_names(self):
        names = [X for X in self.big_df.columns[-4:]]
        tx_reader = functools.partial(BED_Reader, return_type=Transcript, extra_columns=names)
        seg_reader = functools.partial(BED_Reader, return_type=SegmentChain, extra_columns=names)
        tests = [
            (seg_reader, _TEST_SEGMENTCHAINS, "reader_segmentchain"),
            (tx_reader, _TEST_TRANSCRIPTS, "reader_transcript"),
        ]
        for reader_fn, known_set, name in tests:
            for n, data_str in sorted(self.extracol_data.items()):
                c = 0
                for (test_ivc, known_ivc) in zip(reader_fn(cStringIO.StringIO(data_str)),
                                                 known_set):
                    for x in range(4):
                        colname = names[x]
                        assert_true(colname in test_ivc.attr)
                        assert_equal(str(test_ivc.attr[colname]), str(self.big_df.iloc[c, 12 + x]))

                    # columns: chrom, start, end
                    if n >= 3:
                        # no strand info, so we need to test iv.start, iv.end, iv.chrom
                        err_msg = "%s failed endpoint equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.spanning_segment.start, test_ivc.spanning_segment.start, err_msg
                        yield self.check_equal, known_ivc.spanning_segment.end, test_ivc.spanning_segment.end, err_msg
                        yield self.check_equal, known_ivc.spanning_segment.chrom, test_ivc.spanning_segment.chrom, err_msg
                    # column: name
                    if n >= 4:
                        err_msg = "%s failed name equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr["ID"], test_ivc.attr["ID"], err_msg
                    # column: score
                    if n >= 5:
                        err_msg = "%s failed score equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr.get(
                            "score", 0
                        ), test_ivc.attr["score"], err_msg
                    # column : strand
                    if n >= 6:
                        err_msg = "%s failed strand equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.spanning_segment.strand, test_ivc.spanning_segment.strand
                    # column: color
                    if n >= 9:
                        err_msg = "%s failed color equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr.get(
                            "color", "#000000"
                        ), test_ivc.attr["color"], err_msg
                    # columns: exon/block info
                    if n == 12:
                        err_msg = "%s failed block equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        for iv1, iv2 in zip(known_ivc, test_ivc):
                            assert_equal(iv1, iv2, err_msg)
                        err_msg = "%s failed position set on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.get_position_set(
                        ), test_ivc.get_position_set(), err_msg

                    c += 1

                yield self.check_equal, c, len(
                    known_set
                ), "Not all intervals loaded! Expected %s, found %s." % (len(known_set), c)

    def test_bed_import_3to12plus4_columns_with_int(self):
        tx_reader = functools.partial(BED_Reader, return_type=Transcript, extra_columns=4)
        seg_reader = functools.partial(BED_Reader, return_type=SegmentChain, extra_columns=4)
        tests = [
            (seg_reader, _TEST_SEGMENTCHAINS, "reader_segmentchain"),
            (tx_reader, _TEST_TRANSCRIPTS, "reader_transcript"),
        ]
        for reader_fn, known_set, name in tests:
            for n, data_str in sorted(self.extracol_data.items()):
                c = 0
                for (test_ivc, known_ivc) in zip(reader_fn(cStringIO.StringIO(data_str)),
                                                 known_set):
                    for x in range(4):
                        colname = "custom%s" % x
                        assert_true(colname in test_ivc.attr)
                        assert_equal(str(test_ivc.attr[colname]), str(self.big_df.iloc[c, 12 + x]))

                    # columns: chrom, start, end
                    if n >= 3:
                        # no strand info, so we need to test iv.start, iv.end, iv.chrom
                        err_msg = "%s failed endpoint equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.spanning_segment.start, test_ivc.spanning_segment.start, err_msg
                        yield self.check_equal, known_ivc.spanning_segment.end, test_ivc.spanning_segment.end, err_msg
                        yield self.check_equal, known_ivc.spanning_segment.chrom, test_ivc.spanning_segment.chrom, err_msg
                    # column: name
                    if n >= 4:
                        err_msg = "%s failed name equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr["ID"], test_ivc.attr["ID"], err_msg
                    # column: score
                    if n >= 5:
                        err_msg = "%s failed score equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr.get(
                            "score", 0
                        ), test_ivc.attr["score"], err_msg
                    # column : strand
                    if n >= 6:
                        err_msg = "%s failed strand equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.spanning_segment.strand, test_ivc.spanning_segment.strand
                    # column: color
                    if n >= 9:
                        err_msg = "%s failed color equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr.get(
                            "color", "#000000"
                        ), test_ivc.attr["color"], err_msg
                    # columns: exon/block info
                    if n == 12:
                        err_msg = "%s failed block equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        for iv1, iv2 in zip(known_ivc, test_ivc):
                            assert_equal(iv1, iv2, err_msg)
                        err_msg = "%s failed position set on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.get_position_set(
                        ), test_ivc.get_position_set(), err_msg

                    c += 1

                yield self.check_equal, c, len(
                    known_set
                ), "Not all intervals loaded! Expected %s, found %s." % (len(known_set), c)

    def test_bed_export_3to12plus4_columns_with_names(self):
        names = [X for X in self.big_df.columns[-4:]]
        tests = [
            (Transcript.from_bed, _TEST_TRANSCRIPTS, "tx_frombed_plus4_int"),
            (SegmentChain.from_bed, _TEST_SEGMENTCHAINS, "segchain_frombed_plus4_int"),
        ]
        for import_fn, known_set, name in tests:
            extracol12plus = [X.split("\t") for X in self.extracol_data[12].strip("\n").split("\n")]
            for n, data_str in sorted(self.extracol_data.items()):
                for c, line in enumerate(data_str.strip("\n").split("\n")):
                    out_line = import_fn(line, extra_columns=names).as_bed(as_int=False).strip("\n")
                    out_items = out_line.split("\t")[:n] + out_line.split("\t")[-4:]
                    expected_items = extracol12plus[c][:n] + extracol12plus[c][-4:]
                    msg = "%s BED %s+%s export unequal for lines:\nin:  %s\nout: %s\nexp: %s" % (
                        name, n, 4, line, "\t".join(out_items), "\t".join(expected_items)
                    )
                    yield self.check_equal, out_items, expected_items, msg

                yield self.check_equal, c + 1, len(
                    known_set
                ), "Not all intervals loaded! Expected %s, found %s." % (len(known_set), c)

    def test_bed_export_3to12plus4_columns_with_int(self):
        tests = [
            (Transcript.from_bed, _TEST_TRANSCRIPTS, "tx_frombed_plus4_int"),
            (SegmentChain.from_bed, _TEST_SEGMENTCHAINS, "segchain_frombed_plus4_int"),
        ]
        for import_fn, known_set, name in tests:
            extracol12plus = [X.split("\t") for X in self.extracol_data[12].strip("\n").split("\n")]
            for n, data_str in sorted(self.extracol_data.items()):
                for c, line in enumerate(data_str.strip("\n").split("\n")):
                    out_line = import_fn(line, extra_columns=4).as_bed(as_int=False).strip("\n")
                    out_items = out_line.split("\t")[:n] + out_line.split("\t")[-4:]
                    expected_items = extracol12plus[c][:n] + extracol12plus[c][-4:]
                    msg = "%s BED %s+%s export unequal for lines:\nin:  %s\nout: %s\nexp: %s" % (
                        name, n, 4, line, "\t".join(out_items), "\t".join(expected_items)
                    )
                    yield self.check_equal, out_items, expected_items, msg

                yield self.check_equal, c + 1, len(
                    known_set
                ), "Not all intervals loaded! Expected %s, found %s." % (len(known_set), c)

    def test_bed_import_3to12_columns(self):
        tx_reader = functools.partial(BED_Reader, return_type=Transcript)
        tests = [
            (BED_Reader, _TEST_SEGMENTCHAINS, "reader_segmentchain"),
            (tx_reader, _TEST_TRANSCRIPTS, "reader_transcript"),
        ]
        for reader_fn, known_set, name in tests:
            for n, data_str in sorted(self.data.items()):
                c = 0
                for (test_ivc, known_ivc) in zip(reader_fn(cStringIO.StringIO(data_str)),
                                                 known_set):
                    # columns: chrom, start, end
                    if n >= 3:
                        # no strand info, so we need to test iv.start, iv.end, iv.chrom
                        err_msg = "%s failed endpoint equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.spanning_segment.start, test_ivc.spanning_segment.start, err_msg
                        yield self.check_equal, known_ivc.spanning_segment.end, test_ivc.spanning_segment.end, err_msg
                        yield self.check_equal, known_ivc.spanning_segment.chrom, test_ivc.spanning_segment.chrom, err_msg
                    # column: name
                    if n >= 4:
                        err_msg = "%s failed name equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr["ID"], test_ivc.attr["ID"], err_msg
                    # column: score
                    if n >= 5:
                        err_msg = "%s failed score equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr.get(
                            "score", 0
                        ), test_ivc.attr["score"], err_msg
                    # column : strand
                    if n >= 6:
                        err_msg = "%s failed strand equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.spanning_segment.strand, test_ivc.spanning_segment.strand
                    # column: color
                    if n >= 9:
                        err_msg = "%s failed color equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc.attr, test_ivc.attr
                        )
                        yield self.check_equal, known_ivc.attr.get(
                            "color", "#000000"
                        ), test_ivc.attr["color"], err_msg
                    # columns: exon/block info
                    if n == 12:
                        err_msg = "%s failed block equality on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        for iv1, iv2 in zip(known_ivc, test_ivc):
                            assert_equal(iv1, iv2, err_msg)
                        err_msg = "%s failed position set on %s-column BED input: %s,%s" % (
                            name, n, known_ivc, test_ivc
                        )
                        yield self.check_equal, known_ivc.get_position_set(
                        ), test_ivc.get_position_set(), err_msg

                    c += 1

                yield self.check_equal, c, len(
                    known_set
                ), "Not all intervals loaded! Expected %s, found %s." % (len(known_set), c)

    def test_ivcollection_thick_start_end_8to12_columns(self):
        """Checks equality of thickstart and thickend attributes for SegmentChain objects"""
        for n, data_str in sorted(self.data.items()):
            for c, (test_ivc, known_ivc) in enumerate(zip(BED_Reader(
                    cStringIO.StringIO(data_str), return_type=SegmentChain), _TEST_SEGMENTCHAINS)):
                if n >= 8:
                    err_msg = "Failed thickstart/end equality on %s-column BED input: %s,%s" % (
                        n, known_ivc.attr, test_ivc.attr
                    )
                    if known_ivc.attr.get("thickstart", None) is not None:
                        yield self.check_equal, known_ivc.attr["thickstart"], test_ivc.attr[
                            "thickstart"
                        ], err_msg
                    if known_ivc.attr.get("thickend", None) is not None:
                        yield self.check_equal, known_ivc.attr.get("thickend"), test_ivc.attr[
                            "thickend"
                        ], err_msg

            yield self.check_equal, c, 20 - 1, "Not all intervals loaded! Expected %s, found %s." % (
                20 - 1, c
            )

    def test_transcript_cds_start_end_8to12_columns(self):
        """Checks equality of endpoints of coding regions for Transcript objects"""
        for n, data_str in sorted(self.data.items()):
            for c, (test_ivc, known_ivc) in enumerate(zip(BED_Reader(
                    cStringIO.StringIO(data_str), return_type=Transcript), _TEST_TRANSCRIPTS)):
                if n >= 8:
                    err_msg = "Failed thickstart/end equality on %s-column BED input: %s,%s" % (
                        n, known_ivc.attr, test_ivc.attr
                    )
                    if known_ivc.attr.get("cds_genome_start", None) is not None:
                        yield self.check_equal, known_ivc.attr["cds_start"], test_ivc.attr[
                            "cds_start"
                        ], err_msg
                        yield self.check_equal, known_ivc.attr["cds_genome_start"], test_ivc.attr[
                            "cds_genome_start"
                        ], err_msg
                        yield self.check_equal, known_ivc.cds_genome_start, test_ivc.cds_genome_start, err_msg
                        yield self.check_equal, known_ivc.cds_start, test_ivc.cds_start, err_msg
                    if known_ivc.attr.get("cds_genome_end", None) is not None:
                        yield self.check_equal, known_ivc.attr["cds_end"], test_ivc.attr["cds_end"
                                                                                         ], err_msg
                        yield self.check_equal, known_ivc.attr["cds_genome_end"], test_ivc.attr[
                            "cds_genome_end"
                        ], err_msg
                        yield self.check_equal, known_ivc.cds_genome_end, test_ivc.cds_genome_end, err_msg
                        yield self.check_equal, known_ivc.cds_end, test_ivc.cds_end, err_msg

            yield self.check_equal, c, 20 - 1, "Not all intervals loaded! Expected %s, found %s." % (
                20 - 1, c
            )

    def test_track_subtype_parsing(self):
        reader = BED_Reader(cStringIO.StringIO(_NARROW_PEAK_TEXT))
        for c, (found, expected) in enumerate(zip(reader, _NARROW_PEAK_CHAINS)):
            found.attr.pop("color")
            found.attr.pop("score")
            assert_dict_equal(found.attr, expected.attr)
            assert_equal(found, expected)

        assert_equal(c, len(_NARROW_PEAK_CHAINS) - 1)

    def test_track_subtype_raises_warning_if_wrong_extra_columns(self):
        reader = BED_Reader(cStringIO.StringIO(_NARROW_PEAK_TEXT), extra_columns=14)
        with warnings.catch_warnings(record=True) as warns:
            warnings.simplefilter("always")
            ltmp = list(reader)
            assert_greater_equal(len(warns), 0)


#===============================================================================
# INDEX: test data
#===============================================================================

# test dataset, constructed manually to include various edge cases
_TEST_SEGMENTCHAINS = [
    # single-interval
    SegmentChain(GenomicSegment("chrA", 100, 1100, "+"), ID="IVC1p"),
    SegmentChain(GenomicSegment("chrA", 100, 1100, "-"), ID="IVC1m"),
    # multi-interval
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"), GenomicSegment("chrA", 2100, 2600, "+"), ID="IVC2p"
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"), GenomicSegment("chrA", 2100, 2600, "-"), ID="IVC2m"
    ),
    # multi-interval, with score
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"),
        GenomicSegment("chrA", 2100, 2600, "+"),
        ID="IVC3p",
        score=500
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"),
        GenomicSegment("chrA", 2100, 2600, "-"),
        ID="IVC3m",
        score=500
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"),
        GenomicSegment("chrA", 2100, 2600, "+"),
        GenomicSegment("chrA", 2605, 2700, "+"),
        ID="IVC4p",
        score=500
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"),
        GenomicSegment("chrA", 2100, 2600, "-"),
        ID="IVC4m",
        score=500
    ),
    # multi-interval, with score and color
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"),
        GenomicSegment("chrA", 2100, 2600, "+"),
        GenomicSegment("chrA", 2605, 2700, "+"),
        ID="IVC5p",
        score=500,
        color="#007ADF"
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"),
        GenomicSegment("chrA", 2100, 2600, "-"),
        GenomicSegment("chrA", 2605, 2700, "-"),
        ID="IVC5m",
        score=500,
        color="#007ADF"
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"),
        GenomicSegment("chrA", 2100, 2600, "+"),
        GenomicSegment("chrA", 2605, 2700, "+"),
        ID="IVC6p",
        score=500,
        color="#007ADF"
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"),
        GenomicSegment("chrA", 2100, 2600, "-"),
        GenomicSegment("chrA", 2605, 2700, "-"),
        ID="IVC6m",
        score=500,
        color="#007ADF"
    ),
    # multi-interval, with score, color, thickstart, and thickend, internally
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"),
        GenomicSegment("chrA", 2100, 2600, "+"),
        GenomicSegment("chrA", 2605, 2700, "+"),
        ID="IVC7p",
        score=500,
        color="#007ADF",
        thickstart=2200,
        thickend=2400
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"),
        GenomicSegment("chrA", 2100, 2600, "-"),
        GenomicSegment("chrA", 2605, 2700, "-"),
        ID="IVC7m",
        score=500,
        color="#007ADF",
        thickstart=2200,
        thickend=2400
    ),
    # multi-interval, thickend and thickstart covering whole SegmentChain
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"),
        GenomicSegment("chrA", 2100, 2600, "+"),
        GenomicSegment("chrA", 2605, 2700, "+"),
        ID="IVC8p",
        score=500,
        color="#007ADF",
        thickstart=100,
        thickend=2700
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"),
        GenomicSegment("chrA", 2100, 2600, "-"),
        GenomicSegment("chrA", 2605, 2700, "-"),
        ID="IVC8m",
        score=500,
        color="#007ADF",
        thickstart=100,
        thickend=2700
    ),
    # multi-interval, thickend and thickstart at exon-exon junctions
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"),
        GenomicSegment("chrA", 2100, 2600, "+"),
        GenomicSegment("chrA", 2605, 2700, "+"),
        ID="IVC9p",
        score=500,
        color="#007ADF",
        thickstart=2100,
        thickend=2600
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"),
        GenomicSegment("chrA", 2100, 2600, "-"),
        GenomicSegment("chrA", 2605, 2700, "-"),
        ID="IVC9m",
        score=500,
        color="#007ADF",
        thickstart=2100,
        thickend=2600
    ),
    # multi-interval, thickend and thickstart at exon-exon junctions
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "+"),
        GenomicSegment("chrA", 2100, 2600, "+"),
        GenomicSegment("chrA", 2605, 2700, "+"),
        ID="IVC10p",
        score=500,
        color="#007ADF",
        thickstart=1099,
        thickend=2101
    ),
    SegmentChain(
        GenomicSegment("chrA", 100, 1100, "-"),
        GenomicSegment("chrA", 2100, 2600, "-"),
        GenomicSegment("chrA", 2605, 2700, "-"),
        ID="IVC10m",
        score=500,
        color="#007ADF",
        thickstart=1099,
        thickend=2101
    ),
]

# same data, as transcripts
_TEST_TRANSCRIPTS = [Transcript(*X.segments, **X.attr) for X in _TEST_SEGMENTCHAINS]

_BED_HEADER = """browser position chrA:100-1100
track name=test_data description='my test data'
"""

# same data, as BED12 block
_BED12_DATA = """chrA    100    1100    IVC1p    0.0    +    100    100    0,0,0    1    1000,    0,
chrA    100    1100    IVC1m    0.0    -    100    100    0,0,0    1    1000,    0,
chrA    100    2600    IVC2p    0.0    +    100    100    0,0,0    2    1000,500,    0,2000,
chrA    100    2600    IVC2m    0.0    -    100    100    0,0,0    2    1000,500,    0,2000,
chrA    100    2600    IVC3p    500.0    +    100    100    0,0,0    2    1000,500,    0,2000,
chrA    100    2600    IVC3m    500.0    -    100    100    0,0,0    2    1000,500,    0,2000,
chrA    100    2700    IVC4p    500.0    +    100    100    0,0,0    3    1000,500,95,    0,2000,2505,
chrA    100    2600    IVC4m    500.0    -    100    100    0,0,0    2    1000,500,    0,2000,
chrA    100    2700    IVC5p    500.0    +    100    100    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC5m    500.0    -    100    100    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC6p    500.0    +    100    100    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC6m    500.0    -    100    100    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC7p    500.0    +    2200    2400    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC7m    500.0    -    2200    2400    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC8p    500.0    +    100    2700    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC8m    500.0    -    100    2700    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC9p    500.0    +    2100    2600    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC9m    500.0    -    2100    2600    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC10p    500.0    +    1099    2101    0,122,223    3    1000,500,95,    0,2000,2505,
chrA    100    2700    IVC10m    500.0    -    1099    2101    0,122,223    3    1000,500,95,    0,2000,2505,""".replace(
    "    ", "\t"
)

_EXTRA_COLS = """numcol    floatcol    strcol    attrcol
0    3.14    a    gene_id "gene_0"; transcript_id "transcript_0";
1    2.72523    abc    gene_id "gene_1"; transcript_id "transcript_1";
2    30.12350    DEF    gene_id "gene_2"; transcript_id "transcript_2";
3    15123.20    ghi    gene_id "gene_3"; transcript_id "transcript_3";
4    2.0    alongword    gene_id "gene_4"; transcript_id "transcript_4";
5    -3.1234    a sentence with spaces    gene_id "gene_5"; transcript_id "transcript_5";
6    -20.5    some notes with "quotes"    gene_id "gene_6"; transcript_id "transcript_6";
7    -1e10    1    gene_id "gene_7"; transcript_id "transcript_7";
8    2e5    2    gene_id "gene_8"; transcript_id "transcript_8";
9    2.3e6    3.0    gene_id "gene_9"; transcript_id "transcript_9";
10    0.03    string1    gene_id "gene_10"; transcript_id "transcript_10";
11    1.0    string2    gene_id "gene_11"; transcript_id "transcript_11";
12    2.0    string3    gene_id "gene_12"; transcript_id "transcript_12";
13    3.0    string4 string5 string6    gene_id "gene_13"; transcript_id "transcript_13";
14    4.0    test    gene_id "gene_14"; transcript_id "transcript_14";
15    5.0    testetst    gene_id "gene_15"; transcript_id "transcript_15";
16    6.0    testsatsdfasf    gene_id "gene_16"; transcript_id "transcript_16";
17    7.0    asdgahghfzgdasdfasdf    gene_id "gene_17"; transcript_id "transcript_17";
18    8.0    asdfasdfadsfgaasdg    gene_id "gene_18"; transcript_id "transcript_18";
19    9.0    asdfasdfdasfdas    gene_id "gene_19"; transcript_id "transcript_19";
""".replace("    ", "\t")

_NARROW_PEAK_TEXT = """track type=narrowPeak
chrI    100    15000    feature1    0    +    341.2    -123.2    -513.3    50
chrII    320    15000    feature2    0    -    2.1    -5123.2    0    650""".replace("    ", "\t")

_NARROW_PEAK_CHAINS = [
    SegmentChain(
        GenomicSegment("chrI", 100, 15000, "+"),
        ID='feature1',
        signalValue=341.2,
        pValue=-123.2,
        qValue=-513.3,
        peak=50,
        _bedx_column_order=["signalValue", "pValue", "qValue", "peak"],
        thickstart=100,
        thickend=100
    ),
    SegmentChain(
        GenomicSegment("chrII", 320, 15000, "-"),
        ID='feature2',
        signalValue=2.1,
        pValue=-5123.2,
        qValue=0.0,
        peak=650,
        _bedx_column_order=["signalValue", "pValue", "qValue", "peak"],
        thickstart=320,
        thickend=320
    ),
]
