/*
 * Copyright 2021 CloudWeGo 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 remote

import (
	"io"
	"net"
)

// ByteBufferFactory is used to create ByteBuffer.
type ByteBufferFactory interface {
	NewByteBuffer(conn net.Conn) (ByteBuffer, error)
}

// NocopyWrite is to write []byte without copying, and splits the original buffer.
// It is used with linked buffer implement.
type NocopyWrite interface {
	// WriteDirect will wrap buf as a new data node no copy, then insert into the linked buffer.
	// remainCap is the remain capacity of origin buff.
	WriteDirect(buf []byte, remainCap int) error
	// MallocAck correct the real malloc len to n
	MallocAck(n int) error
}

// FrameWrite is to write header and data buffer separately to avoid memory copy
type FrameWrite interface {
	// WriteHeader set header buffer without copy
	WriteHeader(buf []byte) (err error)
	// WriteData set data buffer without copy
	WriteData(buf []byte) (err error)
}

// ByteBuffer is the core abstraction of buffer in Kitex.
type ByteBuffer interface {
	io.ReadWriter

	// Next reads the next n bytes sequentially and returns the original buffer.
	Next(n int) (p []byte, err error)

	// Peek returns the next n bytes without advancing the reader.
	Peek(n int) (buf []byte, err error)

	// Skip is used to skip the next few bytes quickly. It's faster than Next and doesn't cause release.
	Skip(n int) (err error)

	// Release will free the buffer. After release, buffer read by Next/Skip/Peek is invalid.
	// Param e is used when the buffer release depend on error.
	// For example, usually the write buffer will be released inside flush,
	// but if flush error happen, write buffer may need to be released explicitly.
	Release(e error) (err error)

	// ReadableLen returns the total length of readable buffer.
	// Return: -1 means unreadable.
	ReadableLen() (n int)

	// ReadLen returns the size already read.
	ReadLen() (n int)

	// ReadString is a more efficient way to read string than Next.
	ReadString(n int) (s string, err error)

	// ReadBinary like ReadString.
	// Returns a copy of original buffer.
	ReadBinary(p []byte) (n int, err error)

	// Malloc n bytes sequentially in the writer buffer.
	Malloc(n int) (buf []byte, err error)

	// WrittenLen returns the total length of the buffer writtenLen.
	WrittenLen() (length int)

	// WriteString is a more efficient way to write string, using the unsafe method to convert the string to []byte.
	WriteString(s string) (n int, err error)

	// WriteBinary writes the []byte directly. Callers must guarantee that the []byte doesn't change.
	WriteBinary(b []byte) (n int, err error)

	// Flush writes any malloc data to the underlying io.Writer.
	// The malloced buffer must be set correctly.
	Flush() (err error)

	// NewBuffer returns a new writable remote.ByteBuffer.
	NewBuffer() ByteBuffer
	// AppendBuffer appends buf to the original buffer.
	AppendBuffer(buf ByteBuffer) (err error)

	// Bytes return the backing bytes slice of this buffer
	Bytes() (buf []byte, err error)
}

// ByteBufferIO wrap ByteBuffer to implement io.ReadWriter
type ByteBufferIO struct {
	buffer ByteBuffer
}

// NewByteBufferIO wraps ByBuffer to io.ReadWriter
func NewByteBufferIO(buffer ByteBuffer) io.ReadWriter {
	return &ByteBufferIO{
		buffer: buffer,
	}
}

// Write implements the io.ReadWriter interface.
func (p *ByteBufferIO) Write(b []byte) (int, error) {
	return p.buffer.WriteBinary(b)
}

// Read implements the io.ReadWriter interface.
func (p *ByteBufferIO) Read(b []byte) (n int, err error) {
	var buf []byte
	readable := p.buffer.ReadableLen()
	if readable == 0 {
		return 0, io.EOF
	} else if len(b) <= readable {
		buf, err = p.buffer.Next(len(b))
		if err != nil {
			return
		}
		n = len(b)
	} else {
		buf, err = p.buffer.Next(readable)
		if err != nil {
			return
		}
		n = readable
	}
	copy(b, buf)
	return
}
