module Pod
  class Installer

    # This class is responsible of creating and configuring the static library
    # target in Pods project. Every target is generated from a target
    # definition of the Podfile.
    #
    class TargetInstaller

      # @return [Project] The Pods project.
      #
      attr_reader :project

      # @return [TargetDefinition] The target definition whose target needs to
      # be generated.
      #
      attr_reader :target_definition

      # @param  [Array<LocalPod>] pods the pods are required by the target
      #         definition of this installer.
      #
      attr_reader :pods

      # @param  [Project]           project           @see project
      # @param  [TargetDefinition]  target_definition @see target_definition
      # @param  [Array<LocalPod>]   pods              @see pods
      #
      def initialize(project, target_definition, pods)
        @project           = project
        @target_definition = target_definition
        @pods              = pods
      end

      # Creates the target in the Pods project and its support files.
      #
      # @return [void]
      #
      def install
        add_target
        add_build_files_to_target
        add_file_reference_for_support_files
        configure_target

        create_xcconfig_file
        create_prefix_header
        create_bridge_support_file
        create_copy_resources_script
      end

      # TODO This has to be removed, but this means the specs have to be
      # updated if they need a reference to the prefix header.
      #
      def prefix_header_filename
        library.prefix_header_name
      end

      #-----------------------------------------------------------------------#

      # @!group Installation steps

      private

      # Adds the library for the {#target_definition} to the Pods
      # project.
      #
      # @return [void]
      #
      def add_target
        @library = @project.add_pod_library(target_definition)
        @target  = @library.target
      end

      # Adds the build files of the pods to the target.
      #
      # @return [void]
      #
      def add_build_files_to_target
        pods.each { |p| p.add_build_files_to_target(target) }
      end

      # Adds the file references for the support files that are generated by
      # the installation process to the Pods project.
      #
      # @return [void]
      #
      def add_file_reference_for_support_files
        g = project.support_files_group.new_group(target_definition.label)
        g.new_file(library.copy_resources_script_name)
        g.new_file(library.prefix_header_name)
        g.new_file(library.xcconfig_name).tap { |f| @xcconfig_file_ref = f }
      end

      # Configures the build settings of the target.
      #
      # @note   The `PODS_HEADERS_SEARCH_PATHS` overrides the xcconfig.
      #
      # @return [void]
      #
      def configure_target
        set = {}
        set['OTHER_LDFLAGS']                 = ''
        set['GCC_PREFIX_HEADER']             = library.prefix_header_name
        set['PODS_ROOT']                     = '${SRCROOT}'
        set['PODS_HEADERS_SEARCH_PATHS']     = '${PODS_BUILD_HEADERS_SEARCH_PATHS}'
        set['GCC_WARN_INHIBIT_ALL_WARNINGS'] =
          @target_definition.inhibit_all_warnings? ? 'YES' : 'NO'

        @target.build_configurations.each do |c|
          c.base_configuration_reference = xcconfig_file_ref
          c.build_settings.merge!(set)
        end
      end

      # Generates the contents of the xcconfig file and saves it to disk.
      #
      # @note   The `ALWAYS_SEARCH_USER_PATHS` flag is enabled to support
      #         libraries like `EmbedReader`.
      #
      # @return [void]
      #
      def create_xcconfig_file
        UI.message "- Generating xcconfig file at #{UI.path(library.xcconfig_path)}" do
          xcconfig = Xcodeproj::Config.new({
            'ALWAYS_SEARCH_USER_PATHS'         => 'YES',
            'OTHER_LDFLAGS'                    => default_ld_flags,
            'HEADER_SEARCH_PATHS'              => '${PODS_HEADERS_SEARCH_PATHS}',
            'PODS_ROOT'                        => library.relative_pods_root,
            'PODS_BUILD_HEADERS_SEARCH_PATHS'  => quote(sandbox.build_headers.search_paths),
            'PODS_PUBLIC_HEADERS_SEARCH_PATHS' => quote(sandbox.public_headers.search_paths),
            'PODS_HEADERS_SEARCH_PATHS'        => '${PODS_PUBLIC_HEADERS_SEARCH_PATHS}'
          })
          pods.each { |pod| xcconfig.merge!(pod.xcconfig) }
          xcconfig.save_as(library.xcconfig_path)
          library.xcconfig = xcconfig
        end
      end

      # Creates a prefix header file which imports `UIKit` or `Cocoa`. This
      # file also include any prefix header content reported by the
      # specification of the pods.
      #
      # @return [void]
      #
      def create_prefix_header
        UI.message "- Generating prefix header at #{UI.path(library.prefix_header_path)}" do
          gen = Generator::PrefixHeader.new(target_definition.platform, pods)
          gen.save_as(library.prefix_header_path)
        end
      end

      # Generates the bridge support metadata if requested by the {Podfile}.
      #
      # @return [void]
      #
      # TODO: Why is this added to the copy resources script?
      #
      def create_bridge_support_file
        if target_definition.podfile.generate_bridge_support?
          UI.message "- Generating BridgeSupport metadata at #{UI.path library.bridge_support_path}" do
            generator = Generator::BridgeSupport.new(pods.map do |pod|
              pod.relative_header_files.map { |header| sandbox.root + header }
            end.flatten)
            generator.save_as(library.bridge_support_path)
            copy_resources_script.resources << library.bridge_support_name
          end
        end
      end

      # Creates a script that copies the resources to the bundle of the client
      # target.
      #
      # TODO:   This should be replaced by an Xcode copy resources build phase.
      #
      # @return [void]
      #
      def create_copy_resources_script
        UI.message "- Generating copy resources script at #{UI.path(library.copy_resources_script_path)}" do
          copy_resources_script.resources << pods.map { |p| p.relative_resource_files }.flatten
          copy_resources_script.save_as(library.copy_resources_script_path)
        end
      end

      #-----------------------------------------------------------------------#

      # @!group Installation products.

      public

      # @return [Library] the library for the target definition.
      #
      # @note   Generated by the {#add_target} step.
      #
      attr_reader :library

      # @return [PBXNativeTarget] the target generated by the installation
      #         process.
      #
      # @note   Generated by the {#add_target} step.
      #
      attr_reader :target

      # @return [PBXFileReference] the file reference to the xcconfig file of
      #         the target.
      #
      # @note   Generated by the {#add_file_reference_for_support_files} step.
      #
      attr_reader :xcconfig_file_ref

      #-----------------------------------------------------------------------#

      # @!group Private helpers.

      private

      # @return [Sandbox] sandbox the sandbox where the support files should
      #         be generated.
      #
      def sandbox
        project.sandbox
      end

      # Creates and caches the copy resource script.
      #
      # @return [CopyResourcesScript]
      #
      # TODO:   This should be replaced by an Xcode copy resources build phase.
      #
      def copy_resources_script
        @copy_resources_script ||= Generator::CopyResourcesScript.new
      end

      # Converts an array of strings to a single string where the each string
      # is surrounded by double quotes and separated by a space. Used to
      # represent strings in a xcconfig file.
      #
      # @param  [Array<String>] strings
      #         a list of strings.
      #
      # @return [String] the resulting string.
      #
      def quote(strings)
        strings.map { |s| %W|"#{s}"| }.join(" ")
      end

      # @return [String] the default linker flags. `-ObjC` is always included
      #         while `-fobjc-arc` is included only if requested in the
      #         Podfile.
      #
      def default_ld_flags
        flags = %w[ -ObjC ]
        requires_arc = pods.any? { |pod| pod.requires_arc? }
        if  requires_arc && target_definition.podfile.set_arc_compatibility_flag?
          flags << '-fobjc-arc'
        end
        flags.join(" ")
      end
    end
  end
end

