//Copyright 2007 Asim Siddiqui
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
// file: SsrfUtil.h
// Purpose: Header file for container writer class
// Author: Asim Siddiqui
// Update History
// 18th March 2007  Asim Siddiqui        created
// Some code contributed by James Bonfield and under a different OS license
//
#include <ios>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <io_lib/Read.h>
#include <io_lib/misc.h>
#include <io_lib/hash_table.h>
#include <io_lib/ztr.h>
#include <zlib.h>

#include <SRF_File.hh>
#include <SRF_ReadSet.hh>
#include <SRF_Container.hh>

#include <SRF_util.hh>

using namespace std;
/* Comment this out if number of cycles != 36 */
#define MAX_CYCLES 100

// creates a pascal style string. The first char is the length of the string
ADOPT char*
SRF_cStrToPascalStr( const char* str )
{
    int len;
    unsigned char lengthStr;
    char* newStr;
    char* strPtr;

    len = strlen( str );
    if ( len > 255 )
    {
        printf("ERROR: String too long\n");
        exit(1);
    }

    lengthStr = (unsigned char) len;

    newStr = new char[ len + 2 ];

    strPtr = newStr;
    *strPtr = lengthStr;
    strPtr++;
    strcpy( strPtr, str );

    return newStr;
}

std::string
SRF_readString( std::fstream& input )
{
    int len;
    char lenStr;    

    input.read (&lenStr, 1);

    len = (int) lenStr;

//    std::cout << len <<std::endl;

    char* str = new char[len + 1];
    str[len] = '\0';

    input.read( str, len );

    std::string readString( str );
    delete [] str;

    return readString;
}

void
SRF_ReportError( const std::string& errorMesg )
{
    std::cout << errorMesg << std::endl;
}
//
/* #define USE_HASH_HEADER */



/*
 * ======================================================================
 * This software has been created by Genome Research Limited (GRL).
 *
 * GRL hereby grants permission to use, copy, modify and distribute
 * this software and its documentation for non-commercial purposes
 * without fee at the user's own risk on the basis set out below.
 *
 * GRL neither undertakes nor accepts any duty whether contractual or
 * otherwise in connection with the software, its use or the use of
 * any derivative, and makes no representations or warranties, express
 * or implied, concerning the software, its suitability, fitness for
 * a particular purpose or non-infringement.
 *
 * In no event shall the authors of the software or GRL be responsible
 * or liable for any loss or damage whatsoever arising in any way
 * directly or indirectly out of the use of this software or its
 * derivatives, even if advised of the possibility of such damage.
 *
 * Our software can be freely distributed under the conditions set out
 * above, and must contain this copyright notice.
 * ======================================================================
 */

/*
 * Author: James Bonfield, March 2006
 *
 * This code converts _sig, _prb and _seq files to ZTR files.
 */
#ifdef USE_HASH_HEADER
/*
 * The following define a constant portion of a ZTR file that does not differ
 * from one entry to the next.
 *
 * It consists of hard coded entries for the ZTR magic number, a BPOS chunk
 * for 35 basecalls (1 .. 35), a CLIP chunk and the first 14 bytes of
 * the SMP4 chunk.
 * The rest of the ztr file differs so we output one ZTR_START only and
 * follow this up by the remaining different ztr bits for each file.
 */
#define ZTR_START "\256ZTR\r\n\032\n"                  /* magic number */    \
                  "\001\002"                           /* version */         \
                  "BPOS\000\000\000\000\000\000\000\020"                     \
                  "\001\052\000\000\000\115\107\200\102\001"                 \
		  "\115\004\000\115\042\001"           /* 35bp BPOS */       \
                  "CLIP\000\000\000\000\000\000\000\011"                     \
                  "\000\000\000\000\001\000\000\000\043" /* CLIP 1,35 */     \
                  "SMP4\000\000\000\000\000\000\001\032\000\000" /* SMP4...*/
#endif

/*
 * Outputs a Read struct in ZTR format.
 * This is basically a rewrite of "fwrite_reading(outfp, r, TT_ZTR1)" but
 * with our own crafted compression functions for optimal ZTR size
 */
ADOPT ztr_t*
SRF_output_ztr( Read *r) {
      return read2ztr( r );
}

/*
 * Fetches a sequence from a *_*_*_seq.txt file. The format is lines of:
 *
 * 3       56      5       818     TACACACAGAGTGAAAGAAAATCT.T.ATCTACACA
 *
 * We don't care about any field here except the last; the sequence.
 * 'trim' designates how many bases to clip off from the left end. Eg in the
 * above the first "T" is present on all sequences and is not part of the
 * result returned.
 *
 * Returns: Seq on success (static, non-reentrant)
 *          NULL on fialure
 */
char *SRF_get_seq(FILE *fp, int trim) {
    static char line[1024];
    char *cp;

    if (NULL == fgets(line, 1023, fp))
	return NULL;

    if (cp = strchr(line, '\n'))
	*cp = 0;
    cp = strrchr(line, '\t');
    return cp ? cp + 1 + trim : NULL;
}

/*
 * Fetches the probability arrays as an Nx4 array from the *_prb.txt file.
 * The format of the lines is a series of 4-tuples (4 integer numbers,
 * themselves separated by spaced) which are separated by tabs.
 *
 * The data returned is statically allocated, so it should not be freed and
 * the function is not rentrant.
 *
 * Returns point to Nx4 array of ints holding the log-odds scores.
 *         NULL on failure
 */
int (*SRF_get_prb(FILE *fp, int trim))[4] {
    char line[MAX_CYCLES*20 +1];
    static int prb[MAX_CYCLES][4];
    char *cp;
    int c; /* cycle */

    if (NULL == fgets(line, MAX_CYCLES*20, fp))
	return NULL;

    for (c = -trim, cp = strtok(line, "\t"); cp;
	 cp = strtok(NULL, "\t"), c++) {
	if (c < 0)
	    continue;
	if (4 != sscanf(cp, "%d %d %d %d",
			&prb[c][0], &prb[c][1], &prb[c][2], &prb[c][3]))
	    return NULL;
    }
    
    return prb;
}

/*
 * Fetches the signal strength arrays as an Nx4 array from the *_sig.txt file.
 * The format of the lines is a series of 4-tuples (4 floating point numbers,
 * themselves separated by spaced) which are separated by tabs.
 *
 * The data returned is statically allocated, so it should not be freed and
 * the function is not rentrant.
 *
 * Returns point to Nx4 array of ints holding the signal strengths.
 *         NULL on failure
 */
float (*SRF_get_sig(FILE *fp, int trim))[4] {
    char line[MAX_CYCLES*30 +1];
    static float sig[MAX_CYCLES][4];
    char *cp;
    int c, i, j; /* cycle */

    if (NULL == fgets(line, MAX_CYCLES*30, fp))
	return NULL;

    for (c = -4-trim, cp = strtok(line, "\t"); cp;
	 cp = strtok(NULL, "\t"), c++) {
	if (c < 0)
	    continue;

	if (4 != sscanf(cp, "%f %f %f %f",
			&sig[c][0], &sig[c][1], &sig[c][2], &sig[c][3]))
	    return NULL;
    }

    /*
     * Remove negative values.
     * Should we normalise this by moving up and setting a baseline
     * instead? Or maybe we need to bite the bullet and allow for -ve
     * values in ztr files.
     */
    for (i = 0; i < c; i++) {
	for (j = 0; j < 4; j++) {
	    if (sig[i][j] < 0)
		sig[i][j] = 0;
	}
    }
    
    return sig;
}

/*
 * Creates an io_lib Read object from sequence, probabilities and signal
 * strengths.
 *
 * Returns: allocated Read on success
 *	    NULL on failure
 */
// Read *create_read(const char *seq, int (*prb)[4], float (*sig)[4]) {
Read *SRF_create_read(const char *seq, std::vector<SRF_BasePrb> prbs )
{
    size_t nbases = strlen(seq), i;
    Read *r;
    int max = 0;
    
    if (NULL == (r = read_allocate(nbases, nbases)))
	return NULL;

    for (i = 0; i < nbases; i++) {
	/* Confidence values */
//#if 0
//	/* FIXME: for now we don't have -ve confidences */
//	r->prob_A[i] = MAX(prb[i][0], 0);
//	r->prob_C[i] = MAX(prb[i][1], 0);
//	r->prob_G[i] = MAX(prb[i][2], 0);
//	r->prob_T[i] = MAX(prb[i][3], 0);
//#else
        if ( prbs.size() > 0 )
        {
	    r->prob_A[i] = prbs[i].a_prb;
    	    r->prob_C[i] = prbs[i].c_prb;
	    r->prob_G[i] = prbs[i].g_prb;
	    r->prob_T[i] = prbs[i].t_prb;
        }
//#endif

//	/* Traces */
//	if ((r->traceA[i] = sig[i][0]) > max) max = sig[i][0];
//	if ((r->traceC[i] = sig[i][1]) > max) max = sig[i][1];
//	if ((r->traceG[i] = sig[i][2]) > max) max = sig[i][2];
//	if ((r->traceT[i] = sig[i][3]) > max) max = sig[i][3];
	
	/* Sequence & position */
	r->base[i] = seq[i];
	r->basePos[i] = i;
    }

    r->maxTraceVal = max;
    r->leftCutoff = 0;
    r->rightCutoff = nbases+1;

    /* Info: none for present */

    return r;
}
