':type pokemon: Pokemon'

def _do_catch(self, pokemon, encounter_id, catch_rate_by_ball, is_vip=False):

    if (self.use_pinap_on_vip and is_vip and (pokemon.level <= self.pinap_on_level_below) and (self.pinap_operator == 'and')):
        berry_id = ITEM_PINAPBERRY
    else:
        berry_id = ITEM_RAZZBERRY
    if (self.pinap_operator == 'or'):
        if ((self.use_pinap_on_vip and is_vip) or (pokemon.level <= self.pinap_on_level_below)):
            berry_id = ITEM_PINAPBERRY
    berry_count = self.inventory.get(berry_id).count
    ball_count = {}
    for ball_id in [ITEM_POKEBALL, ITEM_GREATBALL, ITEM_ULTRABALL]:
        ball_count[ball_id] = self.inventory.get(ball_id).count
    min_ultraball_to_keep = ball_count[ITEM_ULTRABALL]
    if ((self.min_ultraball_to_keep is not None) and (self.min_ultraball_to_keep >= 0)):
        min_ultraball_to_keep = self.min_ultraball_to_keep
    maximum_ball = (ITEM_GREATBALL if (ball_count[ITEM_ULTRABALL] < min_ultraball_to_keep) else ITEM_ULTRABALL)
    ideal_catch_rate_before_throw = (self.vip_berry_threshold if is_vip else self.berry_threshold)
    ideal_catch_rate_before_throw = (1 if (self.pinap_ignore_threshold and (berry_id == ITEM_PINAPBERRY)) else ideal_catch_rate_before_throw)
    used_berry = False
    original_catch_rate_by_ball = catch_rate_by_ball
    if DEBUG_ON:
        print ((((((('Pokemon    Level:    ' + str(pokemon.level)) + '    Berries    count:    ') + str(berry_count)) + '    Berries    ID:    ') + str(berry_id)) + '    Catch    rate:    ') + str(ideal_catch_rate_before_throw))
    while True:
        current_ball = ITEM_POKEBALL
        while ((ball_count[current_ball] == 0) and (current_ball < maximum_ball)):
            current_ball += 1
        if (ball_count[current_ball] == 0):
            self.emit_event('no_pokeballs', formatted='No    pokeballs    left!    Fleeing...')
            return WorkerResult.ERROR
        num_next_balls = 0
        next_ball = current_ball
        while (next_ball < maximum_ball):
            next_ball += 1
            num_next_balls += ball_count[next_ball]
        if ((berry_count == 0) and (berry_id == ITEM_PINAPBERRY)):
            berry_id = ITEM_RAZZBERRY
            ideal_catch_rate_before_throw = (self.vip_berry_threshold if is_vip else self.berry_threshold)
            berry_count = self.inventory.get(berry_id).count
        berries_to_spare = ((berry_count > 0) if is_vip else (berry_count > (num_next_balls + 30)))
        changed_ball = False
        if (self.smart_pinap_enabled and (((not is_vip) and (self.inventory.get(ITEM_PINAPBERRY).count > self.smart_pinap_to_keep) and (catch_rate_by_ball[current_ball] > self.smart_pinap_threshold)) or (is_vip and (self.inventory.get(ITEM_PINAPBERRY).count > 0) and (catch_rate_by_ball[current_ball] >= self.vip_berry_threshold))) and (not used_berry)):
            berry_id = ITEM_PINAPBERRY
            new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
            self.inventory.get(berry_id).remove(1)
            berry_count -= 1
            used_berry = True
        if ((catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw) and berries_to_spare and (not used_berry)):
            new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
            if (new_catch_rate_by_ball != catch_rate_by_ball):
                catch_rate_by_ball = new_catch_rate_by_ball
                self.inventory.get(berry_id).remove(1)
                berry_count -= 1
                used_berry = True
        best_ball = current_ball
        while (best_ball < maximum_ball):
            best_ball += 1
            if ((catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw) and (ball_count[best_ball] > 0)):
                current_ball = best_ball
                changed_ball = True
        if ((catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw) and (berry_count > 0) and (not used_berry)):
            new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
            if (new_catch_rate_by_ball != catch_rate_by_ball):
                catch_rate_by_ball = new_catch_rate_by_ball
                self.inventory.get(berry_id).remove(1)
                berry_count -= 1
                used_berry = True
        if changed_ball:
            action_delay(self.catchsim_changeball_wait_min, self.catchsim_changeball_wait_max)
        throw_parameters = {'normalized_reticle_size': 1.95, 'spin_modifier': 1.0, 'normalized_hit_position': 1.0, 'throw_type_label': 'Excellent'}
        self.generate_spin_parameter(throw_parameters)
        self.generate_throw_quality_parameters(throw_parameters)
        ball_count[current_ball] -= 1
        self.inventory.get(current_ball).remove(1)
        action_delay(self.catchsim_catch_wait_min, self.catchsim_catch_wait_max)
        self.emit_event('threw_pokeball', formatted='{throw_type}{spin_label}    throw!    Used    {ball_name},    with    chance    {success_percentage}    ({count_left}    left)', data={'throw_type': throw_parameters['throw_type_label'], 'spin_label': throw_parameters['spin_label'], 'ball_name': self.inventory.get(current_ball).name, 'success_percentage': self._pct(catch_rate_by_ball[current_ball]), 'count_left': ball_count[current_ball]})
        hit_pokemon = 1
        if ((random() >= self.catch_throw_parameters_hit_rate) and (not is_vip)):
            hit_pokemon = 0
        request = self.bot.api.create_request()
        request.catch_pokemon(encounter_id=encounter_id, pokeball=current_ball, normalized_reticle_size=throw_parameters['normalized_reticle_size'], spawn_point_id=self.spawn_point_guid, hit_pokemon=hit_pokemon, spin_modifier=throw_parameters['spin_modifier'], normalized_hit_position=throw_parameters['normalized_hit_position'])
        response_dict = request.call()
        try:
            catch_pokemon_status = response_dict['responses']['CATCH_POKEMON']['status']
        except KeyError:
            break
        if (catch_pokemon_status == CATCH_STATUS_FAILED):
            self.emit_event('pokemon_capture_failed', formatted='{pokemon}    capture    failed..    trying    again!', data={'pokemon': pokemon.name})
            used_berry = False
            catch_rate_by_ball = original_catch_rate_by_ball
            if self.catchsim_flee_count:
                sleep(((randrange(self.catchsim_flee_count) + 1) * self.catchsim_flee_duration))
            continue
        elif (catch_pokemon_status == CATCH_STATUS_VANISHED):
            with self.bot.database as conn:
                c = conn.cursor()
                c.execute("SELECT    COUNT(name)    FROM    sqlite_master    WHERE    type='table'    AND    name='vanish_log'")
            result = c.fetchone()
            while True:
                if (result[0] == 1):
                    conn.execute('INSERT    INTO    vanish_log    (pokemon,    cp,    iv,    encounter_id,    pokemon_id)    VALUES    (?,    ?,    ?,    ?,    ?)', (pokemon.name, pokemon.cp, pokemon.iv, str(encounter_id), pokemon.pokemon_id))
                break
            else:
                self.emit_event('vanish_log', sender=self, level='info', formatted='vanish_log    table    not    found,    skipping    log')
                break
            self.emit_event('pokemon_vanished', formatted='{}    vanished!'.format(pokemon.name), data={'pokemon': pokemon.name, 'encounter_id': self.pokemon['encounter_id'], 'latitude': self.pokemon['latitude'], 'longitude': self.pokemon['longitude'], 'pokemon_id': pokemon.pokemon_id})
            with self.bot.database as conn:
                c = conn.cursor()
                c.execute('SELECT    DISTINCT    COUNT(encounter_id)    FROM    vanish_log    WHERE    dated    >    (SELECT    dated    FROM    catch_log    WHERE    dated    IN    (SELECT    MAX(dated)    FROM    catch_log))')
            result = c.fetchone()
            self.consecutive_vanishes_so_far = result[0]
            if ((self.rest_completed == False) and (self.consecutive_vanishes_so_far >= self.consecutive_vanish_limit)):
                self.start_rest()
            if (self._pct(catch_rate_by_ball[current_ball]) == 100):
                self.bot.softban = True
        elif (catch_pokemon_status == CATCH_STATUS_SUCCESS):
            if (self.rest_completed == True):
                self.rest_completed = False
            pokemon.unique_id = response_dict['responses']['CATCH_POKEMON']['captured_pokemon_id']
            self.bot.metrics.captured_pokemon(pokemon.name, pokemon.cp, pokemon.iv_display, pokemon.iv)
            awards = response_dict['responses']['CATCH_POKEMON']['capture_award']
            (exp_gain, candy_gain, stardust_gain) = self.extract_award(awards)
            with self.bot.database as conn:
                c = conn.cursor()
                c.execute("SELECT    DISTINCT    COUNT(encounter_id)    FROM    catch_log    WHERE    dated    >=    datetime('now','-1    day')")
            result = c.fetchone()
            if is_vip:
                self.emit_event('pokemon_vip_caught', formatted='Vip    Captured    {pokemon}!    (CP:    {cp}    IV:    {iv}    {iv_display}    NCP:    {ncp}    Shiny:    {shiny})    Catch    Limit:    ({caught_last_24_hour}/{daily_catch_limit})    +{exp}    exp    +{stardust}    stardust', data={'pokemon': pokemon.name, 'ncp': str(round(pokemon.cp_percent, 2)), 'cp': str(int(pokemon.cp)), 'iv': str(pokemon.iv), 'iv_display': str(pokemon.iv_display), 'shiny': pokemon.shiny, 'exp': str(exp_gain), 'stardust': stardust_gain, 'encounter_id': str(self.pokemon['encounter_id']), 'latitude': str(self.pokemon['latitude']), 'longitude': str(self.pokemon['longitude']), 'pokemon_id': str(pokemon.pokemon_id), 'caught_last_24_hour': str(result[0]), 'daily_catch_limit': str(self.daily_catch_limit)})
            else:
                self.emit_event('pokemon_caught', formatted='Captured    {pokemon}!    (CP:    {cp}    IV:    {iv}    {iv_display}    NCP:    {ncp}    Shiny:    {shiny})    Catch    Limit:    ({caught_last_24_hour}/{daily_catch_limit})    +{exp}    exp    +{stardust}    stardust', data={'pokemon': pokemon.name, 'ncp': str(round(pokemon.cp_percent, 2)), 'cp': str(int(pokemon.cp)), 'iv': str(pokemon.iv), 'iv_display': str(pokemon.iv_display), 'shiny': pokemon.shiny, 'exp': str(exp_gain), 'stardust': stardust_gain, 'encounter_id': str(self.pokemon['encounter_id']), 'latitude': str(self.pokemon['latitude']), 'longitude': str(self.pokemon['longitude']), 'pokemon_id': str(pokemon.pokemon_id), 'caught_last_24_hour': str(result[0]), 'daily_catch_limit': str(self.daily_catch_limit)})
            inventory.pokemons().add(pokemon)
            inventory.player().exp += exp_gain
            self.bot.stardust += stardust_gain
            candy = inventory.candies().get(pokemon.pokemon_id)
            candy.add(candy_gain)
            self.emit_event('gained_candy', formatted='Candy    gained:    {gained_candy}.    You    now    have    {quantity}    {type}    candy!', data={'gained_candy': str(candy_gain), 'quantity': candy.quantity, 'type': candy.type})
            self.bot.softban = False
            try:
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute("SELECT    COUNT(name)    FROM    sqlite_master    WHERE    type='table'    AND    name='catch_log'")
                result = c.fetchone()
                while True:
                    if (result[0] == 1):
                        conn.execute('INSERT    INTO    catch_log    (pokemon,    cp,    iv,    encounter_id,    pokemon_id)    VALUES    (?,    ?,    ?,    ?,    ?)', (pokemon.name, pokemon.cp, pokemon.iv, str(encounter_id), pokemon.pokemon_id))
                    break
                else:
                    self.emit_event('catch_log', sender=self, level='info', formatted='catch_log    table    not    found,    skipping    log')
                    break
                user_data_caught = os.path.join(_base_dir, 'data', ('caught-%s.json' % self.bot.config.username))
                with open(user_data_caught, 'ab') as outfile:
                    json.dump(OrderedDict({'datetime': str(datetime.now()), 'pokemon': pokemon.name, 'cp': pokemon.cp, 'iv': pokemon.iv, 'encounter_id': self.pokemon['encounter_id'], 'pokemon_id': pokemon.pokemon_id, 'latitude': self.pokemon['latitude'], 'longitude': self.pokemon['longitude']}), outfile)
                    outfile.write('\n')
                if (exp_gain >= 500):
                    sleep(randrange(self.catchsim_newtodex_wait_min, self.catchsim_newtodex_wait_max))
            except IOError as e:
                self.logger.info(('[x]    Error    while    opening    location    file:    %s' % e))
        elif (catch_pokemon_status == CATCH_STATUS_MISSED):
            self.emit_event('pokemon_capture_failed', formatted='Pokeball    thrown    to    {pokemon}    missed..    trying    again!', data={'pokemon': pokemon.name})
            action_delay(self.catchsim_catch_wait_min, self.catchsim_catch_wait_max)
            continue
        break
