class DefaultReservedValueService implements ReservedValueService {

    @Override
    @Transactional
    public List<ReservedValue> reserve( TextPattern textPattern, int numberOfReservations, Map<String, String> values, Date expires )
        throws ReserveValueException, TextPatternGenerationException
    {
        long startTime = System.currentTimeMillis();
        int attemptsLeft = 10;

        List<ReservedValue> resultList = new ArrayList<>();

        TextPatternSegment generatedSegment = getGeneratedSegment( textPattern );

        String key = textPatternService.resolvePattern( textPattern, values );

        // Used for searching value tables
        String valueKey = ( generatedSegment != null ?
            key.replaceAll( Pattern.quote( generatedSegment.getRawSegment() ), "%" ) :
            key );

        ReservedValue reservedValue = new ReservedValue( textPattern.getOwnerObject().name(), textPattern.getOwnerUid(),
            key,
            valueKey,
            expires );

        if ( !hasEnoughValuesLeft( reservedValue,
            TextPatternValidationUtils.getTotalValuesPotential( generatedSegment ),
            numberOfReservations ) )
        {
            throw new ReserveValueException( "Not enough values left to reserve " + numberOfReservations + " values." );
        }

        if ( generatedSegment == null && numberOfReservations == 1 )
        {
            reservedValue.setValue( key );
            return reservedValueStore.reserveValues( reservedValue, Lists.newArrayList( key ) );
        }

        List<String> usedGeneratedValues = new ArrayList<>();

        int numberOfValuesLeftToGenerate = numberOfReservations;

        try
        {
            while ( attemptsLeft-- > 0 && numberOfValuesLeftToGenerate > 0 )
            {
                if ( System.currentTimeMillis() - startTime >= GENERATION_TIMEOUT )
                {
                    throw new TimeoutException( "Generation and reservation of values took too long" );
                }

                List<String> resolvedPatterns = new ArrayList<>();

                List<String> generatedValues = new ArrayList<>();

                int maxGenerateAttempts = 10;

                while ( generatedValues.size() < numberOfValuesLeftToGenerate && maxGenerateAttempts-- > 0 )
                {
                    generatedValues.addAll( generateValues( textPattern, key, numberOfReservations - resultList.size() ) );
                    generatedValues.removeAll( usedGeneratedValues );
                }

                usedGeneratedValues.addAll( generatedValues );

                // Get a list of resolved patterns
                for ( int i = 0; i < numberOfReservations - resultList.size(); i++ )
                {
                    resolvedPatterns.add( textPatternService.resolvePattern( textPattern,
                        ImmutableMap.<String, String>builder()
                            .putAll( values )
                            .put( generatedSegment.getMethod().name(), generatedValues.get( i ) )
                            .build() ) );
                }

                resultList.addAll( reservedValueStore.reserveValues( reservedValue, resolvedPatterns ) );

                numberOfValuesLeftToGenerate = numberOfReservations - resultList.size();
            }
        }
        catch ( TimeoutException ex )
        {
            log.warn( String.format(
                "Generation and reservation of values for %s wih uid %s timed out. %s values was reserved. You might be running low on available values",
                textPattern.getOwnerObject().name(), textPattern.getOwnerUid(), resultList.size() ) );
        }

        return resultList;
    }

    public  DefaultReservedValueService( TextPatternService textPatternService, ReservedValueStore reservedValueStore,
        SequentialNumberCounterStore sequentialNumberCounterStore );

}