#' Compute probabilty of data knowing the genotype.
#' 
#' @param obs.a the number of observed `A`
#' @param obs.c the number of observed `C`
#' @param obs.g the number of observed `G`
#' @param obs.t the number of observed `T`
#' @param haplotype.1 a character indicating the first haplotype
#'                    (A, C, G, ou T)
#' @param haplotype.2 a character indicating the second haplotype
#'                    (A, C, G, ou T)
#' @param sequencing.error a numeric ranging betweing 0 and 1
#'                    indicating the sequencing error rate                   
#' 
#' @return returns the log of the probality of the data 
#'         knowning the genotype.
#'         
#' @examples 
#'     pdata_genotype(4,3,0,1,'A','C',0.01)
#'     
#' @export
pdata_genotype = function(obs.a,obs.c,obs.g,obs.t,
                          haplotype.1,haplotype.2,
                          sequencing.error) {
  
}

#' Compute probability of nucleotide observation knowning genotype.
#' 
#' @param haplotype.1 a character indicating the first haplotype
#'                    (A, C, G, ou T)
#' @param haplotype.2 a character indicating the second haplotype
#'                    (A, C, G, ou T)
#' @param sequencing.error a numeric ranging betweing 0 and 1
#'                    indicating the sequencing error rate                   
#' 
#' @return a vector of four probabilities for A, C, G and T 
#'         observations
#'
#' @examples 
#'   pnuc_genotype("A","T",0.01)
#'   
#' @export
pnuc_genotype = function(haplotype.1,haplotype.2,
              sequencing.error) {
  
  if (haplotype.1 == haplotype.2)
    rep = pnuc_homo_genotype(haplotype.1,sequencing.error)
  else
    rep = pnuc_hetero_genotype(haplotype.1,
                               haplotype.2,
                               sequencing.error)
  
  return(rep)
}

#' Compute probability of nucleotide observation 
#' knowning an heterozygote genotype.
#' 
#' @param haplotype.1 a character indicating the first haplotype
#'                    (A, C, G, ou T)
#' @param haplotype.2 a character indicating the second haplotype
#'                    (A, C, G, ou T)
#' @param sequencing.error a numeric ranging betweing 0 and 1
#'                    indicating the sequencing error rate                   
#' 
#' @return a vector of four probabilities for A, C, G and T 
#'         observations
#'
#' @examples 
#'   pnuc_hetero_genotype("A","T",0.01)
#'   
#' @export
pnuc_hetero_genotype = function(haplotype.1,haplotype.2,
              sequencing.error) {
  pnuc = rep(sequencing.error/6*5,4)
  names(pnuc)=c("A","C","G","T")
  pnuc[haplotype.1] = 0.5 - sequencing.error/6*5
  pnuc[haplotype.2] = pnuc[haplotype.1]
  
  return(pnuc)
  
}

#' Compute probability of nucleotide observation 
#' knowning an homozygote genotype.
#' 
#' @param haplotype   a character indicating the first haplotype
#'                    (A, C, G, ou T)
#' @param sequencing.error a numeric ranging betweing 0 and 1
#'                    indicating the sequencing error rate                   
#' 
#' @return a vector of four probabilities for A, C, G and T 
#'         observations
#'
#' @examples 
#'   pnuc_hetero_genotype("A","T",0.01)
#'   
#' @export
pnuc_homo_genotype = function(haplotype,
              sequencing.error) {
  
  pnuc = rep(sequencing.error/3,4)
  names(pnuc)=c("A","C","G","T")
  pnuc[haplotype] = 1 - sequencing.error
  
  return(pnuc)
}


#' Computes pior probability for a genotype.
#' 
#' This function estimates the prior probabilty of a genotype
#' from the prior probability of heterozygosis by supposing
#' equiprobability of genotypes in a class 
#' (heterozygote vs homozygote).
#' 
#' @param haplotype.1 a character indicating the first haplotype
#'                    (A, C, G, ou T)
#' @param haplotype.2 a character indicating the second haplotype
#'                    (A, C, G, ou T)
#' @param pHe         A numiric value ranging between 0 and 1,
#'                    the heterozygote pior probability
#' @param log logical; if TRUE, probabilities p are given as log(p).
#'                    
#' @return A numiric value ranging between 0 and 1, the prior
#'         probability of the genotype specified by 
#'         haplotype.1 and haplotype.2 parameters.
#'         
#' @examples
#'   prior.genotype("A","C",1e-3)
#'   
#' @export
prior.genotype = function(haplotype.1,haplotype.2,pHe, log=FALSE) {
  if (haplotype.1 == haplotype.2)
    p = (1 - pHe) / 4
  else
    p = pHe / 6
    
  if (log)
    p = log(p)
  
  return(p)
}


all.genotypes = function() {
  g = matrix(0,nrow=10,ncol = 2)
  i=0
  for (g1 in c("A","C","G","T"))
    for (g2 in c("A","C","G","T"))
      if (g1 <= g2) {
        i = i+1
        g[i,1]=g1
        g[i,2]=g2
      }
      
  return(g)
}

#' @export
genotyping = function(sequencing,sequencing.error,phetero) {
  genotypes = all.genotypes()
  
  pnucs = t(mapply(pnuc_genotype,genotypes[,1],
                                 genotypes[,2],sequencing.error))
  colnames(pnucs)=c("pA","pC","pG","pT")
  rownames(pnucs)=apply(genotype:::all.genotypes(),
                        MARGIN = 1,
                        paste0,collapse="")
  
  genotype.priors = as.numeric(mapply(prior.genotype,
                                      genotypes[,1],
                                      genotypes[,2],
                                      phetero,
                                      TRUE))
  
  
  probas = matrix(0.0,nrow = nrow(sequencing),
                  ncol = nrow(pnucs))
  
  
  ACGT.cols = match(c('A','C','G','T'),names(sequencing))
  
  for (i in seq_len(nrow(sequencing))) {
      obs = as.numeric(sequencing[i,ACGT.cols])
    for (j in seq_len(nrow(pnucs))) {
      theo= as.numeric(pnucs[j,1:4])
       probas[i,j] = dmultinomialACGT(obs[1],obs[2],obs[3],obs[4],
                              theo[1],theo[2],theo[3],theo[4],
                              log = TRUE
                             )
    }
  }
  colnames(probas) = rownames(pnucs)
  
  probas = sweep(probas,
                 MARGIN = 2,
                 STATS = genotype.priors,
                 FUN = "+")
  
  obs.prior = apply(probas,MARGIN = 1,log_sum_exp)
  
  probas = sweep(probas,
                 MARGIN = 1,
                 STATS = obs.prior,
                 FUN = "-")
  
  second = function(x) max(x[-which.max(x)])
  
  best.genotype   = apply(probas, MARGIN = 1, which.max)
  
  second.genotype = apply(probas, MARGIN = 1, function(x) which(x==second(x))[1])
  

  g.good = rownames(pnucs)[best.genotype]
  haplotype1 = substr(as.character(g.good),1,1)
  haplotype2 = substr(as.character(g.good),2,2)
  
  g.alt  = rownames(pnucs)[second.genotype]

  lod = apply(probas, MARGIN = 1, max)
  score = lod - apply(probas, MARGIN = 1, second)
  
  true = apply(sequencing[,1:2],MARGIN = 1,function(x) paste0(sort(x),collapse = ""))
   
  sequencing = cbind(sequencing,probas,g.good,haplotype1,haplotype2,g.alt,lod,score = exp(score),ok=true==g.good)
  
  return(sequencing)
}
