// each file buffer stores an array of coded blocks
// +-------+------------+-------+------------+-----
// | coeff | coded data | coeff | coded data | ...
// +-------+------------+-------+------------+-----
CodedBlockPtr CodeTorrent::ReadGenBuf(int gen, int k) {

	// NOTE: error checking must be done prior to invoking this method!
	//		(i.e. are there strictly less than k blocks in this file buffer?)
	// NOTE: k begins at 0

	CodedBlockPtr tempBlock;
	tempBlock = AllocCodedBlock(num_blocks_gen[gen], block_size);
	tempBlock->gen = gen;

	const char *ext = ".temp";
	char *ext2 = new char[3]; // what if more than 100 gens?

#ifdef WIN32
	itoa(gen, ext2, 10); // base : 10
#else
	sprintf(ext2, "%d", gen);
#endif

	int fname_len = strlen(filename);
	int ext_len = strlen(ext);
	int ext2_len = strlen(ext2);

	// this is dumb but works
	char *filename_read = new char[fname_len + ext_len + ext2_len + 1];

	memcpy(filename_read, filename, fname_len);
	memcpy(filename_read + fname_len, ext, ext_len);
	memcpy(filename_read + fname_len + ext_len, ext2, ext2_len);

	filename_read[fname_len + ext_len + ext2_len] = '\0';

	fp = fopen(filename_read, "rb");
	if(fp) HAGGLE_DBG2("Opening file %s for writing with file descriptor %d\n", filename_read, fileno(fp));

	if (!fp) {
		HAGGLE_ERR("CODETORRENT ERROR: cache access error!\n");
	}

	if (fseek(fp, (num_blocks_gen[gen] + block_size) * k, SEEK_SET)) {
		HAGGLE_ERR("CODETORRENT ERROR: cache access error!\n");
	}

	int cf = fread(tempBlock->coeffs, 1, num_blocks_gen[gen], fp);
	int sm = fread(tempBlock->sums, 1, block_size, fp);

	if (cf != num_blocks_gen[gen] || sm != block_size) {
		HAGGLE_ERR("CODETORRENT ERROR: cache reading error!\n");
	}

	fclose(fp);

	delete[] filename_read;
	delete[] ext2;

	return tempBlock;
}
CodedBlock* CodeTorrent::CopyCodedBlock(CodedBlockPtr ptr) {
	CodedBlock *new_blk = AllocCodedBlock(ptr->num_blocks_gen, ptr->block_size);
	new_blk->gen = ptr->gen;
	new_blk->num_blocks_gen = ptr->num_blocks_gen;
	new_blk->block_size = ptr->block_size;

	memcpy(new_blk->coeffs, ptr->coeffs, new_blk->num_blocks_gen);
	memcpy(new_blk->sums, ptr->sums, new_blk->block_size);

	return new_blk;
}
// re-encode a block from generation "gen"
// return NULL pointer if failed (e.g. no data in buffer)
CodedBlockPtr CodeTorrent::ReEncode(int gen) {

	int j;

	// TODO: Make this work with file buffers
    // CBMEN, HL - Allocate memory here
    CodedBlockPtr cb = AllocCodedBlock(num_blocks_gen[gen], block_size);
    if (!cb)
        return NULL;
	cb->gen = gen;
	cb->num_blocks_gen = num_blocks_gen[gen];
	cb->block_size = block_size;

	//std::vector<CodedBlockPtr> *tempBuf = new std::vector<CodedBlockPtr>[GetRankVec()[gen]];
	std::vector<CodedBlockPtr> tempBuf;

	gettimeofday(&ct_vector_beg, &ct_tz);

	for (std::vector<CodedBlockPtr>::iterator it = buf.begin(); it != buf.end(); ++it) {
		if ((*it)->gen == gen)
			tempBuf.push_back(CopyCodedBlock((*it)));
	}

	int blocks_in_file = rank_vec[gen] - rank_vec_in_buf[gen];

	for (j = 0; j < blocks_in_file; j++) {
		tempBuf.push_back(ReadGenBuf(gen, j));
	}

	gettimeofday(&ct_vector_end, &ct_tz);
	//printf("[CT_Encode] Vector time: %ld.%03ld\n", CT_GetTimeDifference(ct_vector_beg, ct_vector_end) / 1000,
	//		CT_GetTimeDifference(ct_vector_beg, ct_vector_end) % 1000);

	//gettimeofday(&ct_encode_beg, &ct_tz);

	if (!nc->ReEncodeBlock(tempBuf, cb)) {
		return NULL;
	}

	//gettimeofday(&ct_encode_end, &ct_tz);
	//printf("[CT_Encode] Encode time: %ld.%03ld\n", CT_GetTimeDifference(ct_encode_beg, ct_encode_end)/1000, CT_GetTimeDifference(ct_encode_beg, ct_encode_end)%1000);

	for (j = 0; j < tempBuf.size(); j++)
		FreeCodedBlock(tempBuf[j]);
	tempBuf.clear();

    // CBMEN, HL - no need for redundant copy
    return cb;
	//return CopyCodedBlock(cb);
}
// encode a block from generation "gen"
CodedBlockPtr SingleBlockEncoder::EncodeSingleBlock(int gen, int blockIdInGen, BlockPtr fragBlockData, int fragDataSize, int num_blocks_gen) {
	int block_size = fragDataSize;
//	int buffer_size = 2 * block_size;

	if(fragDataSize != block_size){
		//TODO: Report error		
		return NULL;
	}

	// create a new copy
	CodedBlockPtr cb_to = AllocCodedBlock(num_blocks_gen, block_size);

	cb_to->gen = gen;
	cb_to->num_blocks_gen = num_blocks_gen; 
	cb_to->block_size = block_size;

	//Make a fake data to encode
	std::vector<BlockPtr> fakeData;
	BlockPtr pblk;
	for(int i=0;i<num_blocks_gen;i++){
		pblk = AllocBlock((block_size));
		memset(pblk, 0, (block_size));
		if(i==blockIdInGen){
			memcpy(pblk, fragBlockData, (block_size));
		}
		fakeData.push_back(pblk);
	}

	nc->EncodeSingleBlock(fakeData, cb_to, blockIdInGen);

	//release fake data
	for(int i=0;i<(int) fakeData.size();i++){
		FreeBlock(fakeData[i]);
	}
	fakeData.clear();

	return cb_to;
}
// encode a block from generation "gen"
CodedBlockPtr CodeTorrent::Encode(int gen) {

	// if the data's not already in memory, load it
	if (gen_in_memory != gen) {
		LoadFile(gen);
		if(data.size() == 0) return NULL; // MOS
	}

	// create a new copy
	CodedBlockPtr cb_to = AllocCodedBlock(num_blocks_gen[gen], block_size);

	cb_to->gen = gen;
	cb_to->num_blocks_gen = num_blocks_gen[gen];
	cb_to->block_size = block_size;

	gettimeofday(&ct_encode_beg, &ct_tz);
	nc->EncodeBlock(data, cb_to);
	gettimeofday(&ct_encode_end, &ct_tz);
//	printf("[CT_Encode] Encode time: %ld.%03ld\n", CT_GetTimeDifference(ct_encode_beg, ct_encode_end) / 1000,
//			CT_GetTimeDifference(ct_encode_beg, ct_encode_end) % 1000);

	return cb_to;
}