##
# Cucumber 3 introduced a new feature called ParameterType that replaced
# a former concept known as Transforms. While we are eager to adopt this
# new concept, we cannot drop support for the former concept because of
# our extensive use of cucumber_factory (see legacy_transforms.rb).
#
# This wrapper allows us to define ParameterTypes in one place, while
# applying the same transformation to LegacyTransforms.

PARAMETER_TYPE_CONTEXT = self

class ParameterTypeProxy

  @type_registry = {}

  class << self

    # This method acts as a proxy to define and store Cucumber 3 ParameterTypes
    def add(name:, regexp:, transformer:, **parameter_type_options)
      if @type_registry[name.to_sym]
        raise "The ParameterType #{name.to_sym} has already been defined!"
      end
      @type_registry[name.to_sym] = OpenStruct.new(regexp: regexp, transformer: transformer)

      PARAMETER_TYPE_CONTEXT.ParameterType(
        name: name.to_s,
        regexp: regexp,
        transformer: transformer,
        **parameter_type_options
      )
    end

    # Applies a ParameterTypes transformation to a step definition or
    # substring, if it matches its regular expression
    def apply(parameter_type_name, step_fragment)
      return step_fragment if step_fragment.blank?
      parameter_type = @type_registry[parameter_type_name]

      if (matched = step_fragment.match(parameter_type.regexp))
        args = matched.captures.empty? ? [step_fragment] : matched.captures
        parameter_type.transformer.call(*args)
      else
        step_fragment
      end
    end

    # Applies one of multiple ParameterTypes to a step definition or substring.
    def apply_first_matching(parameter_type_names, step_fragment)
      parameter_type_names.each do |parameter_type_name|
        transformed_step_fragment = apply(parameter_type_name, step_fragment)
        return transformed_step_fragment if transformed_step_fragment != step_fragment
      end

      step_fragment
    end

    # #transform can be used to apply ParameterType transformations from
    # within the scope of a Legacy-Transformation.
    def transform(parameter_type_name, *args)
      parameter_type = @type_registry[parameter_type_name.to_sym]
      parameter_type.transformer.call(*args)
    end

  end
end

