#' @export

fitBIRT=function(z,model=c("beta","samejima","simplex","normal"),inflated=c("auto","zero","one","both","none"),iter=2000,warmup=NULL,nchains=1,prior=NULL,silent=FALSE,...){
  model=match.arg(model)
  inflated=match.arg(inflated)
  args=list(...)
  if(silent) refresh=0
  else{
    if(is.null(args$refresh)) refresh=max(iter/10, 1) else refresh=args$refresh
  }

  if(any(z>1,na.rm=TRUE) | any(z<0,na.rm=TRUE)) stop("Item scores shoud be between 0 and 1")

  if(inflated=="none") if(any(z==0,na.rm=TRUE)|any(z==1,na.rm=TRUE)) stop("There are boundary responses in your data, use zero and/or one inflation, or inflated=\"auto\"")
  if(inflated=="zero") if(any(z==1,na.rm=TRUE)) stop("You use zero inflation, but there is one inflation in your data (as well)")
  if(inflated=="one") if(any(z==0,na.rm=TRUE)) stop("You use one inflation, but there is one inflation in your data (as well)")

   N=nrow(z)
   nit=ncol(z)

   if(inflated=="zero" | inflated=="both") zero_inf=1:nit
   if(inflated=="one" | inflated=="both") one_inf=1:nit
   if(inflated=="zero" | inflated=="none") one_inf=rep(0,nit)
   if(inflated=="one" | inflated=="none") zero_inf=rep(0,nit)

   auto=FALSE
   if(inflated=="auto"){
     auto=TRUE
     zeros=apply(z,2,min,na.rm=TRUE)
     zero_inf=1*(zeros==0)

     ones=apply(z,2,max,na.rm=TRUE)
     one_inf=1*(ones==1)

     zero_inf[zero_inf==1]=1:sum(zero_inf)
     one_inf[one_inf==1]=1:sum(one_inf)
     if(max(zero_inf)==0 & max(one_inf)==0) inflated="none"
     if(max(zero_inf)==0 & max(one_inf)!=0) inflated="one"
     if(max(zero_inf)!=0 & max(one_inf)==0) inflated="zero"
     if(max(zero_inf)!=0 & max(one_inf)!=0) inflated="both"
   }

   if(is.null(prior)){
     prior=list(sT=1,sA=10,sB=10,sP=10)
     if(inflated!="none") prior$sG=10
   }
   else{
     if(!is.list(prior)) stop("'prior' should be a list")
     nn=names(prior)
     if(!("sT"%in%nn)) prior$sT=1
     if(!("sA"%in%nn)) prior$sA=10
     if(!("sB"%in%nn)) prior$sB=10
     if(!("sP"%in%nn)) prior$sP=10
     if(inflated!="none" & (!("sG"%in%nn))) prior$sG=10
    }


   par_names=c("theta","phi","alpha","beta", "gamma0","gamma1")
   if(inflated=="none") par_names=par_names[1:4]
   if(inflated=="one") par_names=par_names[c(1:4,6)]
   if(inflated=="zero") par_names=par_names[1:5]
   if(inflated=="both") par_names=par_names[1:6]

   inits=list()
   for(jj in 1:nchains) inits[[jj]]=list(
     ln_alpha=stats::rnorm(nit,0,.2),
     ln_phi=stats::rnorm(nit,0,.2),
     beta=stats::rnorm(nit,1,.5),
     theta=stats::rnorm(N,0,1),
     gamma0=-abs(stats::rnorm(max(zero_inf),1,.2)),
     gamma1=(abs(stats::rnorm(max(one_inf),1,.2))))


   if(inflated=="none") for(jj in 1:nchains) inits[[jj]]=inits[[jj]][-c(5,6)]
   if(inflated=="zero" | max(zero_inf)==1) for(jj in 1:nchains) inits[[jj]]=inits[[jj]][-c(6)]
   if(inflated=="one"| max(one_inf)==1) for(jj in 1:nchains) inits[[jj]]=inits[[jj]][-c(5)]
   if(max(zero_inf)==1) for(jj in 1:nchains) inits[[jj]]=inits[[jj]][-c(5)]
   if(max(one_inf)==1) for(jj in 1:nchains) inits[[jj]]=inits[[jj]][-c(6)]

   if(is.null(warmup)) warmup = floor(iter/2)
   z[is.na(z)]=-999

   cap_first <- function(x) {
     paste0(toupper(substr(x, 1, 1)), substr(x, 2, nchar(x)))
   }

   if(inflated=="none") input=append(list(N=N,z=z,nit=nit),prior)
   else input=append(list(N=N,z=z,nit=nit,zero_inf=zero_inf,one_inf=one_inf,n_zero=max(zero_inf),n_one=max(one_inf)),prior)

   if(!silent){
     cat("Fitting the",cap_first(model),"Bounded Continuous IRT model")
     if(inflated!="none" & inflated!="both") cat(" with",paste0(cap_first(inflated), "-inflation"))
     if(inflated=="both") cat(" with both Zero- and One-inflation")
     cat(" . . .\n\n")
     if(auto & (inflated=="zero"|inflated=="both")) cat("     Zero inflation found for items: ",which(zero_inf>0),"\n")
     if(auto & (inflated=="one"|inflated=="both"))  cat("     One inflation found for items: ",which(one_inf>0),"\n")
     cat("\n")
   }

   if(model=="samejima"){
     z0=z
     z0[z0!=0 & z0!=1 & z0!=-999]=stats::qlogis(z[z!=0 & z!=1 & z!=-999])
     input$z=z0
     stanmodels$SamejimaIRT=stanmodels$NormalIRT
     stanmodels$SamejimaIRT_inf=stanmodels$NormalIRT_inf
     if(inflated=="none") fit <- rstan::sampling(stanmodels$SamejimaIRT, iter=iter, warmup=warmup, chains=nchains,
                                                 data = input ,init=inits,pars=par_names,refresh=refresh,...)
     else fit <- rstan::sampling(stanmodels$SamejimaIRT_inf, iter=iter, warmup=warmup, chains=nchains,
                                 data = input ,init=inits,pars=par_names,refresh=refresh,...)
   }
   if(model=="beta"){
     if(inflated=="none") fit <- rstan::sampling(stanmodels$BetaIRT, iter=iter, warmup=warmup, chains=nchains,
                                                 data = input,init=inits,pars=par_names,refresh=refresh,...)
     else fit <- rstan::sampling(stanmodels$BetaIRT_inf, iter=iter, warmup=warmup, chains=nchains,
                                 data = input,init=inits,pars=par_names,refresh=refresh,...)
   }
   if(model=="simplex"){
    if(inflated=="none") fit <- rstan::sampling(stanmodels$SimplexIRT, iter=iter, warmup=warmup, chains=nchains,
                                                data = input,init=inits,pars=par_names,refresh=refresh,...)
    else fit <- rstan::sampling(stanmodels$SimplexIRT_inf, iter=iter, warmup=warmup, chains=nchains,
                                data = input,init=inits,pars=par_names,refresh=refresh,...)
   }
   if(model=="normal"){
     if(inflated=="none") fit <- rstan::sampling(stanmodels$NormalIRT, iter=iter, warmup=warmup, chains=nchains,
                                                 data = input,init=inits,pars=par_names,refresh=refresh,...)
     else fit <- rstan::sampling(stanmodels$NormalIRT_inf, iter=iter, warmup=warmup, chains=nchains,
                                 data = input,init=inits,pars=par_names,refresh=refresh,...)
    }

   theta.est=apply(rstan::extract(fit,"theta")$theta,2,mean)
   beta.est=apply(rstan::extract(fit,"beta")$beta,2,mean)
   alpha.est=apply(rstan::extract(fit,"alpha")$alpha,2,mean)
   phi.est=apply(rstan::extract(fit,"phi")$phi,2,mean)
   if(inflated=="zero" |inflated=="both"){
     gamma0_c.est=apply(rstan::extract(fit,"gamma0")$gamma0,2,mean)
     gamma0.est=zero_inf+-Inf
     gamma0.est[zero_inf>0]=gamma0_c.est
   } else gamma0.est=NULL

   if(inflated=="one"|inflated=="both"){
     gamma1_c.est=apply(rstan::extract(fit,"gamma1")$gamma1,2,mean)
     gamma1.est=one_inf+Inf
     gamma1.est[one_inf>0]=gamma1_c.est
   } else gamma1.est=NULL

   theta.sd=apply(rstan::extract(fit,"theta")$theta,2,stats::sd)
   beta.sd=apply(rstan::extract(fit,"beta")$beta,2,stats::sd)
   alpha.sd=apply(rstan::extract(fit,"alpha")$alpha,2,stats::sd)
   phi.sd=apply(rstan::extract(fit,"phi")$phi,2,stats::sd)
   if(inflated=="zero" |inflated=="both"){
     gamma0_c.sd=apply(rstan::extract(fit,"gamma0")$gamma0,2,stats::sd)
     gamma0.sd=zero_inf
     gamma0.sd[zero_inf>0]=gamma0_c.sd
   } else gamma0.sd=NULL

   if(inflated=="one" |inflated=="both"){
     gamma1_c.sd=apply(rstan::extract(fit,"gamma1")$gamma1,2,stats::sd)
     gamma1.sd=one_inf
     gamma1.sd[one_inf>0]=gamma1_c.sd
   } else gamma1.sd=NULL



   item_out=cbind(alpha.est,alpha.sd,beta.est,beta.sd,phi.est,phi.sd,gamma0.est,gamma0.sd,gamma1.est,gamma1.sd)
   colnames(item_out)[1:6]=c("alpha","sd","beta","sd","phi","sd")
   rownames(item_out)=paste("item",1:nit,sep="")
   if(inflated=="zero" |inflated=="both") colnames(item_out)[7:8]=c("gamma0","sd")
   if(inflated=="both") colnames(item_out)[9:10]=c("gamma1","sd")
   if(inflated=="one") colnames(item_out)[7:8]=c("gamma1","sd")

   theta_out=cbind(theta.est,theta.sd)
   colnames(theta_out)=c("theta_p","sd")

   total_output=list(output=item_out,theta=theta_out,mcmc=fit,z=z,model=model,inflated=inflated,prior=prior)
   class(total_output)="BoundIRT"
   return(total_output)
}
