// Copyright The gittuf Authors
// SPDX-License-Identifier: Apache-2.0

package main

import (
	"bufio"
	"bytes"
	"strconv"
)

func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

func splitInput(data []byte, atEOF bool) (int, []byte, error) {
	if atEOF {
		return len(data), data, bufio.ErrFinalToken
	}

	if len(data) == 0 {
		// Request more data.
		return 0, nil, nil
	}

	if bytes.HasPrefix(data, flushPkt) {
		// We have the flushPkt that'll otherwise cause it to hang.
		// This packet isn't followed by a newline a lot of the time, so we just
		// end up requesting data perennially.
		return len(flushPkt), flushPkt, nil
	}

	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have more data to process, so we just return the current line.
		return i + 1, dropCR(data[0 : i+1]), nil
	}

	// Request more data.
	return 0, nil, nil
}

func splitOutput(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF {
		return len(data), data, bufio.ErrFinalToken
	}

	if len(data) == 0 {
		// Request more data.
		return 0, nil, nil
	}

	if bytes.HasPrefix(data, flushPkt) {
		// We have the flushPkt that'll otherwise cause it to hang.
		// This packet isn't followed by a newline a lot of the time, so we just
		// end up requesting data perennially.
		return len(flushPkt), flushPkt, nil
	}

	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		if i == len(data)-1 {
			// This is the very last newline, we need to return ErrFinalToken to
			// not block the stdin scanner anymore.
			// This is the fundamental difference between this function and
			// splitInput, because this can block stdin.
			return len(data), dropCR(data), bufio.ErrFinalToken
		}

		// We have more data to process, so we just return the current line.
		return i + 1, dropCR(data[0 : i+1]), nil
	}

	// Request more data.
	return 0, nil, nil
}

func splitPacket(data []byte, atEOF bool) (int, []byte, error) {
	if len(data) < 4 {
		return 0, nil, nil // request more
	}

	lengthB := data[:4]
	if bytes.Equal(lengthB, flushPkt) || bytes.Equal(lengthB, delimiterPkt) || bytes.Equal(lengthB, endOfReadPkt) {
		return 4, lengthB, nil
	}

	length, err := strconv.ParseInt(string(lengthB), 16, 64)
	if err != nil {
		return -1, nil, err
	}

	l := int(length)
	if l > len(data) {
		return 0, nil, nil // request more in a new buffer
	}

	if atEOF {
		if l == len(data) {
			return l, data, bufio.ErrFinalToken
		}
	}

	return l, data[:l], nil
}
