#ifndef MPAGSCIPHER_PLAYFAIRCIPHER_HPP
#define MPAGSCIPHER_PLAYFAIRCIPHER_HPP

#include <map>
#include <string>
#include <vector>

#include "CipherMode.hpp"

/**
 * \file CaesarCipher.hpp
 * \brief Contains the declaration of the PlayfairCipherclass
 */

/**
 * \class PlayfairCipher
 * \brief Encrypt or decrypt text using the Playfair cipher with the given key
 */
class PlayfairCipher
{
  public:
    /**
     * Create a new PlayfairCipher, using the given string as key
     *
     * \param key the string to use as the key to be used in the cipher
     */
    explicit PlayfairCipher(const std::string& key);

    /**
     * Set the key to be used in the Cipher
     *
     * \param key std::string to use as the key
     */
    void setKey(const std::string& key);

    /**
     * Retrieve the map translating co-ordinates to characters
     * Used for unit testing
     *
     * \return map for translating (x,y) co-ordinates to a char in the cipher grid
     *
     */
    const std::map<char, std::pair<int, int>> getChar2CoordMap();

    /**
     * Retrieve the map translating characters to co-ordinates
     * Used for unit testing
     *
     * \return map for translating a char to (x,y) co-ordinates in the cipher grid
     *
     */
    const std::map<std::pair<int, int>, char> getCoord2CharMap();

    /**
     * Retrieve the std::string representation of the key to be used in the Playfair cipher
     * Used for unit testing
     *
     * \return the Playfair cipher key
     */
    const std::string getKey();

    /**
     * Parse the text passed to applyCipher according to the Playfair cipher rules so that it is appropriate for
     * encoding Swaps J for I, inserts X between repeat letters (Q between repeat X), appends a Z if the string is odd
     * length (X if it ends in Z)
     *
     * \param inputText raw text to encrypt
     * \return The parsed text, with J->I, double letters split up and any appending performed.
     */
    std::string parseInputText(const std::string& inputText) const;

    /**
     * Apply the cipher to the provided text. If an odd length string is passed to the decryptor, no decryption will be performed
     *
     * \param inputText the text to encrypt or decrypt
     * \param cipherMode whether to encrypt or decrypt the input text
     * \return the result of applying the cipher to the input text
     */
    std::string applyCipher(const std::string& inputText, const CipherMode cipherMode) const;

  private:
    /// The alphabet - used to determine the cipher character given the plain character and the key
    const std::vector<char> alphabet_ = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
                                         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};

    /// The size of the alphabet
    const std::vector<char>::size_type alphabetSize_ = alphabet_.size();

    /// The cipher key to be used according to the Playfair algorithm
    std::string key_ = "";

    /// Maps to track each char as a co-ordinate and to track each co-ordinate as a char.
    /// co ordinates are (x,y); the key ABCDEFGHIKLMNOPQRSTUVWXYZ would have A; E and Z at the (0,0); (0,4) and (4,4)
    /// positions respectively.
    std::map<char, std::pair<int, int>> char2Coord_;
    std::map<std::pair<int, int>, char> coord2Char_;
};

#endif // MPAGSCIPHER_PLAYFAIRCIPHER_HPP
