#########################################################################/**
# @RdocFunction writeCelHeader
#
# @title "Writes a CEL header to a connection"
#
# @synopsis 
# 
# \description{
#   @get "title".
# }
# 
# \arguments{
#   \item{con}{A @connection.}
#   \item{header}{A @list structure describing the CEL header, similar
#     to the structure returned by @see "readCelHeader".}
#   \item{outputVersion}{A @character string specifying the output format.
#     Currently only CEL version 4 (binary;XDA) are supported.}
#   \item{...}{Not used.}
# }
# 
# \value{
#   Returns (invisibly) the pathname of the file created.
# }
#
# \details{
#   Currently only CEL version 4 (binary;XDA) headers can be written.
# }
#
# \section{Redundant fields}{
#   The CEL v4 header contains redundant information. To avoid inconsistency
#   this method generates such redundant values from the original values.
#   This is consistent to how the CEL reader in Fusion SDK does it, cf.
#   @see "readCelHeader".  The redundant information is in the (CEL v3)
#   \code{header} field, which contains the CEL header information as it
#   would appear in the CEL v3 format.  This in turn contains a DAT header
#   field reproducing the DAT header from the image analysis.  It is from
#   this DAT header that the chip type is extracted.
# }
#
# @author "HB"
#
# @keyword "file"
# @keyword "IO"
# @keyword "internal"
#*/#########################################################################
writeCelHeader <- function(con, header, outputVersion=c("4"), ...) {
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Local functions
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  writeDWord <- function(con, data, ...) {
    writeBin(con=con, as.integer(data), size=4, endian="little");
  }
  
  writeInteger <- function(con, data, ...) {
    writeBin(con=con, as.integer(data), size=4, endian="little");
  }
  
  writeString <- function(con, str, ...) {
    # Strings must not be null terminated! /HB 2006-09-10
    writeInteger(con=con, nchar(str));
    writeChar(con=con, str, eos=NULL);
  }

  writeCelHeaderV4 <- function(con, header, ...) {
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Validation
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Get the version of the CEL header
    version <- .getCelHeaderVersion(header);
    if (version ==  "1") {
      # A CEL header in the Calvin CEL file format
      header <- .getCelHeaderV4(header);
    } else if (version == "3") {
      # A CEL header in the ASCII (v3) CEL file format
      stop("Failed to write CEL v4 header. Argument 'header' is a CEL v3 header, which is not supported: ", version);
    } else if (version == "4") {
      # A CEL header in the XBR (v4) CEL file format
      header <- .getCelHeaderV4(header);
    } else {
      stop("Failed to write CEL v4 header. Unsupported CEL header version: ", version);
    }

    # Number of columns and rows, and total number of cells
    if (header$rows < 1)
      stop("Number of rows must be at least one: ", rows);
    if (header$cols < 1)
      stop("Number of columns must be at least one: ", cols);
  

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Unwrap/wrap CEL header
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # First, try to unwrap CEL header in case it isn't.
    if (header$version == "4") {
      tryCatch({
        header <- .unwrapCelHeaderV4(header);
      }, error = function(ex) {
        # If this happens, we assumes that the header was already unwrapped.
      });
    }
  
    # Then wrap it up again to make sure it has the right format.  This will
    # also override redundant fields.
    header <- .wrapCelHeaderV4(header);


    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Write to connection
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Magic number (always set to 64)
    writeInteger(con=con, 64);

    # Version number (always set to 4)
    writeInteger(con=con, 4);

    rows <- header$rows;
    cols <- header$cols;
    writeInteger(con=con, c(rows, cols, rows*cols));

    # "Header as defined in the HEADER section of the version 3 CEL 
    #  files. The string contains TAG=VALUE separated by a space where
    #  the TAG names are defined in the version 3 HEADER section."
    # Note that Fusion SDK and hence readCelHeader() ignores the Algorithm
    # and Algorithm Parameters part of this header; instead it reads it
    # from the CEL header (below) and adds it to this when returned.
    # Thus, if you wish to update any of these two fields you need to udate
    # the ones below (in addition to here?!?).
    writeString(con=con, header$header);

    # "The algorithm name used to create the CEL file".
    writeString(con=con, header$algorithm);

    # "The parameters used by the algorithm. The format is TAG:VALUE
    #  pairs separated by semi-colons or TAG=VALUE pairs separated 
    #  by spaces."
    writeString(con=con, header$parameters);

    # "Cell margin used for computing the cells intensity value."
    writeInteger(con=con, header$cellmargin);

    # "Number of outlier cells."
    writeDWord(con=con, header$noutliers);

    # "Number of masked cells."
    writeDWord(con=con, header$nmasked);
  } # writeCelHeaderV4()

  
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Validate arguments
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Open CEL file
  if (!inherits(con, "connection"))
    stop("Argument 'con' must be a connection: ", class(con));

  # Argument 'outputVersion':
  outputVersion <- match.arg(outputVersion);
  if (outputVersion == "4") {
    writeCelHeaderV4(con=con, header=header, ...);
  } else {
    stop("Unsupported output version: ", outputVersion);
  }
} # writeCelHeader()


############################################################################
# HISTORY:
# 2007-08-16
# o Now writeCelHeader() can write CEL header given CCG v1 CEL headers,
#   in addition to v4 headers.
# o Added argument 'outputVersion' to writeCelHeader().  It still only
#   supports version 4, though.
# 2007-08-15
# o Created internal function writeCelHeaderV4().
# 2006-09-10
# o BUG FIX: A hard to find bug was that strings in the CEL header must 
#   *not* be written with a null terminator.
# 2006-09-07
# o With help of all the private unwrap and wrap functions, it seems to
#   work now.
# 2006-09-03
# o Created.
############################################################################
