#' A series of internal functions used by the Tabu search.
#' Directly accessing these functions is not advised unless you know what you are doing.
#'
#' @keywords internal
#'

# Internal function used to match parameters in label against
# those in a search table (ptab)
par.matches <- function(ptab, label, block = NULL) {

  # Collapse whitespace in label (if any)
  label <- gsub("[[:space:]]", "", label)

  # Look through lhsoprhs
  lhsoprhs <- paste(ptab$lhs, ptab$op, ptab$rhs, sep = "")

  # Look through labels
  matches <- label == lhsoprhs | label == ptab$label

  # Check against block
  if (!is.null(block)) {
    matches <- matches & ptab$block == block
  }
  return(matches)
}

# Internal function that modifies search table
manip.ptab <- function(ptab, label, task, nullval = NULL, block = NULL) {
  if (!task %in% c("remove", "free", "fix", "nullval")) {
    stop()
  }

  matches <- par.matches(ptab, label, block = block)

  nmatches <- sum(matches)
  if (nmatches > 1) {
    warning("More than one match for parameter found")
  } else if (nmatches < 1) {
    warning("No matches for parameter found")
  }

  if (task == "remove") {
    # Remove from table
    ptab <- ptab[!matches, ]
  } else if (task == "free") {
    # Set free
    ptab$free[matches] <- 1
  } else if (task == "fix") {
    # Fix
    ptab$free[matches] <- 0

    if (!is.null(nullval)) {
      ptab$nullval[matches] <- nullval
    }
  } else if (task == "nullval") {
    # Change nullval
    ptab$nullval[matches] <- nullval
  }

  return(ptab)
}

# Given a search table and a label, sets the parameter free in the search table
#
# ptab - search table
# label - combination of lhs, op, and rhs as would appear in lavaan model.syntax
#    or a parameter label from the label column of a lavaan parameter table
# block - optional numeric value specifying the group number to which the parameter corresponds
free.param <- function(ptab, label, block = NULL) {
  ptab <- manip.ptab(ptab, label, "free", block = block)
  return(ptab)
}

# Given a search table and a label, sets the parameter fixed in the search table
#
# ptab - search table
# label - combination of lhs, op, and rhs as would appear in lavaan model.syntax
#    or a parameter label from the label column of a lavaan parameter table
# nullval - optional numeric value specifying what the parameter should be set to when fixed (existing value in ptab is otherwise used)
# block - optional numeric value specifying the group number to which the parameter corresponds
fix.param <- function(ptab, label, nullval = NULL, block = NULL) {
  ptab <- manip.ptab(ptab, label, "fix", nullval = nullval, block = block)
  return(ptab)
}

# Given a search table and a label, removes the parameter from the search table
#
# ptab - search table
# label - combination of lhs, op, and rhs as would appear in lavaan model.syntax
#    or a parameter label from the label column of a lavaan parameter table
# block - optional numeric value specifying the group number to which the parameter corresponds
rm.param <- function(ptab, label, block = NULL) {
  ptab <- manip.ptab(ptab, label, "remove", block = block)
  return(ptab)
}

# Given a search table and a label, sets the value that the parameter would be
# fixed to if the parameter is fixed
#
# ptab - search table
# label - combination of lhs, op, and rhs as would appear in lavaan model.syntax
#    or a parameter label from the label column of a lavaan parameter table
# nullval - numeric value specifying what the parameter should be set to when fixed
# block - optional numeric value specifying the group number to which the parameter corresponds
nullval.param <- function(ptab, label, nullval, block = NULL) {
  ptab <- manip.ptab(ptab, label, "nullval", nullval = nullval, block = block)
  return(ptab)
}

# Given a fitted lavaan model and a search table, checks whether any of the
# parameters in the search table are involved in (in)equality constraints
check.const <- function(fitted.model, pars) {
  ptab <- lavaan::parTable(fitted.model)
  const <- ptab[ptab$op == "==" | ptab$op == ">" | ptab$op == "<", ]
  if (nrow(const) > 0) {
    plabels <- c(pars$label, pars$plabel)
    plabels <- plabels[plabels != ""]
    flag <- FALSE
    for (j in 1:nrow(const)) {
      var.lhs <- all.vars(parse(file = "", text = const$lhs[j]))
      var.rhs <- all.vars(parse(file = "", text = const$rhs[j]))
      if (any(plabels %in% c(var.lhs, var.rhs))) {
        flag <- TRUE
      }
    }
  } else {
    flag <- FALSE
  }
  return(flag)
}
