/****************************************************************************
 *                                                                          *
 *     Loki - Programs for genetic analysis of complex traits using MCMC    *
 *                                                                          *
 *             Simon Heath - University of Washington                       *
 *                                                                          *
 *                      August 1997                                         *
 *                                                                          *
 * loki_utils.c:                                                            *
 *                                                                          *
 * Utility routines for loki                                                *
 *                                                                          *
 * Copyright (C) Simon C. Heath 1997, 2000, 2002                            *
 * This is free software.  You can distribute it and/or modify it           *
 * under the terms of the Modified BSD license, see the file COPYING        *
 *                                                                          *
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <math.h>
#include <limits.h>

#include "utils.h"
#include "loki.h"
#include "loki_peel.h"

static int sort_sex;

size_t print_orig_family(FILE *fptr,const int id,const int fg)
{
	int fam;
	size_t sz;
	
	fam=id>0?id_array[id-1].fam_code:0;
	if(!fam) sz=1;
	else if(fam_recode.flag==ST_STRING) sz=strlen(fam_recode.recode[fam-1].string);
	else sz=(size_t)(1.000001+log((double)fam_recode.recode[fam-1].value)/log(10.0));
	if(fg) sz+=3;
	if(fptr) {
		if(fg) {
			if(!fam) (void)fputs("[*]:",fptr);
			else if(fam_recode.flag==ST_STRING) (void)fprintf(fptr,"[%s]:",fam_recode.recode[fam-1].string);
			else (void)fprintf(fptr,"[%d]:",fam_recode.recode[fam-1].value);
		} else {
			if(!fam) (void)fputs("*",fptr);
			else if(fam_recode.flag==ST_STRING) (void)fprintf(fptr,"%s",fam_recode.recode[fam-1].string);
			else (void)fprintf(fptr,"%d",fam_recode.recode[fam-1].value);
		}
	}
	return sz;
}

void print_marker_name(FILE *fptr,const int i)
{
	if(marker[i].index) fprintf(fptr,"%s(%d)",marker[i].name,marker[i].index);
	else fputs(marker[i].name,fptr);
}

int has_orig_id(int i)
{
	int j=0;
	
	if(i) {
		if(id_recode.flag==ST_STRING) {
			if(id_recode.recode[i-1].string) j=1;
		} else if(id_recode.recode[i-1].value!=INT_MAX) j=1;
	}
	return j;
}

size_t print_orig_id1(FILE *fptr,const int i)
{
	size_t sz;
	int j;
	double z;
	char *s;
	
	if(!i) sz=1;
	else if(id_recode.flag==ST_STRING) {
		s=id_recode.recode[i-1].string;
		sz=s?strlen(s):1;
	} else {
		j=id_recode.recode[i-1].value;
		if(j==INT_MAX) sz=1;
		else {
			z=(double)abs(j);
			sz=(size_t)(1.000001+log(z)/log(10.0));
			if(j<0) sz++;
		}
	}
	if(fptr) {
		if(!i) (void)fputc('*',fptr);
		else {
			if(id_recode.flag==ST_STRING) {
				s=id_recode.recode[i-1].string;
				(void)fprintf(fptr,"%s",s?s:"*");
			} else {
				j=id_recode.recode[i-1].value;
				if(j==INT_MAX) (void)fputc('*',fptr);
				else (void)fprintf(fptr,"%d",j);
			}
		}
	}
	return sz;
}

size_t print_orig_id(FILE *fptr,const int i)
{
	size_t sz=0;
	
	if(family_id) sz=print_orig_family(fptr,i,1);
	sz+=print_orig_id1(fptr,i);
	return sz;
}

size_t get_max_idlen(void)
{
	int i;
	size_t sz;
	static size_t max;
	
	if(!max) {
		for(i=0;i<ped_size;i++) {
			sz=(int)print_orig_id(0,i+1);
			if(sz>max) max=sz;
		}
	}
	return max;
}

void print_orig_triple(FILE *fptr,const int i)
{
	if(family_id) {
		(void)print_orig_family(fptr,i,0);
		(void)fputc(' ',fptr);
	}
	if(i)	{
		(void)print_orig_id1(fptr,i);
		(void)fputc(' ',fptr);
		(void)print_orig_id1(fptr,id_array[i-1].sire);
		(void)fputc(' ',fptr);
		(void)print_orig_id1(fptr,id_array[i-1].dam);
	}
	else (void)fputs("* * *",fptr);
}

void print_orig_allele_id(FILE *fptr,const int i)
{
	if(i>0) {
		(void)print_orig_id(fptr,i);
		(void)fputc('m',fptr);
	} else {
		(void)print_orig_id(fptr,-i);
		(void)fputc('p',fptr);
	}
}

void print_allele_type1(FILE *fptr,const int locus,const int j)
{
	if(j==marker[locus].lumped) (void)fputs(LUMPED_ALLELE,fptr);
	else if(marker[locus].rec_flag==ST_STRING) (void)fputs(marker[locus].recode[j].string,fptr);
	else (void)fprintf(fptr,"%d",marker[locus].recode[j].value);
}

/* Print the original allele code for (recoded) allele i */
void print_allele_type(FILE *fptr,const int locus,const int comp,const int i)
{
	print_allele_type1(fptr,locus,allele_trans[locus][comp][i]);
}

/* Print original code for maternal or paternal allele (depending on flag) for
 * individual id.  Takes account of set recoding */
void print_allele_name(FILE *fptr,const int id,const int locus,const int flag)
{
	lk_ulong c;
	int i,j,allele,comp;
	
	comp=id_array[id].comp;
	allele=id_array[id].allele[flag]-1;
	c=LK_ONE<<allele;
	if(c&req_set[flag][locus][id]) {
		i=j=0;
		c=req_set[flag][locus][id];
		while(c)	{
			if(c&1) {
				(void)fputc(j++?',':'[',fptr);
				print_allele_type(fptr,locus,comp,i);
			}
			i++;
			c>>=1;
		}
		(void)fputc(']',fptr);
	} else print_allele_type(fptr,locus,comp,allele);
}

void set_sort_sex(const int s)
{
	sort_sex=s;
}

/* Comparison function for qsort(), used to sort loci into position
 * order within a linkage group */
int cmp_loci_pos(const void *s1,const void *s2)
{
	double x1,x2;
	int i;
	
	i=*((const int *)s1);
	if(i>=0) x1=marker[i].locus.pos[sort_sex];
	else x1=tlocus[-1-i].locus.pos[sort_sex];
	i=*((const int *)s2);
	if(i>=0) x2=marker[i].locus.pos[sort_sex];
	else x2=tlocus[-1-i].locus.pos[sort_sex];
	if(x1<x2) return -1;
	if(x1>x2) return 1;
	return 0;
}

/* Get list of loci (marker + trait) in linkage group.  Returns number found
 * in count */
void get_locuslist(int *perm,const int link,int *count,int flag)
{
	int i,j;

	for(i=0;i<linkage[link].n_markers;i++) perm[i]=linkage[link].mk_index[i];
	if(!flag) for(j=0;j<n_tloci;j++) if((tlocus[j].locus.flag&TL_LINKED) && tlocus[j].locus.link_group==link) perm[i++]= -(j+1);
	*count=i;
}

