# Input Parameters
csvpath <- input[[1]] #If Destring is an action then this must point to the new csv path

# IMP!!!: INPUT json STRUCUTRE: 'destring' should precede 'encodeMissing'. We can sort here and process. Can be done later
# IMP!!!: INPUT json STRUCUTRE: 'encodeMissing': There must be only one '<=' and '>=' operator. Again it can be done here to get min and max. Can be done later

#'[{"action":"recalcStats","name":"hhid","properties":[]},{"action":"recalcStats","name":"A0","properties":[{"catValue": "1", "catLabl": "male"}, {"catValue": "2", "catLabl": "female"}]}]'
typeOfActionsInput <- input[[2]]
libPath <- input[[3]]
workingDirectory <- input[[4]]

if(libPath == "MAC") {
  libPath <- NULL
  Sys.setlocale(category = "LC_ALL", locale = "UTF-8")
}

if (is.null(libPath) || libPath == '') {
  library(jsonlite)
  library(readr)
  library(plyr)
} else {
  .libPaths(libPath)
  library(jsonlite, lib.loc=libPath)
  library(readr, lib.loc=libPath)
  library(plyr, lib.loc=libPath)
}

setwd(workingDirectory)
source("fn.calculate.varStats.R")
source("fn.common.utilities.R")

typeOfActions <- fromJSON(typeOfActionsInput)
listOfVariables <- typeOfActions[,'name']

# read matching variables from CSV
matchingDF <- read.matchingVariables(listOfVariables, csvpath)

#CALCULATE stats FOR variables that were DESTRINGED
destringInputActionsPredicate <- typeOfActions$action=='destring'
destringInputActions <- typeOfActions[destringInputActionsPredicate,c('name','properties')]
destringedVarStatsList <- NA
if (length(destringInputActions) >= 1) {
  destringedVarStatsList <- lapply(destringInputActions$name,function(varName){        
    if (sapply(matchingDF[varName], is.numeric)) { 
      calculate.varStats(matchingDF,varName)
    }
  })
}

recalcStatsInputActionsPredicate <- typeOfActions$action=='recalcStats'
recalcStatsInputActions <- typeOfActions[recalcStatsInputActionsPredicate,c('name','properties')]
recalcStatsVarStatsList <- NA
if (length(recalcStatsInputActions) >= 1) {
  recalcStatsVarStatsList <- lapply(recalcStatsInputActions$name,function(varName){
    
    freqTable <- count(matchingDF, varName)
    colnames(freqTable) <- c("Value","freq")
    catList <- recalcStatsInputActions[recalcStatsInputActions$name == varName, 'properties']
    
    if(length(catList) >= 1){
       catgryDF <- as.data.frame(catList)
       catgry <- lapply(catgryDF$catValu, function(val){
         if(is.na(val)){
           catg <- catgryDF[is.na(catgryDF$catValu),]
           freq <-  freqTable[is.na(freqTable$Value), 'freq']
         }
         else{
           catg <- na.omit(catgryDF[catgryDF$catValu==val,])
           freq <- na.omit(freqTable[freqTable$Value==val, 'freq'])
         }
         if(length(freq) == 0 || is.na(freq)){
           freq <- 0
         }
        list(catValu=val,labl=catg$labl,labelled=TRUE,catStat=list(type="freq",text=freq))
       })
       append(calculate.varStats(matchingDF,varName), list(catgry=catgry))
     }    
  })
}

#CALCULATE stats FOR variables that were ENCODED WITH MISSING VALUES
encMissInputActionsPredicate <- typeOfActions$action=='encodeMissing'
encMissInputActions <- typeOfActions[encMissInputActionsPredicate,c('name','properties')]
encMissVarStatsList <- lapply(encMissInputActions$name,function(varName){
  
  # Replace the DF data with NA for user specified missing values
  missingData <- subset(encMissInputActions, name==varName)[,2][[1]]
  # missingData ->
  #    op  val val2
  # 1  =   999
  # 2  >=   40
  # 3  <=   57
  # 4  ..   60  100

  # apply missing conditions if any then calaculate statistics
  if(length(missingData) >0) {

    #. apply equal to operator
    equalOpPredicate <- missingData$op == '='
    equalOpData <- missingData[equalOpPredicate,]

    # if the type is character, then apply the missing equal to condition
    if(!sapply(matchingDF[varName], is.numeric)){
      replaceValPredicate <- matchingDF[,varName] %in% equalOpData$val
    } else {
      # type is numeric then parse the values to numeric and apply condition
      replaceValPredicate <- matchingDF[,varName] %in% as.numeric(equalOpData$val)

      #. apply range operator
      rangeOpPredicate <- missingData$op == '..'
      rangeOpData <- missingData[rangeOpPredicate,]
      if(length(rangeOpData$op) == 1){
        replaceValPredicate <- replaceValPredicate | (matchingDF[,varName] >= as.numeric(rangeOpData$val) & matchingDF[,varName] <= as.numeric(rangeOpData$val2))
      }
      
      #. apply greater than operator 
      gteOpPredicate <- missingData$op == '>='
      gteOpData <- missingData[gteOpPredicate,]   
      if (length(gteOpData$op) >= 1) {
        replaceValPredicate <- replaceValPredicate | (matchingDF[,varName] >= as.numeric(gteOpData$val))
      } 

      #. apply less than operator
      lteOpPredicate <- missingData$op == '<='
      lteOpData <- missingData[lteOpPredicate,]  
      if (length(lteOpData$op) >= 1) {
        replaceValPredicate <- replaceValPredicate | (matchingDF[,varName] <= as.numeric(lteOpData$val))
      }
    }
    # to remove NA from replaceValPredicate, to resolve the error of variables with NA(missing) values
    replaceValPredicate <- replaceValPredicate | is.na(replaceValPredicate)
    matchingDF[replaceValPredicate,varName]<-NA
  }
  #. calculate variable statistics
  calculate.varStats(matchingDF,varName)     
})

statsList <- list(destringedVarStatsList,encMissVarStatsList, recalcStatsVarStatsList)

# return number of records
statsJson <- toJSON(statsList,pretty=TRUE,force=TRUE,digits=22)

# write output to file, return may not work with large number of categories
# convert json to "UTF-8" format to avoid unicode issues.
write(encode.UTF(statsJson), typeOfActionsInput)

