package crypto

import (
	"crypto/rc4"
	"encoding/binary"
)

type Ciphers struct {
	client *rc4.Cipher // For decryption (client->server)
	server *rc4.Cipher // For encryption (server->client)
}

// NewCiphers returns a new pair of ciphers based on the given key
func NewCiphers(key int64) (*Ciphers, error) {
	// Encryption only when key > 0
	if key <= 0 {
		return &Ciphers{}, nil
	}

	// key (little-endian bytes)
	keyBytes := make([]byte, 8)
	binary.LittleEndian.PutUint64(keyBytes, uint64(key))

	// ~key (little-endian bytes)
	notKeyBytes := make([]byte, 8)
	binary.LittleEndian.PutUint64(notKeyBytes, uint64(^key))

	clientCipher, err := rc4.NewCipher(notKeyBytes)
	if err != nil {
		return nil, err
	}
	serverCipher, err := rc4.NewCipher(keyBytes)
	if err != nil {
		return nil, err
	}

	// Burn first 20 bytes on both streams
	drop := make([]byte, 20)
	clientCipher.XORKeyStream(drop, drop)
	serverCipher.XORKeyStream(drop, drop)

	return &Ciphers{client: clientCipher, server: serverCipher}, nil
}

// Decrypt decrypts data received from the client
func (c *Ciphers) Decrypt(data []byte) {
	if c.client != nil {
		c.client.XORKeyStream(data, data)
	}
}

// Encrypt encrypts data to be sent to the client
func (c *Ciphers) Encrypt(data []byte) {
	if c.server != nil {
		c.server.XORKeyStream(data, data)
	}
}

// IsEncrypted returns true if encryption is active
func (c *Ciphers) IsEncrypted() bool {
	return c != nil && c.server != nil
}
