#' Class providing object with RegressionDiscontinuityClass
#'
#' @docType class
#' @export
#' @return Object of \code{\link[R6:R6Class]{R6::R6Class}} with methods for Regression discontinuity analysis
#' @format \code{\link[R6:R6Class]{R6::R6Class}} 
#' @examples
#' library (LRErdd)
#' library (R6)
#' library(ggplot2)
#' 
#' data(grants)
#' 
#' # Selection of the covariates of interest
#' cov <- c("HSTech", "hsgrade","Y2005")
#' # Create a RegressionDiscontinuityClass with the required information
#' rdo <- RegressionDiscontinuityClass$new( data = grants , forcing = "S",
#'                                          threshold = 15000 , Z1S1 = "lower",
#'                                          covariates = cov)
#' 
#' # New object structure
#' rdo
#' # Summary of the class
#' rdo$full_print()
#' # Summary of statistics
#' rdo$summary_statistics_mean()
#' 
#' \donttest{
#' rdo$bandwidth_selection(typerange="percentage",range_value=25,
#'                         num_it=50,plot=TRUE)
#' 
#' #Distributionpreandpostmatching
#' rdo$distribution_plot(typerange="percentage",range_value=25,
#'                       num_it=50,covariate="sex",typecov="binary")
#' 
#' # Summary of bandwidths
#' rdo$summary_bandwidth(buffers=c(100,500,1000),num_it=1000)
#' 
#' 
#' # Causal effect with the four methods :
#' # sharp FEP , sharp Neyman , Fuzzy Neyman and Fuzzy FEP
#' rdo$causal_effect(method='Sharp FEP',buffers=c(100,500,1000),
#'                   num_it=50,outcome="dropout",typeoutcome="binary",plot=TRUE)
#' 
#' rdo$causal_effect(method='Sharp Neyman',buffers=c(100,500,1000),
#'                   num_it=50,outcome="dropout",typeoutcome="binary",
#'                   sided="onesided",cin=0.05,plot=TRUE)
#' 
#' rdo$causal_effect(method='Fuzzy Neyman',buffers=c(100,500,1000),
#'                   num_it=50,outcome="dropout",typeoutcome="binary",
#'                   treatm_cov="W",plot=TRUE)
#' 
#' rdo$causal_effect(method='Fuzzy FEP',buffers=c(100,500,1000),
#'                   num_it=50,outcome="dropout",typeoutcome="binary",
#'                   sided="twosided",treatm_cov="W",num_it_fuzzy=5,
#'                   plot=TRUE)
#' }
#'
#' @field data data frame with needed variables
#' @field forcing Forcing variables name in data
#' @field threshold The value of the cutoff
#' @field Z1S1 lower or upper will be the corresponding to the group that will have 1 value
#' @field covariates Covariates that will be used 
#' @section Methods:
#' \describe{
#'   \item{\code{new(RegressionDiscontinuityClass)}}{This method is used to create object of this class with the next variables.}
#'
#'   \item{\code{initialize(data, forcing, threshold, Z1S1, covariates)}}{This method initialize the object with the corresponding information.}
#'   \item{\code{full_print()}}{This method print a summary of the object.}
#'   \item{\code{summary_statistics_mean()}}{This method a summary of the variables based on the mean.}
#'   \item{\code{bandwidth_selection(typerange = "percentage", range_value = 50, num_it = 50, plot = TRUE)}}{This methods defines the type of bandwidth that will be considered for the analysis.}
#'   \item{\code{distribution_plot(typerange = "percentage", range_value = 25, num_it = 50, covariate, typecov)}}{This method plots the selected variables and the bandwidths.}
#'   \item{\code{summary_bandwidth(buffers = c(100, 500, 1000), num_it = 50)}}{This method create a table with the summary of the selected variables.}
#'   \item{\code{causal_effect(method = 'Sharp FEP', buffers = c(100, 500, 1000), num_it = 50, outcome = "dropout", typeoutcome, sided = "onesided", cin = 0.05, treatm_cov = self$W, num_it_fuzzy = 5, plot = TRUE)}}{This method is the main function of the package, implementing the four modes of inference on causal effects.}
#' }
#' @field A Name of the variable indicating eligibility status
#' @field W Name of the treatment received variable
#' @import ggplot2
#' @importFrom cowplot plot_grid
#' @importFrom R6 R6Class
RegressionDiscontinuityClass <- R6::R6Class("RegressionDiscontinuityClass", 
    list(
      data = data.frame(matrix(1, ncol = 2, nrow = 1)),
      forcing = "S",
      A = "A",
      W = "W",
      threshold = 15000,
      Z1S1 = "upper",
      covariates = c("sex", "HSTech"),
      
    initialize = function(data, forcing, threshold, Z1S1, covariates) {
        stopifnot(is.data.frame(data))
        stopifnot(is.character(forcing))
        stopifnot(is.numeric(threshold))
        stopifnot(is.character(Z1S1))
        stopifnot(is.vector(covariates))
        
        self$data <- data
        self$forcing <- forcing
        self$threshold <- threshold
        self$Z1S1 <- Z1S1
        self$covariates <- covariates
      },
      
      full_print = function(){
        print("------------------------------------")
        print(paste("Names of data frame ->",names(self$data)))
        print(paste("Forcing variable ->",self$forcing))
        print(paste("Threshold ->",self$threshold))
        print(paste("Z1S1 ->",self$Z1S1))
        print(paste("Covariate ->",self$covariates))
        print("------------------------------------")
      },
      
      summary_statistics_mean = function(){
        forcingvar2 <- self$data[,self$forcing]
        
        s0<-self$threshold
        
        whichunder2 <- ifelse(self$Z1S1=="upper",0,1)
        
        if(whichunder2==1)
        {
          Z <- ifelse(forcingvar2 <= s0, 1, 0)
        } else  if(whichunder2==0)
        {
          Z <- ifelse(forcingvar2 <= s0, 0, 1)
        }
        
        S <- forcingvar2  #Forcing variable
        X <- self$data[, self$covariates, drop = FALSE]
   
        whichupper <- ifelse(whichunder2 == 1, 0, 1)
        
        MEAN<-
          cbind(apply(X,2, mean),
                apply(X[Z==0, , drop = FALSE],2, mean),apply(X[Z==1, , drop = FALSE],2, mean))
        
        MEAN <- data.frame(names(X),MEAN)
        rownames(MEAN) <- NULL
        colnames(MEAN)<- c("Name","All", "Z=0", "Z=1")
        
        MEAN[,2:4] <- round(MEAN[,2:4],2)
        MEAN$Name <- as.character(MEAN$Name)
        mmrow <- nrow(MEAN)+1
        MEAN[mmrow,1] <- "N"
        MEAN[mmrow,2] <- round(as.numeric(nrow(X)),0)
        MEAN[mmrow,3] <- round(as.numeric(nrow(X[Z==0, , drop = FALSE])),0)
        MEAN[mmrow,4] <- round(as.numeric(nrow(X[Z==1, , drop = FALSE])),0)
        
        MEAN
      },
      bandwidth_selection = function(typerange = "percentage", range_value = 50, num_it = 50, plot = TRUE){
         
         forcingvalues <- as.vector(self$data[,self$forcing])
         
         data2 <- data.frame(forcingvalues, self$data)
         names(data2)[1] <- "forcingvalues"
         
         threshold <- self$threshold
         
         ver <- cutting_range (forcingvalues, threshold, typerange, range_value)
         ver1 <- ver[[1]][1]
         ver2 <- ver[[2]][1]
         
         data3 <- self$data[forcingvalues >= ver1 & forcingvalues <= ver2, ]
         
            a <- data.frame(nrow(data3),ver1,ver2,ver2-ver1)
            names(a) <- c("Number of records","Down limit","Up limit", "Difference")

            bek <- as.numeric(ifelse(forcingvalues <= self$threshold,0,1))
            
            fmin <- min(forcingvalues,na.rm = TRUE)
            fmax <- max(forcingvalues,na.rm = TRUE)
            
            maxmax <- ifelse(fmin > fmax, fmin, fmax)
            bbw <- (ver2 - ver1)/2
            
            whichunder <- ifelse(self$Z1S1=="upper",0,1)
            
            lencov <- length(self$covariates)

            if(lencov == 1)
            {
               df <- data.frame(matrix(888,1,5))
               names(df) <- c("Covariate","Initial p-value","Initial Adjusted p-value","Bandwidth Specific p-value","Bandwidth Specific Adjusted p-value")
               
               
               if(nrow(data2)==nrow(data3))
               {
                  get <- rand_pajd (dataset = self$data, forcing_var_name = self$forcing, covariate = self$covariates, niter = num_it,
                                            cut_value = self$threshold, whichunder = whichunder, bandwidth = bbw)
                  
                  get2 <- get
               } else {
                  get <- rand_pajd (dataset = self$data, forcing_var_name = self$forcing, covariate = self$covariates, niter = num_it,
                                            cut_value = self$threshold, whichunder = whichunder, bandwidth = bbw)
                  
                  get2 <- rand_pajd (dataset = self$data, forcing_var_name = self$forcing, covariate = self$covariates, niter = num_it, 
                                             cut_value = self$threshold, whichunder = whichunder, bandwidth = maxmax)
               }
   
               df[1,1]<-self$covariates
               df[1,2]<-get2[,2]
               df[1,3]<-get2[,3]
               df[1,4]<-get[,2]
               df[1,5]<-get[,3]
               
               
            } else if(lencov > 1)
            {
               df <- data.frame(matrix(888,lencov,5))
               names(df) <- c("Covariate","Initial Pvalue","Initial Adj pvalue","Bandwidth specific Pvalue","Bandwidth specific Adj pvalue")
               
               covv <- self$covariates
      
               if(nrow(data2)==nrow(data3))
               {
                  get <- rand_pajd (dataset = data3, forcing_var_name = self$forcing, covariate = covv, niter = num_it, 
                                            cut_value = self$threshold, whichunder = whichunder, bandwidth = bbw)
                  get2 <- get
               } else {
                  get <- rand_pajd (dataset = data3, forcing_var_name = self$forcing, covariate = covv, niter = num_it, 
                                            cut_value = self$threshold, whichunder = whichunder, bandwidth = bbw)
                  get2 <- rand_pajd (dataset = data2, forcing_var_name = self$forcing, covariate = covv, niter = num_it, 
                                             cut_value = self$threshold, whichunder = whichunder, bandwidth = maxmax)
               }
      
               df <- data.frame(get2,get)
               df <- df[,-4]
               names(df) <- c("Variable","Initial Pvalue","Initial Adj pvalue","Bandwidth specific Pvalue","Bandwidth specific Adj pvalue")
               # }
            }
            
            b <- df
         
         # plot
         
         if(plot)
         {
   
         p <- ggplot2::ggplot(data2, aes(x=forcingvalues)) + 
               geom_histogram(aes(y = after_stat(density)), colour="black", fill="deepskyblue3")+
               geom_density(alpha=.6, fill="grey") +
               geom_vline(xintercept = c(ver1,ver2),colour = "red",linetype="dashed", linewidth = 1) +
               geom_vline(xintercept = c(self$threshold),colour = "black",linetype="dashed", linewidth = 1) + 
               labs(x = self$forcing)
         
            list(RangeInfo = a, pvalues = b, RangePlot = p)
         } else {
            list(RangeInfo = a, pvalues = b)
         }
   
      },
      distribution_plot = function(typerange = "percentage", range_value = 25, num_it = 50, covariate, typecov){
         dataori <- self$data
         forcingvalues <- dataori[,self$forcing]
         
         ver <- cutting_range (forcingvalues, self$threshold, typerange, range_value)
         ver1 <- ver[[1]][1]
         ver2 <- ver[[2]][1]
         
         data3 <- self$data[forcingvalues >= ver1 & forcingvalues <= ver2, ]
         forc3 <- data3[,self$forcing]
         
         
         Zori <- as.factor(ifelse(forcingvalues <= self$threshold, 1, 0))
         Z3 <- as.factor(ifelse(forc3 <= self$threshold, 1, 0))
         
         covaori <- data.frame(dataori[,covariate])
         cova3 <- data.frame(data3[,covariate])
         
         if(typecov=="binary")
         {
            covaori <- as.vector(covaori[,1])
            
            d <- as.data.frame(prop.table(table(data.frame(Zori,covaori)),1))
            d$Dataset <- "Complete"
            names(d) <- c("Var1", "Var2",   "Freq",   "Dataset")
            
            cova3 <- as.vector(cova3[,1])
            
            d2 <- as.data.frame(prop.table(table(data.frame(Z3,cova3)),1))
            d2$Dataset <- "Balanced"
            names(d2) <- c("Var1", "Var2",   "Freq",   "Dataset")
            
            d3 <- rbind(d,d2)
            d3 <- d3[order(d3$Var1),]
            
            d3$Dataset <- factor(d3$Dataset,levels = c("Complete","Balanced"))
            
            d3$Freq <- round(d3$Freq,2)
            
            gg <- ggplot(data=d3, aes(x=Var1, y=Freq, fill=Var2)) +
               geom_bar(stat="identity") +
               geom_text(aes(label=Freq), vjust=1.6, color="white",size=3.5)+
               scale_fill_brewer(palette="Paired") + theme(legend.position="none") + facet_grid(.~Dataset)
            
            
         } else if(typecov=="conti")
         {
            # numeric or integer or double
            datori2 <- data.frame(covaori,Zori)
            names(datori2) <- c("Independent", "Dependent")
            datori3 <- data.frame(cova3,Z3)
            names(datori3) <- c("Independent", "Dependent")
            
            a <- ggplot(datori2, aes(x=Independent, fill=Dependent)) +
               geom_density(alpha=0.4)
            
            
            b <- ggplot(datori3, aes(x=Independent, fill=Dependent)) +
               geom_density(alpha=0.4)
            
            gg <- cowplot::plot_grid(a, b, labels=c("A", "B"), ncol = 2, nrow = 1)
         }
         
         print(gg)
      },
      summary_bandwidth = function(buffers = c(100, 500, 1000), num_it = 50){
         whichunder <- ifelse(self$Z1S1=="upper",0,1)
            
         rand_pajd_bw (dataset = self$data, forcing_var_name = self$forcing, covariates = self$covariates,
                                           niter=num_it, bandwidth = buffers, cut_value = self$threshold, whichunder = whichunder)
         
      },
      
      causal_effect = function(method = 'Sharp FEP', buffers = c(100, 500, 1000), num_it = 50, outcome = "dropout", typeoutcome,
                                         sided = "onesided", cin = 0.05, treatm_cov = self$W, num_it_fuzzy = 5, plot = TRUE)
      {
         data2 <- self$data
         whichunder2 <- ifelse(self$Z1S1=="upper",0,1)
         
         if(method == 'Sharp FEP')
         {
            taula <- sharp_fep_bw (dataset = data2, forcing_var_name = self$forcing, Y_name = outcome, niter= num_it,
                                           bandwidth = buffers, cut_value = self$threshold, whichunder = whichunder2)
            tau3 <- list(taula$df)
            if(plot)
            {
               l3 <- length(taula$hist_data)
               l4 <- length(taula$hist_data[[1]])
               ve <- 0
               ge <- "k"
               obs <- 0
               for(h in 1:l3)
               {
                  ve <- c(ve,taula$hist_data[[h]])
                  ge <- c(ge,rep(taula$df[h,1],l4))
                  obs <- c(obs,rep(taula$df[h,4],l4))
               }
               ve <- ve[-1]
               ge <- ge[-1]
               obs <- obs[-1]

               dfg <- data.frame(ge,ve,obs)
               dfg$ge <- as.numeric(as.character(dfg$ge))
               dfg <- dfg[order(dfg$ge),]

               g1 <- ggplot(dfg, aes(x=ve))+
                  geom_histogram(color="black", fill="white") + xlab("Fisher test") + 
                  facet_grid(. ~ ge) + geom_vline(aes(xintercept=obs, color="red"),linetype="dashed") + theme(legend.position="none")
               tau3 <- list(table = taula$df, plot = g1)
            }
         }
         
         if(method == 'Sharp Neyman')
         {
            
            taula <- sharp_neyman_bw (dataset = data2,forcing_var_name = self$forcing, Y_name = outcome, niter = num_it,
                                              bandwidth = buffers, cut_value = self$threshold, whichunder = whichunder2, cin = cin)
            tau3 <- taula
            
            if(plot)
            {
               names(taula)[c(3,5,6)] <- c("ACE","L","U")
               taula <- taula[order(taula$Bandwidth),]
               taula$Bandwidth <- as.factor(taula$Bandwidth)
 
               g1 <- ggplot(taula, aes(x = Bandwidth, y = ACE)) +
                  geom_point(size = 4) +
                  geom_errorbar(data = taula, aes(x = Bandwidth, ymax = U, ymin = L), width = 0, linewidth = 1) + geom_hline(yintercept = 0,col= "red")
               tau3 <- list(table = taula, plot = g1)
            }
            
         }
         
         if(method == 'Fuzzy Neyman')
         {
            taula <- fuzzy_neyman_bw (dataset = data2, forcing_var_name = self$forcing, Y_name = outcome, niter = num_it,
                                              W = treatm_cov, bandwidth = buffers, cut_value = self$threshold, 
                                              whichunder = whichunder2, cin = cin)
            tau3 <- taula
            if(plot)
            {
               names(taula)[c(3,5,6)] <- c("ACE","L","U")
               taula <- taula[order(taula$Bandwidth),]
               taula$Bandwidth <- as.factor(taula$Bandwidth)
               
               g1 <- ggplot(taula, aes(x = Bandwidth, y = ACE)) +
                  geom_point(size = 4) +
                  geom_errorbar(data = taula, aes(x = Bandwidth, ymax = U, ymin = L),width = 0, linewidth = 1) + facet_grid(.~Estimand, scales = "free") + geom_hline(yintercept = 0,col= "red")
               
               tau3 <- list(table = taula, plot = g1)
               }
            }
         
         if(method == 'Fuzzy FEP')
         {
            
            if(sided=="onesided")
            {
               
               s0 <- self$threshold
               if (whichunder2 == 1) {
                  assigVar2 <- ifelse(data2[, self$forcing] <= s0, 1, 0)
               } else if (whichunder2 == 0) {
                  assigVar2 <- ifelse(data2[, self$forcing] <= s0, 0, 1)
               }
               
               data2[,treatm_cov] <- ifelse(assigVar2 == 0, 0, data2[,treatm_cov])
            }
            
            Y_name <- outcome
            W_name <- treatm_cov
            M2 = num_it_fuzzy
            
            data2$W <- data2[, W_name]
            data2$Y <- data2[, Y_name]
            bandwidth <- buffers
            len_bw <- length(bandwidth)
            cut_value <- self$threshold
            s0 <- cut_value
            
            if (whichunder2 == 1) {
               data2$assigVar <- ifelse(data2[, self$forcing] <= 
                                           cut_value, 1, 0)
            }
            else if (whichunder2 == 0) {
               data2$assigVar <- ifelse(data2[, self$forcing] <= 
                                           cut_value, 0, 1)
            }
            data2$Z <- data2$assigVar
            Sh <- data2[, self$forcing]
            pbalioak <- c()
            nak <- c()
            df2 <- data.frame(matrix(888, 1, 4))
            names(df2) <- c("Bandwidth", "Statistic: IV estimate of CACE", 
                            "Statistic: MLE of CACE", "Statistic: Posterior median of CACE")
            dfvec <- data.frame(matrix(888, 1, 3))
            names(dfvec) <- c("CACE.IV", "CACE.MLE", "CACE.PM")
            dfp <- 0

               n <- len_bw
               
               for (b in 1:len_bw) {

                  h <- bandwidth[b]/2
                  dat_bw <- data2[Sh >= s0 - h & Sh <= s0 + h, ]
                  N <- dim(dat_bw)[1]

                  zg <- dat_bw[, "assigVar"]
                  yg <- dat_bw[, Y_name]
                  wg <- dat_bw[, W_name]
                  if (typeoutcome == "binary") {
                     if (sided == "onesided") {
                        fu <- fuzzy_fep1sided(dat_bw, Y = yg, W = wg, 
                                              Z = zg, Y_name = Y_name, M2 = M2)
                        ft <- c(bandwidth[b], fu[[1]])
                     }
                     else if (sided == "twosided") {
                        fu <- fuzzy_fep2sided(dat_bw, Y = yg, W = wg, 
                                              Z = zg, Y_name = Y_name, M2 = M2)
                        ft <- c(bandwidth[b], fu[[1]])
                     }
                  }
                  else if (typeoutcome == "numeric") {
                     if (sided == "onesided") {
                        fu <- fuzzy_fep_numeric1sided(dat_bw, Y = yg, 
                                                      W = wg, Z = zg, Y_name = Y_name, M2 = M2)
                        ft <- c(bandwidth[b], fu[[1]])
                     }
                     else if (sided == "twosided") {
                        fu <- fuzzy_fep_numeric2sided(dat_bw, Y = yg, 
                                                      W = wg, Z = zg, Y_name = Y_name, M2 = M2)
                        ft <- c(bandwidth[b], fu[[1]])
                     }
                  }
                  df2 <- rbind(df2, ft)
                  dfvec <- rbind(dfvec, fu[[2]])
                  dfp <- rbind(dfp, fu[[3]])
               }
               df2 <- df2[-1, ]
               dfvec <- dfvec[-1, ]
               dfp <- dfp[-1, ]
               taula <- list(df2, dfvec, dfp)
               tau3 <- taula[[1]]
               
               if(plot)
               {
                  l3 <- length(taula[[2]][,1])*3 
                  l4 <- length(taula[[1]][,1]) 
                  ve <- 0
                  ge <- "k"
                  obs <- 0
                  izen <- "K"

                  ve <- c(ve,taula[[2]][,1],taula[[2]][,2],taula[[2]][,3]) # simulated data
                  
                  for(h in 1:l4)
                  {
                     ge <- c(ge,rep(taula[[1]][h,1],l3*3))  # bandwidth
                     izen <- c(izen,rep("CACE.IV",l3),rep("CACE.MLE",l3),rep("CACE.PM",l3)) # test
                     obs <- c(obs,rep(taula[[3]][h,1],l3), rep(taula[[3]][h,2],l3), rep(taula[[3]][h,3],l3))
                     
                  }
                  ve <- ve[-1]
                  ge <- ge[-1]
                  obs <- obs[-1]
                  izen <- izen[-1]
                  
                  dfg <- data.frame(ge,ve,obs,izen)
                  bnd <- as.numeric(as.character(dfg$ge))
                  dfg$ge <- bnd
                  dfg <- dfg[order(dfg$ge),]

                  p1 <- ggplot(dfg, aes(x=ve))+
                     geom_histogram(color="black", fill="white") + xlab("Simulated values") + 
                     facet_grid(izen ~ ge) + geom_vline(aes(xintercept=obs, color="red"),linetype="dashed") + theme(legend.position="none")
                  
                  tau3 <- list(table = taula, plot = p1)
                  
               }
         }
         tau3
      }
     )
   )
   
   
   
   
   
