// This file is automatically generated. Do not edit - make changes to relevant got file.

// Copyright ©2011-2012 The bíogo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package align

import (
	"github.com/biogo/biogo/alphabet"
	"github.com/biogo/biogo/feat"

	"fmt"
	"os"
	"text/tabwriter"
)

//line sw_affine_type.got:17
func drawSWAffineTableQLetters(rSeq, qSeq alphabet.QLetters, index alphabet.Index, table [][3]int, a SWAffine) {
	tw := tabwriter.NewWriter(os.Stdout, 0, 0, 0, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Printf("rSeq: %s\n", rSeq)
	fmt.Printf("qSeq: %s\n", qSeq)
	for l := 0; l < 3; l++ {
		fmt.Fprintf(tw, "%c\tqSeq\t", "MUL"[l])
		for _, l := range qSeq {
			fmt.Fprintf(tw, "%c\t", l)
		}
		fmt.Fprintln(tw)

		r, c := rSeq.Len()+1, qSeq.Len()+1
		fmt.Fprint(tw, "rSeq\t")
		for i := 0; i < r; i++ {
			if i != 0 {
				fmt.Fprintf(tw, "%c\t", rSeq[i-1].L)
			}

			for j := 0; j < c; j++ {
				p := pointerSWAffineQLetters(rSeq, qSeq, i, j, l, table, index, a, c)
				var vi interface{}
				if vi = table[i*c+j][l]; vi == minInt {
					vi = "-Inf"
				}
				fmt.Fprintf(tw, "%s % 4v\t", p, vi)
			}
			fmt.Fprintln(tw)
		}
		fmt.Fprintln(tw)
	}
	tw.Flush()
}

func pointerSWAffineQLetters(rSeq, qSeq alphabet.QLetters, i, j, l int, table [][3]int, index alphabet.Index, a SWAffine, c int) string {
	if i == 0 || j == 0 {
		return "   "
	}
	rVal := index[rSeq[i-1].L]
	qVal := index[qSeq[j-1].L]
	if rVal < 0 || qVal < 0 {
		return "   "
	}
	switch p := i*c + j; {
	case table[p][l] == 0:
		return "   "
	case table[p-c][up]+a.Matrix[rVal][gap] == table[p][l] && table[(i-1)*c+j-1][l] != 0:
		return "⬆ u"
	case table[p-1][left]+a.Matrix[gap][qVal] == table[p][l] && table[(i-1)*c+j-1][l] != 0:
		return "⬅ l"

	case table[p-c][diag]+a.GapOpen+a.Matrix[rVal][gap] == table[p][l] && table[(i-1)*c+j-1][l] != 0:
		return "⬆ m"
	case table[p-1][diag]+a.GapOpen+a.Matrix[gap][qVal] == table[p][l] && table[(i-1)*c+j-1][l] != 0:
		return "⬅ m"

	case table[p-c-1][diag]+a.Matrix[rVal][qVal] == table[p][l] && table[(i-1)*c+j-1][l] != 0:
		return "⬉ m"
	case table[p-c-1][up]+a.Matrix[rVal][qVal] == table[p][l] && table[(i-1)*c+j-1][l] != 0:
		return "⬉ u"
	case table[p-c-1][left]+a.Matrix[rVal][qVal] == table[p][l] && table[(i-1)*c+j-1][l] != 0:
		return "⬉ l"
	default:
		return [3]string{"⌜  ", "⬆ u", "⬅ l"}[l]
	}
}

func (a SWAffine) alignQLetters(rSeq, qSeq alphabet.QLetters, alpha alphabet.Alphabet) ([]feat.Pair, error) {
	let := len(a.Matrix)
	if let < alpha.Len() {
		return nil, ErrMatrixWrongSize{Size: let, Len: alpha.Len()}
	}
	la := make([]int, 0, let*let)
	for _, row := range a.Matrix {
		if len(row) != let {
			return nil, ErrMatrixNotSquare
		}
		la = append(la, row...)
	}
	r, c := rSeq.Len()+1, qSeq.Len()+1
	table := make([][3]int, r*c)

	var (
		index = alpha.LetterIndex()

		maxS, maxI, maxJ = 0, 0, 0

		score int
	)

	for i := 1; i < r; i++ {
		for j := 1; j < c; j++ {
			var (
				rVal = index[rSeq[i-1].L]
				qVal = index[qSeq[j-1].L]
			)
			if rVal < 0 {
				return nil, fmt.Errorf("align: illegal letter %q at position %d in rSeq", rSeq[i-1].L, i-1)
			}
			if qVal < 0 {
				return nil, fmt.Errorf("align: illegal letter %q at position %d in qSeq", qSeq[j-1].L, j-1)
			}
			p := i*c + j

			diagScore := table[p-c-1][diag]
			upScore := table[p-c-1][up]
			leftScore := table[p-c-1][left]

			score = max3(diagScore, upScore, leftScore)
			matched := score == diagScore
			score += la[rVal*let+qVal]
			switch {
			case score > 0:
				if score >= maxS && matched {
					maxS, maxI, maxJ = score, i, j
				}
			default:
				score = 0
			}
			table[p][diag] = score

			score = max2(
				table[p-c][diag]+a.GapOpen+la[rVal*let],
				table[p-c][up]+la[rVal*let],
			)
			if score < 0 {
				score = 0
			}
			table[p][up] = score

			score = max2(
				table[p-1][diag]+a.GapOpen+la[qVal],
				table[p-1][left]+la[qVal],
			)
			if score < 0 {
				score = 0
			}
			table[p][left] = score
		}
	}
	if debugSmithAffine {
		drawSWAffineTableQLetters(rSeq, qSeq, index, table, a)
	}

	var aln []feat.Pair
	score, last, layer := 0, diag, diag
	i, j := maxI, maxJ
loop:
	for i > 0 && j > 0 {
		var (
			rVal = index[rSeq[i-1].L]
			qVal = index[qSeq[j-1].L]
		)
		switch p := i*c + j; table[p][layer] {
		case 0:
			break loop
		case table[p-c][up] + la[rVal*let]:
			if last != up && p != len(table)-1 {
				aln = append(aln, &featPair{
					a:     feature{start: i, end: maxI},
					b:     feature{start: j, end: maxJ},
					score: score,
				})
				maxI, maxJ = i, j
				score = 0
			}
			score += table[p][layer] - table[p-c][up]
			i--
			layer = up
			last = up
		case table[p-1][left] + la[qVal]:
			if last != left && p != len(table)-1 {
				aln = append(aln, &featPair{
					a:     feature{start: i, end: maxI},
					b:     feature{start: j, end: maxJ},
					score: score,
				})
				maxI, maxJ = i, j
				score = 0
			}
			score += table[p][layer] - table[p-1][left]
			j--
			layer = left
			last = left
		case table[p-c][diag] + a.GapOpen + la[rVal*let]:
			if last != up && p != len(table)-1 {
				aln = append(aln, &featPair{
					a:     feature{start: i, end: maxI},
					b:     feature{start: j, end: maxJ},
					score: score,
				})
				maxI, maxJ = i, j
				score = 0
			}
			score += table[p][layer] - table[p-c][diag]
			i--
			layer = diag
			last = up
		case table[p-1][diag] + a.GapOpen + la[qVal]:
			if last != left && p != len(table)-1 {
				aln = append(aln, &featPair{
					a:     feature{start: i, end: maxI},
					b:     feature{start: j, end: maxJ},
					score: score,
				})
				maxI, maxJ = i, j
				score = 0
			}
			score += table[p][layer] - table[p-1][diag]
			j--
			layer = diag
			last = left
		case table[p-c-1][diag] + la[rVal*let+qVal]:
			if last != diag {
				aln = append(aln, &featPair{
					a:     feature{start: i, end: maxI},
					b:     feature{start: j, end: maxJ},
					score: score,
				})
				maxI, maxJ = i, j
				score = 0
			}
			score += table[p][layer] - table[p-c-1][diag]
			i--
			j--
			layer = diag
			last = diag
		case table[p-c-1][up] + la[rVal*let+qVal]:
			if last != diag {
				aln = append(aln, &featPair{
					a:     feature{start: i, end: maxI},
					b:     feature{start: j, end: maxJ},
					score: score,
				})
				maxI, maxJ = i, j
				score = 0
			}
			score += table[p][layer] - table[p-c-1][up]
			i--
			j--
			layer = up
			last = diag
		case table[p-c-1][left] + la[rVal*let+qVal]:
			if last != diag {
				aln = append(aln, &featPair{
					a:     feature{start: i, end: maxI},
					b:     feature{start: j, end: maxJ},
					score: score,
				})
				maxI, maxJ = i, j
				score = 0
			}
			score += table[p][layer] - table[p-c-1][left]
			i--
			j--
			layer = left
			last = diag

		default:
			panic(fmt.Sprintf("align: sw affine internal error: no path at row: %d col:%d layer:%s\n", i, j, "mul"[layer:layer+1]))
		}
	}

	aln = append(aln, &featPair{
		a:     feature{start: i, end: maxI},
		b:     feature{start: j, end: maxJ},
		score: score,
	})

	for i, j := 0, len(aln)-1; i < j; i, j = i+1, j-1 {
		aln[i], aln[j] = aln[j], aln[i]
	}

	return aln, nil
}
