/* testSearch - test the search functionality */

/* Copyright (C) 2011 The Regents of the University of California 
 * See README in this or parent directory for licensing information. */
#include "common.h"
#include "cart.h"
#include "cheapcgi.h"
#include "hgFind.h"
#include "hdb.h"
#include "hui.h"


/* Need to get a cart in order to use hgFind. */
struct cart *cart = NULL;
char *excludeVars[] = { NULL };

struct searchTestCase 
    {
    struct searchTestCase *next;
    char *searchTerm;
    char *database;
    int posCount;
    struct searchResults *results;
    };

struct searchResults
    {
    struct searchResults *next;
    char *tableName;
    char *chrom;
    int start;
    int end;
    char *browserName;
    };


void usage()
{
errAbort("testSearch - test search functionality.\n"
    "usage:\n"
    "  testSearch inputFile\n");
}

struct searchTestCase *readInput(char *fileName)
{
struct lineFile *lf = lineFileOpen(fileName, TRUE);
char *line;
char *headerLine[3];
char *resultLine[5];
struct searchTestCase *testCase;
struct searchTestCase *testCaseList = NULL;
struct searchResults *result;
struct searchResults *resultList;
int elementCount;
int matchCount;
int pos = 0;

while (lineFileNext(lf, &line, NULL))
    {
    elementCount = chopString(line, " ", headerLine, ArraySize(headerLine));
    if (elementCount != 3)
        {
	fprintf(stderr, "Error: formatting problem, exitting\n");
	return NULL;
	}
    resultList = NULL;
    matchCount = sqlUnsigned(headerLine[2]);
    for (pos = 0; pos < matchCount; pos++)
        {
	lineFileNext(lf, &line, NULL);
	elementCount = chopString(line, " ", resultLine, ArraySize(resultLine));
	if (elementCount != 5)
	    {
	    fprintf(stderr, "Error: formatting problem, exitting\n");
	    return NULL;
	    }
	AllocVar(result);
        result->tableName = cloneString(resultLine[0]);
        result->chrom = cloneString(resultLine[1]);
	result->start = sqlUnsigned(resultLine[2]);
	result->end = sqlUnsigned(resultLine[3]);
        result->browserName = cloneString(resultLine[4]);
        slAddHead(&resultList, result);
	}
    AllocVar(testCase);
    testCase->searchTerm = headerLine[0];
    testCase->database = headerLine[1];
    testCase->posCount = matchCount;
    testCase->results = resultList;
    slAddHead(&testCaseList, testCase);
    }
return testCaseList;
}

struct searchTestCase *morph(char *database, char *searchTerm, struct hgPositions *hgpList)
/* convert an hgpList into a searchTestCase */
{
struct hgPositions *hgp = NULL;
struct hgPosTable *table = NULL;
struct hgPos *pos = NULL;
struct searchTestCase *testCase = NULL;
struct searchResults *result = NULL;
struct searchResults *resultList = NULL;
int count = 0;

AllocVar(testCase);
testCase->database = cloneString(database);
testCase->searchTerm = cloneString(searchTerm);

for (hgp = hgpList; hgp != NULL; hgp = hgp->next)
    {
    if (hgp->tableList != NULL)
        {
        for (table = hgp->tableList; table != NULL; table = table->next)
            {
	    if (table->posList != NULL)
	        {
	        for (pos = table->posList; pos != NULL; pos = pos->next)
	            {
		    count++;
		    AllocVar(result);
		    result->tableName = cloneString(table->name);
		    result->chrom = cloneString(pos->chrom);
		    result->start = pos->chromStart;
		    result->end = pos->chromEnd;
		    result->browserName = cloneString(pos->browserName);
		    slAddHead(&resultList, result);
		    }
	        }
	    }
	}
    }
testCase->posCount = count;
testCase->results = resultList;
return testCase;
}


void compareResults(struct searchTestCase *expected, struct searchTestCase *actual)
/* don't assume the results are in the same order */
/* for each excepted, just do a linear read of the actual list */
/* all of these lists should be small enough so performance isn't an issue */
{
struct searchResults *result1, *result2;

if (differentString(expected->searchTerm, actual->searchTerm))
    {
    fprintf(stderr, "Error: mismatched search terms: expected = %s, actual = %s\n", 
            expected->searchTerm, actual->searchTerm);
    return;
    }
if (differentString(expected->database, actual->database))
    {
    fprintf(stderr, "Error: mismatched databases: expected = %s, actual = %s\n", expected->database, actual->database);
    return;
    }
if (expected->posCount != actual->posCount)
    {
    fprintf(stderr, "Error: mismatched posCount: expected = %d, actual = %d\n", expected->posCount, actual->posCount);
    return;
    }

result1 = expected->results;
while (result1)
    {
    boolean matchFound = FALSE;
    result2 = actual->results;
    while (result2)
        {
	if (differentString(result1->tableName, result2->tableName)) 
	    {
	    result2 = result2->next;
	    continue;
	    }
	if (differentString(result1->chrom, result2->chrom)) 
	    {
	    result2 = result2->next;
	    continue;
	    }
	if (result1->start != result2->start)
	    {
	    result2 = result2->next;
	    continue;
	    }
	if (result1->end != result2->end)
	    {
	    result2 = result2->next;
	    continue;
	    }
	if (differentString(result1->browserName, result2->browserName)) 
	    {
	    result2 = result2->next;
	    continue;
	    }
	matchFound = TRUE;
	break;
        }
    if (!matchFound)
        {
	fprintf(stderr, "Error: no match found for expected result %s\n", result1->tableName);
        fprintf(stderr, "position = %s:%d-%d\n", result1->chrom, result1->start, result1->end);
        fprintf(stderr, "browserName = %s\n", result1->browserName);
	}
    result1 = result1->next;
    }

result1 = actual->results;
while (result1)
    {
    boolean matchFound = FALSE;
    result2 = expected->results;
    while (result2)
        {
	if (differentString(result1->tableName, result2->tableName)) 
	    {
	    result2 = result2->next;
	    continue;
	    }
	if (differentString(result1->chrom, result2->chrom)) 
	    {
	    result2 = result2->next;
	    continue;
	    }
	if (result1->start != result2->start)
	    {
	    result2 = result2->next;
	    continue;
	    }
	if (result1->end != result2->end)
	    {
	    result2 = result2->next;
	    continue;
	    }
	if (differentString(result1->browserName, result2->browserName)) 
	    {
	    result2 = result2->next;
	    continue;
	    }
	matchFound = TRUE;
	break;
        }
    if (!matchFound)
        {
	fprintf(stderr, "Error: no match found for actual result %s\n", result1->tableName);
        fprintf(stderr, "position = %s:%d-%d\n", result1->chrom, result1->start, result1->end);
        fprintf(stderr, "browserName = %s\n", result1->browserName);
	}
    result1 = result1->next;
    }

}

void searchAndCompare(struct searchTestCase *testCaseList)
/* Read testCaseList.  Execute each search.  Report unexpected results. */
{
struct hgPositions *hgpList = NULL;
struct searchTestCase *morphOutput = NULL;

while (testCaseList)
    {
    verbose(1, "database = %s, searchTerm = %s\n", testCaseList->database, testCaseList->searchTerm);
    hgpList = hgPositionsFind(testCaseList->database, testCaseList->searchTerm, "", "hgTracks", cart, FALSE);
    /* handle cases where there are no expected matches */
    if (testCaseList->posCount == 0)
        {
        if (hgpList == NULL || hgpList->posCount == 0) 
	    {
	    testCaseList = testCaseList->next;
	    continue;
	    }
	fprintf(stderr, "Error: expected no matches, got some\n");
	fprintf(stderr, "database = %s, searchTerm = %s\n", 
	        testCaseList->database, testCaseList->searchTerm);
	continue;
	}
    morphOutput = morph(testCaseList->database, testCaseList->searchTerm, hgpList);
    compareResults(testCaseList, morphOutput);
    testCaseList = testCaseList->next;
    }
}


int main(int argc, char *argv[])
{
struct searchTestCase *testCases =  NULL;

cgiSpoof(&argc, argv);
cart = cartForSession(hUserCookie(), excludeVars, NULL);

if (argc != 2)
    usage();

testCases = readInput(argv[1]);
searchAndCompare(testCases);

return 0;
}
