// Copyright (C) 2022 Marius Schellenberger
// crypto parts taken from https://github.com/filosottile/age

package crypto

import (
	"cachefs/pkg/provider"
	"crypto/rand"
	"encoding/binary"
	"io"
	"math"
)

func isZero(nonce []byte) bool {
	for i := 0; i < len(nonce); i++ {
		if nonce[i] != 0 {
			return false
		}
	}
	return true
}

func initNonce(f provider.File) (nonce []byte, err error) {
	nonce = make([]byte, KDFNonceSize)
	n, err := io.ReadFull(f, nonce)
	if err != nil || n != len(nonce) || isZero(nonce) {
		_, err = io.ReadFull(rand.Reader, nonce)
		if err != nil {
			return
		}
		_, err = f.Seek(0, io.SeekStart)
		if err != nil {
			return
		}
		_, err = f.Write(nonce)
	}
	return
}

func incNonce(nonce *[NonceSize]byte) {
	for i := len(nonce) - 2; i >= 0; i-- {
		nonce[i]++
		if nonce[i] != 0 {
			break
		} else if i == 0 {
			// The counter is 88 bits, this is unreachable.
			panic("stream: chunk counter wrapped around")
		}
	}
}

func Size(s int64) int64 {
	return KDFNonceSize + s + (int64(math.Ceil(float64(s)/ChunkSize)) * Overhead)
}

func RealSize(s int64) int64 {
	s -= KDFNonceSize
	return s - (((s / FullSize) + 1) * Overhead)
}

func chunkNum(o int64) int64 {
	return int64(math.Ceil(float64(o) / ChunkSize))
}

func align(o int64) (cn, off, roff int64) {
	cn = chunkNum(o)
	off = cn * FullSize
	roff = o - (cn * ChunkSize)
	if roff < 0 {
		cn--
		off = cn * FullSize
		roff = o - (cn * ChunkSize)
	}
	return
}

func inc(nonce *[NonceSize]byte, n int64) {
	for i := int64(0); i < n; i++ {
		incNonce(nonce)
	}
}

func put(nonce *[NonceSize]byte, n int64) {
	binary.BigEndian.PutUint64((*nonce)[3:11], uint64(n))
}
