package ocf

import (
	
	
	
	
	
	
	

	
	
)

// CodecName represents a compression codec name.
type CodecName string

// Supported compression codecs.
const (
	Null      CodecName = "null"
	Deflate   CodecName = "deflate"
	Snappy    CodecName = "snappy"
	ZStandard CodecName = "zstandard"
)

type codecOptions struct {
	DeflateCompressionLevel int
	ZStandardOptions        zstdOptions
}

type zstdOptions struct {
	EOptions []zstd.EOption
	DOptions []zstd.DOption
}

func resolveCodec( CodecName,  codecOptions) (Codec, error) {
	switch  {
	case Null, "":
		return &NullCodec{}, nil

	case Deflate:
		return &DeflateCodec{compLvl: .DeflateCompressionLevel}, nil

	case Snappy:
		return &SnappyCodec{}, nil

	case ZStandard:
		return newZStandardCodec(.ZStandardOptions), nil

	default:
		return nil, fmt.Errorf("unknown codec %s", )
	}
}

// Codec represents a compression codec.
type Codec interface {
	// Decode decodes the given bytes.
	Decode([]byte) ([]byte, error)
	// Encode encodes the given bytes.
	Encode([]byte) []byte
}

// NullCodec is a no op codec.
type NullCodec struct{}

// Decode decodes the given bytes.
func (*NullCodec) ( []byte) ([]byte, error) {
	return , nil
}

// Encode encodes the given bytes.
func (*NullCodec) ( []byte) []byte {
	return 
}

// DeflateCodec is a flate compression codec.
type DeflateCodec struct {
	compLvl int
}

// Decode decodes the given bytes.
func ( *DeflateCodec) ( []byte) ([]byte, error) {
	 := flate.NewReader(bytes.NewBuffer())
	,  := io.ReadAll()
	if  != nil {
		_ = .Close()
		return nil, 
	}
	_ = .Close()

	return , nil
}

// Encode encodes the given bytes.
func ( *DeflateCodec) ( []byte) []byte {
	 := bytes.NewBuffer(make([]byte, 0, len()))

	,  := flate.NewWriter(, .compLvl)
	_, _ = .Write()
	_ = .Close()

	return .Bytes()
}

// SnappyCodec is a snappy compression codec.
type SnappyCodec struct{}

// Decode decodes the given bytes.
func (*SnappyCodec) ( []byte) ([]byte, error) {
	 := len()
	if  < 5 {
		return nil, errors.New("block does not contain snappy checksum")
	}

	,  := snappy.Decode(nil, [:-4])
	if  != nil {
		return nil, 
	}

	 := binary.BigEndian.Uint32([-4:])
	if crc32.ChecksumIEEE() !=  {
		return nil, errors.New("snappy checksum mismatch")
	}

	return , nil
}

// Encode encodes the given bytes.
func (*SnappyCodec) ( []byte) []byte {
	 := snappy.Encode(nil, )

	 = append(, 0, 0, 0, 0)
	binary.BigEndian.PutUint32([len()-4:], crc32.ChecksumIEEE())

	return 
}

// ZStandardCodec is a zstandard compression codec.
type ZStandardCodec struct {
	decoder *zstd.Decoder
	encoder *zstd.Encoder
}

func newZStandardCodec( zstdOptions) *ZStandardCodec {
	,  := zstd.NewReader(nil, .DOptions...)
	,  := zstd.NewWriter(nil, .EOptions...)
	return &ZStandardCodec{
		decoder: ,
		encoder: ,
	}
}

// Decode decodes the given bytes.
func ( *ZStandardCodec) ( []byte) ([]byte, error) {
	defer func() { _ = .decoder.Reset(nil) }()
	return .decoder.DecodeAll(, nil)
}

// Encode encodes the given bytes.
func ( *ZStandardCodec) ( []byte) []byte {
	defer .encoder.Reset(nil)
	return .encoder.EncodeAll(, nil)
}