// 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 norm

import "io"

type normWriter struct {
	rb  reorderBuffer
	w   io.Writer
	buf []byte
}

// Write implements the standard write interface.  If the last characters are
// not at a normalization boundary, the bytes will be buffered for the next
// write. The remaining bytes will be written on close.
func (w *normWriter) Write(data []byte) (n int, err error) {
	// Process data in pieces to keep w.buf size bounded.
	const chunk = 4000

	for len(data) > 0 {
		// Normalize into w.buf.
		m := len(data)
		if m > chunk {
			m = chunk
		}
		w.rb.src = inputBytes(data[:m])
		w.rb.nsrc = m
		w.buf = doAppend(&w.rb, w.buf, 0)
		data = data[m:]
		n += m

		// Write out complete prefix, save remainder.
		// Note that lastBoundary looks back at most 31 runes.
		i := lastBoundary(&w.rb.f, w.buf)
		if i == -1 {
			i = 0
		}
		if i > 0 {
			if _, err = w.w.Write(w.buf[:i]); err != nil {
				break
			}
			bn := copy(w.buf, w.buf[i:])
			w.buf = w.buf[:bn]
		}
	}
	return n, err
}

// Close forces data that remains in the buffer to be written.
func (w *normWriter) Close() error {
	if len(w.buf) > 0 {
		_, err := w.w.Write(w.buf)
		if err != nil {
			return err
		}
	}
	return nil
}

// Writer returns a new writer that implements Write(b)
// by writing f(b) to w. The returned writer may use an
// internal buffer to maintain state across Write calls.
// Calling its Close method writes any buffered data to w.
func (f Form) Writer(w io.Writer) io.WriteCloser {
	wr := &normWriter{rb: reorderBuffer{}, w: w}
	wr.rb.init(f, nil)
	return wr
}

type normReader struct {
	rb           reorderBuffer
	r            io.Reader
	inbuf        []byte
	outbuf       []byte
	bufStart     int
	lastBoundary int
	err          error
}

// Read implements the standard read interface.
func (r *normReader) Read(p []byte) (int, error) {
	for {
		if r.lastBoundary-r.bufStart > 0 {
			n := copy(p, r.outbuf[r.bufStart:r.lastBoundary])
			r.bufStart += n
			if r.lastBoundary-r.bufStart > 0 {
				return n, nil
			}
			return n, r.err
		}
		if r.err != nil {
			return 0, r.err
		}
		outn := copy(r.outbuf, r.outbuf[r.lastBoundary:])
		r.outbuf = r.outbuf[0:outn]
		r.bufStart = 0

		n, err := r.r.Read(r.inbuf)
		r.rb.src = inputBytes(r.inbuf[0:n])
		r.rb.nsrc, r.err = n, err
		if n > 0 {
			r.outbuf = doAppend(&r.rb, r.outbuf, 0)
		}
		if err == io.EOF {
			r.lastBoundary = len(r.outbuf)
		} else {
			r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf)
			if r.lastBoundary == -1 {
				r.lastBoundary = 0
			}
		}
	}
}

// Reader returns a new reader that implements Read
// by reading data from r and returning f(data).
func (f Form) Reader(r io.Reader) io.Reader {
	const chunk = 4000
	buf := make([]byte, chunk)
	rr := &normReader{rb: reorderBuffer{}, r: r, inbuf: buf}
	rr.rb.init(f, buf)
	return rr
}
