# encoding: UTF-8

SendMessageTimeout = Win32API.new('user32', 'SendMessageTimeout', 'LLLPLLP', 'L') 
HWND_BROADCAST = 0xffff
WM_SETTINGCHANGE = 0x001A
SMTO_ABORTIFHUNG = 2

class	Delphivm

	class IDEServices
    attr :idever
    attr :workdir
    GROUP_FILE_EXT = ['groupproj', 'bdsgroup']
    IDEInfos = {
			'D100' => {regkey: 'Software\Borland\BDS\4.0', name: '2006', desc: 'Borland Developer Stuido 4.0'},
			'D150' => {regkey: 'Software\Embarcadero\BDS\8.0', name: 'XE', desc: 'Embarcadero RAD Stuido XE'},
			'D160' => {regkey: 'Software\Embarcadero\BDS\9.0', name: 'XE2', desc: 'Embarcadero RAD Stuido XE2'},
			'D170' => {regkey: 'Software\Embarcadero\BDS\10.0', name: 'XE3', desc: 'Embarcadero RAD Stuido XE3'}
    }

    def self.idelist
      result = []
      IDEInfos.each {|ide, info| result << ide if (Win32::Registry::HKEY_CURRENT_USER.open(info[:regkey]) {|reg| reg} rescue false)}
      result.sort.reverse
    end
    
    def self.ideused
			#TODO ensure we return only ides listed at IDEInfos
		  ROOT.glob("{src,samples}/**/*.{#{GROUP_FILE_EXT.join(',')}}").map {|f| f.dirname.basename.to_s.split('-')[0]}.uniq.sort
    end
    
		def self.use(ide_tag)
			bin_paths = ide_paths.map{ |p| p + 'bin' }
			path = Win32::Registry::HKEY_CURRENT_USER.open('Environment'){|r| r['PATH']}

			path = path.split(';')
			path.reject! { |p| bin_paths.include?(p) }
			new_path = ide_paths(ide_tag.upcase).map{ |p| p + 'bin' }.first
			path.unshift new_path
			path = path.join(';')
			self.winpath= path
			return new_path
		end
		
		def initialize(idever, workdir)
			@idever = idever.upcase
			@workdir = workdir
			@reg = Win32::Registry::HKEY_CURRENT_USER     
		end
     
    def [](key)
      @reg.open(IDEInfos[idever][:regkey]) {|r|  r[key] }
    end
      
    def set_env
      ENV["PATH"] =  '$(BDSCOMMONDIR)\bpl;' + ENV["PATH"]
      ENV["PATH"] = self['RootDir'] + 'bin;' + ENV["PATH"]
      ENV["BDSPROJECTGROUPDIR"] = workdir.win
      ENV["IDEVERSION"] = idever
    end

    def start
      set_env
      Process.detach(spawn "#{self['App']}", "-r#{prj_slug}")
      say "started bds -r#{prj_slug}"
    end
    
    def prj_slug
      workdir.basename.to_s.upcase
    end
    
		def supports_msbuild?(idever)
      ide_number = idever[1..-1].to_i
			ide_number > 140			
		end
		
    def msbuild(config, target)
      set_env
      self.class.winshell(out_filter: ->(line){line =~ /(error)/}) do |i|
        Pathname.glob(workdir + "{src,samples}/#{idever}**/*.{#{GROUP_FILE_EXT.join(',')}}") do |f|
          f_to_show = f.relative_path_from(workdir)
          say "#{target} (#{config}) #{f_to_show.win} ...."
          # paths can contains spaces so we need use quotes
					if supports_msbuild?(idever)
          	i.puts %Q["#{self['RootDir'] + 'bin\rsvars.bat'}"]
          	i.puts %Q[msbuild /nologo /t:#{target} /p:Config=#{config} "#{f.win}"]
					else						
          	i.puts %Q[bds -b "#{f.win}"]
					end
        end  
      end    
    end
    
    def self.winshell(options = {})
			acmd = options[:cmd] || 'cmd /k'
			out_filter = options[:out_filter] || ->(line){true}
			err_filter = options[:err_filter] || ->(line){true}

			Open3.popen3(acmd) do |i,o,e,t|
				err_t = Thread.new(e) do |stm|
					while (line = stm.gets)
						say "STDERR: #{line}" if err_filter.call(line)
					end
				end 

				out_t = Thread.new(o) do |stm|
					while (line = stm.gets)
						say "#{line}" if out_filter.call(line) 
					end
				end

				begin
					yield i if block_given?
					i.close
					err_t.join
					out_t.join
					o.close
					e.close
				rescue Exception => excep
					say excep
				end
				t.value
			end      
		end

  private
    
		def self.say(msg)
			puts msg
		end
		
    def say(msg)
			self.class.say(msg)
		end

		def self.ide_paths(idetag=nil)
			result = []
			@reg = Win32::Registry::HKEY_CURRENT_USER 
			IDEInfos.each { |key, data|
				@reg.open(data[:regkey]) {|r| result << 'RootDir' } if idetag.nil? || idetag.to_s == key
			} 
			result
		end
    
		def self.winpath=(path)
			Win32::Registry::HKEY_CURRENT_USER.open('Environment', Win32::Registry::KEY_WRITE) do |r| 
				r['PATH'] = path
			end
			SendMessageTimeout.call(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 'Environment', SMTO_ABORTIFHUNG, 5000, 0)    
		end
  end
end