/* wigBedToStep - Convert bed-style wiggle into variable step or fixed step wiggle.. */

/* Copyright (C) 2011 The Regents of the University of California 
 * See README in this or parent directory for licensing information. */
#include "common.h"
#include "linefile.h"
#include "hash.h"
#include "options.h"
#include "bed.h"

void usage()
/* Explain usage and exit. */
{
errAbort(
  "wigBedToStep - Convert bed-style wiggle into variable step or fixed step wiggle.\n"
  "usage:\n"
  "   wigBedToStep in.bed out.wiggle\n"
  "options:\n"
  "   -forceVarStep    force variable step output even if it can be fixed step.\n"
  "   -forseSpanOne    force output of each base even if increases the number of datapoints.\n"
  );
}

static struct optionSpec options[] = {
    {"forceVarStep", OPTION_BOOLEAN},
    {"forceSpanOne", OPTION_BOOLEAN},
   {NULL, 0},
};

boolean validVariableStep(struct bed *bedList, int *retSpan)
/* Test whether all the spans are the same i.e. chromEnd-chromStart. */
{
struct bed *cur;
int minSpan = 1000000;
int maxSpan = 0;
if (bedList == NULL)
    return FALSE;
for (cur = bedList; cur != NULL; cur = cur->next)
    {
    int span = cur->chromEnd - cur->chromStart;
    if (span > maxSpan)
	maxSpan = span;
    if (span < minSpan)
	minSpan = span;
    }
if (minSpan != maxSpan)
    warn("Inconsistent spans across bed (min = %d, max = %d). Setting span to minimum.", minSpan, maxSpan);
*retSpan = minSpan;
return TRUE;
}

boolean validFixedStep(struct bed *bedList, int *retStep)
/* Test whether all the starts on each chrom are spaced at a fixed */
/* amount. */
{
struct bed *cur;
struct bed *prev;
int step = -1;
if (!bedList)
    return FALSE;
cur = prev = bedList;
while (cur != NULL)
    {
    if (prev == cur)
	{
	cur = cur->next;
	}
    else if (sameString(cur->chrom, prev->chrom))
	{
	if (step == -1)
	    step = cur->chromStart - prev->chromStart;
	else if ((cur->chromStart - prev->chromStart) != step)
	    return FALSE;	    
	prev = cur;
	cur = cur->next;
	}
    else 
	prev = cur;
    }
*retStep = step;
return TRUE;
}

void printDecLine(FILE *f, char *chrom, int chromStart, int span, int step, 
		  boolean doFixed)
/* print out a wiggle header line */
{
fprintf(f, "%s chrom=%s ", (doFixed) ? "fixedStep" : "variableStep", chrom);
if (doFixed)
    fprintf(f, "start=%d step=%d ", chromStart+1, step);
fprintf(f, "span=%d\n", span);
}

void printDataLine(FILE *f, int chromStart, char *data, boolean doFixed)
/* print out a wiggle data line */
{
if (!doFixed)
    fprintf(f, "%d\t", chromStart+1);
fprintf(f, "%s\n", data);
} 

void wiggleOut(FILE *f, struct bed *bedList, int span, int step)
/* the guts of the program.  go through the data line-by-line and output to a new file. */
{
struct bed *cur = bedList;
boolean doFixed = (step != -1);
boolean spanOne = FALSE;
int theSpan = span;
int theStep = step;
boolean printHeader = TRUE;
if (optionExists("forceSpanOne"))
    /* forcing span=1 changes the step and span to be one... obviously */
    {
    spanOne = TRUE;
    theSpan = 1;
    theStep = 1;
    doFixed = TRUE;
    }
if (optionExists("forceVarStep"))
    doFixed = FALSE;
for (; cur != NULL; cur = cur->next)
    {
    if (printHeader)
	printDecLine(f, cur->chrom, cur->chromStart, theSpan, theStep, doFixed);
    if (spanOne)
	{
	int i;
	for (i = cur->chromStart; i < cur->chromEnd; i++)
	    printDataLine(f, i, cur->name, doFixed);
	}
    else
	printDataLine(f, cur->chromStart, cur->name, doFixed);
    if (cur->next)
	/* check the various ways possible that the next line will need a header */
	{
	struct bed *next = cur->next;
	if (!sameString(cur->chrom, next->chrom))
	    printHeader = TRUE;
	else if (!spanOne && (cur->chromStart + theStep != next->chromStart))
	    printHeader = TRUE;
	else if (spanOne && (cur->chromEnd != next->chromStart))
	    printHeader = TRUE;
	else
	    printHeader = FALSE;
	}
    }
}

void wigBedToStep(char *inFile, char *outFile)
/* wigBedToStep - Convert bed-style wiggle into variable step or fixed step wiggle.. */
{
struct bed *bedList = bedLoadNAll(inFile, 4);
int span = 1;
int step = -1;
FILE *out = mustOpen(outFile, "w");
slSort(&bedList, bedCmp);
if (!optionExists("forceSpanOne"))
    {
    if (!validVariableStep(bedList, &span))
	errAbort("The inputted file is empty.");
    }
if (!optionExists("forceVarStep") && !optionExists("forceSpanOne"))
    validFixedStep(bedList, &step);
wiggleOut(out, bedList, span, step);
bedFreeList(&bedList);
carefulClose(&out);
}

int main(int argc, char *argv[])
/* Process command line. */
{
optionInit(&argc, argv, options);
if (argc != 3)
    usage();
wigBedToStep(argv[1], argv[2]);
return 0;
}
