// check every second to see if merges are done
bool waitForMergeToFinish ( ) {
	// if registered
	static bool s_registered = false;
	if ( s_registered ) {
		g_loop.unregisterSleepCallback ( NULL , sleepCallback );
		s_registered = false;
	}
	CollectionRec *cr = g_collectiondb.getRec("qatest123");
	if ( ! cr ) { qatest(); return true; }
	// tight merge the rdb that was dumped
	long i; for ( i = 0 ; i < RDB_END ; i++ ) {
		Rdb *rdb = getRdbFromId ( i );
		if ( ! rdb ) continue;
		RdbBase *base = rdb->getBase ( cr->m_collnum );
		if ( ! base ) continue;
		// . force a tight merge as soon as dump completes
		// . the dump should already be going
		if ( base->m_nextMergeForced ) return false;
		// still waiting on this merge
		break;
	}
	// if not still waiting return true
	if ( i >= RDB_END ) return true;
	// sleep for 1 second
	g_loop.registerSleepCallback ( 1000 , // 1000 ms
				       NULL , // state
				       sleepCallback ,
				       0 ); // niceness
	s_registered = true;
	return false;
}
// once we have triggered the dump this will cause all rdbs to tightmerge
void doneDumping ( void *state , TcpSocket *sock ) {
	CollectionRec *cr = g_collectiondb.getRec("qatest123");
	if ( ! cr ) { qatest(); return; }
	// tight merge the rdb that was dumped
	for ( long i = 0 ; i < RDB_END ; i++ ) {
		Rdb *rdb = getRdbFromId ( i );
		if ( ! rdb ) continue;
		RdbBase *base = rdb->getBase ( cr->m_collnum );
		if ( ! base ) continue;
		// . force a tight merge as soon as dump completes
		// . the dump should already be going
		base->m_nextMergeForced = true;
	}
	// wait for tight merges to complete now
	qatest();
}
void gotList33 ( void *state ) {
	long *rdbId = (long *)state;
	if ( ! s_list.isEmpty() ) {
		log("qa: delete failed. list is not empty rdbid=%li.",*rdbId);
		s_failures++;
	}
	// resume main loop
	qatest();
}
void doneSearching2 ( void *state , TcpSocket *sock ) {
	//loadQueries1();
	long ii = s_qi2 - 1;
	// get checksum of it
	HttpMime hm;
	hm.set ( sock->m_readBuf , sock->m_readOffset , NULL );
	char *page = sock->m_readBuf + hm.getMimeLen() ;
	// we will need to ignore fields like the latency etc.
	// perhaps pass that in as a cgi parm. &qa=1
	long crc = hash32n ( page );
	if ( crc != s_checksums[ii] ) {
		log("qatest: query '%s' checksum %lu != %lu",
		    s_queries[ii],
		    s_checksums[ii],
		    crc);
		s_failures++;
	}
	// resume the qa loop
	qatest();
}
// check every second to see if spidering phase is completed
bool checkSpidersDone ( ) {
	// if registered
	static bool s_registered = false;
	if ( s_registered ) {
		g_loop.unregisterSleepCallback ( NULL , sleepCallback );
		s_registered = false;
	}
	// we have to adjust this once we know how many pages we'll archive
	CollectionRec *cr = g_collectiondb.getRec("qatest123");
	if ( ! cr ) { qatest(); return true; }
	// return true if all done
	if ( cr->m_globalCrawlInfo.m_pageDownloadSuccessesThisRound >= 200 )
		return true;
	// sleep for 1 second
	g_loop.registerSleepCallback ( 1000 , // 1000 ms
				       NULL , // state
				       sleepCallback ,
				       0 ); // niceness
	s_registered = true;
	return false;
}
bool sendPageQA ( TcpSocket *sock , HttpRequest *hr ) {
	char pbuf[32768];
	SafeBuf sb(pbuf, 32768);

	//char format = hr->getReplyFormat();

	// set this. also sets gr->m_hr
	GigablastRequest gr;
	// this will fill in GigablastRequest so all the parms we need are set
	g_parms.setGigablastRequest ( sock , hr , &gr );


	//
	// . handle a request to update the crc for this test
	// . test id identified by "ajaxUrlHash" which is the hash of the test's url
	//   and the test name, QATest::m_testName
	long ajax = hr->getLong("ajax",0);
	unsigned long ajaxUrlHash ;
	ajaxUrlHash = (unsigned long long)hr->getLongLong("uh",0LL);
	unsigned long ajaxCrc ;
	ajaxCrc = (unsigned long long)hr->getLongLong("crc",0LL);

	if ( ajax ) {
		// make sure it is initialized
		if ( s_ht.m_ks ) {
			// overwrite current value with provided one because 
			// the user click on an override checkbox to update 
			// the crc
			s_ht.addKey ( &ajaxUrlHash , &ajaxCrc );
			saveHashTable();
		}
		// send back the urlhash so the checkbox can turn the
		// bg color of the "diff" gray
		SafeBuf sb3;
		sb3.safePrintf("%lu",ajaxUrlHash);
		g_httpServer.sendDynamicPage(sock,
					     sb3.getBufStart(),
					     sb3.length(),
					     -1/*cachetime*/);
		return true;
	}
		

	// if they hit the submit button, begin the tests
	long submit = hr->hasField("action");

	long n = sizeof(s_qatests)/sizeof(QATest);


	if ( submit && g_qaInProgress ) {
		g_errno = EINPROGRESS;
		g_httpServer.sendErrorReply(sock,g_errno,mstrerror(g_errno));
		return true;
	}

	// set m_doTest
	for ( long i = 0 ; submit && i < n ; i++ ) {
		QATest *qt = &s_qatests[i];
		char tmp[10];
		sprintf(tmp,"test%li",i);
		qt->m_doTest = hr->getLong(tmp,0);
	}

	if ( submit ) {
		// reset all the static thingies
		resetFlags();
		// save socket
		g_qaSock = sock;
		g_numErrors = 0;
		g_qaOutput.reset();
		g_qaOutput.safePrintf("<html><body>"
				      "<title>QA Test Results</title>\n");

		g_qaOutput.safePrintf("<SCRIPT LANGUAGE=\"javascript\">\n"
				      // update s_ht with the new crc for this test
				      "function submitchanges(urlhash,crc) "
				      "{\n "
				      "var client=new XMLHttpRequest();\n"
				      "client.onreadystatechange=gotsubmitreplyhandler;"
				      "var "
				      "u='/admin/qa?ajax=1&uh='+urlhash+'&crc='+crc;\n"
				      "client.open('GET',u);\n"
				      "client.send();\n"
				      
				      // use that to fix background to gray
				      "var w=document.getElementById(urlhash);\n"
				      // set background color
				      "w.style.backgroundColor = '0xe0e0e0';\n"

				      // gear spinning after checkbox
				      "}\n\n "

				      // call this when we got the reply that the 
				      // checkbox went through
				      "function gotsubmitreplyhandler() {\n"
				      // return if reply is not fully ready
				      "if(this.readyState != 4 )return;\n"
				      // if error or empty reply then do nothing
				      "if(!this.responseText)return;\n"
				      // response text is the urlhash32, unsigned long
				      "var id=this.responseText;\n"
				      // use that to fix background to gray
				      "var w=document.getElementById(id);\n"
				      // set background color
				      "w.style.backgroundColor = '0xe0e0e0';\n"
				      "}\n\n"

				      "</SCRIPT> ");
		// and run the qa test loop
		if ( ! qatest( ) ) return false;
		// what happened?
		log("qa: qatest completed without blocking");
	}

	// show tests, all checked by default, to perform

	g_pages.printAdminTop ( &sb , sock , hr );

	sb.safePrintf("<SCRIPT LANGUAGE=\"javascript\">\n"
		     "function checkAll(name, num)\n "
		      "{ "
		      "    for (var i = 0; i < num; i++) {\n"
		      "      var e = document.getElementById(name + i);\n"
		      //"alert(name+i);"
		      "      e.checked = !e.checked ;\n "
		      "  }\n"
		      "}\n\n "

		      "</SCRIPT> ");

	//sb.safePrintf("<form name=\"fo\">");

	sb.safePrintf("\n<table %s>\n",TABLE_STYLE);
	sb.safePrintf("<tr class=hdrow><td colspan=2>"
		      "<center><b>QA Tests</b></center>"
		      "</td></tr>");

	// header row
	sb.safePrintf("<tr><td><b>Do Test?</b> <a style=cursor:hand;"
		      "cursor:pointer; "
		      "onclick=\"checkAll('test', %li);\">(toggle)</a>",n);
	sb.safePrintf("</td><td><b>Test Name</b></td></tr>\n");
	
	// . we keep the ptr to each test in an array
	// . print out each qa function
	for ( long i = 0 ; i < n ; i++ ) {
		QATest *qt = &s_qatests[i];
		char *bg;
		if ( i % 2 == 0 ) bg = LIGHT_BLUE;
		else              bg = DARK_BLUE;
		sb.safePrintf("<tr bgcolor=#%s>"
			      "<td><input type=checkbox value=1 name=test%li "
			      "id=test%li></td>"
			      "<td>%s"
			      "<br>"
			      "<font color=gray size=-1>%s</font>"
			      "</td>"
			      "</tr>\n"
			      , bg
			      , i
			      , i
			      , qt->m_testName
			      , qt->m_testDesc
			      );
	}

	sb.safePrintf("</table>\n<br>\n");
	//	      "</form>\n");

	g_pages.printAdminBottom ( &sb , hr );


	g_httpServer.sendDynamicPage(sock,
				     sb.getBufStart(),
				     sb.length(),
				     -1/*cachetime*/);

	return true;
}
void qatestWrapper ( int fd , void *state ) {
	qatest();
}
void qatestWrapper ( void *state , TcpSocket *sock ) { qatest(); }	
void sleepCallback ( int fd , void *state ) {
	qatest();
}
void doneAddingUrls ( void *state ) {
	qatest();
}