# @Opulent
module Opulent
  # @Parser
  class Parser
    # Match an if-else control structure
    #
    def control(parent, indent)
      # Accept eval or multiline eval syntax and return a new node,
      return unless (structure = accept(:control))
      structure = structure.to_sym

      # Handle each and the other control structures
      condition = accept(:line_feed).strip

      # Process each control structure condition
      if structure == :each
        # Check if arguments provided correctly
        unless condition.match Tokens[:each_pattern]
          Logger.error :parse, @code, @i, @j, :each_arguments
        end

        # Conditions for each iterator
        condition = [
          Regexp.last_match[1].split(' '),
          Regexp.last_match[2].split(/,(.+)$/).map(&:strip).map(&:to_sym)
        ]

        # Array loop as default
        condition[0].unshift '{}' if condition[0].length == 1
      end

      # Else and default structures are not allowed to have any condition
      # set and the other control structures require a condition
      if structure == :else
        unless condition.empty?
          Logger.error :parse, @code, @i, @j, :condition_exists
        end
      else
        if condition.empty?
          Logger.error :parse, @code, @i, @j, :condition_missing
        end
      end

      # Add the condition and create a new child to the control parent.
      # The control parent keeps condition -> children matches for our
      # document's content
      # add_options = proc do |control_parent|
      #   control_parent.value << condition
      #   control_parent.children << []
      #
      #   root control_parent
      # end

      # If the current control structure is a parent which allows multiple
      # branches, such as an if or case, we create an array of conditions
      # which can be matched and an array of children belonging to each
      # conditional branch
      if [:if, :unless].include? structure
        # Create the control structure and get its child nodes
        control_structure = [structure, [condition], {}, [], indent]
        root control_structure, indent

        # Turn children into an array because we allow multiple branches
        control_structure[@children] = [control_structure[@children]]

        # Add it to the parent node
        parent[@children] << control_structure

      elsif structure == :case
        # Create the control structure and get its child nodes
        control_structure = [
          structure,
          [],
          { condition: condition },
          [],
          indent
        ]

        # Add it to the parent node
        parent[@children] << control_structure

      # If the control structure is a child structure, we need to find the
      # node it belongs to amont the current parent. Search from end to
      # beginning until we find the node parent
      elsif control_child structure
        # During the search, we try to find the matching parent type
        unless control_parent(structure).include? parent[@children][-1][@type]
          Logger.error :parse,
                       @code,
                       @i,
                       @j,
                       :control_child,
                       control_parent(structure)
        end

        # Gather child elements for current structure
        control_structure = [structure, [condition], {}, [], indent]
        root control_structure, indent

        # Add the new condition and children to our parent structure
        parent[@children][-1][@value] << condition
        parent[@children][-1][@children] << control_structure[@children]

      # When our control structure isn't a complex composite, we create
      # it the same way as a normal node
      else
        control_structure = [structure, condition, {}, [], indent]
        root control_structure, indent

        parent[@children] << control_structure
      end
    end

    # Check if the current control structure requires a parent node and
    # return the parent's node type
    #
    def control_child(structure)
      [:else, :elsif, :when].include? structure
    end

    # Check if the current control structure requires a parent node and
    # return the parent's node type
    #
    def control_parent(structure)
      case structure
      when :else then [:if, :unless, :case]
      when :elsif then [:if]
      when :when then [:case]
      end
    end
  end
end