#include <stdlib.h>
#include <stdio.h>
#include <mmlib.h>

/*
  mergetwo pid1 pid2
*/

#define ERROREXIT(msg) ( fprintf(stderr,"%s:%d: %s\n",__FILE__,__LINE__,msg), exit(1) )


#define MAXBUF 100

static int
mergerequest(
	     const pid_t pid1, const MPAGEINFO info1,
	     const pid_t pid2, const MPAGEINFO info2,
	     MEQUALPAIR *const pagepairp)
{
  if (info1.p_addr == info2.p_addr)
    {
      if (info1.p_addr == NULL)
	printf("Some p_addr == NULL\n");
      return 1==2;
    }
  if (info1.count < info2.count)
    {
      pagepairp->spid = pid1;
      pagepairp->sv_addr = info1.v_addr;
      pagepairp->dpid = pid2;
      pagepairp->dv_addr = info2.v_addr;
    }
  else
    {
      pagepairp->spid = pid2;
      pagepairp->sv_addr = info2.v_addr;
      pagepairp->dpid = pid1;
      pagepairp->dv_addr = info1.v_addr;
    }
  return 1==1;
}


size_t
getcompletepageinfos(
		     const pid_t pid, const void *start, const void *end,
		     MPAGEINFO * const pageinfos,
		     const size_t nel,
		     unsigned long (*hashfunction1)(const void* addr, size_t size),
		     unsigned long (*hashfunction2)(const void* addr, size_t size) )
{
  const void *cont;
  size_t i, iel, hashed1, hashed2;
  MPAGEINFO * pageinfos2;

  printf("retrieving at most %d pages from process %d\n", nel, pid);
  if ( ( iel = m_getpageinfos( pid, start, end, pageinfos, nel, &cont ) ) == -1 )
    ERROREXIT(strerror(errno));
  printf("got %d pages from %d, cont == %p\n", iel, nel, cont);

  for ( i = 0; i < iel; i++)
    {
      if ( pageinfos[i].v_addr == NULL )
	printf("pageinfos[%d] == NULL\n",i);
      if ( pageinfos[i].count == 0 )
	printf("pageinfos[%d] == 0\n",i);
      if ( pageinfos[i].p_addr == 0 )
	printf("pageinfos[%d] == 0\n",i);
    }

  printf("hashing with bip_hash...\n");
  if ( ( hashed1 = m_hashpages( pid, pageinfos, iel, hashfunction1) ) == -1 )
    ERROREXIT(strerror(errno));


  pageinfos2 = calloc(iel, sizeof(MPAGEINFO));
  if (pageinfos2 == NULL)
    ERROREXIT("cannot allocate space for pageinfos2");
  for (i = 0 ; i < iel; i++)
    {
      pageinfos2[i].v_addr = pageinfos[i].v_addr;
    }

  printf("now hashing with the corresponding library function ...\n");

  if ( ( hashed2 = m_hashpages( pid, pageinfos2, iel, hashfunction2) ) == - 1)
    ERROREXIT(strerror(errno));

  if (hashed1 != hashed2)
    ERROREXIT("the hashes are differnt");

  for ( i = 0; i < iel; i++)
    {
      /*
      printf("pageinfos[%d] = { %p, %p, %d, %lx }\n",
	     i,
	     pageinfos[i].v_addr,
	     pageinfos[i].p_addr,
	     pageinfos[i].count,
	     pageinfos[i].hash );
      */
      if ( pageinfos[i].v_addr != pageinfos2[i].v_addr )
	printf("pageinfos[%d]: v_addr not equal: %p vs %p\n",
	       i, pageinfos[i].v_addr, pageinfos2[i].v_addr);
      if ( pageinfos[i].p_addr != pageinfos2[i].p_addr )
	printf("pageinfos[%d]: p_addr not equal: %p vs %p\n",
	       i, pageinfos[i].p_addr, pageinfos2[i].p_addr);
      if ( pageinfos[i].count != pageinfos2[i].count )
	printf("pageinfos[%d]: count not equal: %d vs %d\n",
	       i, pageinfos[i].count, pageinfos2[i].count);
      if ( pageinfos[i].hash != pageinfos2[i].hash )
	printf("pageinfos[%d]: hash not equal: %lx vs %lx\n",
	       i, pageinfos[i].hash, pageinfos2[i].hash);
    }
  return iel;
}     

int
main(const int argc, const char * const argv[])
{
  MPAGEINFO *pageinfos1, *pageinfos2;
  MEQUALPAIR *pagepairs;
  ssize_t nel, iel1, iel2, i1, i2, j, eq;
  pid_t pid1, pid2;
  const void *minp, *maxp;

  if (argc != 5)
    ERROREXIT("Usage: mergetwo nel hexaddr-hexaddr pid1 pid2");
  nel = atol(argv[1]);
  if ( sscanf(argv[2],"%p-%p",&minp,&maxp) != 2)
    ERROREXIT("2nd argument should be hexaddr-hexaddr");
  pid1 = atol(argv[3]);
  pid2 = atol(argv[4]);

  pageinfos1 = calloc(nel, sizeof(MPAGEINFO));
  pageinfos2 = calloc(nel, sizeof(MPAGEINFO));
  pagepairs = calloc(nel, sizeof(MEQUALPAIR));
  printf("Space allocated\n");

  if ( pageinfos1 == NULL 
       || pageinfos2 == NULL
       || pagepairs == NULL )
    ERROREXIT("No more memory");


  printf("testing m_hash_const...\n");
  iel1 = getcompletepageinfos( pid1, minp, maxp, pageinfos1, nel, m_hash_const, m_libhash_const );
  iel2 = getcompletepageinfos( pid2, minp, maxp, pageinfos2, nel, m_hash_const, m_libhash_const);

  printf("testing m_hash_addrothalf...\n");
  iel1 = getcompletepageinfos( pid1, minp, maxp, pageinfos1, nel, m_hash_addrothalf, m_libhash_addrothalf );
  iel2 = getcompletepageinfos( pid2, minp, maxp, pageinfos2, nel, m_hash_addrothalf, m_libhash_addrothalf);
  
  j = 0;
  i1 = i2 = 0; eq = 0;
  while (i1 < iel1 && i2 < iel2 )
    {
      if ( pageinfos1[i1].v_addr == pageinfos1[i2].v_addr )
	{
	  eq++;
	  if (mergerequest(pid1, pageinfos1[i1],  pid2, pageinfos2[i2], &pagepairs[j]))
	    j++;
	  i1++;
	  i2++;
	}
      else if ( pageinfos1[i1].v_addr > pageinfos2[i2].v_addr )
	i2++;
      else
	i1++;
    }
  printf("Prepared %d requests (%d were virtually equal), now merging ...\n",j,eq);
  j = m_merge(pagepairs, j);
  if ( j == -1 )
    ERROREXIT(strerror(errno));
  printf("mergetwo merged %d pages\n",j);

  return 0;
}
     
