"""
.. module:: CGammaAPIEvasionProblem
	:synopsis: Class performs black-box evasion attacks, applying Gamma on APIs.

.. moduleauthor:: Luca Demetrio <luca.demetrio@dibris.unige.it>

"""

import copy
import random
import string
import numpy as np
import lief
from secml.array import CArray

from secml_malware.attack.blackbox.c_gamma_evasion import CGammaEvasionProblem
from secml_malware.attack.blackbox.c_wrapper_phi import CWrapperPhi


class CGammaSectionsEvasionProblem(CGammaEvasionProblem):

	def __init__(self,
				 section_population: list,
				 model_wrapper: CWrapperPhi,
				 population_size: int,
				 penalty_regularizer: float,
				 iterations: int,
				 seed: int = None,
				 is_debug: bool = False,
				 hard_label : bool = False,
				 threshold : float = 0.5,
				 loss : str = 'l1',
				 random_names : bool = True
				 ):
		super(CGammaSectionsEvasionProblem, self).__init__(section_population,
														model_wrapper,
														population_size,
														penalty_regularizer,
														iterations,
														seed,
														is_debug,
														hard_label,
														threshold,
														loss)
		self.random_names = random_names
		self.names_ = []
		self.best_names_ = []

	def init_starting_point(self, x: CArray):
		super().init_starting_point(x)
		self.best_names_ = []
		self.names_ = []

	def apply_feasible_manipulations(self, t, x: CArray) -> CArray:
		x_adv = copy.deepcopy(x)
		x_adv = x_adv.tolist()
		lief_adv: lief.PE.Binary = lief.PE.parse(x_adv[0])
		names = []
		for i in range(self.latent_space_size):
			content = self.section_population[i]
			content_to_append = content[:int(round(len(content) * t[i]))]
			if self.best_names_ != []:
				section_name = self.best_names_[i]
			else:
				section_name = ''.join(random.choice(string.ascii_letters) for _ in range(8))
			names.append(section_name)
			s = lief.PE.Section(content_to_append, section_name)
			lief_adv.add_section(s)
		self.names_.append(names)
		builder = lief.PE.Builder(lief_adv)
		builder.build()
		x_adv = CArray(builder.get_build())
		x_adv = x_adv.reshape((1, x_adv.shape[-1]))
		return x_adv

	def export_internal_results(self, irregular=None):
		confidence, fitness, sizes = super().export_internal_results(irregular)
		index = int(np.argmin(self._slice_sequence(self.population_size, self.fitness_, irregular)[-1]))
		self.best_names_ = self.names_[index]
		return confidence, fitness, sizes
