require 'cocoapods-tdfire-binary/binary_url_manager'
require 'cocoapods-tdfire-binary/binary_state_store'

module Pod
  class Specification
  	#--------------------------------------------------------------------#
  	# =>  获取自身以及子组件的属性合并值
  	#--------------------------------------------------------------------#
    # def all_value_for_attribute(name)
  		# (Array(self) + Array(recursive_subspecs))
    #   .map { |s| s.attributes_hash[name] }
    #   .compact
    # end
    #
    # def all_hash_value_for_attribute(name)
  		# all_value_for_attribute(name).reduce({}, :merge)
    # end
    #
    # def all_array_value_for_attribute(name)
  		# all_value_for_attribute(name).flatten
    # end
    #
    # def store_array_value_with_attribute_and_reference_spec(name, spec)
  		# temp = spec.all_array_value_for_attribute(name)
  		# temp += attributes_hash[name] unless attributes_hash[name].nil?
  		# store_attribute(name, temp) unless temp.empty?
    # end
    #
    # def store_hash_value_with_attribute_and_reference_spec(name, spec, &select)
  		# temp = spec.all_hash_value_for_attribute(name)
  		# temp.merge!(attributes_hash[name]) unless attributes_hash[name].nil?
			# temp.select! { |k, v| yield k } if block_given?
    #   store_attribute(name, temp) unless temp.empty?
    # end

    def tdfire_recursive_value(name, platform = :ios)
      subspec_consumers = recursive_subspecs
                              .select { |s| s.supported_on_platform?(platform) }
                              .map { |s| s.consumer(platform) }
                              .uniq
      value = (Array(consumer(platform)) + subspec_consumers).map { |c| c.send(name) }.flatten.uniq
      value
    end
  	#--------------------------------------------------------------------#
  end
end

module Pod
	module Tdfire
		class BinarySpecificationRefactor
			attr_accessor :target_spec

			def initialize(target_spec)
				@target_spec = target_spec
			end

      def configure_source
        # 在设置了 use_frameworks! 的情况下才会生效
        set_use_static_framework

        # cocoapods-package 打包时,只让 iOS 平台生效
        set_platform_limitation(target_spec) if Pod::Tdfire::BinaryStateStore.limit_platform
      end

			#--------------------------------------------------------------------#
			# 生成default subspec TdfireBinary ,并将源码依赖时的配置转移到此 subspec 上
			def configure_binary_default_subspec(spec)
        # 限制二进制只支持 iOS 平台 (这里是 parent spec)
        set_platform_limitation(spec)

				default_subspec = "TdfireBinary"
				target_spec.subspec default_subspec do |ss|
					subspec_refactor = BinarySpecificationRefactor.new(ss)
					subspec_refactor.configure_binary(spec)
        end

				# 创建源码依赖时的 subspec,并且设置所有的 subspec 依赖 default_subspec
				spec.subspecs.each do |s|
					target_spec.subspec s.base_name do |ss|
						ss.dependency "#{target_spec.root.name}/#{default_subspec}"
					end
				end

				target_spec.default_subspec = default_subspec
				target_spec.default_subspec = default_subspec

				# Pod::UI.message "Tdfire: subspecs for #{target_spec.name}: #{target_spec.subspecs.map(&:name).join(', ')}"
			end

			#--------------------------------------------------------------------#
			# spec 是二进制依赖时的配置
			def configure_binary(spec)
        # 限制二进制只支持 iOS 平台 (这里是 spec 或者 subpsec)
        set_platform_limitation(spec)

				# 组件 frameworks 的依赖
				target_spec.vendored_frameworks = "#{target_spec.root.name}.framework"
        # 如果不加这一句,会提示找不到 bundle。测试时需要 shift + cmd + k 清除 production
        #
        # static framework 不像 dynamic framework ,后者在生成 production 后,会有一个专门的 Frameworks 文件夹,其内部的结构和打包前是一致的, bundle 也会在 .framework 文件中
        # 而 static framework 的可执行文件部分,会被合并到 App 的可执行文件中, bundle 按逻辑会放到 main bundle 中,
        # 但是 CocoaPods 并不会帮 vendored_frameworks 中的 static framework  做 bundle 的拷贝工作,
        # 所以这里需要暴露 static framework 中的 bundle ,明确让 CocoaPods 拷贝 bundle 到 main bundle,
        # 可以查看 高德地图 和 友盟等 framework ,都已这种方式处理
        #
        # 如果组件不是用的 resource_bundle,而是用的 resources ,那么这里就不会拷贝组件的资源文件
        #

        framework_resources = spec.tdfire_recursive_value('resources', :ios)
        resource_bundles = spec.tdfire_recursive_value('resource_bundles', :ios)
        # 不判断 lint 会报错 did not match any file
        target_spec.resources = ["#{target_spec.root.name}.framework/Resources/*", "#{target_spec.root.name}.framework/Versions/A/Resources/*"] if resource_bundles.select(&:any?).any? || framework_resources.any?
        # Pod::UI.message "Tdfire: resources for binary: #{target_spec.tdfire_recursive_value('resources', :ios).join(', ')}"

        # cococapods 会将以下头文件添加入 user search path ,这样使用者可以使用 " " 对头文件进行引用
        #
        #
        # 不能通过下面这种方式判断
        # Dir.glob("#{target_spec.root.name}.framework/Headers/*").count

        target_spec.source_files = ["#{target_spec.root.name}.framework/Headers/*", "#{target_spec.root.name}.framework/Versions/A/Headers/*"]
        target_spec.public_header_files = ["#{target_spec.root.name}.framework/Headers/*", "#{target_spec.root.name}.framework/Versions/A/Headers/*"]

        available_platforms(spec).each do |platform|
          # Pod::UI.section("Tdfire: copying configuration for platform #{platform}") do
            target_platform = target_spec.send(platform.to_sym)

            # 保留对 frameworks lib 的依赖
            %w[frameworks libraries weak_frameworks].each do |name|
              value = spec.tdfire_recursive_value(name, platform )
              target_platform.send("#{name}=", value) unless value.empty?

              # Pod::UI.message "Tdfire: #{name} for #{platform}: #{target_spec.tdfire_recursive_value(name, platform)}"
            end

            # 保留对其他组件的依赖
            dependencies = spec.tdfire_recursive_value('dependencies', platform)

            # 去除对自身子组件的依赖
            dependencies
                .select { |d| d.root_name != target_spec.root.name }
                .each { |d| target_platform.dependency(d.name, d.requirement.to_s) }

            # Pod::UI.message "Tdfire: dependencies for #{platform}: #{target_spec.tdfire_recursive_value('dependencies', platform).map(&:name).join(', ')}"
          # end
        end
      end

      def set_platform_limitation(spec)
        target_spec.platform = :ios, deployment_target(spec, :ios)
      end
			#--------------------------------------------------------------------#
			# spec 是源码依赖时的配置
			def set_preserve_paths(spec)
        available_platforms(spec).each do |platform|
          # Pod::UI.message("Tdfire: set preserve paths for platform #{platform}")
          # 源码、资源文件
          #
          source_files = spec.tdfire_recursive_value('source_files', platform)
          resources = spec.tdfire_recursive_value('resources', platform)
          resource_bundles = spec.tdfire_recursive_value('resource_bundles', platform)

          source_preserve_paths = source_files + resources + resource_bundles.map(&:values).flatten

          # 二进制文件
          framework_preserve_paths = [framework_name]
          preserve_paths = source_preserve_paths + framework_preserve_paths

          # 保留原有的 preserve_paths
          target_preserve_paths = target_spec.tdfire_recursive_value('preserve_paths', platform)
          preserve_paths += target_preserve_paths unless target_preserve_paths.empty?

          target_platform = target_spec.send(platform.to_sym)
          target_platform.preserve_paths = preserve_paths.uniq

          # Pod::UI.message "Tdfire: preserve paths for #{platform}: #{preserve_paths.join(', ')}"
        end
			end

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

			def set_use_static_framework
        # 1.4.0 版本生效
        # 使用 use_frameworks! 后,生成静态 Framework
        # 有些组件 podspec 没有进行二进制配置, Podfile 使用 use_frameworks! 就会出现  has transitive dependencies that include static frameworks 错误
				# target_spec.static_framework = true if target_spec.respond_to?('static_framework')
			end

			#--------------------------------------------------------------------#
			def set_framework_download_script
				download_url = Pod::Tdfire::BinaryUrlManager.pull_url_for_pod_version(target_spec.root.name, target_spec.version)

				download_script = <<-EOF
        #!/bin/sh

        if [[ -d #{framework_name} ]]; then
          echo "Tdfire: #{framework_name} is not empty"
          exit 0
        fi

        if [[ ! -d tdfire_download_temp ]]; then
          mkdir tdfire_download_temp
        fi

        cd tdfire_download_temp

        curl --fail -O -J -v #{download_url}

        if [[ -f #{framework_name}.zip ]]; then
          echo "Tdfire: copying #{framework_name} ..."

          unzip #{framework_name}.zip
          cp -fa #{framework_name} ../
        fi

        cd ..
        rm -fr tdfire_download_temp

        echo "pod cache path for #{target_spec.root.name}: $(pwd)"
				EOF

				combined_download_script = %Q[echo '#{download_script}' > download.sh && sh download.sh && rm download.sh]
				combined_download_script += " && " << target_spec.prepare_command unless target_spec.prepare_command.nil?

				target_spec.prepare_command = combined_download_script
			end

			#--------------------------------------------------------------------#
			private

      def deployment_target(spec, platform = :ios)
        target_spec.deployment_target(platform) || spec.deployment_target(platform)
      end

      def available_platforms(spec)
        # 平台信息没设置,表示支持所有平台
        # 所以二进制默认支持所有平台,或者和源码时支持平台一致(平台设置在源码配置lambda外)
        # 支持多平台用 deployment_target,单平台用 platform
        #
        target_spec.available_platforms || spec.available_platforms || [:ios]
      end

			def framework_name
				"#{target_spec.root.name}.framework"
			end
		end
	end
end