def monoalphabetic(self):
        """
        Encrypts or decrypts the file using a monoalphabetic cipher.

        :return string: The encrypted/decrypted file data.
        """
        key = list(dict.fromkeys(self.key))
        cipher_text = list(self.CHARS)

        key = list(dict.fromkeys(key))
        for index, key_char in enumerate(key, 0):
            if key_char in self.CHARS:
                cipher_text.remove(key_char)
                cipher_text.insert(index, key_char)

        cipher = dict(zip(self.CHARS, cipher_text))
        logger.info(f"cipher: {cipher}")
        keys = cipher.keys()
        values = cipher.values()

        data = ""
        for character in self.file_data:
            try:
                if self.crypt_type == "encrypt":
                    data += list(keys)[list(values).index(character)]
                else:
                    data += list(values)[list(keys).index(character)]
            except (ValueError, TypeError) as e:
                logger.error(e)
                print(f"Invalid character '{character}' in file {self.file}.")
        logger.info(f"data: {data}")

        return data
    def caesar_cipher(self):
        """
        Encrypts or decrypts the file using a caesar cipher.

        :return string: The encrypted/decrypted file data.
        """
        chars = list(self.CHARS * 2)
        data = ""
        crypt_operator = add if self.crypt_type == "encrypt" else sub

        for character in self.file_data:
            try:
                index = crypt_operator(chars.index(character), self.key)
                data += chars[index]
            except (ValueError, TypeError) as e:
                logger.error(e)
                print(f"Invalid character '{character}' in file {self.file}.")
        logger.info(f"data: {data}")
        return data
def main():
    """ Beginning of the program. """
    # file = None
    # for arg in sys.argv:
    #     if ".txt" in arg or ".py" not in arg or ".log" not in arg:
    #         file = arg

    file = input("Enter a file: ")

    file_data = Cryptography()
    file_data.file = file

    crypt_type = input("Please enter 'E' to encrypt or 'D' to decrypt\n>> ")
    file_data.crypt_type = crypt_type

    crypt_type = "encrypt" if crypt_type == 'E' else "decrypt"

    file_data.crypt_method = file_data.crypt_method

    key = input("Please enter a key for your data\n>> ")
    file_data.key = key

    print(f"crypt_method: {file_data.crypt_method}")
    new_data = file_data.crypt_methods[file_data.crypt_method]()

    crypt_methods = defaultdict(str, {
        'C': "Caesar",
        'M': "Monoalphabetic",
        'P': "Polyalphabetic"
    })

    if DEBUG is False:
        crypt_method = crypt_methods[file_data.crypt_method]
        new_file_name = f"{crypt_method}_{crypt_type.capitalize()}ed.txt"
        logger.info(f"{type(new_data)}: {new_data}")
        Cryptography.write(new_file_name, new_data)
        print(f"Your new {crypt_type}ed file has been created as " +
              f"{new_file_name}.")
    def is_valid_key(key, crypt_method):
        """
        Checks if the key is valid based on the cryptography method.

        :param string | integer key: The key to check.
        :param string crypt_method: The type of encryption/decryption.
        :return True & string (key) | False: Is the key valid?
        """
        logger.info(f"key: {key}, crypt_method: {crypt_method}")
        if crypt_method == 'C':
            while type(key) is not int or key not in range(0, 95):
                try:
                    key = Check.is_integer(key)[1]
                    if key not in range(0, 95):
                        raise ValueError
                except (TypeError, ValueError):
                    print("You must enter an integer between 1 and 95!")
                    key = input("Enter an encryption key\n>> ")
        elif crypt_method in ('M', 'P'):
            pass
        else:
            return False
        return True, key
    def is_in(entered, *required, error_message=None):
        """
        Checks entered values are valid by looking in the required values for a
        match.

        :param string entered: The entered values.
        :param string required: The values to check against.
        :param string error_message: An optional fail message.
        :return True & entered | False: Is the entered value in the required
                values?
        """
        try:
            while (str(entered).upper() not in required
                   and str(entered).lower() not in required):
                logger.info(f"entered: {entered} required: {required}")
                if error_message:
                    print(error_message)
                print("Please enter from the following:\n" +
                      ", ".join(char for char in required))
                entered = input(">> ").upper()
        except (ValueError, NameError):
            return False
        return True, entered
    def polyalphabetic(self):
        """
        Encrypts or decrypts the file using a polyalphabetic cipher.

        :return string: The encrypted/decrypted file data.
        """
        sequence = []
        for key_char in self.key:
            for i, char in enumerate(self.CHARS, 0):
                if key_char == char:
                    sequence.append(i)

        map_data = []
        for data_char in range(0, len(self.file_data), 3):
            for char in sequence:
                map_data.append(char)

        pair_data = []
        for x in zip(self.file_data, map_data):
            pair_data.append(x)

        crypt_operator = add if self.crypt_type == "encrypt" else sub

        data = []
        chars = list(self.CHARS * 2)
        for pair in pair_data:
            try:
                char, shift = pair
                new_char_index = crypt_operator(self.CHARS.index(char), shift)
                data.append(chars[new_char_index])
            except (ValueError, TypeError) as e:
                logger.error(e)
                print(f"Invalid character '{character}' in file {self.file}.")
        logger.info(f"data: {data}")

        return "".join(data)