require 'timeout'
require 'airplayer/progress_bar/base'

module AirPlayer
  class Controller
    BufferingTimeoutError = Class.new(TimeoutError)

    def initialize
      @airplay      = Airplay::Client.new
      @player       = nil
      @progressbar  = nil
      @timeout      = 30
      @interval     = 1
      @total_sec    = 0
      @current_sec  = 0
    rescue Airplay::Client::ServerNotFoundError
      abort '[ERROR] AirPlay device is not found'
    end

    def play(media, options = {})
      raise TypeError unless media.is_a? Media

      device = select_device(options.fetch(:device, nil))
      @airplay.use(device.name)

      display_information(device, media)
      @player = @airplay.send_video(media.open)

      buffering
      @progressbar.progress = @current_sec while playing

      @progressbar.title = :Complete
      pause
      media.close
    rescue BufferingTimeoutError
      abort '[ERROR] Buffering timeout'
    rescue TypeError
      abort '[ERROR] Not media class'
    rescue
      abort 'Play stopped'
    end

    def pause
      @player.stop        if @player
      @progressbar.finish if @progressbar
    end

    def replay
      @player.scrub(0)
      @player.resume

      @progressbar.reset
      @progressbar.resume
    end

    private
      def display_information(device, media)
        puts
        puts " Source: #{media.path}"
        puts "  Title: #{media.title}"
        puts " Device: #{device.name} (#{device.ip})"

        @progressbar = ProgressBar.create(format: '   %a |%b%i| %p%% %t')
      end

      def buffering
        timeout @timeout, BufferingTimeoutError do
          @progressbar.title = :Buffering until playing
          @progressbar.title = :Streaming
          @progressbar.total = @total_sec
        end
      end

      def playing
        scrub = @player.scrub
        @total_sec   = scrub['duration']
        @current_sec = scrub['position']
        sleep @interval
        progress?
      end

      def progress?
        0 < @current_sec && @total_sec - @current_sec > 1
      end

      def select_device(device_number = nil)
        device_number ||= 0
        device = Device.new

        if device.exist?(device_number)
          device.get(device_number)
        else
          puts "Device number #{device_number} is not found. Use default device"
          device.default
        end
      end
  end
end