/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.

   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package ioutil

import (
	"io"
	"sync"
)

// writeCloseInformer wraps passed in write closer with a close channel.
// Caller could wait on the close channel for the write closer to be
// closed.
type writeCloseInformer struct {
	close chan struct{}
	wc    io.WriteCloser
}

// NewWriteCloseInformer creates the writeCloseInformer from a write closer.
func NewWriteCloseInformer(wc io.WriteCloser) (informer io.WriteCloser, closeChan <-chan struct{}) {
	channel := make(chan struct{})
	return &writeCloseInformer{
		close: channel,
		wc:    wc,
	}, channel
}

// Write passes through the data into the internal write closer.
func (w *writeCloseInformer) Write(p []byte) (int, error) {
	return w.wc.Write(p)
}

// Close closes the internal write closer and inform the close channel.
func (w *writeCloseInformer) Close() error {
	err := w.wc.Close()
	close(w.close)
	return err
}

// nopWriteCloser wraps passed in writer with a nop close function.
type nopWriteCloser struct {
	w io.Writer
}

// NewNopWriteCloser creates the nopWriteCloser from a writer.
func NewNopWriteCloser(w io.Writer) io.WriteCloser {
	return &nopWriteCloser{w: w}
}

// Write passes through the data into the internal writer.
func (n *nopWriteCloser) Write(p []byte) (int, error) {
	return n.w.Write(p)
}

// Close is a nop close function.
func (n *nopWriteCloser) Close() error {
	return nil
}

// serialWriteCloser wraps a write closer and makes sure all writes
// are done in serial.
// Parallel write won't intersect with each other. Use case:
// 1) Pipe: Write content longer than PIPE_BUF.
//    See http://man7.org/linux/man-pages/man7/pipe.7.html
// 2) <3.14 Linux Kernel: write is not atomic
//    See http://man7.org/linux/man-pages/man2/write.2.html
type serialWriteCloser struct {
	mu sync.Mutex
	wc io.WriteCloser
}

// NewSerialWriteCloser creates a SerialWriteCloser from a write closer.
func NewSerialWriteCloser(wc io.WriteCloser) io.WriteCloser {
	return &serialWriteCloser{wc: wc}
}

// Write writes a group of byte arrays in order atomically.
func (s *serialWriteCloser) Write(data []byte) (int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.wc.Write(data)
}

// Close closes the write closer.
func (s *serialWriteCloser) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.wc.Close()
}
