# coding: utf-8
describe Hallon::Player do
  let(:player) { Hallon::Player.new(AudioDriverMock) }
  let(:track)  { Hallon::Track.new(mock_track) }
  let(:driver) { player.instance_variable_get('@driver') }
  let(:queue)  { player.instance_variable_get('@queue') } # black box? WHAT?

  describe "events" do
    %w(end_of_track streaming_error play_token_lost).each do |e|
      it "should support listening for #{e}" do
        expect { player.on(e) {} }.to_not raise_error
      end
    end
  end

  describe ".bitrates" do
    it "should be a list of symbols in ascending order" do
      Hallon::Player.bitrates.should eq %w[96k 160k 320k].map(&:to_sym)
    end
  end

  describe "#bitrate=" do
    it "should not fail horribly given a correct bitrate" do
      player.bitrate = :'96k'
    end

    it "should fail horrible given a bad bitrate" do
      expect { player.bitrate = :'100k' }.to raise_error(ArgumentError)
    end
  end

  describe "#load" do
    it "should load the given track" do
      spotify_api.should_receive(:session_player_load).with(session.pointer, track.pointer)
      player.load(track)
    end

    it "should try to instantiate the track if it’s not a track" do
      spotify_api.should_receive(:session_player_load).with(session.pointer, track.pointer)
      player.load(track.to_str)
    end

    it "should raise an error if load was unsuccessful" do
      spotify_api.should_receive(:session_player_load).and_return(:is_loading)
      expect { player.load(track) }.to raise_error(Hallon::Error, /IS_LOADING/)
    end
  end

  describe "#stop" do
    it "should unload the currently loaded track" do
      spotify_api.should_receive(:session_player_unload).with(session.pointer)
      player.stop
    end
  end

  describe "#prefetch" do
    it "should set up given track for prefetching" do
      spotify_api.should_receive(:session_player_prefetch).with(session.pointer, track.pointer)
      player.prefetch(track)
    end
  end

  describe "#play" do
    it "should start playback of given track" do
      spotify_api.should_receive(:session_player_play).with(session.pointer, true)
      player.play
    end

    it "should load and play given track if one was given" do
      spotify_api.should_receive(:session_player_play).with(session.pointer, true)
      player.should_receive(:load).with(track)
      player.play(track)
    end
  end

  describe "#pause" do
    it "should stop playback of given track" do
      spotify_api.should_receive(:session_player_play).with(session.pointer, false)
      player.pause
    end
  end

  describe "#seek" do
    it "should set up the currently loaded track at given position" do
      spotify_api.should_receive(:session_player_seek).with(session.pointer, 1000)
      player.seek(1)
    end
  end

  describe "#volume_normalization" do
    it "should be settable and gettable" do
      player.volume_normalization?.should be_false
      player.volume_normalization = true
      player.volume_normalization?.should be_true
    end
  end

  context "playing audio" do
    before { session.class.send(:public, :trigger) }

    it "should correctly report the status to libspotify" do
      queue.should_receive(:size).and_return(7)
      driver.should_receive(:drops).and_return(19)
      session.trigger(:get_audio_buffer_stats).should eq [7, 19]
    end

    it "should assume no drops in audio if driver does not support checking" do
      driver.should_receive(:respond_to?).with(:drops).and_return(false)
      driver.should_not_receive(:drops)
      session.trigger(:get_audio_buffer_stats).should eq [0, 0]
    end

    it "should tell the driver to start playback when commanded so by libspotify" do
      driver.should_receive(:play)
      session.trigger(:start_playback)
    end

    it "should tell the driver to stop playback when commanded so by libspotify" do
      driver.should_receive(:pause)
      session.trigger(:stop_playback)
    end

    it "should tell the driver to pause when pause is requested" do
      driver.should_receive(:pause)
      player.pause
    end

    it "should tell the driver to stop when stop is requested" do
      queue.should_receive(:clear)
      driver.should_receive(:stop)
      player.stop
    end

    it "should set the format on music delivery if format changes" do
      queue.format.should_not eq :new_format
      session.trigger(:music_delivery, :new_format, [1, 2, 3])
      queue.format.should eq :new_format
    end

    # why? it says so in the docs!
    it "should clear the audio queue when receiving 0 audio frames" do
      queue.should_receive(:clear)
      session.trigger(:music_delivery, queue.format, [])
    end

    context "the output streaming" do
      it "should feed music to the output stream if the format stays the same" do
        Thread.stub(:start).and_return{ |*args, &block| block[*args] }

        player # create the Player
        session.trigger(:music_delivery, driver.format, [1, 2, 3])

        # it should block while player is stopped
        begin
          player.status.should be :stopped
          Timeout::timeout(0.1) { driver.stream.call and "call was not blocking" }
        rescue
          :timeout
        end.should eq :timeout

        session.trigger(:start_playback)
        player.status.should be :playing
        driver.stream.call(1).should eq [1]
        driver.stream.call(nil).should eq [2, 3]
      end

      it "should set the driver format and return no audio if audio format has changed" do
        Thread.stub(:start).and_return{ |*args, &block| block[*args] }

        player # create the Player
        session.trigger(:start_playback)
        session.trigger(:music_delivery, :new_format, [1, 2, 3])

        driver.should_receive(:format=).with(:new_format)
        driver.stream.call.should be_nil

        # driver.should_not_receive(:format)
        driver.should_receive(:format).and_return(:new_format)
        driver.stream.call.should eq [1, 2, 3]
      end

      it "should set the driver format on initialization" do
        Thread.stub(:start).and_return{ |*args, &block| block[*args] }

        AudioDriverMock.any_instance.should_receive(:format=)
        player
      end
    end
  end
end
