//
// Copyright (c) 2017-2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
	"errors"
	"io"
	"os"
	"strings"
)

// HexByteReader is an I/O Reader type.
type HexByteReader struct {
	file string
	f    *os.File
	data []byte

	// total length of "data"
	len int

	// how much of "data" has been sent back to the caller
	offset int
}

// NewHexByteReader returns a new hex byte reader that escapes all
// hex-encoded characters.
func NewHexByteReader(file string) *HexByteReader {
	var f *os.File

	// treat dash as an alias for standard input
	if file == stdinFile {
		f = os.Stdin
	}

	return &HexByteReader{
		file: file,
		f:    f,
	}
}

// Read is a Reader that converts "\x" to "\\x"
func (r *HexByteReader) Read(p []byte) (n int, err error) {
	size := len(p)

	if r.data == nil {
		if r.f == nil {
			r.f, err = os.Open(r.file)
			if err != nil {
				return 0, err
			}
		}

		// read the entire file
		bytes, err := io.ReadAll(r.f)
		if err != nil {
			return 0, err
		}

		// although logfmt is happy to parse an empty file, this is
		// surprising to users, so make it an error.
		if len(bytes) == 0 {
			return 0, errors.New("file is empty")
		}

		// perform the conversion
		s := string(bytes)
		result := strings.ReplaceAll(s, `\x`, `\\x`)

		// store the data
		r.data = []byte(result)
		r.len = len(r.data)
		r.offset = 0
	}

	// calculate how much data is left to copy
	remaining := r.len - r.offset

	if remaining == 0 {
		return 0, io.EOF
	}

	// see how much data can be copied on this call
	limit := size

	if remaining < limit {
		limit = remaining
	}

	for i := 0; i < limit; i++ {
		// index into the stored data
		src := r.offset

		// copy
		p[i] = r.data[src]

		// update
		r.offset++
	}

	return limit, nil
}
