# Copyright 2008 Michiel de Hoon.
# Revisions copyright 2009 Leighton Pritchard.
# Revisions copyright 2010 Peter Cock.
# All rights reserved.
#
# This file is part of the Biopython distribution and governed by your
# choice of the "Biopython License Agreement" or the "BSD 3-Clause License".
# Please see the LICENSE file that should have been included as part of this
# package.
"""Code to parse output from the EMBOSS eprimer3 program.

As elsewhere in Biopython there are two input functions, read and parse,
for single record output and multi-record output. For primer3, a single
record object is created for each target sequence and may contain
multiple primers.

i.e. If you ran eprimer3 with a single target sequence, use the read
function. If you ran eprimer3 with multiple targets, use the parse
function to iterate over the retsults.
"""


# --- primer3


class Record(object):
    """Represent information from a primer3 run finding primers.

    Members:

        - primers  - list of Primer objects describing primer pairs for
          this target sequence.
        - comments - the comment line(s) for the record

    """

    def __init__(self):
        """Initialize the class."""
        self.comments = ""
        self.primers = []


class Primers(object):
    """A primer set designed by Primer3.

    Members:

        - size - length of product, note you can use len(primer) as an
          alternative to primer.size

        - forward_seq
        - forward_start
        - forward_length
        - forward_tm
        - forward_gc

        - reverse_seq
        - reverse_start
        - reverse_length
        - reverse_tm
        - reverse_gc

        - internal_seq
        - internal_start
        - internal_length
        - internal_tm
        - internal_gc

    """

    def __init__(self):
        """Initialize the class."""
        self.size = 0
        self.forward_seq = ""
        self.forward_start = 0
        self.forward_length = 0
        self.forward_tm = 0.0
        self.forward_gc = 0.0
        self.reverse_seq = ""
        self.reverse_start = 0
        self.reverse_length = 0
        self.reverse_tm = 0.0
        self.reverse_gc = 0.0
        self.internal_seq = ""
        self.internal_start = 0
        self.internal_length = 0
        self.internal_tm = 0.0
        self.internal_gc = 0.0

    def __len__(self):
        """Length of the primer product (i.e. product size)."""
        return self.size


def parse(handle):
    """Iterate over primer3 output as Bio.Emboss.Primer3.Record objects."""
    # Skip blank lines at head of file
    while True:
        line = handle.readline()
        if line.strip():
            break  # Starting a record

    # Read each record
    record = None
    primer = None
    while True:
        if line.startswith("# EPRIMER3") or line.startswith("# PRIMER3"):
            # Record data
            if record is not None:
                yield record
            record = Record()
            record.comments += line
            primer = None
        elif line.startswith("#"):
            if line.strip() != "#                      Start  Len   Tm     GC%   Sequence":
                record.comments += line
        elif not line.strip():
            pass
        elif line[5:19] == "PRODUCT SIZE: ":
            primer = Primers()
            primer.size = int(line[19:])
            record.primers.append(primer)
        elif line[5:19] == "FORWARD PRIMER":
            words = line.split()
            if not primer or primer.size == 0:
                primer = Primers()
                record.primers.append(primer)
            primer.forward_start = int(words[2])
            primer.forward_length = int(words[3])
            primer.forward_tm = float(words[4])
            primer.forward_gc = float(words[5])
            primer.forward_seq = words[6]
        elif line[5:19] == "REVERSE PRIMER":
            words = line.split()
            if not primer or primer.size == 0:
                primer = Primers()
                record.primers.append(primer)
            primer.reverse_start = int(words[2])
            primer.reverse_length = int(words[3])
            primer.reverse_tm = float(words[4])
            primer.reverse_gc = float(words[5])
            primer.reverse_seq = words[6]
        elif line[5:19] == "INTERNAL OLIGO":
            words = line.split()
            if not primer or primer.size == 0:
                primer = Primers()
                record.primers.append(primer)
            primer.internal_start = int(words[2])
            primer.internal_length = int(words[3])
            primer.internal_tm = float(words[4])
            primer.internal_gc = float(words[5])
            try:
                primer.internal_seq = words[6]
            except IndexError:  # eprimer3 reports oligo without sequence
                primer.internal_seq = ""
        try:
            line = next(handle)
        except StopIteration:
            break
    if record:
        yield record


def read(handle):
    """Parse primer3 output into a Bio.Emboss.Primer3.Record object.

    This is for when there is one and only one target sequence. If
    designing primers for multiple sequences, use the parse function.
    """
    iterator = parse(handle)
    try:
        first = next(iterator)
    except StopIteration:
        raise ValueError("No records found in handle")
    try:
        second = next(iterator)
    except StopIteration:
        second = None
    if second is not None:
        raise ValueError("More than one record found in handle")
    return first
