### This file is part of the 'foreign' package for R.

# Copyright (c) 2004-2015  R Development Core Team
# Enhancements Copyright (c) 2006 Stephen Weigand

#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  A copy of the GNU General Public License is available at
#  http://www.r-project.org/Licenses/

make.SAS.names <- function(varnames, validvarname = c("V7", "V6")){
  validvarname <- match.arg(validvarname)
  nmax <- if(validvarname == "V7") 32L else 8L

  x <- sub("^([0-9])", "_\\1", varnames)
  x <- gsub("[^a-zA-Z0-9_]", "_", x)
  x <- abbreviate(x, minlength = nmax)

  if (any(nchar(x) > nmax) || any(duplicated(x)))
      stop(gettextf("Cannot uniquely abbreviate the variable names to %d or fewer characters", nmax), domain = NA)
  names(x) <- varnames
  x
}

make.SAS.formats <- function(varnames){
  x <- sub("^([0-9])", "_\\1", varnames)
  x <- gsub("[^a-zA-Z0-9_]", "_", x)
  x <- sub("([0-9])$", "\\1f", x) # can't end in digit so append 'f'
  x <- abbreviate(x, minlength = 8L)

  if(any(nchar(x) > 8L) || any(duplicated(x)))
    stop("Cannot uniquely abbreviate format names to conform to ",
         " eight-character limit and not ending in a digit")
  names(x) <- varnames
  x
}

writeForeignSAS <- function(df, datafile, codefile, dataname = "rdata",
                          validvarname = c("V7", "V6"), libpath = NULL)
{
    ## FIXME: re-write this to hold a connection open
    factors <- vapply(df, is.factor, NA)
    strings <- vapply(df, is.character, NA)
    logicals <- vapply(df, is.logical, NA)
    dates <- vapply(df, FUN = function(x) inherits(x, "Date") || inherits(x, "dates") || inherits(x, "date"), NA)
    xdates <- vapply(df, FUN = function(x)  inherits(x, "dates") || inherits(x, "date"), NA)
    datetimes <- vapply(df, FUN = function(x) inherits(x, "POSIXt"), NA)

    varlabels <- names(df)
    varnames <- make.SAS.names(names(df), validvarname = validvarname)
    if (any(varnames != varlabels))
        message("Some variable names were abbreviated or otherwise altered.")


    dfn <- df
    if (any(factors))
        dfn[factors] <- lapply(dfn[factors], as.numeric)
    if (any(logicals))
        dfn[logicals] <- lapply(dfn[logicals], as.numeric)
    if (any(datetimes))
        dfn[datetimes] <- lapply(dfn[datetimes],
                                 function(x) format(x, "%d%b%Y %H:%M:%S"))
    if(any(xdates))
        dfn[xdates] <- lapply(dfn[xdates], function(x) as.Date(as.POSIXct(x)))

    ## https://kb.iu.edu/d/aydn
    write.table(dfn, file = datafile, row.names = FALSE, col.names = FALSE,
                sep = ",", quote = TRUE, na = "", qmethod = "double")
    lrecl <- max(vapply(readLines(datafile), nchar, 0L)) + 4L

    cat("* Written by R;\n", file = codefile)
    cat("* ", deparse(sys.call(-2L))[1L], ";\n\n",
        file = codefile, append = TRUE)
    if (any(factors)) {
        cat("PROC FORMAT;\n", file=codefile, append=TRUE)
        fmtnames <- make.SAS.formats(varnames[factors])
        fmt.values <- lapply(df[, factors, drop = FALSE], levels)
        names(fmt.values) <- fmtnames
        for (f in fmtnames) {
            cat("value", f, "\n", file = codefile, append = TRUE)
            values <- fmt.values[[f]]
            for(i in 1L:length(values)){
                cat("    ", i,"=", adQuote(values[i]), "\n",
                    file=codefile, append = TRUE)
            }
            cat(";\n\n",file=codefile,append = TRUE)
        }
    }

    if (!is.null(libpath)) {
    	cat("libname ROutput '", libpath, "';\n", file = codefile,
            append = TRUE, sep = "")
    	cat("DATA ROutput.", dataname, ";\n", file = codefile,
            append = TRUE, sep = "")
    } else cat("DATA ", dataname, ";\n", file = codefile, append = TRUE)

    if (any(strings)) {
        cat("LENGTH", file = codefile, append = TRUE)
        lengths <- vapply(df[,strings, drop = FALSE],
                          FUN = function(x) max(nchar(x), 1L, na.rm = TRUE), 0L)
        names(lengths) <- varnames[strings]
        for(v in varnames[strings])
            cat("\n", v, "$", lengths[v], file = codefile, append = TRUE)
        cat("\n;\n\n", file = codefile, append = TRUE)
    }

    if (any(dates)) {
        cat("INFORMAT", file = codefile, append = TRUE)
        for(v in varnames[dates])
            cat("\n", v, file = codefile, append = TRUE)
        cat("\n YYMMDD10.\n;\n\n", file = codefile, append = TRUE)
    }

    if (any(datetimes)) {
        cat("INFORMAT", file = codefile, append = TRUE)
        for(v in varnames[datetimes])
            cat("\n", v, file = codefile, append = TRUE)
        cat("\n DATETIME18.\n;\n\n", file = codefile, append = TRUE)
    }

    cat("INFILE ",adQuote(datafile),
        "\n     DSD",
        "\n     LRECL=", lrecl, ";\n",
        file = codefile ,append = TRUE)

    cat("INPUT", file = codefile, append = TRUE)
    for(v in 1L:ncol(df))
        cat("\n", varnames[v], file = codefile, append = TRUE)
        if(strings[v]) cat(" $ ", file = codefile, append = TRUE)

    cat("\n;\n", file = codefile, append = TRUE)

    for(v in 1L:ncol(df))
        if (varnames[v] != names(varnames)[v])
            cat("LABEL ", varnames[v],"=", adQuote(varlabels[v]), ";\n",
                file = codefile, append = TRUE)

    if (any(factors))
        for (f in 1L:length(fmtnames))
            cat("FORMAT", names(fmtnames)[f], paste(fmtnames[f],".", sep = ""),
                ";\n", file = codefile, append = TRUE)

    if (any(dates))
        for(v in varnames[dates])
            cat("FORMAT", v, "yymmdd10.;\n", file = codefile, append = TRUE)

    if (any(datetimes))
        for(v in varnames[datetimes])
            cat("FORMAT", v, "datetime18.;\n", file = codefile, append = TRUE)

    cat("RUN;\n", file=  codefile, append = TRUE)
}

