# -*- encoding: utf-8 -*-

require 'rake'
require 'albacore/paths'
require 'albacore/cmd_config'
require 'albacore/cross_platform_cmd'
require 'albacore/logging'
require 'albacore/facts'

module Albacore
  module Build
    class Cmd
      include CrossPlatformCmd
      def initialize work_dir, executable, parameters
        @work_dir = work_dir
        @executable = executable
        @parameters = parameters
      end
      def execute
        sh make_command, :work_dir => @work_dir
      end
    end

    # The configuration class for xbuild and msbuild. MSDN docs at: http://msdn.microsoft.com/en-us/library/vstudio/ms164311.aspx
    class Config
      include CmdConfig
      include Logging

      attr_accessor :sln, :file, :target, :properties

      def initialize
        @parameters = Set.new

        w = lambda { |e| CrossPlatformCmd.which(e) ? e : nil }

        @exe = w.call( "msbuild" ) ||
               w.call( "xbuild" )  ||
               heuristic_executable

        debug "build using '#{@exe}'"
        logging "minimal"
      end

      # the sln file to build
      def sln= val
        @parameters.add val
        @sln = val
      end

      def sln
        @sln
      end

      def file= val
        @parameters.add val
        @file = val
      end

      def file
        @file
      end

      # see target method for docs
      def target= t
        target t
      end

      # Call for each target that you want to add, or pass an array or
      # strings. Only unique targets will be passed to MsBuild.
      #
      # From MSDN:
      #
      # "Build the specified targets in the project. Specify each target separately, or use a semicolon or comma to separate multiple targets, as the following example shows:
      # /target:Resources;Compile
      # If you specify any targets by using this switch, they are run instead of any targets in the DefaultTargets attribute in the project file. For more information, see Target Build Order (http://msdn.microsoft.com/en-us/library/vstudio/ee216359.aspx) and How to: Specify Which Target to Build First (http://msdn.microsoft.com/en-us/library/vstudio/ms171463.aspx).
      # A target is a group of tasks. For more information, see MSBuild Targets (http://msdn.microsoft.com/en-us/library/vstudio/ms171462.aspx)."
      #
      # t  :: the array or string target to add to the list of tarets to build
      def target t
        update_array_prop "target", method(:make_target), :targets, t
      end

      # Allows you to add properties to MsBuild; example:
      #
      # b.prop 'WarningLevel', 2
      # b.prop 'BuildVersion', '3.5.0'
      #
      # From MSDN:
      #
      # "Set or override the specified project-level properties, where name is the property name and value is the property value. Specify each property separately, or use a semicolon or comma to separate multiple properties, as the following example shows:
      # /property:WarningLevel=2;OutputDir=bin\Debug"
      #
      # The properties will be automatically converted to the correct syntax
      def prop k, v
        @properties ||= Hash.new
        @parameters.delete "/property:#{make_props}" if @properties.any?
        @properties[k] = v
        @parameters.add "/property:#{make_props}"
      end

      # Specifies the amount of information to display in the build log. 
      # Each logger displays events based on the verbosity level that you set for that logger.
      # You can specify the following verbosity levels: q[uiet], m[inimal], 
      # n[ormal], d[etailed], and diag[nostic]. 
      def logging mode
        modes = %w{quiet minimal normal detailed diagnostic}.collect{ |m| "/verbosity:#{m}" }
        @parameters.subtract modes
        @parameters.add "/verbosity:#{mode}"
      end

      def logging= mode
        logging mode
      end

      # Specifies the number of parallel worker processes to build with
      # Defaults to the number of logical cores
      def cores num
        @parameters.add "/maxcpucount:#{num}"
      end

      # Pass the parameters that you specify to the console logger, which displays build information in the console window. You can specify the following parameters:
      # * PerformanceSummary. Show the time that’s spent in tasks, targets, and projects.
      # * Summary. Show the error and warning summary at the end.
      # * NoSummary. Don't show the error and warning summary at the end.
      # * ErrorsOnly. Show only errors.
      # * WarningsOnly. Show only warnings.
      # * NoItemAndPropertyList. Don't show the list of items and properties that would appear at the start of each project build if the verbosity level is set to diagnostic.
      # * ShowCommandLine. Show TaskCommandLineEvent messages.
      # * ShowTimestamp. Show the timestamp as a prefix to any message.
      # * ShowEventId. Show the event ID for each started event, finished event, and message.
      # * ForceNoAlign. Don't align the text to the size of the console buffer.
      # * DisableConsoleColor. Use the default console colors for all logging messages.
      # * DisableMPLogging. Disable the multiprocessor logging style of output when running in non-multiprocessor mode.
      # * EnableMPLogging. Enable the multiprocessor logging style even when running in non-multiprocessor mode. This logging style is on by default.
      # * Verbosity. Override the /verbosity setting for this logger.
      def clp param
        update_array_prop "consoleloggerparameters", method(:make_clp), :clp, param
      end

      # Set logging verbosity to quiet
      def be_quiet
        logging "quiet"
      end

      # Don't display the startup banner or the copyright message.
      def nologo
        @parameters.add "/nologo"
      end

      private
      def update_array_prop prop_name, callable_prop_val, field_sym, value
        field = :"@#{field_sym}"
        # @targets ||= []
        instance_variable_set field, [] unless instance_variable_defined? field
        # parameters.delete "/target:#{make_target}" if @targets.any?
        @parameters.delete "/#{prop_name}:#{callable_prop_val.call}" if 
          instance_variable_get(field).any?
        if value.respond_to? 'each'
          value.each { |v| instance_variable_get(field) << v }
        else
          instance_variable_get(field) << value
        end
        instance_variable_get(field).uniq!
        # @parameters.add "/target:#{make_target}"
        @parameters.add "/#{prop_name}:#{callable_prop_val.call}"
      end

      def make_target
        @targets.join(';')
      end

      def make_props
        @properties.collect { |k, v|
          "#{k}=#{v}"
        }.join(';')
      end

      def make_clp
        @clp.join(';')
      end

      def heuristic_executable
        if ::Rake::Win32.windows?
          trace 'build tasktype finding msbuild.exe'
          %w{v4.0.30319 v4.0 v3.5 v2.0}.collect { |fw_ver|
            msb = File.join ENV['WINDIR'], 'Microsoft.NET', 'Framework', fw_ver, 'msbuild.exe'
            CrossPlatformCmd.which(msb) ? msb : nil
          }.first
        else
          nil
        end
      end
    end
    class Task
      def initialize command_line
        @command_line = command_line
      end
      def execute
        @command_line.execute
      end
    end
  end
end