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

#include "common.h"
#include "regexHelper.h"
#include "linefile.h"
#include "dystring.h"
#include "jksql.h"
#include "decoration.h"

#include "bigBed.h"
#include "basicBed.h"


char *decorationCommaSepFieldNames = "chrom,chromStart,chromEnd,name,score,strand,thickStart,thickEnd,color,blockCount,blockSizes,chromStarts,decoratedItem,style,fillColor,glyph";

struct decoration *decorationLoad(char **row)
/* Load a decoration from row fetched with select * from decoration
 * from database.  Dispose of this with decorationFree(). */
{
struct decoration *ret;

AllocVar(ret);
ret->blockCount = sqlSigned(row[9]);
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->thickStart = sqlUnsigned(row[6]);
ret->thickEnd = sqlUnsigned(row[7]);
ret->color = sqlUnsigned(row[8]);
{
int sizeOne;
sqlSignedDynamicArray(row[10], &ret->blockSizes, &sizeOne);
assert(sizeOne == ret->blockCount);
}
{
int sizeOne;
sqlSignedDynamicArray(row[11], &ret->chromStarts, &sizeOne);
assert(sizeOne == ret->blockCount);
}
ret->decoratedItem = cloneString(row[12]);
ret->style = cloneString(row[13]);
ret->fillColor = cloneString(row[14]);
ret->glyph = cloneString(row[15]);
return ret;
}

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

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

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

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

struct decoration *decorationCommaIn(char **pS, struct decoration *ret)
/* Create a decoration out of a comma separated string. 
 * This will fill in ret if non-null, otherwise will
 * return a new decoration */
{
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->thickStart = sqlUnsignedComma(&s);
ret->thickEnd = sqlUnsignedComma(&s);
ret->color = sqlUnsignedComma(&s);
ret->blockCount = sqlSignedComma(&s);
{
int i;
s = sqlEatChar(s, '{');
AllocArray(ret->blockSizes, ret->blockCount);
for (i=0; i<ret->blockCount; ++i)
    {
    ret->blockSizes[i] = sqlSignedComma(&s);
    }
s = sqlEatChar(s, '}');
s = sqlEatChar(s, ',');
}
{
int i;
s = sqlEatChar(s, '{');
AllocArray(ret->chromStarts, ret->blockCount);
for (i=0; i<ret->blockCount; ++i)
    {
    ret->chromStarts[i] = sqlSignedComma(&s);
    }
s = sqlEatChar(s, '}');
s = sqlEatChar(s, ',');
}
ret->decoratedItem = sqlStringComma(&s);
ret->style = sqlStringComma(&s);
ret->fillColor = sqlStringComma(&s);
ret->glyph = sqlStringComma(&s);
*pS = s;
return ret;
}

void decorationFree(struct decoration **pEl)
/* Free a single dynamically allocated decoration such as created
 * with decorationLoad(). */
{
struct decoration *el;

if ((el = *pEl) == NULL) return;
freeMem(el->chrom);
freeMem(el->name);
freeMem(el->blockSizes);
freeMem(el->chromStarts);
freeMem(el->decoratedItem);
freeMem(el->style);
freeMem(el->fillColor);
freeMem(el->glyph);
freez(pEl);
}

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

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

void decorationOutput(struct decoration *el, FILE *f, char sep, char lastSep) 
/* Print out decoration.  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, "%u", el->thickStart);
fputc(sep,f);
fprintf(f, "%u", el->thickEnd);
fputc(sep,f);
fprintf(f, "%u", el->color);
fputc(sep,f);
fprintf(f, "%d", el->blockCount);
fputc(sep,f);
{
int i;
if (sep == ',') fputc('{',f);
for (i=0; i<el->blockCount; ++i)
    {
    fprintf(f, "%d", 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, "%d", el->chromStarts[i]);
    fputc(',', f);
    }
if (sep == ',') fputc('}',f);
}
fputc(sep,f);
if (sep == ',') fputc('"',f);
fprintf(f, "%s", el->decoratedItem);
if (sep == ',') fputc('"',f);
fputc(sep,f);
if (sep == ',') fputc('"',f);
fprintf(f, "%s", el->style);
if (sep == ',') fputc('"',f);
fputc(sep,f);
if (sep == ',') fputc('"',f);
fprintf(f, "%s", el->fillColor);
if (sep == ',') fputc('"',f);
fputc(sep,f);
if (sep == ',') fputc('"',f);
fprintf(f, "%s", el->glyph);
if (sep == ',') fputc('"',f);
fputc(lastSep,f);
}

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

static char *decorationAutoSqlString =
"table decoration"
"\"Browser extensible data (12 fields) plus information about what item this decorates and how.\""
"    ("
"    string chrom;      \"Chromosome (or contig, scaffold, etc.)\""
"    uint   chromStart; \"Start position in chromosome\""
"    uint   chromEnd;   \"End position in chromosome\""
"    string name;       \"Name of item\""
"    uint   score;      \"Score from 0-1000\""
"    char[1] strand;    \"+ or -\""
"    uint thickStart;   \"Start of where display should be thick (start codon)\""
"    uint thickEnd;     \"End of where display should be thick (stop codon)\""
"    uint color;        \"Primary RGB color for the decoration\""
"    int blockCount;    \"Number of blocks\""
"    int[blockCount] blockSizes; \"Comma separated list of block sizes\""
"    int[blockCount] chromStarts; \"Start positions relative to chromStart\""
"    string decoratedItem; \"Identity of the decorated item in chr:start-end:item_name format\""
"    string style;      \"Draw style for the decoration (e.g. block, glyph)\""
"    string fillColor;  \"Secondary color to use for filling decoration, blocks, supports RGBA\""
"    string glyph;  \"The glyph to draw in glyph mode; ignored for other styles\""
"    )"
;

#include "asParse.h"

struct asObject *decorationAsObj()
/* Return asObject describing fields of a decoration object */
{
return asParseText(decorationAutoSqlString);
}

struct decoration *decorationFromInterval (char *chrom, struct bigBedInterval *interval)
/* Convert a bigBedInterval read from a bigBed file into a decoration structure.
 * errAbort if the bigBedInterval doesn't have enough rows to support this.
 */
{
int sizeOne;
char *extra = cloneString(interval->rest);
int numCols = 12 + 5 - 3; // ideally we'd support variable-size beds as long as they have
                          // the decoration fields. For now it's 5 extra fields minus the
                          // 3 (chr/start/end) that aren't part of "rest".
char *row[numCols];
int wordCount = chopTabs(extra, row);
if (wordCount < numCols - 1)
    errAbort("decorationFromInterval: expected at least %d columns in 'rest' field, found %d columns", numCols, wordCount);

struct decoration *new = NULL;
AllocVar(new);
new->chrom = cloneString(chrom);
new->chromStart = interval->start;
new->chromEnd = interval->end;

new->name = cloneString(row[0]);
new->score = sqlUnsigned(row[1]);
new->strand[0] = *row[2];
new->thickStart = sqlUnsigned(row[3]);
new->thickEnd = sqlUnsigned(row[4]);
new->color = bedParseColor(row[5]);
new->blockCount = sqlUnsigned(row[6]);
sqlSignedDynamicArray(row[7], &new->blockSizes, &sizeOne);
assert(sizeOne == new->blockCount);
sqlSignedDynamicArray(row[8], &new->chromStarts, &sizeOne);
assert(sizeOne == new->blockCount);
new->decoratedItem = cloneString(row[9]);
new->style = cloneString(row[10]);
new->fillColor = cloneString(row[11]);
new->glyph = cloneString(row[12]);
new->next = NULL;
return new;
}

decorationStyle decorationGetStyle(struct decoration *decoration)
/* Return the enum value that corresponds to the string in the decoration's style field */
{
decorationStyle retVal = DECORATION_STYLE_UNKNOWN;
if (sameWordOk(decoration->style, "Block"))
    retVal = DECORATION_STYLE_BLOCK;
if (sameWordOk(decoration->style, "Glyph"))
    retVal = DECORATION_STYLE_GLYPH;
return retVal;
}

int decorationGetParentExtent(struct decoration *decoration, char **chrom, int *start, int *end)
/* Parse out the chrom:start-end from the "decoratedItem" field of a decoration and return
 * those in the corresponding pointers if they're non-NULL.  The chromosome string must be
 * freeMem()ed after use.
 */
{
regmatch_t substrArr[4];
if (regexMatchSubstrNoCase(decoration->decoratedItem, "([^:]+):([0-9]+)-([0-9]+):", substrArr, 4))
    {
    if (chrom != NULL)
        *chrom = regexSubstringClone(decoration->decoratedItem, substrArr[1]);
    if (start != NULL)
        *start = regexSubstringInt(decoration->decoratedItem, substrArr[2]);
    if (end != NULL)
        *end = regexSubstringInt(decoration->decoratedItem, substrArr[3]);
    return 1;
    }
return 0;

// consider replacing with a call to hgParseChromRange, feeding in db.  It will naturally
// stop parsing end at the final :, or I could clonesString and strrchr to set the final
// colon to 0 before calling the parser.
}
