/**
 * Copyright INRIA and ENS, contributors Peterlongo and Chikhi
 * pierre.peterlongo@inria.fr
 * rayan.chikhi@irisa.fr
 *
 * This software is a computer program whose purpose is to detect the
 * presence of a starter in a set of NGS reads, and to provide the neighbor
 * in case of success..
 *
 * This software is governed by the CeCILL license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".

 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.

 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
 */

/*
* higher-level functions based on the generic hash functions used in mapsembler
 */

#ifdef __cplusplus
extern "C"
{
#endif

#include <assert.h>
#include "misc_tools.h"
#include "advanced_hash.h"

  //#define debug_libchash_access

 void hash_set_int_to_key(hash_t map, char * key, int value){
	 int * p_value;
	 if(hash_entry_by_key(map, key, (void **) (&p_value)) == 0){ // first time a value is added to this key
	 #ifdef debug_libchash_access
	 		printf("first time a value is set to this key %s\n", key); // DEB
	 #endif
	 		p_value= (int *)malloc(sizeof(int));
	 		*p_value = value;
	 		hash_insert(map, key, p_value, sizeof(int *));
	 	}
	 	else {
	 #ifdef debug_libchash_access
	 		printf("new time a value is set to this key %s\n", key); // DEB
	 #endif
	 		*p_value=value;
	 	}
 }

 int* hash_get_int_value(hash_t map, char * key){
 	 int * p_value;
 	 if(hash_entry_by_key(map, key, (void **) (&p_value)) != 0){
 		 return p_value;
 	 	}
 	 return NULL;
  }

 void hash_increase_int_value_if_exists(hash_t map, char * key){
	 int * p_value;
	 if(hash_entry_by_key(map, key, (void **) (&p_value)) != 0){
		 *p_value=(*p_value)+1;
	 	}
 }

 /*
  * requirement: the hash table cells are lists
  * these functions add elements to the lists
  * not sure whether it really belongs to this file.. more like to a hash_common.c file
  */
void hash_add_int_to_list(hash_t map, char * key, int value){
	int * p_value = (int *)malloc(sizeof(int));
	*p_value = value;
	//	printf("add %d to %s\n", value, key);
	hash_add_something_to_list(map, key, p_value);
}

void hash_add_something_to_list(hash_t map, const char * key, void * something){
	list * somethings;
	if(hash_entry_by_key(map, key, (void **) (&somethings)) == 0){ // first time a value is added to this key
#ifdef debug_libchash_access
		printf("first time a value is added to this key %s\n", key); // DEB
#endif
		somethings = list_create();
		list_add(somethings, something);
		hash_insert(map, key, somethings, sizeof(list *));
	}
	else {// we add the current couple (read, position) to the list of the well mapped read for this fragment
		list_add(somethings, something);
#ifdef debug_libchash_access
		printf("new time a value is added to this key %s\n", key); // DEB
#endif

	}
	//	printf("size of list = %d\n", somethings->size);
}

/*
 * return a flat list of reads/positions, sorted by increasing positions
 * inspired by print_fragment_cover
 */
void* hash_list_sorted_by_positions(hash_t map)
{
	char *key=NULL;
	int position_fragment;
	list *mapping_positions;
	list *sorted_list_of_reads_and_positions=list_create();
	hash_iter iterator;
	iterator=hash_iterator_init(map); // get the first element of the Mapped hashmap

	// Go through the hashmap Mapped and for each fragment
	// get the reads that have seeds that match somewhere on this fragment
	if((long int)iterator!=-1){
		while(!hash_iterator_is_end(map, iterator)){     // go through all the mapped reads
			hash_iterator_return_entry(map, iterator, &key, (void**)&mapping_positions); // get key(read) and corresponding mapping positions (pwi)
			if(mapping_positions){
				cell_mapsembler *c=mapping_positions->first;
				while(c!=NULL){
					position_fragment=((*(int *) (c->val)));
					//printf("adding read %s with position %d to sorted_list\n",key,position_fragment);
					/*if (key[0]==NULL)
					{
						printf("empty read added to list\n");exit(1); // that's normal, that's error correction for now (..)
					}*/
					list_add(sorted_list_of_reads_and_positions,create_charint_couple(key,position_fragment));
					c=c->prox;
				}
			}
			iterator=hash_iterator_next(map, iterator);
		}
	}

	// printf("before sorting:\n");
	// list_of_charint_couple_print(sorted_list_of_reads_and_positions);

	list_quicksort_by_charint_couple(&sorted_list_of_reads_and_positions);

	//	printf("after sorting:\n");
	//list_of_charint_couple_print(sorted_list_of_reads_and_positions);

	return (void *)sorted_list_of_reads_and_positions;
}

/*
 * fully delete and free the hash table
 *
 * in hashmap, hash_delete recursively deletes while calling specific free function at each element,
 * so we do this too
 *
 */
int hash_delete(hash_t map, void (*specific_free)(const void *)){
	 hash_iter iterator=hash_iterator_init(map);

	 /*	 is the hash table empty?  */
	 if (iterator < 0)
		 return 0;

	char *key;
	void *data;

	/*
	 * free all lists
	 */
	while (!hash_iterator_is_end(map,iterator))
	{
		hash_iterator_return_entry(map,iterator,&key,(void **)&data);
		specific_free(data);
		iterator=hash_iterator_next(map,iterator);
	}

	/*
	 * free hash table
	 */
	hash_free(map);
	return 0;
}

/*
 * clear the hash table but don't delete it (ie. no need to reinitialize it)
 */
void hash_clear(hash_t map, void (*specific_free)(const void *)){
	 hash_iter iterator=hash_iterator_init(map);

	 /*	 is the hash table empty?  */
	 if (iterator < 0)
		 return;

	char *key;
	void *data;

	/*
	 * free all lists
	 */

	while (!hash_iterator_is_end(map,iterator))
	{
		hash_iterator_return_entry(map,iterator,&key,(void **)&data);
		specific_free(data);
		iterator=hash_iterator_next(map,iterator);
	}

	/*
	 * clear hash table
	 */
	hash_clear_structure(map);
}

#ifdef __cplusplus
}
#endif
