import logging
import os
import re

from multiqc.base_module import BaseMultiqcModule, ModuleNoSamplesFound
from multiqc.plots import linegraph

log = logging.getLogger(__name__)


class MultiqcModule(BaseMultiqcModule):
    def __init__(self):
        super(MultiqcModule, self).__init__(
            name="methylQA",
            anchor="methylqa",
            target="methylqa",
            href="http://methylqa.sourceforge.net/",
            info="Methylation sequencing data quality assessment tool.",
            doi="10.1016/j.ymeth.2014.10.032",
        )

        # Find and load any methylQA reports
        self.methylqa_data = dict()
        self.methylqa_coverage_counts = dict()
        self.methylqa_coverage_percentages = dict()
        for f in self.find_log_files("methylqa"):
            self.parse_methylqa_logs(f)

        # Filter to strip out ignored sample names
        self.methylqa_data = self.ignore_samples(self.methylqa_data)

        if len(self.methylqa_data) == 0:
            raise ModuleNoSamplesFound

        log.info(f"Found {len(self.methylqa_data)} reports")

        # Superfluous function call to confirm that it is used in this module
        # Replace None with actual version if it is available
        self.add_software_version(None)

        # Write parsed report data to a file
        self.write_data_file(self.methylqa_data, "multiqc_methylQA")

        # Basic Stats Table
        self.methylqa_general_stats_table()

        # Alignment bar plot - only one section, so add to the module intro
        self.add_section(plot=self.methylqa_alignment_plot())

    def parse_methylqa_logs(self, f):
        # Get s_name from first input file if possible
        s_name = f["s_name"]
        if f["f"][0].startswith("files provided"):
            s_name = self.clean_s_name(os.path.basename(f["f"][0]), f)

        parsed_data = {}
        regexes = {
            "mappable_reads": r"uniquely mappable reads \(pair\):\s*(\d+)",
            "quality_failed": r"quality failed mapped reads \(pair\) in the bismark bam:\s*(\d+)",
            "oversized_reads": r"oversized mapped reads \(pair\) in the bismark bam:\s*(\d+)",
            "mapped_bases": r"total base of uniquely mapped reads \(pair\):\s*(\d+)",
            "coverage": r"total base of uniquely mapped reads \(pair\) cover genome base \(\d+\):\s*([\d\.]+)X",
            "CHG_count_meth": r"number of methylated C in CHG context \(was protected\):\s*(\d+)",
            "CHG_count_unmeth": r"number of not methylated C in CHG context \(was converted\):\s*(\d+)",
            "CHG_percent_meth": r"C->T convertion rate in CHG context:\s*([\d\.]+)%",
            "CHH_count_meth": r"number of methylated C in CHH context \(was protected\):\s*(\d+)",
            "CHH_count_unmeth": r"number of not methylated C in CHH context \(was converted\):\s*(\d+)",
            "CHH_percent_meth": r"C->T convertion rate in CHH context:\s*([\d\.]+)%",
            "CG_count_meth": r"number of methylated C in CpG context \(was protected\):\s*(\d+)",
            "CG_count_unmeth": r"number of not methylated C in CpG context \(was converted\):\s*(\d+)",
            "CG_percent_meth": r"C->T convertion rate in CpG context:\s*([\d\.]+)%",
            "CN_count_meth": r"number of methylated C in Unknown context \(was protected\):\s*(\d+)",
            "CN_count_unmeth": r"number of not methylated C in Unknown context \(was converted\):\s*(\d+)",
            "CN_percent_meth": r"C->T convertion rate in Unknown context:\s*([\d\.]+)%",
        }
        for k, r in regexes.items():
            match = re.search(r, f["f"])
            if match:
                parsed_data[k] = float(match.group(1))

        # Parse the coverage histogram
        hist = False
        for line in f["f"].split(os.linesep):
            if hist is not False:
                try:
                    s = line.split()
                    hist["counts"][s[0]] = int(s[1])
                    hist["percentages"][s[0]] = float(s[2])
                except IndexError:
                    break
            if re.search(r"Times covered\s+Count\s+Percent\s+", line):
                hist = {"counts": dict(), "percentages": dict()}
        if hist is not False and len(hist) > 0:
            self.methylqa_coverage_counts[s_name] = hist["counts"]
            self.methylqa_coverage_percentages[s_name] = hist["percentages"]

        if len(parsed_data) > 0:
            if s_name in self.methylqa_data:
                log.debug(f"Duplicate sample name found! Overwriting: {s_name}")
            self.add_data_source(f, s_name)
            self.methylqa_data[s_name] = parsed_data

    def methylqa_general_stats_table(self):
        """Take the parsed stats from the methylQA report and add it to the
        basic stats table at the top of the report"""

        headers = {"coverage": {"title": "Fold Coverage", "min": 0, "suffix": "X", "scale": "YlGn"}}
        self.general_stats_addcols(self.methylqa_data, headers)

    def methylqa_alignment_plot(self):
        if len(self.methylqa_coverage_counts) == 0:
            return '<div class="alert alert-danger">No histogram data found.</div>'

        pconfig = {
            "id": "methylqa_coverage",
            "title": "methylQA: CpG Coverage",
            "categories": True,
            "ylab": "CpG Counts",
            "xlab": "Times covered",
            "ymin": 0,
            "data_labels": [
                {"name": "Counts", "ylab": "CpG Counts"},
                {"name": "Percentages", "ylab": "% CpGs", "ymax": 100},
            ],
        }
        return linegraph.plot([self.methylqa_coverage_counts, self.methylqa_coverage_percentages], pconfig)
