from acsploit.options import Options
import itertools
from .java_common import generate_via_half_string_merge, java_hash

options = Options()
options.add_option('n_collisions', 10, 'Number of colliding strings to create')
options.add_option('n_substrings', 100, 'Number of target and zero hash substrings to use')
options.add_option('hash_table_size', 2**32, 'Size of target hash table')
options.add_option('target_type', 'preimage', 'Whether the target is an image (hash output) or preimage (hash input)',
                   ['image', 'preimage'])
options.add_option('target', 'hello', 'Image or preimage of desired hash value')

DESCRIPTION = 'Produces hash collisions for the Java hash function.' \
              '\n\n  ' \
              'Uses a faster and less memory intensive technique but generates longer strings. "n_substrings" is the ' \
              'number of initial target and zero strings to generate which will then be combined to create the ' \
              'desired number of collisions. Setting this to larger values requires more time and memory but generates ' \
              'smaller strings.'


DEFAULT_INPUT = 'char'


def combine_targets_and_zeros(target_strings, zero_strings, n_collisions):
    """Combines the zero strings with the target strings until the desired number of collisions is reached"""
    i = 0
    ret = target_strings.copy()
    while True:
        i = i + 1
        for s in itertools.product(zero_strings, repeat=i):
            for t in target_strings:
                ret.append(''.join(s) + t)
                if len(ret) == n_collisions:
                    return ret


def run(generator, output):
    """Generate hash collisions in two steps: 1) generate substrings with a hash of the target and a hash of zero and 2)
        combine the strings by incrementally increasing the number of zero strings prepended."""
    forbidden_string = ''
    n_collisions = options['n_collisions']
    n_substrings = options['n_substrings']
    if options['target_type'] == 'preimage':
        target = java_hash(options['target'], options['hash_table_size'])
        forbidden_string = options['target']
    else:
        target = int(options['target'])

    number_target_strings = n_substrings if n_collisions > n_substrings else n_collisions
    target_strings = generate_via_half_string_merge(generator, target, number_target_strings,
                                                    options['hash_table_size'], forbidden_string)

    if len(target_strings) == n_collisions:
        ret = target_strings
    else:
        zero_strings = generate_via_half_string_merge(generator, 0, n_substrings,
                                                      options['hash_table_size'], forbidden_string)
        ret = combine_targets_and_zeros(target_strings, zero_strings, n_collisions)

    ret.sort()
    output.output(ret)
