msaPrettyPrint <- function(x, y, output=c("pdf", "tex", "dvi", "asis"),
                           subset=NULL, file=NULL, alFile=NULL,
                           askForOverwrite=TRUE, psFonts=FALSE, code=NA,
                           paperWidth=11, paperHeight=8.5, margins=c(0.1, 0.3),
                           shadingMode=c("identical", "similar", "functional"),
                           shadingModeArg=NA,
                           shadingColors=c("blues", "reds", "greens", "grays",
                                           "black"),
                           showConsensus=c("bottom", "top", "none"),
                           consensusColors=c("ColdHot", "HotCold", "BlueRed",
                                             "RedBlue", "GreenRed", "RedGreen",
                                             "Gray"),
                           consensusThreshold=50,
                           showLogo=c("top", "bottom", "none"),
                           logoColors=c("chemical", "rasmol", "hydropathy",
                                        "structure", "standard area",
                                        "accessible area"),
                           showLogoScale=c("none", "leftright",
                                           "left", "right"),
                           showNames=c("left", "right", "none"),
                           showNumbering=c("right", "left", "none"),
                           showLegend=TRUE, furtherCode=NA, verbose=FALSE,
                           index=TRUE)
{
    xname <- deparse(substitute(x))
    output <- match.arg(output)

    if (is.null(file))
    {
        if (length(grep("[^\\w]", xname, perl=TRUE)) > 0)
        {
            warning("Cannot use default file name '", xname, ".", output,
                    "' because it would contain invalid characters => ",
                    "resorting to 'msaPrettyPrintOutput.", output, "'!")
            xname <- "msaPrettyPrintOutput"
        }

        file <- paste(xname, output, sep=".")
    }

    if (is.null(alFile))
        alFile <- tempfile(pattern="seq", tmpdir=tempdir(), fileext=".fasta")
    else if (is.character(alFile) &&
             substr(alFile, nchar(alFile) - 5, nchar(alFile)) == ".fasta")
    {
        if (askForOverwrite && file.exists(alFile))
        {
            message("File ", alFile, " exists. Overwrite? (y/N)")

            answer <- try(tolower(scan(what=character(), nmax=1,
                                       quiet=TRUE)), silent=TRUE)

            if (nchar(answer) != 1 || substr(answer, 1, 1) != "y")
                return(invisible(NULL))
        }
    }
    else
        stop("The parameter alFile has an invalid argument!")

    if (!is(x, "MultipleAlignment"))
        stop("The parameter x has an invalid argument! \n",
             "x must be a multiple alignment object!")

    if (output != "asis")
    {
        if (!is.numeric(paperWidth) || length(paperWidth) != 1 ||
            paperWidth <= 0)
            stop("The parameter paperWidth must be ",
                 "single positive number (unit: inches)!")

        if (!is.numeric(paperHeight) || length(paperHeight) != 1 ||
            paperHeight <= 0)
            stop("The parameter paperHeight must be ",
                 "single positive number (unit: inches)!")

        if (!is.numeric(margins) || length(margins) != 2)
            stop("The parameter margins must be ",
                 "two positive numbers (unit: inches)!")
    }

    if (!identical(subset, NULL) && !identical(subset, NA))
    {
        if (is.numeric(subset))
        {
            if (max(subset) < .Machine$integer.max)
                subset <- as.integer(subset)
            else
               stop("One or more values for parameter subset ",
                    "are larger than integer!")
        }
        else if (!is.integer(subset))
            stop("The parameter subset has an invalid argument!")

        if (length(subset) < 2)
            stop("The parameter subset is expected to be \n",
                 " a vector with at least 2 entries!")

        if (!all(subset %in% 1:nrow(x)))
            stop("Some values in parameter subset are out of range!")
    }
    else if (length(rowmask(x)) > 0)
    {
        if (setdiff(IRanges(start=1, end=nrow(x)), rowmask(x))
            <= .Machine$integer.max)
            subset <- as.integer(setdiff(IRanges(start=1, end=nrow(x)),
                                         rowmask(x)))
        else
            stop("There is no typecast possible in parameter subset!")
    }
    else
        subset <- 1:nrow(x)

    shadingMode <- match.arg(shadingMode)
    shadingColors <- match.arg(shadingColors)
    showConsensus <- match.arg(showConsensus)
    consensusColors <- match.arg(consensusColors)
    showLogo <- match.arg(showLogo)
    logoColors <- match.arg(logoColors)
    showLogoScale <- match.arg(showLogoScale)
    showNames <- match.arg(showNames)
    showNumbering <- match.arg(showNumbering)

    if (!is.numeric(consensusThreshold) || length(consensusThreshold) < 1 ||
        length(consensusThreshold) > 2 ||
        any(consensusThreshold < 0) || any(consensusThreshold > 100))
        stop("The parameter consensusThreshold must be \n",
             "one or two numbers between 0 and 100 !")
    else if (length(consensusThreshold) == 2 &&
             consensusThreshold[1] >= consensusThreshold[2])
        stop("The second percentage in consensusThreshold must be \n",
             "at least as large as the first one!")

    if (shadingMode %in% c("identical", "similar"))
    {
        if (!identical(shadingModeArg, NA) &&
                (!is.numeric(shadingModeArg) ||
                 length(shadingModeArg) > 2 ||
                 length(shadingModeArg) < 1 ||
                 (length(shadingModeArg) == 2 &&
                     shadingModeArg[1] > shadingModeArg[2])||
                 shadingModeArg[1] < 0 ||
                 shadingModeArg[1] > 100 ||
                 (length(shadingModeArg) == 2 &&
                     (shadingModeArg[2] < 0 ||
                      shadingModeArg[2] > 100))))
           stop("If identical or similarity shading is used, shadingModeArg\n",
                 "must be a single numeric threshold between 0 and 100 or\n",
                 "two thresholds between 0 and 100 in increasing order!")
    }
    else if (identical(shadingMode, "functional"))
    {
        if (!identical(shadingModeArg, NA))
            shadingModeArg <- match.arg(shadingModeArg,
                                       c("charge", "hydropathy", "structure",
                                         "chemical", "rasmol", "standard area",
                                         "accessible area"))
        else
            stop("Missing shadingModeArg for functional shading mode. \n",
                 "Valid values are: \n",
                 "\"charge\", \n",
                 "\"hydropathy\", \n",
                 "\"structure\", \n",
                 "\"chemical\",\n",
                 " \"rasmol\",\n",
                 "\"standard area\",\n",
                 "\"accessible area\"!")
    }
    else if (!identical(shadingMode, NA))
        stop("The parameter shadingModeArg has an invalid argument!")

    if (showConsensus != "none" && showConsensus == showLogo)
        stop("Cannot display consensus sequence and sequence logo ",
             "on the same side!")

    if (showNames != "none" && showNames == showNumbering)
        stop("Cannot display sequence names and numbering on the same side!")

    if (!identical(code, NA) && !is.character(code))
        stop("The parameter code has an invalid argument!")

    if (!identical(furtherCode, NA) && !is.character(furtherCode))
        stop("The parameter furtherCode has an invalid argument!")

    if (missing(y))
        toShow <- IRanges(start=1, end=ncol(x))
    else if (is(y, "IRanges"))
    {
        if (all(start(y) >= 1) && all(end(y) <= ncol(x)))
            toShow <- reduce(y)
        else
            stop("The parameter y has invalid ranges: out of bounds!")
    }
    else if ((is.numeric(y) || is.integer(y)) && length(y) == 2 && y[1] >= 1 &&
             y[2] <= ncol(x) && y[1] < y[2])
        toShow <- IRanges(start=y[1], end=y[2])
    else
        stop("The parameter y has an invalid argument!")

    if (length(colmask(x)) > 0)
        toShow <- setdiff(toShow, colmask(x))

    if (sum(width(toShow)) == 0)
        stop("Sequences empty or everything masked: nothing to be shown!")

    jobname <- ""
    suffix <- ""

    if (output != "asis")
    {
        if (!is.character(file) || length(file) > 1)
            stop("The argument for parameter file must be \n",
                 "a single character string!")

        if (substr(file, nchar(file) - 2, nchar(file)) != output)
            stop("The file name suffix and output type do not match!")

        jobname <- substr(file, 1, nchar(file) - 4)

        if (length(grep("[^\\w\\-/\\\\:.]", jobname, perl=TRUE)) > 0)
        {
            warning("Cannot use file name '", file,
                    "' because it contains invalid characters => \n",
                    "resorting to 'msaPrettyPrintOutput.", output, "'!")
            jobname <- "msaPrettyPrintOutput"
            file <- paste0(jobname, output)
        }

        if (askForOverwrite && file.exists(file))
        {
            message("File ", file, " exists. Overwrite? (y/N)")

            answer <- try(tolower(scan(what=character(), nmax=1,
                                       quiet=TRUE)), silent=TRUE)

            if (nchar(answer) != 1 || substr(answer, 1, 1) != "y")
                return(invisible(NULL))
        }
    }

    writeXStringSet(as(unmasked(x), "XStringSet")[subset], filepath=alFile)

    if (verbose)
        message("Multiple alignment written to temporary file ", alFile)

    texOutput <- paste0("\\begin{texshade}{", stratifyFilenames(alFile), "}")

    if (is(x, "AAMultipleAlignment"))
        texOutput <- c(texOutput, "\\seqtype{P}")
    else
        texOutput <- c(texOutput, "\\seqtype{N}")

    if (length(toShow) == 1)
    {
        if (sum(width(toShow)) < ncol(x))
            texOutput <- c(texOutput, paste("\\setends{consensus}{",
                                            start(toShow), "..", end(toShow),
                                            "}", sep=""))
    }
    else
    {
        coList <- sapply(1:length(toShow),
                         function(i) paste(start(toShow)[i], "..",
                                           end(toShow)[i], sep=""))

        texOutput <- c(texOutput, paste("\\setdomain{consensus}{",
                                        paste(coList, collapse=","), "}",
                                        sep=""))
    }

    if (identical(code, NA))
    {
        if (identical(shadingModeArg, NA))
            texOutput <- c(texOutput,
                               paste("\\shadingmode{", shadingMode, "}",
                                     sep=""))
        else
            texOutput <- c(texOutput,
                               paste("\\shadingmode[",
                                     shadingModeArg, "]{",
                                     shadingMode, "}", sep=""))

        if (length(consensusThreshold) == 2)
            texOutput <- c(texOutput, paste("\\threshold[",
                                            consensusThreshold[2], "]{",
                                            consensusThreshold[1], "}",
                                            sep=""))
        else
            texOutput <- c(texOutput, paste("\\threshold{",
                                            consensusThreshold[1], "}",
                                            sep=""))

        if (showConsensus != "none")
        {
            texOutput <- c(texOutput,
                           paste("\\showconsensus[", consensusColors,
                                 "]{", showConsensus, "}", sep=""))
        }
        else
            texOutput <- c(texOutput, "\\hideconsensus")

        texOutput <- c(texOutput, paste("\\shadingcolors{",
                                        shadingColors, "}", sep=""))

        if (showLogo != "none")
            texOutput <- c(texOutput,
                           paste("\\showsequencelogo[", logoColors,
                                 "]{", showLogo, "}", sep=""))

        if (showLogoScale == "none")
            texOutput <- c(texOutput, "\\hidelogoscale")
        else
            texOutput <- c(texOutput,
                           paste("\\showlogoscale{", showLogoScale, "}",
                                 sep=""))

        if (showNames != "none")
        {
            seqNames <- rownames(x)[subset]
            pattern <- "[^a-zA-Z0-9,;:.?!/\\-\\(\\)\\'\" ]"
            seqNames <- gsub(pattern, " ", seqNames, perl=TRUE)

            texOutput <- c(texOutput,
                           paste("\\shownames{", showNames, "}", sep=""),
                           paste("\\nameseq{", 1:length(subset), "}{",
                                 seqNames, "}", sep=""))
        }
        else
            texOutput <- c(texOutput, "\\hidenames")

        if (showNumbering != "none")
            texOutput <- c(texOutput,
                           paste("\\shownumbering{", showNumbering, "}",
                                 sep=""))
        else
            texOutput <- c(texOutput, "\\hidenumbering")

        if (showLegend)
            texOutput <- c(texOutput, "\\showlegend")

        if (!identical(furtherCode, NA))
            texOutput <- c(texOutput, furtherCode)
    }
    else
        texOutput <- c(texOutput, code)

    texOutput <- c(texOutput, "\\end{texshade}")

    if (output == "asis")
        cat(texOutput, sep="\n")
    else
    {
        texHeader <- c("\\documentclass[10pt]{article}", "")

        if (psFonts)
            texHeader <- c(texHeader, "\\usepackage{times}")

        texHeader <- c(texHeader, "\\usepackage{texshade}")

        texHeader <- c(texHeader, "", "\\headheight=0pt", "\\headsep=0pt",
                       "\\hoffset=0pt", "\\voffset=0pt",
                       paste0("\\paperwidth=", paperWidth, "in"),
                       paste0("\\paperheight=", paperHeight, "in"),
                       "\\ifx\\pdfoutput\\undefined",
                       "\\relax",
                       "\\else",
                       "\\pdfpagewidth=\\paperwidth",
                       "\\pdfpageheight=\\paperheight",
                       "\\fi",
                       paste0("\\oddsidemargin=", margins[1] - 1, "in"),
                       paste0("\\topmargin=", margins[2] - 1, "in"),
                       paste0("\\textwidth=",
                              paperWidth - 2 * margins[1], "in"),
                       paste0("\\textheight=",
                              paperHeight - 2 * margins[2],"in"),
                       "", "\\pagestyle{empty}", "", "\\begin{document}")
        texFooter <- "\\end{document}"

        if (output == "tex")
            writeLines(c(texHeader, texOutput, texFooter), con=file)
        else
        {
            texfile <- paste(jobname, "tex", sep=".")

            if (askForOverwrite && file.exists(texfile))
            {
                message("File ", texfile, " exists. Overwrite? (y/N)")

                answer <- try(tolower(scan(what=character(), nmax=1,
                                           quiet=TRUE)), silent=TRUE)

                if (nchar(answer) != 1 || substr(answer, 1, 1) != "y")
                    return(invisible(NULL))
            }

            writeLines(c(texHeader, texOutput, texFooter), con=texfile)

            if (verbose)
                message("File ", texfile, " created")

            texi2dvi(texfile, quiet=!verbose, pdf=identical(output, "pdf"),
                     texinputs=system.file("tex", package="msa"),
                     clean=TRUE, index=index)
        }

        if (verbose)
            message("Output file ", file, " created")
    }

    invisible(texOutput)
}
