/* encodePeak.c was originally generated by the autoSql program, which also 
 * generated encodePeak.h and encodePeak.sql.  This module links the database and
 * the RAM representation of objects. */

/* Copyright (C) 2011 The Regents of the University of California 
 * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */

#include "common.h"
#include "linefile.h"
#include "dystring.h"
#include "jksql.h"
#include "hdb.h"
#include "encode/encodePeak.h"


struct encodePeak *encodePeakLoad(char **row)
/* Load a encodePeak from row fetched with select * from encodePeak
 * from database.  Dispose of this with encodePeakFree(). */
{
struct encodePeak *ret;

AllocVar(ret);
ret->blockCount = sqlUnsigned(row[10]);
ret->chrom = cloneString(row[0]);
ret->chromStart = sqlUnsigned(row[1]);
ret->chromEnd = sqlUnsigned(row[2]);
ret->name = cloneString(row[3]);
ret->score = sqlUnsigned(row[4]);
safecpy(ret->strand, sizeof(ret->strand), row[5]);
ret->signalValue = sqlFloat(row[6]);
ret->pValue = sqlFloat(row[7]);
ret->qValue = sqlFloat(row[8]);
ret->peak = sqlSigned(row[9]);
{
int sizeOne;
sqlUnsignedDynamicArray(row[11], &ret->blockSizes, &sizeOne);
assert(sizeOne == ret->blockCount);
}
{
int sizeOne;
sqlUnsignedDynamicArray(row[12], &ret->blockStarts, &sizeOne);
assert(sizeOne == ret->blockCount);
}
return ret;
}

struct encodePeak *encodePeakLoadAll(char *fileName) 
/* Load all encodePeak from a whitespace-separated file.
 * Dispose of this with encodePeakFreeList(). */
{
struct encodePeak *list = NULL, *el;
struct lineFile *lf = lineFileOpen(fileName, TRUE);
char *row[13];

while (lineFileRow(lf, row))
    {
    el = encodePeakLoad(row);
    slAddHead(&list, el);
    }
lineFileClose(&lf);
slReverse(&list);
return list;
}

struct encodePeak *encodePeakLoadAllByChar(char *fileName, char chopper) 
/* Load all encodePeak from a chopper separated file.
 * Dispose of this with encodePeakFreeList(). */
{
struct encodePeak *list = NULL, *el;
struct lineFile *lf = lineFileOpen(fileName, TRUE);
char *row[13];

while (lineFileNextCharRow(lf, chopper, row, ArraySize(row)))
    {
    el = encodePeakLoad(row);
    slAddHead(&list, el);
    }
lineFileClose(&lf);
slReverse(&list);
return list;
}

struct encodePeak *encodePeakCommaIn(char **pS, struct encodePeak *ret)
/* Create a encodePeak out of a comma separated string. 
 * This will fill in ret if non-null, otherwise will
 * return a new encodePeak */
{
char *s = *pS;

if (ret == NULL)
    AllocVar(ret);
ret->chrom = sqlStringComma(&s);
ret->chromStart = sqlUnsignedComma(&s);
ret->chromEnd = sqlUnsignedComma(&s);
ret->name = sqlStringComma(&s);
ret->score = sqlUnsignedComma(&s);
sqlFixedStringComma(&s, ret->strand, sizeof(ret->strand));
ret->signalValue = sqlFloatComma(&s);
ret->pValue = sqlFloatComma(&s);
ret->qValue = sqlFloatComma(&s);
ret->peak = sqlSignedComma(&s);
ret->blockCount = sqlUnsignedComma(&s);
{
int i;
s = sqlEatChar(s, '{');
AllocArray(ret->blockSizes, ret->blockCount);
for (i=0; i<ret->blockCount; ++i)
    {
    ret->blockSizes[i] = sqlUnsignedComma(&s);
    }
s = sqlEatChar(s, '}');
s = sqlEatChar(s, ',');
}
{
int i;
s = sqlEatChar(s, '{');
AllocArray(ret->blockStarts, ret->blockCount);
for (i=0; i<ret->blockCount; ++i)
    {
    ret->blockStarts[i] = sqlUnsignedComma(&s);
    }
s = sqlEatChar(s, '}');
s = sqlEatChar(s, ',');
}
*pS = s;
return ret;
}

void encodePeakFree(struct encodePeak **pEl)
/* Free a single dynamically allocated encodePeak such as created
 * with encodePeakLoad(). */
{
struct encodePeak *el;

if ((el = *pEl) == NULL) return;
freeMem(el->chrom);
freeMem(el->name);
freeMem(el->blockSizes);
freeMem(el->blockStarts);
freez(pEl);
}

void encodePeakFreeList(struct encodePeak **pList)
/* Free a list of dynamically allocated encodePeak's */
{
struct encodePeak *el, *next;

for (el = *pList; el != NULL; el = next)
    {
    next = el->next;
    encodePeakFree(&el);
    }
*pList = NULL;
}

void encodePeakOutput(struct encodePeak *el, FILE *f, char sep, char lastSep) 
/* Print out encodePeak.  Separate fields with sep. Follow last field with lastSep. */
{
if (sep == ',') fputc('"',f);
fprintf(f, "%s", el->chrom);
if (sep == ',') fputc('"',f);
fputc(sep,f);
fprintf(f, "%u", el->chromStart);
fputc(sep,f);
fprintf(f, "%u", el->chromEnd);
fputc(sep,f);
if (sep == ',') fputc('"',f);
fprintf(f, "%s", el->name);
if (sep == ',') fputc('"',f);
fputc(sep,f);
fprintf(f, "%u", el->score);
fputc(sep,f);
if (sep == ',') fputc('"',f);
fprintf(f, "%s", el->strand);
if (sep == ',') fputc('"',f);
fputc(sep,f);
fprintf(f, "%g", el->signalValue);
fputc(sep,f);
fprintf(f, "%g", el->pValue);
fputc(sep,f);
fprintf(f, "%g", el->qValue);
fputc(sep,f);
fprintf(f, "%d", el->peak);
fputc(sep,f);
fprintf(f, "%u", el->blockCount);
fputc(sep,f);
{
int i;
if (sep == ',') fputc('{',f);
for (i=0; i<el->blockCount; ++i)
    {
    fprintf(f, "%u", el->blockSizes[i]);
    fputc(',', f);
    }
if (sep == ',') fputc('}',f);
}
fputc(sep,f);
{
int i;
if (sep == ',') fputc('{',f);
for (i=0; i<el->blockCount; ++i)
    {
    fprintf(f, "%u", el->blockStarts[i]);
    fputc(',', f);
    }
if (sep == ',') fputc('}',f);
}
fputc(lastSep,f);
}

/* -------------------------------- End autoSql Generated Code -------------------------------- */

enum encodePeakType encodePeakInferType(int numFields, char *type)
/* Given a track type string and number of fields in a row, infer the type */
{
if (!type)
    return 0;
if (((sameString(type, "narrowPeak") || sameString(type, "encodePeak")) 
     && (numFields == ENCODE_PEAK_NARROW_PEAK_FIELDS)))
    return narrowPeak;
else if (((sameString(type, "broadPeak") || sameString(type, "encodePeak")) 
     && (numFields == ENCODE_PEAK_BROAD_PEAK_FIELDS)))
    return broadPeak;
else if (((sameString(type, "gappedPeak") || sameString(type, "encodePeak"))
     && (numFields == ENCODE_PEAK_GAPPED_PEAK_FIELDS)))
    return gappedPeak;
else if (sameString(type, "encodePeak") && (numFields == ENCODEPEAK_NUM_COLS))
    return encodePeak;
return 0;
}

int encodePeakNumFields(char *db, char *trackName)
/* Just quickly count th number of fields. */
{
struct sqlConnection *conn = hAllocConn(db);
struct slName *fieldNames = sqlFieldNames(conn, trackName);
int numFields = slCount(fieldNames);
hFreeConn(&conn);
if (sameString(fieldNames->name, "bin"))
    numFields--;
slFreeList(&fieldNames);
return numFields;
}

enum encodePeakType encodePeakInferTypeFromTable(char *db, char *table, char *tdbType)
/* Given the trackDb figure out the peak type. Returns zero on error. */ 
{
int numFields = encodePeakNumFields(db, table);
if (!tdbType)
    errAbort("unknown type in table %s", table);
return encodePeakInferType(numFields, tdbType);
}

struct encodePeak *encodePeakGeneralLoad(char **row, enum encodePeakType pt)
/* Make a new encodePeak and return it. */
{
struct encodePeak *peak;
if (!pt)
    return NULL;
if (pt == encodePeak)
    return encodePeakLoad(row);
AllocVar(peak);
peak->chrom = cloneString(row[0]);
peak->chromStart = sqlUnsigned(row[1]);
peak->chromEnd = sqlUnsigned(row[2]);
peak->name = cloneString(row[3]);
peak->score = sqlUnsigned(row[4]);
peak->peak = -1;
safecpy(peak->strand, sizeof(peak->strand), row[5]);
if ((pt == broadPeak) || (pt == narrowPeak))
    {
    peak->signalValue = sqlFloat(row[6]);
    peak->pValue = sqlFloat(row[7]);
    peak->qValue = sqlFloat(row[8]);
    if (pt == narrowPeak)
	peak->peak = sqlSigned(row[9]);
    }
else  /* must be gappedPeak */
    {
    int sizeOne;
    peak->blockCount = sqlUnsigned(row[9]);
    sqlUnsignedDynamicArray(row[10], &peak->blockSizes, &sizeOne);
    assert(sizeOne == peak->blockCount);
    sqlUnsignedDynamicArray(row[11], &peak->blockStarts, &sizeOne);
    assert(sizeOne == peak->blockCount);
    peak->signalValue = sqlFloat(row[12]);
    peak->pValue = sqlFloat(row[13]);
    peak->qValue = sqlFloat(row[14]);
    }
return peak;
}

struct encodePeak *narrowPeakLoad(char **row)
/* Load a narrowPeak */
{
return encodePeakGeneralLoad(row, narrowPeak);
}

struct encodePeak *broadPeakLoad(char **row)
/* Load a broadPeak */
{
return encodePeakGeneralLoad(row, broadPeak);
}

struct encodePeak *gappedPeakLoad(char **row)
/* Load a gappedPeak */
{
return encodePeakGeneralLoad(row, gappedPeak);
}

struct encodePeak *encodePeakLineFileLoad(char **row, enum encodePeakType pt, struct lineFile *lf)
/* From a linefile line, load an encodePeak row.  Errors outputted */
/* have line numbers, etc. Does more error checking as well. */
{
struct encodePeak *peak;
if (!pt)
    errAbort("Unknown peak type set for track");
AllocVar(peak);
peak->chrom = cloneString(row[0]);
peak->chromStart = lineFileNeedNum(lf, row, 1);
peak->chromEnd = lineFileNeedNum(lf, row, 2);
peak->peak = -1;
if (peak->chromEnd < 1)
    lineFileAbort(lf, "chromEnd less than 1 (%d)", peak->chromEnd);
if (peak->chromEnd < peak->chromStart)
    lineFileAbort(lf, "chromStart after chromEnd (%d > %d)", 
    	peak->chromStart, peak->chromEnd);
peak->name = cloneString(row[3]);
peak->score = lineFileNeedNum(lf, row, 4);
safecpy(peak->strand, sizeof(peak->strand), row[5]);
if (peak->strand[0] != '+' && peak->strand[0] != '-' && peak->strand[0] != '.')
    lineFileAbort(lf, "Expecting +, -, or . in strand");
if (pt != gappedPeak)
/* deal with signalValue, pValue, qValue, and peak */
    {
    peak->signalValue = (float)lineFileNeedDouble(lf, row, 6);
    peak->pValue = (float)lineFileNeedDouble(lf, row, 7);
    peak->qValue = (float)lineFileNeedDouble(lf, row, 8);
    if ((pt == narrowPeak) || (pt == encodePeak))
	{	
	peak->peak = lineFileNeedNum(lf, row, 9);
	if (peak->peak >= (int)peak->chromEnd)
	    lineFileAbort(lf, "peak site past chromEnd (%d > %d)", peak->peak, peak->chromEnd);
	}
    }
else  /* must be gappedPeak */
/* deal with thickStart, thickEnd, itemRgb even though they're not used */
    {
    int thickStart = lineFileNeedNum(lf, row, 6);
    int thickEnd = lineFileNeedNum(lf, row, 7);
    int itemRgb = 0;
    char *comma;
    /*	Allow comma separated list of rgb values here	*/
    comma = strchr(row[8], ',');
    if (comma)
	itemRgb = bedParseRgb(row[8]);
    else
	itemRgb = lineFileNeedNum(lf, row, 8);
    if ((thickStart != 0) || (thickEnd != 0) || (itemRgb != 0))
	lineFileAbort(lf, "thickStart, thickEnd, and itemRgb columns not used in gappedPeak type, set all to 0");
    }
/* Deal with blocks */
if ((pt == gappedPeak) || (pt == encodePeak))
    {
    int i, count;
    int blockCountIx, blockSizesIx, blockStartsIx;
    if (pt == gappedPeak)
	{
	blockCountIx = 9;
	blockSizesIx = 10;
	blockStartsIx = 11;
	}
    else
	{
	blockCountIx = 10;
	blockSizesIx = 11;
	blockStartsIx = 12;
	}
    peak->blockCount = lineFileNeedNum(lf, row, blockCountIx);
    sqlUnsignedDynamicArray(row[blockSizesIx], &peak->blockSizes, &count);
    if (count != peak->blockCount)
	lineFileAbort(lf,  "expecting %d elements in array", peak->blockCount);
    sqlUnsignedDynamicArray(row[blockStartsIx], &peak->blockStarts, &count);
    if (count != peak->blockCount)
	lineFileAbort(lf, "expecting %d elements in array", peak->blockCount);
    // tell the user if they appear to be using absolute starts rather than 
    // relative... easy to forget!  Also check block order, coord ranges...
    for (i=0;  i < peak->blockCount;  i++)
	{
	if (peak->blockStarts[i]+peak->chromStart >= peak->chromEnd)
	    {
	    if (peak->blockStarts[i] >= peak->chromStart)
		lineFileAbort(lf, 
		    "BED blockStarts offsets must be relative to chromStart, "
		    "not absolute.  Try subtracting chromStart from each offset "
		    "in blockStarts.");
	    else
		lineFileAbort(lf, 
		    "BED blockStarts[i]+chromStart must be less than chromEnd.");
	    }
	}
    if (peak->blockStarts[0] != 0)
	lineFileAbort(lf, 
	    "BED blocks must span chromStart to chromEnd.  "
	    "BED blockStarts[0] must be 0 (==%d) so that (chromStart + "
	    "blockStarts[0]) equals chromStart.", peak->blockStarts[0]);
    i = peak->blockCount-1;
    if ((peak->chromStart + peak->blockStarts[i] + peak->blockSizes[i]) !=
	peak->chromEnd)
	lineFileAbort(lf, BAD_BLOCKS);
    }
if (pt == gappedPeak)
    /* deal with final three columns of a gappedPeak */
    {
    peak->signalValue = (float)lineFileNeedDouble(lf, row, 12);
    peak->pValue = (float)lineFileNeedDouble(lf, row, 13);
    peak->qValue = (float)lineFileNeedDouble(lf, row, 14);    
    }
return peak;
}

void encodePeakOutputWithType(struct encodePeak *el, enum encodePeakType pt, FILE *f)
/* Print out encodePeak different ways depending on narrowPeak, broadPeak, etc. */
/* but make it tab-separated. */
{
if (!pt)
    errAbort("cannot output using invalid peak type");
fprintf(f, "%s\t%u\t%u\t%s\t%u\t%s\t", el->chrom, el->chromStart, el->chromEnd, 
	el->name, el->score, el->strand);
if (pt != gappedPeak)
    {
    fprintf(f, "%g\t%g\t%g", el->signalValue, el->pValue, el->qValue);
    if (pt != broadPeak)
	fprintf(f, "\t%d", el->peak);
    if ((pt == encodePeak) && (el->blockCount > 0))
	{
	int i;
	fprintf(f, "\t%u\t", el->blockCount);
	for (i = 0; i < el->blockCount; i++)
	    fprintf(f, "%u,", el->blockSizes[i]);
	fputc('\t', f);
	for (i = 0; i < el->blockCount; i++)
	    fprintf(f, "%u,", el->blockStarts[i]);
	}
    }
else
    {
    int i;
    fprintf(f, "0\t0\t0\t%u\t", el->blockCount);
    for (i = 0; i < el->blockCount; i++)
	fprintf(f, "%u,", el->blockSizes[i]);
    fputc('\t', f);
    for (i = 0; i < el->blockCount; i++)
	fprintf(f, "%u,", el->blockStarts[i]);
    fputc('\t', f);
    fprintf(f, "%g\t%g\t%g", el->signalValue, el->pValue, el->qValue);
    }
fputc('\n', f);
}
