readSpotifyDataFiles <- function(f, verbose=TRUE) {

  if (!length(f))
    return(f)

  if (length(f) > 1) {
    ## RECURSE
    ALL.DTs <- lapply(f, readSpotifyDataFiles)
    ## Clean Up Names
    nms.all.list <- lapply(ALL.DTs, names)
    nms.all.u    <- unique(unlist(nms.all.list, use.names=FALSE))
    selfname_ (nms.all.u)

    ## Verbose info
    if (verbose)
    {
      nms.out <- sprintf("%-22s", nms.all.u)
      nms.out <- c(head(nms.out, 4), "\n", tail(nms.out, -4))
      cat("\nFlatterning files by table: \n\t", pasteC(nms.out, C="\t"), "\n", sep="")
    }

    ## Flatten, by table type
    DTs <- lapply(nms.all.u, function(report)
        # cat("rbinding report using do.call rbind: ", report, "\n")
        do.call(rbind, c(lapply(ALL.DTs, "[[", report), use.names=TRUE, fill=TRUE))
    )

    return(DTs)
  }

  ## Single file execution
  ## ---------------------------------------------------------------


  verboseMsg(verbose, sprintf("Processing file %100s    ", f), simple=TRUE)

  date_file <- dateFromSpotifyFile(f)
  if (any(is.na(date_file)))
    warning("There are NAs for date_file -- which might cause errors downstream -- for the following files: \n\t", pasteC(f[is.na(date_file)], C="\n\t"))

  raw <- readLines(f)

  hasHeader <- TRUE
  
  headerRows <- grep("^Data point", raw)
  hasInfoDT  <- (headerRows[[1]] > 1)
  # headerRows <- unique(c(1, grep("^Data point", raw)))
  lastRows   <- c(headerRows, length(raw)+1) -1


  skip <- headerRows - 1
  nrows <- diff(lastRows) - as.integer(hasHeader)

  ## ERROR CHECK
  stopifnot(length(skip) == length(nrows))
  stopifnot(length(raw) == sum(nrows) + (length(nrows) * hasHeader) + (lastRows[[1L]] * hasInfoDT))


  if (hasInfoDT) {
    ## New 20141020: 
    DT.info <- extractReportInfo(f, skip=0, nrows=lastRows[[1L]] - 1)
  } else 
    DT.info <- NULL


  ## READ IN THE DATA
  verboseMsg(verbose, "sub table:", simple=TRUE)
  DTs <- lapply(seq(skip), function(i) {
    verboseMsg(verbose, sprintf("  %02i", i), simple=TRUE) 
    if (nrows[[i]] == 0) 
      cbind(data.table("MISSING_DATA"="MISSING_DATA"), as.data.table(read.table(f, header=FALSE, sep="\t", stringsAsFactors=FALSE, skip=skip[[i]], nrows=1 , check.names=FALSE, comment.char=""))[, date_file := date_file], DT.info) 
    else 
      cbind(as.data.table(read.table(f, header=hasHeader, sep="\t", stringsAsFactors=FALSE, skip=skip[[i]], nrows=nrows[[i]] , check.names=FALSE, comment.char=""))[, date_file := date_file], DT.info) 
  })
  verboseMsg(verbose, "", endl=1, simple=TRUE) 

  nms <- sapply(DTs, function(DT) {if ("Data point" %in% names(DT)) DT[1, `Data point`] else if ("MISSING_DATA" %in% names(DT)) DT[1, MISSING_DATA] else ""})
  nms <- gsub(" ", "_", nms)

  if (is.character(nms))
    setattr(DTs, "names", nms)

  setattr(DTs, "date_file", date_file)
  DTs
}
