/* The MIT License

   Copyright (c) by Attractive Chaos <attractor@live.co.uk>

   Permission is hereby granted, free of charge, to any person obtaining
   a copy of this software and associated documentation files (the
   "Software"), to deal in the Software without restriction, including
   without limitation the rights to use, copy, modify, merge, publish,
   distribute, sublicense, and/or sell copies of the Software, and to
   permit persons to whom the Software is furnished to do so, subject to
   the following conditions:

   The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   SOFTWARE.
*/
#ifndef __AC_KSW_H
#define __AC_KSW_H

#include <stdint.h>

/* Per-tier symbol mangling: must be included before any function declarations
 * so that kernel TU compiles (with KERNEL_VARIANT=_avx2 etc.) see the rename
 * macros and emit per-tier mangled names instead of the unmangled originals. */
#include "kernel_dispatch.h"

/* SIMD compatibility for ARM/x86 */
#if defined(__ARM_NEON) || defined(__aarch64__) || defined(APPLE_SILICON)
    #include "simd_compat.h"
#else
    #include <emmintrin.h>
#endif

#define KSW_XBYTE  0x10000
#define KSW_XSTOP  0x20000
#define KSW_XSUBO  0x40000
#define KSW_XSTART 0x80000

//struct _kswq_t;
struct _kswq_t {
	int qlen, slen;
	uint8_t shift, mdiff, max, size;
	__m128i *qp, *H0, *H1, *E, *Hmax;
};

typedef struct _kswq_t kswq_t;

typedef struct {
	int score; // best score
	int te, qe; // target end and query end
	int score2, te2; // second best score and ending position on the target
	int tb, qb; // target start and query start
} kswr_t;

const kswr_t g_defr = { 0, -1, -1, -1, -1, -1, -1 };

#ifdef __cplusplus
extern "C" {
#endif

	/**
	 * Aligning two sequences
	 *
	 * @param qlen    length of the query sequence (typically <tlen)
	 * @param query   query sequence with 0 <= query[i] < m
	 * @param tlen    length of the target sequence
	 * @param target  target sequence
	 * @param m       number of residue types
	 * @param mat     m*m scoring matrix in one-dimension array
	 * @param gapo    gap open penalty; a gap of length l cost "-(gapo+l*gape)"
	 * @param gape    gap extension penalty
	 * @param xtra    extra information (see below)
	 * @param qry     query profile (see below)
	 *
	 * @return        alignment information in a struct; unset values to -1
	 *
	 * When xtra==0, ksw_align() uses a signed two-byte integer to store a
	 * score and only finds the best score and the end positions. The 2nd best
	 * score or the start positions are not attempted. The default behavior can
	 * be tuned by setting KSW_X* flags:
	 *
	 *   KSW_XBYTE:  use an unsigned byte to store a score. If overflow occurs,
	 *               kswr_t::score will be set to 255
	 *
	 *   KSW_XSUBO:  track the 2nd best score and the ending position on the
	 *               target if the 2nd best is higher than (xtra&0xffff)
	 *
	 *   KSW_XSTOP:  stop if the maximum score is above (xtra&0xffff)
	 *
	 *   KSW_XSTART: find the start positions
	 *
	 * When *qry==NULL, ksw_align() will compute and allocate the query profile
	 * and when the function returns, *qry will point to the profile, which can
	 * be deallocated simply by free(). If one query is aligned against multiple
	 * target sequences, *qry should be set to NULL during the first call and
	 * freed after the last call. Note that qry can equal 0. In this case, the
	 * query profile will be deallocated in ksw_align().
	 */
	kswr_t ksw_align(int qlen, uint8_t *query, int tlen, uint8_t *target, int m, const int8_t *mat, int gapo, int gape, int xtra, kswq_t **qry);
	kswr_t ksw_align2(int qlen, uint8_t *query, int tlen, uint8_t *target, int m, const int8_t *mat, int o_del, int e_del, int o_ins, int e_ins, int xtra, kswq_t **qry);

	/**
	 * Banded global alignment
	 *
	 * @param qlen    query length
	 * @param query   query sequence with 0 <= query[i] < m
	 * @param tlen    target length
	 * @param target  target sequence with 0 <= target[i] < m
	 * @param m       number of residue types
	 * @param mat     m*m scoring mattrix in one-dimension array
	 * @param gapo    gap open penalty; a gap of length l cost "-(gapo+l*gape)"
	 * @param gape    gap extension penalty
	 * @param w       band width
	 * @param n_cigar (out) number of CIGAR elements
	 * @param cigar   (out) BAM-encoded CIGAR; caller need to deallocate with free()
	 *
	 * @return        score of the alignment
	 */
	int ksw_global(int qlen, const uint8_t *query, int tlen, const uint8_t *target, int m, const int8_t *mat, int gapo, int gape, int w, int *n_cigar, uint32_t **cigar);
	int ksw_global2(int qlen, const uint8_t *query, int tlen, const uint8_t *target, int m, const int8_t *mat, int o_del, int e_del, int o_ins, int e_ins, int w, int *n_cigar, uint32_t **cigar);

	/**
	 * Extend alignment
	 *
	 * The routine aligns $query and $target, assuming their upstream sequences,
	 * which are not provided, have been aligned with score $h0. In return,
	 * region [0,*qle) on the query and [0,*tle) on the target sequences are
	 * aligned together. If *gscore>=0, *gscore keeps the best score such that
	 * the entire query sequence is aligned; *gtle keeps the position on the
	 * target where *gscore is achieved. Returning *gscore and *gtle helps the
	 * caller to decide whether an end-to-end hit or a partial hit is preferred.
	 *
	 * The first 9 parameters are identical to those in ksw_global()
	 *
	 * @param h0      alignment score of upstream sequences
	 * @param _qle    (out) length of the query in the alignment
	 * @param _tle    (out) length of the target in the alignment
	 * @param _gtle   (out) length of the target if query is fully aligned
	 * @param _gscore (out) score of the best end-to-end alignment; negative if not found
	 *
	 * @return        best semi-local alignment score
	 */
	int ksw_extend(int qlen, const uint8_t *query, int tlen, const uint8_t *target, int m, const int8_t *mat, int gapo, int gape, int w, int end_bonus, int zdrop, int h0, int *qle, int *tle, int *gtle, int *gscore, int *max_off);
	int ksw_extend2(int qlen, const uint8_t *query, int tlen, const uint8_t *target, int m, const int8_t *mat, int o_del, int e_del, int o_ins, int e_ins, int w, int end_bonus, int zdrop, int h0, int *qle, int *tle, int *gtle, int *gscore, int *max_off);

#ifdef __cplusplus
}
#endif

#endif
