require 'appscript'

class I2Cssh
    def initialize servers, ssh_options, i2_options, ssh_environment
        @ssh_prefix         = "ssh " + ssh_options.join(' ')
        @ssh_options        = ssh_options
        @i2_options         = i2_options
        @servers            = servers
        @ssh_environment    = ssh_environment

        app_name = (i2_options[:iterm2]) ? 'iTerm2' : 'iTerm'

        raise Exception.new 'No servers given' if servers.empty?

        @sys_events = Appscript.app.by_name('System Events')
        @iterm = Appscript.app.by_name(app_name)
        @term = @iterm.make(:new => :terminal)

        @profile = i2_options[:profile] || "Default"

        session = @term.sessions.after.make :new => :session
        session.exec :command => "/bin/bash -l"

        compute_geometry
        split_session
        maximize if i2_options[:fullscreen]
        
        start_ssh
        enable_broadcast if i2_options[:broadcast]
    end

    private
    def maximize
      fullscreen_bounds = Appscript.app.by_name('Finder').desktop.window.bounds
      window = @iterm.windows.get.sort_by{|x| x.id_.get}.last
      window.bounds.set fullscreen_bounds.get
    end

    def compute_geometry
        count = @servers.size
        @rows = @i2_options[:rows]
        @columns = @i2_options[:columns]

        if @rows then
          @columns = (count / @rows.to_f).ceil
        elsif @columns then
          @rows = (count / @columns.to_f).ceil
        else
          @columns = Math.sqrt(count).ceil
          @rows = (count / @columns.to_f).ceil
        end
    end

    def split_session
        splitmap = {
            :column => {0 => "d", 1 => 123, 2 => "D", 3=> 124, :x => @columns, :y => @rows}, 
            :row => {0 => "D", 1=> 126, 2 => "d", 3=> 125, :x => @rows, :y => @columns}
        }
        splitconfig = splitmap[@i2_options[:direction]]

        first = true
        2.upto splitconfig[:x] do
          @sys_events.keystroke splitconfig[0], :using => :command_down
        end
        2.upto splitconfig[:y] do
          1.upto splitconfig[:x] do
            @sys_events.key_code splitconfig[1], :using => [:command_down, :option_down] unless first
            first = false
          end
          splitconfig[:x].times do |x|
            @sys_events.keystroke splitconfig[2], :using => :command_down
            @sys_events.key_code splitconfig[3], :using => [:command_down, :option_down] unless @columns - 1 == x
          end
        end
    end

    def enable_broadcast
        @sys_events.keystroke "I", :using => :command_down
        sleep 0.5
        @sys_events.keystroke "\r"
    end

    def start_ssh
        1.upto(@rows*@columns) do |i|
            server = @servers[i-1]
            if server then
                ssh_env = ""

                if @i2_options[:rank] then
                    @ssh_environment['LC_RANK'] = i-1
                end

                if !@ssh_environment.empty? then
                    send_env = "-o SendEnv=#{@ssh_environment.keys.join(",")}"
                    @term.sessions[i].write :text => "#{@ssh_environment.map{|k,v| "export #{k}=#{v}"}.join('; ')}"
                end
                if @i2_options[:sleep] then
                    sleep @i2_options[:sleep] * i
                end
                @term.sessions[i].write :text => "unset HISTFILE && echo -e \"\\033]50;SetProfile=#{@profile}\\a\" && #{@ssh_prefix} #{send_env} #{server}"
            else
                
                @term.sessions[i].write :text => "unset HISTFILE && echo -e \"\\033]50;SetProfile=#{@profile}\\a\""
                sleep 0.3
                @term.sessions[i].foreground_color.set "red"
                @term.sessions[i].write :text => "stty -isig -icanon -echo && echo -e '#{"\n"*100}UNUSED' && cat > /dev/null"
            end
        end
    end
end