package cipher

import (
	"regexp"
	"strings"
)

// Cipher is an interface for all ciphers
type Cipher interface {
	Encode(string) string
	Decode(string) string
}

// ShiftCipher is a cipher that shifts all letters a fixed distance
type ShiftCipher struct {
	width int
}

// NewShift returns a new ShiftCipher
func NewShift(w int) *ShiftCipher {
	if w == 0 || w < -25 || w > 25 {
		return nil
	}
	c := new(ShiftCipher)
	c.width = w
	return c
}

// Encode encodes a string using the current ShiftCipher
func (c *ShiftCipher) Encode(s string) string {
	var ret string
	s = strings.ToLower(s)
	re := regexp.MustCompile("[^a-z]")
	s = re.ReplaceAllString(s, "")
	for i := range s {
		newChar := s[i] + byte(c.width)
		if newChar < 'a' {
			newChar = 'z' - ('a' - newChar) + 1
		}
		if newChar > 'z' {
			newChar = 'a' + (newChar - 'z') - 1
		}
		ret = ret + string(newChar)
	}
	return ret
}

// Decode decodes a string using the current ShiftCipher
func (c *ShiftCipher) Decode(s string) string {
	var ret string
	s = strings.ToLower(s)
	re := regexp.MustCompile("[^a-z]")
	s = re.ReplaceAllString(s, "")
	for i := range s {
		newChar := s[i] - byte(c.width)
		if newChar < 'a' {
			newChar = 'z' - ('a' - newChar) + 1
		}
		if newChar > 'z' {
			newChar = 'a' + (newChar - 'z') - 1
		}
		ret = ret + string(newChar)
	}
	return ret
}

// A CeasarCipher is a ShiftCipher, just hardcoded at 3
type CeasarCipher struct {
	c *ShiftCipher
}

// NewCaesar creates a new CaesarCipher
func NewCaesar() *CeasarCipher {
	c := new(CeasarCipher)
	c.c = NewShift(3)
	return c
}

// Encode just calls the shift cipher in the caesar cipher
func (c *CeasarCipher) Encode(s string) string {
	return c.c.Encode(s)
}

// Decode just calls the shift cipher in the caesar cipher
func (c *CeasarCipher) Decode(s string) string {
	return c.c.Decode(s)
}

// VigenereCipher a cipher where each character can have a different shift width
type VigenereCipher struct {
	key string
}

// Encode encodes the given string
func (c *VigenereCipher) Encode(s string) string {
	var ret string
	s = strings.ToLower(s)
	re := regexp.MustCompile("[^a-z]")
	s = re.ReplaceAllString(s, "")
	for i := range s {
		keyChar := c.key[i%len(c.key)] - 'a'
		newChar := s[i] + keyChar
		if newChar < 'a' {
			newChar = 'z' - ('a' - newChar) + 1
		}
		if newChar > 'z' {
			newChar = 'a' + (newChar - 'z') - 1
		}
		ret = ret + string(newChar)
	}
	return ret
}

// Decode decodes the given string
func (c *VigenereCipher) Decode(s string) string {
	var ret string
	s = strings.ToLower(s)
	re := regexp.MustCompile("[^a-z]")
	s = re.ReplaceAllString(s, "")
	for i := range s {
		keyChar := c.key[i%len(c.key)] - 'a'
		newChar := s[i] - keyChar
		if newChar < 'a' {
			newChar = 'z' - ('a' - newChar) + 1
		}
		if newChar > 'z' {
			newChar = 'a' + (newChar - 'z') - 1
		}
		ret = ret + string(newChar)
	}
	return ret
}

// NewVigenere returns a new Vigenere Cipher
func NewVigenere(key string) *VigenereCipher {
	re := regexp.MustCompile("[^a-z]")
	if len(key) < 3 || re.MatchString(key) {
		return nil
	}
	c := new(VigenereCipher)
	c.key = key
	return c
}
