/* netTrack - stuff to handle loading and display of
 * netAlign type tracks in browser. Nets are derived
 * from cross-species alignments usually. */

/* 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 "hash.h"
#include "linefile.h"
#include "jksql.h"
#include "hdb.h"
#include "hgTracks.h"
#include "netCart.h"
#include "chainNetDbLoad.h"


struct cartOptions
    {
    enum netColorEnum netColor; /*  ChromColors, GrayScale */
    enum netLevelEnum netLevel; /* filter chains by level 1 thru 6 (0==All) */
    };

struct netItem
/* A net track item. */
    {
    struct netItem *next;
    int level;
    char *className;
    int yOffset;
    };

static char *netClassNames[] =  {
    "Level 1", "Level 2", "Level 3", "Level 4", "Level 5", "Level 6", };

static struct netItem *makeNetItems()
/* Make the levels for net alignment track. */
{
struct netItem *ni, *niList = NULL;
int i;
int numClasses = ArraySize(netClassNames);
for (i=0; i<numClasses; ++i)
    {
    AllocVar(ni);
    ni->level = i;
    ni->className = netClassNames[i];
    slAddHead(&niList, ni);
    }
slReverse(&niList);
return niList;
}

static void netLoad(struct track *tg)
/* Load up net tracks.  (Will query database during drawing for a change.) */
{
tg->items = makeNetItems();
}

static void netFree(struct track *tg)
/* Free up netGroup items. */
{
slFreeList(&tg->items);
}

static char *netName(struct track *tg, void *item)
/* Return name of net level track. */
{
struct netItem *ni = item;
return ni->className;
}

static struct track *rTg;
static struct hvGfx *rHvg;
static int rX;          /* X offset of drawing area. */
static int rHeightPer;  /* Height of boxes. */
static int rMidLineOff; /* Offset to draw connecting lines. */
static int rNextLine;   /* Y offset to next line. */
static double rScale;   /* Scale from bases to pixels. */
static boolean rIsFull; /* Full display? */

static char *netColorLastChrom;

static void netColorClearCache()
{
netColorLastChrom = "UNKNOWN";
}

static Color netColor(struct track *tg, char *chrom, int level)
/* return appropriate color for a chromosome/scaffold */
{
struct cartOptions *netCart;

netCart = (struct cartOptions *) tg->extraUiData;

if (netCart->netColor == netColorGrayScale)
    switch(level)
	{
	case 1: return(shadesOfGray[9]); break;
	case 2: return(shadesOfGray[7]); break;
	case 3: return(shadesOfGray[6]); break;
	case 4: return(shadesOfGray[5]); break;
	case 5: return(shadesOfGray[4]); break;
	case 6: return(shadesOfGray[3]); break;
	default: return(shadesOfGray[2]); break;
	}

static Color color;
if (!sameString(chrom, netColorLastChrom))
    color = getSeqColor(chrom, rHvg);
if (0 == color)		/* color 0 is actually an error condition, see */
    color = MG_BLACK;	/* makeChromosomeShades() in simpleTracks.c	*/
netColorLastChrom = chrom;
return color;
}

static void rNetBox(struct hvGfx *hvg, struct cnFill *fill, int start, int end, int y, int level,
	Color color, Color barbColor, int orientation)
/* Draw a scaled box. */
{
int x1,x2,w;

/* Do clipping before scaling because scaling may cause
 * integer overflow. */
if (start < winStart) start = winStart;
if (end > winEnd) end = winEnd;
if (start >= end)
    return;

x1 = round((double)(start-winStart)*rScale) + rX;
x2 = round((double)(end-winStart)*rScale) + rX;
w = x2-x1;

if (w < 1)
    w = 1;
hvGfxBox(rHvg, x1, y, w, rHeightPer, color);
if (w > 3)
    clippedBarbs(rHvg, x1, y+rMidLineOff, w, 2, 5, orientation, barbColor, TRUE);
if (w > 1)
    {
    if (rNextLine > 0)	 /* Put up click info in full mode. */
	{
	struct dyString *bubble = dyStringNew(256);
	char depth[8];
	snprintf(depth, sizeof(depth), "%d", level);
	dyStringPrintf(bubble, "%s %c %dk ",
	    fill->qName, fill->qStrand, fill->qStart/1000);
	mapBoxHc(hvg, start, end, x1, y, w, rHeightPer, rTg->track,
	    depth, bubble->string);
	dyStringFree(&bubble);
	}
    }
}

static void rNetLine(struct hvGfx *hvg, struct cnFill *gap, int y, int level, Color color,
	int orientation)
/* Write out line filling gap and associated info. */
{
int start = gap->tStart;
int end = start + gap->tSize;
int x1,x2,w;

/* Do clipping before scaling because scaling may cause
 * integer overflow. */
if (start < winStart) start = winStart;
if (end > winEnd) end = winEnd;
if (start >= end)
    return;
x1 = round((double)(start-winStart)*rScale) + rX;
x2 = round((double)(end-winStart)*rScale) + rX;
w = x2-x1;

if (w >= 1)
    {
    struct dyString *bubble = dyStringNew(256);
    char depth[8];
    int midY = y + rMidLineOff;
    clippedBarbs(rHvg, x1, midY, w, 2, 5, orientation, color, FALSE);
    hvGfxLine(rHvg, x1, midY, x2, midY, color);
    if (rNextLine > 0)	 /* Put up click info in full mode. */
	{
	snprintf(depth, sizeof(depth), "%d", level);
	dyStringPrintf(bubble, "size %d/%d Ns %d/%d newRep %d/%d",
	    gap->qSize, gap->tSize, gap->qN, gap->tN,
	    gap->qNewR, gap->tNewR);
        mapBoxHc(hvg, start, end, x1, y, w, rHeightPer, rTg->track,
		depth, bubble->string);
	dyStringFree(&bubble);
	}
    }
}


static void rNetDraw(struct track *tg, struct hvGfx *hvg, struct cnFill *fillList, int level, int y)
/* Recursively draw net. */
{
struct cnFill *fill;
struct cnFill *gap;
Color color, invColor;
int orientation;

for (fill = fillList; fill != NULL; fill = fill->next)
    {

    color = netColor(tg, fill->qName, level);
    invColor = hvGfxContrastingColor(rHvg, color);
    orientation = orientFromChar(fill->qStrand);
    if (fill->children == NULL || fill->tSize * rScale < 2.5)
    /* Draw single solid box if no gaps or no room to draw gaps. */
        {
	rNetBox(hvg, fill, fill->tStart, fill->tStart + fill->tSize,
		y, level, color, invColor, orientation);
	}
    else
        {
	int fStart = fill->tStart;
	for ( gap = fill->children; gap != NULL; gap = gap->next)
	    {
	    if (gap->tSize * rScale >= 1)
		{
		rNetBox(hvg, fill, fStart, gap->tStart, y, level,
			color, invColor, orientation);
		fStart = gap->tStart + gap->tSize;
		rNetLine(hvg, gap, y, level+1, color, orientation);
		}
	    }
	rNetBox(hvg, fill, fStart, fill->tStart + fill->tSize, y, level,
		color, invColor, orientation);
	}
    for (gap = fill->children; gap != NULL; gap = gap->next)
        {
	if (gap->children)
	    {
	    rNetDraw(tg, hvg, gap->children, level+2, y + rNextLine);
	    }
	}
    }
}

static void netDraw(struct track *tg, int seqStart, int seqEnd,
        struct hvGfx *hvg, int xOff, int yOff, int width,
        MgFont *font, Color color, enum trackVisibility vis)
/* Draw routine for netAlign type tracks.  This will load
 * the items as well as drawing them. */
{
/* Load Net. */
struct chainNet *net = chainNetLoadRange(database, tg->table, chromName,
	seqStart, seqEnd, NULL);

if (net != NULL)
    {
    /* Copy parameters to statics for recursive routine. */
    rTg = tg;
    rHvg = hvg;
    rX = xOff;

    /* Clear cache. */
    netColorClearCache();

    /* Compute a few other positional things for recursive routine. */
    rHeightPer = tg->heightPer;
    rMidLineOff = rHeightPer/2;
    rIsFull = (vis == tvFull || vis == tvPack || vis == tvSquish);
    if (rIsFull)
	rNextLine = tg->lineHeight;
    else
	rNextLine = 0;
    rScale = scaleForPixels(width);

    /* Recursively do main bit of work. */
    rNetDraw(tg, hvg, net->fillList, 1, yOff);
    chainNetFree(&net);
    }
}

static int netTotalHeight(struct track *tg, enum trackVisibility vis)
/* A copy of tgFixedTotalHeightNoOverflow() with visibility forced */
{
int ret = 0;
switch (vis)
    {
    case tvSquish:
	ret = tgFixedTotalHeightOptionalOverflow(tg, tvFull, (tl.fontHeight/3)+1, (tl.fontHeight/3), FALSE);
	break;
    case tvPack:
	ret = tgFixedTotalHeightOptionalOverflow(tg, tvFull, (tl.fontHeight/2)+1, (tl.fontHeight/2), FALSE);
	break;
    case tvFull:
	ret = tgFixedTotalHeightOptionalOverflow(tg, tvFull, tl.fontHeight+1, tl.fontHeight, FALSE);
	break;
    case tvDense:
	ret = tgFixedTotalHeightOptionalOverflow(tg, tvDense, tl.fontHeight+1, tl.fontHeight, FALSE);
	break;
    case tvHide:
    default:
	break;
    }
return(ret);
}

void netMethods(struct track *tg)
/* Make track group for chain/net alignment. */
{
struct cartOptions *netCart;

AllocVar(netCart);

netCart->netColor = netFetchColorOption(cart, tg->tdb, FALSE);
netCart->netLevel = netFetchLevelOption(cart, tg->tdb, FALSE);

tg->loadItems = netLoad;
tg->freeItems = netFree;
tg->drawItems = netDraw;
tg->itemName = netName;
tg->mapItemName = netName;
tg->totalHeight = netTotalHeight;
tg->itemHeight = tgFixedItemHeight;
tg->itemStart = tgItemNoStart;
tg->itemEnd = tgItemNoEnd;
tg->mapsSelf = TRUE;
tg->extraUiData = (void *) netCart;
}
