require 'spec_helper'

describe Recording do

  before do
    S3Manager.set_unit_test
    @user = FactoryGirl.create(:user)
    @connection = FactoryGirl.create(:connection, :user => @user)
    @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument')
    @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument)
    @music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true)      
    @music_session.connections << @connection
    @music_session.save
  end
  
  it "should not start a recording if the music session doesnt exist" do
    expect { Recording.start("bad_music_session_id", @user) }.to raise_error
  end
  
  it "should set up the recording properly when recording is started with 1 user in the session" do
    @music_session.recording.should == nil
    @recording = Recording.start(@music_session.id, @user)
    @music_session.reload
    @music_session.recording.should == @recording
    @recording.owner_id.should == @user.id
    
    @recorded_tracks = RecordedTrack.where(:recording_id => @recording.id)
    @recorded_tracks.length.should == 1
    @recorded_tracks.first.instrument_id == @track.instrument_id
    @recorded_tracks.first.user_id == @track.connection.user_id    
  end
  
  it "should not start a recording if the session is already being recorded" do
    Recording.start(@music_session.id, @user)
    expect { Recording.start(@music_session.id, @user) }.to raise_error
  end

  it "should return the state to normal properly when you stop a recording" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @music_session.reload
    @music_session.recording.should == nil
    @recording.reload
    @recording.music_session.should == nil
  end


  it "should error when you stop a recording twice" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    expect { @recording.stop }.to raise_error
  end

  it "should be able to start, stop then start a recording again for the same music session" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording2 = Recording.start(@music_session.id, @user)
    @music_session.recording.should == @recording2
  end

  it "should NOT attach the recording to all users in a the music session when recording started" do
    @user2 = FactoryGirl.create(:user)
    @connection2 = FactoryGirl.create(:connection, :user => @user2)
    @instrument2 = FactoryGirl.create(:instrument, :description => 'a great instrument')
    @track2 = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument2)
    
    @music_session.connections << @connection2
  
    @recording = Recording.start(@music_session.id, @user)
    @user.recordings.length.should == 0
    #@user.recordings.first.should == @recording

    @user2.recordings.length.should == 0
    #@user2.recordings.first.should == @recording
  end
  
  it "should report correctly whether its tracks have been uploaded" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.uploaded?.should == false
    @recording.stop
    @recording.reload
    @recording.uploaded?.should == false
    @recording.recorded_tracks.first.fully_uploaded = true
    @recording.uploaded?.should == true
  end
  
  it "should destroy a recording and all its recorded tracks properly" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    @recorded_track = @recording.recorded_tracks.first
    @recording.destroy
    expect { Recording.find(@recording.id) }.to raise_error
    expect { RecordedTracks.find(@recorded_track.id) }.to raise_error
  end

  it "should allow a user to claim a recording" do 
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    @genre = FactoryGirl.create(:genre)
    @recording.claim(@user, "name", @genre, true, true)
    @recording.reload
    @recording.users.length.should == 1
    @recording.users.first.should == @user
    @user.recordings.length.should == 1
    @user.recordings.first.should == @recording
    @recording.claimed_recordings.length.should == 1
    @claimed_recording = @recording.claimed_recordings.first
    @claimed_recording.name.should == "name"
    @claimed_recording.genre.should == @genre
    @claimed_recording.is_public.should == true
    @claimed_recording.is_downloadable.should == true
  end

  it "should fail if a user who was not in the session claims a recording" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    user2 = FactoryGirl.create(:user)
    expect { @recording.claim(user2) }.to raise_error
  end

  it "should fail if a user tries to claim a recording twice" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    @genre = FactoryGirl.create(:genre)
    @recording.claim(@user, "name", @genre, true, true)
    @recording.reload
    expect { @recording.claim(@user, "name", @genre, true, true) }.to raise_error
  end

  it "should allow editing metadata for claimed recordings" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    @genre = FactoryGirl.create(:genre)
    @claimed_recording = @recording.claim(@user, "name", @genre, true, true)
    @genre2 = FactoryGirl.create(:genre)
    @claimed_recording.update_fields(@user, :name => "name2", :genre => @genre2.id, :is_public => false, :is_downloadable => false)
    @claimed_recording.reload
    @claimed_recording.name.should == "name2"
    @claimed_recording.genre.should == @genre2 
    @claimed_recording.is_public.should == false
    @claimed_recording.is_downloadable.should == false
  end

  it "should only allow the owner to edit a claimed recording" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    @genre = FactoryGirl.create(:genre)
    @claimed_recording = @recording.claim(@user, "name", @genre, true, true)
    @user2 = FactoryGirl.create(:user)
    expect { @claimed_recording.update_fields(@user2, "name2") }.to raise_error
  end

  it "should record the duration of the recording properly" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.duration.should be_nil
    @recording.stop
    @recording.reload
    @recording.duration.should_not be_nil
    # Note: it will be 0 since this was fast. You can see something non-zero by just
    # inserting a sleep here.
    # puts @recording.duration
  end

  it "should only destroy a single claimed_recording if there are more than one" do
    @user2 = FactoryGirl.create(:user)
    @connection2 = FactoryGirl.create(:connection, :user => @user2)
    @track = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument)
    @music_session.connections << @connection2
    @music_session.save
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    @genre = FactoryGirl.create(:genre)
    @claimed_recording = @recording.claim(@user, "name", @genre, true, true)
    expect { @claimed_recordign.discard(@user2) }.to raise_error
    @claimed_recording = @recording.claim(@user2, "name2", @genre, true, true)
    @claimed_recording.discard(@user2)
    @recording.reload
    @recording.claimed_recordings.length.should == 1
  end

  it "should destroy the entire recording if there was only one claimed_recording which is discarded" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    @genre = FactoryGirl.create(:genre)
    @claimed_recording = @recording.claim(@user, "name", @genre, true, true)
    @claimed_recording.discard(@user)
    expect { Recording.find(@recording.id) }.to raise_error
    expect { ClaimedRecording.find(@claimed_recording.id) }.to raise_error
  end

  it "should return a file list for a user properly" do
    @recording = Recording.start(@music_session.id, @user)
    @recording.stop
    @recording.reload
    @genre = FactoryGirl.create(:genre)
    @recording.claim(@user, "Recording", @genre, true, true)
    Recording.list(@user)["downloads"].length.should == 0
    Recording.list(@user)["uploads"].length.should == 1
    file = Recording.list(@user)["uploads"].first
    @recorded_track = @recording.recorded_tracks.first
    file.should == @recorded_track.filename
    @recorded_track.upload_start(25000, "md5hash")
    @recorded_track.upload_complete
    Recording.list(@user)["downloads"].length.should == 1
    Recording.list(@user)["uploads"].length.should == 0
    file = Recording.list(@user)["downloads"].first
    file[:type].should == "recorded_track"
    file[:id].should == @recorded_track.id
    file[:length].should == 25000
    file[:md5].should == "md5hash"
    file[:url].should == S3Manager.url(S3Manager.hashed_filename('recorded_track', @recorded_track.id))
  
    # Note that the recording should automatically schedule a mix when the upload completes
    @recording.mixes.length.should == 1
    @mix = Mix.next('server')
    @mix.should_not be_nil
    @mix.finish(50000, "md5hash")
    Recording.list(@user)["downloads"].length.should == 2
    Recording.list(@user)["uploads"].length.should == 0
    file = Recording.list(@user)["downloads"].last
    file[:type].should == "mix"
    file[:id].should == @mix.id
    file[:length].should == 50000
    file[:md5].should == "md5hash"
    file[:url].should == S3Manager.url(S3Manager.hashed_filename('mix', @mix.id))

  end

  it "should create a base mix manifest properly" do
    @user2 = FactoryGirl.create(:user)
    @connection2 = FactoryGirl.create(:connection, :user => @user)
    @instrument2 = FactoryGirl.create(:instrument, :description => 'a great instrument')
    @track2 = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument2)
    @music_session.connections << @connection2
    @music_session.save
    @recording = Recording.start(@music_session.id, @user)
    #sleep 4
    @recording.stop
    @recording.recorded_tracks.length.should == 2
    @recorded_track = @recording.recorded_tracks.first
    @recorded_track.upload_start(25000, "md5hash")
    @recorded_track.upload_complete
    @recorded_track2 = @recording.recorded_tracks.last
    @recorded_track2.upload_start(50000, "md5hash2")
    @recorded_track2.upload_complete
    mix_manifest = @recording.base_mix_manifest
    mix_manifest.should_not be_nil
    files = mix_manifest["files"]
    files.should_not be_nil
    files.length.should == 2
    files.first["codec"].should == "vorbis"
    files.first["offset"].should == 0
    files.first["url"].should == @recording.recorded_tracks.first.url
    files.last["codec"].should == "vorbis"
    files.last["offset"].should == 0
    files.last["url"].should == @recording.recorded_tracks.last.url

    timeline = mix_manifest["timeline"]
    timeline.should_not be_nil
    timeline.length.should == 2
    timeline.first["timestamp"].should == 0
    timeline.first["end"].should be_nil
    mix = timeline.first["mix"]
    mix.should_not be_nil
    mix.length.should == 2
    mix.first["balance"].should == 0
    mix.first["level"].should == 100
    mix.last["balance"].should == 0
    mix.last["level"].should == 100
  
    timeline.last["timestamp"].should == @recording.duration
    timeline.last["end"].should == true
  end
end


