module FoodFishParser::Strict::Grammar

grammar Root
  include Common
  include FishName
  include CatchArea
  include CatchMethod
  include AquacArea
  include AquacMethod

  # Regular root node that requires all text to match fish details.
  #
  # Note that here we prefer fish with catch or aquaculture info,
  # then try fish names, and finally only catch or aquaculture info.
  #
  # The assumption is that all declared fish would have the same amount
  # of information. Hence we first try for all info, then resort to partial
  # info.
  rule root
    (
      ( fish_with_info   ( ws* fish_sep ws* fish_with_info   )* ) /
      ( fish_names_both  ( ws* fish_sep ws* fish_names_both  )* ) /
      ( fish_names_latin ( ws* fish_sep ws* fish_names_latin )* ) /
      ( fish_only_info   ( ws* fish_sep ws* fish_only_info   )* ) /
      ( fish_names_nl    ( ws* fish_sep ws* fish_names_nl    )* )
    )
    ws* '.'? ws*
    <RootNode>
  end

  # Alternate root node that allows fish details to be interspaced with other text.
  #
  # Note that this can be a much more expensive operation.
  rule root_anywhere
    (
      ( ( !char . )* ( !fish_with_info   char+ ( !char . )+ )+ )*
        fish_with_info   ( ( !char . )+ ( !fish_with_info   char+ ( !char . )+ )* fish_with_info   )* !char /
      ( ( !char . )* ( !fish_names_both  char+ ( !char . )+ )+ )*
        fish_names_both  ( ( !char . )+ ( !fish_names_both  char+ ( !char . )+ )* fish_names_both  )* !char /
      ( ( !char . )* ( !fish_names_latin char+ ( !char . )+ )+ )*
        fish_names_latin ( ( !char . )+ ( !fish_names_latin char+ ( !char . )+ )* fish_names_latin )* !char /
      ( ( !char . )* ( !fish_only_info   char+ ( !char . )+ )+ )*
        fish_only_info   ( ( !char . )+ ( !fish_only_info   char+ ( !char . )+ )* fish_only_info   )* !char /
      ( ( !char . )* ( !fish_names_nl    char+ ( !char . )+ )+ )*
        fish_names_nl    ( ( !char . )+ ( !fish_names_nl    char+ ( !char . )+ )* fish_names_nl    )* !char
    )
    <RootNode>
  end

  # separator between fish declarations
  rule fish_sep
    and_or / '.' / ';' / comma
  end

  # fish with catch or aquaculture info
  rule fish_with_info
    (
      # @todo move optional '(' after common fish name to root and properly match start and end brackets
      ( fish_name_any_list ( ws* ( comma / ':' ) )? ws+ fish_catch_info ( ws* ')' )? ) /
      ( fish_name_any_list ( ws* ( comma / ':' ) )? ws+ fish_aquac_info ( ws* ')' )? )
    )
    <FishNode>
  end

  # fish names common and latin
  rule fish_names_both
    fish_name_both_list <FishNode>
  end

  # fish names Latin only
  rule fish_names_latin
    fish_name_latin_list <FishNode>
  end

  # fish names NL only
  rule fish_names_nl
    fish_name_nl_list <FishNode>
  end

  # catch or aquaculture info only (no names)
  rule fish_only_info
    ( fish_catch_info / fish_aquac_info ) <FishNode>
  end

  rule fish_catch_info
    (
      catch_method_indicator ws* catch_method_content
      ( ( ws* comma )? ws+ catch_area_indicator_short ws* catch_area_content )?
    ) / (
      catch_area_indicator ws* catch_area_content
      ( ( ws* comma )? ws+ catch_method_indicator_short ws* catch_method_content )?
    )
  end

  rule fish_aquac_info
    (
      aquac_area_indicator ws* aquac_area_content
        ws* '.' ws* aquac_method_indicator ws* aquac_method_content
    ) / (
      aquac_area_indicator ws* aquac_area_content
        ( ( ws* comma )? ws+ aquac_method_indicator ws* aquac_method_content )?
    ) / (
      aquac_method_indicator ws* aquac_method_content
        ( ( ws* comma )? ws+ aquac_area_indicator_short ws* aquac_area_content )?
    )
  end
end

end