def __init__(self, image_path):
     super(Steganography, self).__init__()
     self._image_path = image_path
     if self._image_path[-4:] != ".bmp":
         raise NotBMPError("File is not type BMP")
     self._pixel_data = PixelData(self._image_path)
class Steganography(object):

    """hide text information from the console or input file in BMP "
    "format image. Or removal of text from the image in BMP format "
    "console or output text file"""

    def __init__(self, image_path):
        super(Steganography, self).__init__()
        self._image_path = image_path
        if self._image_path[-4:] != ".bmp":
            raise NotBMPError("File is not type BMP")
        self._pixel_data = PixelData(self._image_path)

    def _bin_message(self, information, len_byte_message):
        bin_information_len = bin(len_byte_message)[2:].zfill(32)

        bin_information = []
        bin_information.append(bin_information_len)

        for byte in information:
            bin_byte = bin(byte)[2:].zfill(8)
            bin_information.append(bin_byte)
        else:
            bin_information = ''.join(bin_information)

        list_bit = tuple(map(int, bin_information))

        return list_bit

    def _separation(self, bin_message, pixel_data):
        """divides the binary data is too big"""
        separation_bin_message = []

        size_image = pixel_data.get_width() *\
            pixel_data.get_height() * pixel_data.get_pixel_size()

        valueblock = len(bin_message) // size_image
        ost = len(bin_message) % size_image

        for i in range(valueblock):
            block = bin_message[size_image * i:size_image * (i + 1)]
            separation_bin_message.append(block)
        else:
            try:
                block = bin_message[
                    size_image * (i + 1):size_image * (i + 1) + ost]
            except UnboundLocalError:
                block = bin_message[:ost]
            if block:
                separation_bin_message.append(block)

        return separation_bin_message

    @staticmethod
    def _set_bit(byte, index, bit):
        """sets bit number"""
        mask = 1 << index
        byte &= ~mask
        if bit:
            byte |= mask
        return byte

    def hide_information(
            self, information, out_file=None):
        """hide information in the image data"""
        if type(information) is str:
            information = information.encode()
        len_byte_message = len(information)

        capacity_image = self._pixel_data.get_height() *\
            self._pixel_data.get_width() * self._pixel_data.get_pixel_size()
        if capacity_image < len_byte_message + 4:
            raise LengthError('Information does not fit into the picture. \
                Length of the information: {0}. Сapacity image: \
                {1}'.format(len_byte_message, capacity_image))

        bin_message = self._bin_message(information, len_byte_message)

        separation_bin_message =\
            self._separation(bin_message, self._pixel_data)

        byte_array = self._pixel_data.get_pixel_data()
        number_bit = 0
        byte_array = list(byte_array)
        for piece_bin_message in separation_bin_message:
            setBit = (i for i in piece_bin_message)
            for i in range(len(byte_array)):
                try:
                    nextBit = next(setBit)
                    byte_array[i] = Steganography._set_bit(
                            byte_array[i], number_bit, nextBit)
                except StopIteration:
                    break
            number_bit += 1
        self._pixel_data.write_new_image(bytes(byte_array), out_file)

    @staticmethod
    def _get_bit(byte, index):
        """returns the number of bits"""
        bit = str(int(byte & 1 << index != 0))
        return bit

    @staticmethod
    def _get_allByte(bin_information):
        """returns all bytes of binary information"""
        counter = 0
        byte = ""
        all_bytes = []
        for bit in bin_information:
            byte += bit
            counter += 1

            if counter == 8:
                all_bytes.append(int(byte, 2))
                byte = ""
                counter = 0

        return bytes(all_bytes)

    def unhide_information(self, out_file=None):
        """discloses information from image"""
        byteArray = self._pixel_data.get_pixel_data()

        len_bit_information = ""
        len_information = None

        number_bit = 0
        get_byte = (i for i in byteArray)
        for i in range(32):
            try:
                nextByte = next(get_byte)
                len_bit_information +=\
                    Steganography._get_bit(nextByte, number_bit)
            except StopIteration:
                number_bit += 1
                get_byte = (i for i in byteArray)
                nextByte = next(get_byte)
                len_bit_information +=\
                    Steganography._get_bit(nextByte, number_bit)
        else:
            len_information = int(len_bit_information, 2) * 8

        information_need_bits = ""
        for i in range(len_information):
            try:
                nextByte = next(get_byte)
                information_need_bits +=\
                    Steganography._get_bit(nextByte, number_bit)
            except StopIteration:
                number_bit += 1
                get_byte = (i for i in byteArray)
                nextByte = next(get_byte)
                information_need_bits +=\
                    Steganography._get_bit(nextByte, number_bit)

        comlete_information = Steganography._get_allByte(information_need_bits)

        if out_file:
            with open(out_file, "wb") as out_file:
                out_file.write(comlete_information)
        else:
            print(comlete_information.decode('utf-8'))

    @staticmethod
    def test():
        """testing method"""
        stegH = Steganography(os.path.join(path[0], "test", "in_image.bmp"))
        lever = False

        print("Test hide/unhide english txt:")
        with open(os.path.join(
                path[0], "test", "input1.txt"), 'rb') as inputFile:
            stegH.hide_information(
                inputFile.read(), os.path.join(path[0], "test", "steg.bmp"))

            stegU = Steganography(os.path.join(path[0], "test", "steg.bmp"))
            stegU.unhide_information(
                os.path.join(path[0], "test", "output1.txt"))
            if getsize(os.path.join(path[0], "test", "input1.txt")) == getsize(
                    os.path.join(path[0], "test", "output1.txt")):
                print("Verification was successful")
            else:
                print("Validation fails")
                lever = True

        print("Test hide/unhide russian txt:")
        with open(os.path.join(
                path[0], "test", "input5.txt"), 'rb') as inputFile:
            stegH.hide_information(
                inputFile.read(), os.path.join(path[0], "test", "steg.bmp"))

            stegU = Steganography(os.path.join(path[0], "test", "steg.bmp"))
            stegU.unhide_information(
                os.path.join(path[0], "test", "output5.txt"))
            if getsize(os.path.join(path[0], "test", "input5.txt")) == getsize(
                    os.path.join(path[0], "test", "output5.txt")):
                print("Verification was successful")
            else:
                print("Validation fails")
                lever = True

        print("Test hide/unhide en/ru txt:")
        with open(os.path.join(
                path[0], "test", "input6.txt"), 'rb') as inputFile:
            stegH.hide_information(
                inputFile.read(), os.path.join(path[0], "test", "steg.bmp"))

            stegU = Steganography(os.path.join(path[0], "test", "steg.bmp"))
            stegU.unhide_information(
                os.path.join(path[0], "test", "output6.txt"))
            if getsize(os.path.join(path[0], "test", "input6.txt")) == getsize(
                    os.path.join(path[0], "test", "output6.txt")):
                print("Verification was successful")
            else:
                print("Validation fails")
                lever = True

        print("Test hide/unhide png:")
        with open(os.path.join(
                path[0], "test", "input2.png"), 'rb') as inputFile:
            stegH.hide_information(
                inputFile.read(), os.path.join(path[0], "test", "steg.bmp"))

            stegU = Steganography(os.path.join(path[0], "test", "steg.bmp"))
            stegU.unhide_information(
                os.path.join(path[0], "test", "output2.png"))
            if getsize(os.path.join(path[0], "test", "input2.png")) == getsize(
                    os.path.join(path[0], "test", "output2.png")):
                print("Verification was successful")
            else:
                print("Validation fails")
                lever = True

        print("Test hide/unhide rar:")
        with open(os.path.join(
                path[0], "test", "input3.rar"), 'rb') as inputFile:
            stegH.hide_information(
                inputFile.read(), os.path.join(path[0], "test", "steg.bmp"))

            stegU = Steganography(os.path.join(path[0], "test", "steg.bmp"))
            stegU.unhide_information(
                os.path.join(path[0], "test", "output3.rar"))
            if getsize(os.path.join(path[0], "test", "input3.rar")) == getsize(
                    os.path.join(path[0], "test", "output3.rar")):
                print("Verification was successful")
            else:
                print("Validation fails")
                lever = True

        print("Test hide/unhide exe:")
        with open(os.path.join(
                path[0], "test", "input4.exe"), 'rb') as inputFile:
            stegH.hide_information(
                inputFile.read(), os.path.join(path[0], "test", "steg.bmp"))

            stegU = Steganography(os.path.join(path[0], "test", "steg.bmp"))
            stegU.unhide_information(
                os.path.join(path[0], "test", "output4.exe"))
            if getsize(os.path.join(
                path[0], "test", "input4.exe")) == getsize(
                    os.path.join(path[0], "test", "output4.exe")):
                print("Verification was successful")
            else:
                print("Validation fails")
                lever = True

        print("Complete test")

        return lever