#
# Copyright:: Copyright (c) 2014 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

module Omnibus
  # Builds a Mac OS X "product" package (.pkg extension)
  #
  # Mac OS X packages are built in two stages. First, files are packaged up
  # into one or more "component" .pkg files (MacPkg only supports making a
  # single component). This is done with `pkgbuild`. Next the component(s)
  # are combined into a single "product" package, using `productbuild`. It is
  # this container package that can have custom branding (background image)
  # and a license. It can also allow for user customization of which
  # component packages to install, but MacPkg does not expose this feature.
  class Packager::MacPkg < Packager::Base
    validate do
      assert_presence!(resource('background.png'))
      assert_presence!(resource('license.html'))
      assert_presence!(resource('welcome.html'))
    end

    setup do
      purge_directory(install_path)
      purge_directory(staging_dir)
      purge_directory(project.package_dir)
    end

    build do
      build_component_pkg
      generate_distribution
      build_product_pkg

      if project.config[:build_dmg]
        Packager::MacDmg.new(self).run!
      end
    end

    clean do
      # There is nothing to cleanup
    end

    # @see Base#package_name
    def package_name
      "#{name}-#{version}-#{iteration}.pkg"
    end

    # The full path where the product package was/will be written.
    #
    # @return [String] Path to the packge file.
    def final_pkg
      File.expand_path("#{project.package_dir}/#{package_name}")
    end

    #
    # Construct the intermediate build product. It can be installed with the
    # Installer.app, but doesn't contain the data needed to customize the
    # installer UI.
    #
    def build_component_pkg
      execute <<-EOH.gsub(/^ {8}/, '')
        pkgbuild \\
          --identifier "#{identifier}" \\
          --version "#{version}" \\
          --scripts "#{scripts}" \\
          --root "#{install_path}" \\
          --install-location "#{install_path}" \\
          "#{component_pkg}"
      EOH
    end

    #
    # Write the Distribution file to the staging area. This method generates the
    # content of the Distribution file, which is used by +productbuild+ to
    # select the component packages to include in the product package.
    #
    # It also includes information used to customize the UI of the Mac OS X
    # installer.
    #
    def generate_distribution
      File.open(distribution_file, 'w', 0600) do |file|
        file.puts <<-EOH.gsub(/^ {10}/, '')
          <?xml version="1.0" standalone="no"?>
          <installer-gui-script minSpecVersion="1">
              <title>#{name.capitalize}</title>
              <background file="background.png" alignment="bottomleft" mime-type="image/png"/>
              <welcome file="welcome.html" mime-type="text/html"/>
              <license file="license.html" mime-type="text/html"/>

              <!-- Generated by productbuild - - synthesize -->
              <pkg-ref id="#{identifier}"/>
              <options customize="never" require-scripts="false"/>
              <choices-outline>
                  <line choice="default">
                      <line choice="#{identifier}"/>
                  </line>
              </choices-outline>
              <choice id="default"/>
              <choice id="#{identifier}" visible="false">
                  <pkg-ref id="#{identifier}"/>
              </choice>
              <pkg-ref id="#{identifier}" version="#{version}" onConclusion="none">#{component_pkg}</pkg-ref>
          </installer-gui-script>
        EOH
      end
    end

    #
    # Construct the product package. The generated package is the final build
    # product that is shipped to end users.
    #
    def build_product_pkg
      execute <<-EOH.gsub(/^ {8}/, '')
        productbuild \\
          --distribution "#{distribution_file}" \\
          --resources "#{resources_path}" \\
          "#{final_pkg}"
      EOH
    end

    # The identifier for this mac package (the com.whatever.thing.whatever).
    # This is a configurable project value, but a default value is calculated if
    # one is not given.
    #
    # @return [String]
    def identifier
      @identifier ||= project.mac_pkg_identifier ||
        "test.#{sanitize(project.maintainer)}.pkg.#{sanitize(project.name)}"
    end

    # Filesystem path where the Distribution XML file is written.
    #
    # @return [String]
    def distribution_file
      File.expand_path("#{staging_dir}/Distribution")
    end

    # The name of the (only) component package.
    #
    # @return [String] the filename of the component .pkg file to create.
    def component_pkg
      "#{name}-core.pkg"
    end

    # Sanitize the given string for the package identifier.
    #
    # @param [String]
    # @return [String]
    def sanitize(string)
      string.gsub(/[^[:alnum:]]/, '').downcase
    end
  end
end