#!/usr/bin/env python
# Copyright 2004 by Michael Hoffman. 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.
"""Run and process output from the Wise2 package tool psw.

Bio.Wise contains modules for running and processing the output of
some of the models in the Wise2 package by Ewan Birney available from:
ftp://ftp.ebi.ac.uk/pub/software/unix/wise2/
http://www.ebi.ac.uk/Wise2/

Bio.Wise.psw is for protein Smith-Waterman alignments
Bio.Wise.dnal is for Smith-Waterman DNA alignments
"""


import os
import re
import sys

from Bio import Wise


_CMDLINE_PSW = ["psw", "-l", "-F"]
_OPTION_GAP_START = "-g"
_OPTION_GAP_EXTENSION = "-e"
_OPTION_SCORES = "-m"


class AlignmentColumnFullException(Exception):
    """Manage exception in the alignment output."""

    pass


class Alignment(list):
    """Define a container for all alignment Columns, output from running psw."""

    def append(self, column_unit):
        """Add an alignment Column to Alignment."""
        try:
            self[-1].append(column_unit)
        except AlignmentColumnFullException:
            list.append(self, AlignmentColumn(column_unit))
        except IndexError:
            list.append(self, AlignmentColumn(column_unit))


class AlignmentColumn(list):
    """Define a container for the units that made the Column."""

    def _set_kind(self, column_unit):
        if self.kind == "SEQUENCE":
            self.kind = column_unit.kind

    def __init__(self, column_unit):
        """Initialize the class."""
        assert column_unit.unit == 0
        self.kind = column_unit.kind
        list.__init__(self, [column_unit.column, None])

    def __repr__(self):
        """Represent the AlignmentColumn object as a string for debugging."""
        return "%s(%r, %r)" % (self.kind, self[0], self[1])

    def append(self, column_unit):
        """Add a unit to the Column."""
        if self[1] is not None:
            raise AlignmentColumnFullException

        assert column_unit.unit == 1

        self._set_kind(column_unit)
        self[1] = column_unit.column


class ColumnUnit:
    """Define a container for the details of each sequence alignment."""

    def __init__(self, unit, column, kind):
        """Initialize the class."""
        self.unit = unit
        self.column = column
        self.kind = kind

    def __repr__(self):
        """Represent the ColumnUnit object as a string for debugging."""
        return "ColumnUnit(unit=%r, column=%r, kind=%r)" % (
            self.unit,
            self.column,
            self.kind,
        )


_re_unit = re.compile(r"^Unit +([01])- \[ *(-?\d+)- *(-?\d+)\] \[(\w+)\]$")


def parse_line(line):
    """Parse a line from psw.

    >>> print(parse_line("Column 0:"))
    None
    >>> parse_line("Unit  0- [  -1-   0] [SEQUENCE]")
    ColumnUnit(unit=0, column=0, kind='SEQUENCE')
    >>> parse_line("Unit  1- [  85-  86] [SEQUENCE]")
    ColumnUnit(unit=1, column=86, kind='SEQUENCE')
    """
    match = _re_unit.match(line.rstrip())

    if not match:
        return

    return ColumnUnit(int(match.group(1)), int(match.group(3)), match.group(4))


def parse(iterable):
    """Parse a file.

    format

    Column 0:
    Unit  0- [  -1-   0] [SEQUENCE]
    Unit  1- [  85-  86] [SEQUENCE]

    means that seq1[0] == seq2[86] (0-based)
    """
    alignment = Alignment()
    for line in iterable:
        try:
            if os.environ["WISE_PY_DEBUG"]:
                print(line)
        except KeyError:
            pass

        column_unit = parse_line(line)
        if column_unit:
            alignment.append(column_unit)

    return alignment


def align(pair, scores=None, gap_start=None, gap_extension=None, *args, **keywds):
    """Align a pair of DNA files using Wise2 psw."""
    cmdline = _CMDLINE_PSW[:]
    if scores:
        cmdline.extend((_OPTION_SCORES, scores))
    if gap_start:
        cmdline.extend((_OPTION_GAP_START, str(gap_start)))
    if gap_extension:
        cmdline.extend((_OPTION_GAP_EXTENSION, str(gap_extension)))
    temp_file = Wise.align(cmdline, pair, *args, **keywds)
    return parse(temp_file)


def main():
    """Command line implementation."""
    print(align(sys.argv[1:3]))


def _test(*args, **keywds):
    import doctest

    doctest.testmod(sys.modules[__name__], *args, **keywds)


if __name__ == "__main__":
    if __debug__:
        _test()
        """Initialize the class."""
    main()
