package winpwn

import (
	"encoding/hex"
	"fmt"
	"strings"
)

// Enhex hex-encodes data as a lowercase string, the analogue of pwntools'
// enhex (binascii.hexlify).
func Enhex(data []byte) string {
	return hex.EncodeToString(data)
}

// Unhex decodes a hex string back into bytes, the analogue of pwntools'
// unhex.
func Unhex(s string) ([]byte, error) {
	return hex.DecodeString(s)
}

// Xor XORs data against key, cycling key if it's shorter than data --
// the analogue of pwntools' xor(data, key).
func Xor(data, key []byte) []byte {
	if len(key) == 0 {
		out := make([]byte, len(data))
		copy(out, data)
		return out
	}
	out := make([]byte, len(data))
	for i, b := range data {
		out[i] = b ^ key[i%len(key)]
	}
	return out
}

// Hexdump renders data as a classic 16-bytes-per-line hex+ASCII dump
// (offset, hex bytes, printable-ASCII gutter with '.' for non-printable),
// the analogue of pwntools' hexdump(data).
func Hexdump(data []byte) string {
	var sb strings.Builder
	for off := 0; off < len(data); off += 16 {
		end := off + 16
		if end > len(data) {
			end = len(data)
		}
		line := data[off:end]

		fmt.Fprintf(&sb, "%08x  ", off)
		for i := 0; i < 16; i++ {
			if i == 8 {
				sb.WriteByte(' ')
			}
			if i < len(line) {
				fmt.Fprintf(&sb, "%02x ", line[i])
			} else {
				sb.WriteString("   ")
			}
		}
		sb.WriteString(" ")
		for _, b := range line {
			if b >= 0x20 && b < 0x7f {
				sb.WriteByte(b)
			} else {
				sb.WriteByte('.')
			}
		}
		sb.WriteByte('\n')
	}
	return sb.String()
}
