// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package csv

import (
	
	
	
	
	
)

// A Writer writes records using CSV encoding.
//
// As returned by NewWriter, a Writer writes records terminated by a
// newline and uses ',' as the field delimiter. The exported fields can be
// changed to customize the details before the first call to Write or WriteAll.
//
// Comma is the field delimiter.
//
// If UseCRLF is true, the Writer ends each output line with \r\n instead of \n.
//
// The writes of individual records are buffered.
// After all data has been written, the client should call the
// Flush method to guarantee all data has been forwarded to
// the underlying io.Writer.  Any errors that occurred should
// be checked by calling the Error method.
type Writer struct {
	Comma   rune // Field delimiter (set to ',' by NewWriter)
	UseCRLF bool // True to use \r\n as the line terminator
	w       *bufio.Writer
}

// NewWriter returns a new Writer that writes to w.
func ( io.Writer) *Writer {
	return &Writer{
		Comma: ',',
		w:     bufio.NewWriter(),
	}
}

// Write writes a single CSV record to w along with any necessary quoting.
// A record is a slice of strings with each string being one field.
// Writes are buffered, so Flush must eventually be called to ensure
// that the record is written to the underlying io.Writer.
func ( *Writer) ( []string) error {
	if !validDelim(.Comma) {
		return errInvalidDelim
	}

	for ,  := range  {
		if  > 0 {
			if ,  := .w.WriteRune(.Comma);  != nil {
				return 
			}
		}

		// If we don't have to have a quoted field then just
		// write out the field and continue to the next field.
		if !.fieldNeedsQuotes() {
			if ,  := .w.WriteString();  != nil {
				return 
			}
			continue
		}

		if  := .w.WriteByte('"');  != nil {
			return 
		}
		for len() > 0 {
			// Search for special characters.
			 := strings.IndexAny(, "\"\r\n")
			if  < 0 {
				 = len()
			}

			// Copy verbatim everything before the special character.
			if ,  := .w.WriteString([:]);  != nil {
				return 
			}
			 = [:]

			// Encode the special character.
			if len() > 0 {
				var  error
				switch [0] {
				case '"':
					_,  = .w.WriteString(`""`)
				case '\r':
					if !.UseCRLF {
						 = .w.WriteByte('\r')
					}
				case '\n':
					if .UseCRLF {
						_,  = .w.WriteString("\r\n")
					} else {
						 = .w.WriteByte('\n')
					}
				}
				 = [1:]
				if  != nil {
					return 
				}
			}
		}
		if  := .w.WriteByte('"');  != nil {
			return 
		}
	}
	var  error
	if .UseCRLF {
		_,  = .w.WriteString("\r\n")
	} else {
		 = .w.WriteByte('\n')
	}
	return 
}

// Flush writes any buffered data to the underlying io.Writer.
// To check if an error occurred during the Flush, call Error.
func ( *Writer) () {
	.w.Flush()
}

// Error reports any error that has occurred during a previous Write or Flush.
func ( *Writer) () error {
	,  := .w.Write(nil)
	return 
}

// WriteAll writes multiple CSV records to w using Write and then calls Flush,
// returning any error from the Flush.
func ( *Writer) ( [][]string) error {
	for ,  := range  {
		 := .Write()
		if  != nil {
			return 
		}
	}
	return .w.Flush()
}

// fieldNeedsQuotes reports whether our field must be enclosed in quotes.
// Fields with a Comma, fields with a quote or newline, and
// fields which start with a space must be enclosed in quotes.
// We used to quote empty strings, but we do not anymore (as of Go 1.4).
// The two representations should be equivalent, but Postgres distinguishes
// quoted vs non-quoted empty string during database imports, and it has
// an option to force the quoted behavior for non-quoted CSV but it has
// no option to force the non-quoted behavior for quoted CSV, making
// CSV with quoted empty strings strictly less useful.
// Not quoting the empty string also makes this package match the behavior
// of Microsoft Excel and Google Drive.
// For Postgres, quote the data terminating string `\.`.
func ( *Writer) ( string) bool {
	if  == "" {
		return false
	}

	if  == `\.` {
		return true
	}

	if .Comma < utf8.RuneSelf {
		for  := 0;  < len(); ++ {
			 := []
			if  == '\n' ||  == '\r' ||  == '"' ||  == byte(.Comma) {
				return true
			}
		}
	} else {
		if strings.ContainsRune(, .Comma) || strings.ContainsAny(, "\"\r\n") {
			return true
		}
	}

	,  := utf8.DecodeRuneInString()
	return unicode.IsSpace()
}