#include "stdafx.h"
#include "GenePiece.h"
#include "Simulation.h"
#include "Population.h"
#include "Person.h"
#include <stdio.h>
#include <stdlib.h>


struct GenePiece *GenePiece_malloc( struct Simulation *sim, struct Person *founder )
{
	return GenePiece_malloc( NULL, sim->crossoverSites, founder );
}


struct GenePiece *GenePiece_malloc( struct GenePiece *nextPiece, int size, struct Person *foundingPerson )
{
	struct GenePiece *gp;

	// Allocate a new gene piece.
	gp = (struct GenePiece *) malloc( sizeof( struct GenePiece ) );

	// Set the default initial values.
	gp->nextPiece = nextPiece;
	gp->size = size;
	gp->founder = foundingPerson;

	// Return a pointer to the allocated gene piece.
	return gp;
}


struct GenePiece *GenePiece_copy( struct GenePiece *original )
{
	struct GenePiece *gp;

	// Create a copy with the original gene's size and founder.
	gp = GenePiece_malloc( NULL, original->size, original->founder );

	// If the piece we are copying had a following link, copy that too.
	if( original->nextPiece )
		gp->nextPiece = GenePiece_copy( original->nextPiece );

	// Return a pointer to the allocated gene piece.
	return gp;
}


void GenePiece_crossover( struct GenePiece *gp1, struct GenePiece *gp2, int site )
{
	int site1;
	int site2;
	struct GenePiece *newPiece1;
	struct GenePiece *newPiece2;

	site1 = site;
	site2 = site;

	// Find the gene pieces and locations within them where the crossover occurs.
	while( site1 >= gp1->size )
	{
		site1 -= gp1->size;

		// If site1 == 0 (we hit an existing crossover point), we won't be allocating a new GenePiece.
		// Keep the previous pointer so we can change its nextPiece link.
		if( site1 )
			gp1 = gp1->nextPiece;
	}
	while( site2 >= gp2->size )
	{
		site2 -= gp2->size;

		// If site2 == 0 (we hit an existing crossover point), we won't be allocating a new GenePiece.
		// Keep the previous pointer so we can change its nextPiece link.
		if( site2 )
			gp2 = gp2->nextPiece;
	}

	// If this is a new crossover breakpoint for chromosome half 1...
	if( site1 )
	{
		// Cut up the chromosome.
		newPiece1 = GenePiece_malloc( gp1->nextPiece, gp1->size - site1, gp1->founder );
		gp1->nextPiece = newPiece1;
		gp1->size = site1;
	}
	else
		newPiece1 = gp1->nextPiece;

	// If this is a new crossover breakpoint for chromosome half 2...
	if( site2 )
	{
		// Cut up the chromosome.
		newPiece2 = GenePiece_malloc( gp2->nextPiece, gp2->size - site2, gp2->founder );
		gp2->nextPiece = newPiece2;
		gp2->size = site2;
	}
	else
		newPiece2 = gp2->nextPiece;

	// Swap the links.
	gp1->nextPiece = newPiece2;
	gp2->nextPiece = newPiece1;
}


int GenePiece_elementCount( struct GenePiece *gp )
{
	int count = 0;

	while( gp )
	{
		count ++;
		gp = gp->nextPiece;
	}

	return count;
}


float GenePiece_founderGenePercentage( struct GenePiece *gp, struct Person *founder )
{
	int totalFromFounder = 0, totalSize = 0;
	struct GenePiece *thisPiece;

	thisPiece = gp;

	while( thisPiece )
	{
		totalSize += thisPiece->size;

		if( thisPiece->founder == founder )
			totalFromFounder += thisPiece->size;

		thisPiece = thisPiece->nextPiece;
	}

	return (float)( totalFromFounder ) / (float)( totalSize );
}


bool GenePiece_hasFounderGenes( struct GenePiece *gp, struct Person *founder )
{
	struct GenePiece *thisPiece;

	thisPiece = gp;

	while( thisPiece )
	{
		if( thisPiece->founder == founder )
			return true;

		thisPiece = thisPiece->nextPiece;
	}

	return false;
}


float GenePiece_maleGenePercentage( struct GenePiece *gp )
{
	int totalFromMales = 0, totalSize = 0;
	struct GenePiece *thisPiece;

	thisPiece = gp;

	while( thisPiece )
	{
		totalSize += thisPiece->size;

		if( thisPiece->founder->genderIsMale )
			totalFromMales += thisPiece->size;

		thisPiece = thisPiece->nextPiece;
	}

	return (float)( totalFromMales ) / (float)( totalSize );
}


void GenePiece_free( struct GenePiece *gp )
{
	// Recursively free any following pieces.
	if( gp->nextPiece )
		GenePiece_free( gp->nextPiece );

	// Free the gene piece from memory.
	free( gp );
}


void GenePiece_save( struct GenePiece *gp, FILE *out )
{
	int pieces;

	pieces = GenePiece_elementCount( gp );
	fwrite( &pieces, sizeof( pieces ), 1, out );

	while( gp )
	{
		fwrite( &( gp->founder->genderIsMale ), sizeof( gp->founder->genderIsMale ), 1, out );
		fwrite( &( gp->founder->index ), sizeof( gp->founder->index ), 1, out );

		fwrite( &( gp->size ), sizeof( gp->size ), 1, out );

		gp = gp->nextPiece;
	}
}


struct GenePiece *GenePiece_load( FILE *in, struct Simulation *sim )
{
	struct GenePiece *gp, *thisPiece;
	int pieces;
	bool genderIsMale;
	int index;

	fread( &pieces, sizeof( pieces ), 1, in );

	if( ! pieces )
		return NULL;

	gp = (struct GenePiece *) malloc( sizeof( struct GenePiece ) );

	fread( &( genderIsMale ), sizeof( genderIsMale ), 1, in );
	fread( &( index ), sizeof( index ), 1, in );

	if( genderIsMale )
		gp->founder = sim->generations[ 0 ]->males[ index ];
	else
		gp->founder = sim->generations[ 0 ]->females[ index ];

	fread( &( gp->size ), sizeof( gp->size ), 1, in );

	thisPiece = gp;
	pieces --;

	while( pieces )
	{
		thisPiece->nextPiece = (struct GenePiece *) malloc( sizeof( struct GenePiece ) );

		thisPiece = thisPiece->nextPiece;
		pieces --;

		fread( &( genderIsMale ), sizeof( genderIsMale ), 1, in );
		fread( &( index ), sizeof( index ), 1, in );

		if( genderIsMale )
			thisPiece->founder = sim->generations[ 0 ]->males[ index ];
		else
			thisPiece->founder = sim->generations[ 0 ]->females[ index ];

		fread( &( thisPiece->size ), sizeof( thisPiece->size ), 1, in );
	}

	thisPiece->nextPiece = NULL;

	return gp;
}
