require 'calabash-cucumber/launch/simulator_helper'
require 'sim_launcher'
require 'run_loop'


class Calabash::Cucumber::Launcher
  attr_accessor :run_loop
  attr_accessor :device_target
  attr_accessor :ios_version

  def initialize(device_target=:simulator)
    self.device_target = device_target
  end

  class CalabashLauncherTimeoutErr < Timeout::Error
  end

  def calabash_no_stop?
    ENV['NO_LAUNCH']=="1" or ENV['NO_STOP']=="1"
  end

  def device_target?
    ENV['DEVICE_TARGET'] == 'device'
  end

  def simulator_target?
    ENV['DEVICE_TARGET'] == 'simulator'
  end

  def active?
    (simulator_target? || device_target?) && (not run_loop.nil?)
  end

  def ios_major_version
    v = ios_version
    (v && v.split('.')[0])
  end


  def reset_app_jail(sdk, app_path)
    return if device_target?

    app = File.basename(app_path)
    bundle = `find "#{ENV['HOME']}/Library/Application Support/iPhone Simulator/#{sdk}/Applications/" -type d -depth 2 -name "#{app}" | head -n 1`
    return if bundle.empty? # Assuming we're already clean

    sandbox = File.dirname(bundle)
    ['Library', 'Documents', 'tmp'].each do |dir|
      FileUtils.rm_rf(File.join(sandbox, dir))
    end
  end

  def relaunch(args={})
    RunLoop.stop(run_loop) if run_loop

    if device_target?
      default_args = {:app => ENV['BUNDLE_ID']}
      self.run_loop = RunLoop.run(default_args.merge(args))
    else

      sdk = ENV['SDK_VERSION'] || SimLauncher::SdkDetector.new().latest_sdk_version
      path = Calabash::Cucumber::SimulatorHelper.app_bundle_or_raise(app_path)
      if ENV['RESET_BETWEEN_SCENARIOS']=="1"
        reset_app_jail(sdk, path)
      end

      if simulator_target?
        device = (ENV['DEVICE'] || 'iphone').to_sym
        default_args = {:app => path, :device => device}
        self.run_loop = RunLoop.run(default_args.merge(args))
      else
        ## sim launcher
        Calabash::Cucumber::SimulatorHelper.relaunch(path, sdk, ENV['DEVICE'] || 'iphone', args)
      end

    end
    ensure_connectivity
  end


  def ensure_connectivity
    begin
      max_retry_count = (ENV['MAX_CONNECT_RETRY'] || 10).to_i
      timeout = (ENV['CONNECT_TIMEOUT'] || 30).to_i
      retry_count = 0
      connected = false
      puts "Waiting for App to be ready"
      until connected do
        raise "MAX_RETRIES" if retry_count == max_retry_count
        retry_count += 1
        begin
          Timeout::timeout(timeout, CalabashLauncherTimeoutErr) do
            until connected
              begin
                connected = (ping_app == '200')
                break if connected
              rescue Exception => e
                #p e
                #retry
              ensure
                sleep 1 unless connected
              end
            end
          end
        rescue CalabashLauncherTimeoutErr => e
          puts "Timed out...Retry.."
        end
      end
    rescue e
      p e
      msg = "Unable to make connection to Calabash Server at #{ENV['DEVICE_ENDPOINT']|| "http://localhost:37265/"}\n"
      msg << "Make sure you don't have a firewall blocking traffic to #{ENV['DEVICE_ENDPOINT']|| "http://localhost:37265/"}.\n"
      raise msg
    end
  end

  def ping_app
    url = URI.parse(ENV['DEVICE_ENDPOINT']|| "http://localhost:37265/")

    http = Net::HTTP.new(url.host, url.port)
    res = http.start do |sess|
      sess.request Net::HTTP::Get.new "version"
    end
    status = res.code
    begin
      http.finish if http and http.started?
    rescue Exception => e

    end

    if status=='200'
      version_body = JSON.parse(res.body)
      if version_body['iOS_version']
        self.ios_version = version_body['iOS_version']
      end
    end

    status
  end

  def stop
    RunLoop.stop(run_loop)
  end

  def app_path
    ENV['APP_BUNDLE_PATH'] || (defined?(APP_BUNDLE_PATH) && APP_BUNDLE_PATH)
  end

  def calabash_notify(world)
    if world.respond_to?(:on_launch)
      world.on_launch
    end
  end
end