<commit_before>use std::time::{Duration, Instant};

use futures::{Future, BoxFuture, Async, Poll};
use futures::future;
use rustfm_scrobble;
use rustfm_scrobble::metadata::Scrobble;

use metadata::{Track, Artist};
use session::Session;
use util::SpotifyId;

#[derive(Clone, Debug)]
pub struct ScrobblerConfig {
    pub api_key: String,
    pub api_secret: String,
    pub username: String,
    pub password: String
}

pub struct Scrobbler {
    config: ScrobblerConfig,
    scrobbler: rustfm_scrobble::Scrobbler,

    session: Session,
    current_track_id: Option<SpotifyId>,
    current_track_start: Option<Instant>,
    current_track_meta: Option<Scrobble>,
    current_track_scrobbled: bool,

    auth_future: BoxFuture<(), rustfm_scrobble::ScrobblerError>,
    new_track_future: BoxFuture<(), ()>,
    now_playing_future: BoxFuture<(), ScrobbleError>,
    meta_fetch_future: BoxFuture<Scrobble, ScrobbleError>,
    scrobble_future: Option<BoxFuture<(), ScrobbleError>>
}

#[derive(Debug)]
pub struct ScrobbleError {
    msg: String
}

impl ScrobbleError {

    pub fn new(msg: String) -> ScrobbleError {
        ScrobbleError {
            msg: msg
        }
    }

}

impl Scrobbler {

    pub fn new(config: ScrobblerConfig, session: Session) -> Scrobbler {
        let mut scrobbler = Scrobbler {
            session: session,
            scrobbler: rustfm_scrobble::Scrobbler::new(config.api_key.clone(), config.api_secret.clone()),
            current_track_id: None,
            current_track_start: None,
            current_track_meta: None,
            current_track_scrobbled: false,
            auth_future: future::empty().boxed(),
            new_track_future: future::empty().boxed(),
            now_playing_future: future::empty().boxed(),
            meta_fetch_future: future::empty().boxed(),
            scrobble_future: None,
            config: config
        };

        scrobbler.start_auth();
        scrobbler
    }

    pub fn start_auth(&mut self) {
        self.auth_future = self.auth();
    }

    pub fn auth(&mut self) -> BoxFuture<(), rustfm_scrobble::ScrobblerError> {
        match self.scrobbler.authenticate(self.config.username.clone(), self.config.password.clone()) {
            Ok(_) => future::ok(()),
            Err(err) => future::err(err)
        }.boxed()
    }

    pub fn update_current_track(&mut self, track_id: SpotifyId) {
        // TODO: Doesn't understand when a track is played on repeat
        let mut new_track = false;

        match self.current_track_id {
            None => { 
                new_track = true;
            },
            Some(id) => {
                if id != track_id {
                    new_track = true;
                }
            }
        }

        if !new_track {
            return
        }

        if self.can_scrobble_track() {
            self.start_scrobble();
        }

        self.new_track_future = self.set_new_track(track_id);
    }

    pub fn set_new_track(&mut self, track_id: SpotifyId) -> BoxFuture<(), ()> {
        self.current_track_id = Some(track_id);
        self.current_track_start = Some(Instant::now());
        self.current_track_meta = None;
        self.current_track_scrobbled = false;

        future::ok(()).boxed()
    }

    pub fn get_track_meta(&mut self, track_id: SpotifyId) -> BoxFuture<Scrobble, ScrobbleError> {
        let metadata = self.session.metadata().clone();

        metadata.get::<Track>(track_id).and_then(move |track| {
            let track_name = track.name;
            let artist = *track.artists.first().expect("No artists");
            metadata.get::<Artist>(artist).map(|artist| (track_name, artist.name.clone()))
        }).map_err(move |err| {
            ScrobbleError::new(format!("{:?}", err).to_owned())
        }).and_then(move |(track, artist)| {
            // TODO: Album support
            future::ok(Scrobble::new(artist, track, String::new()))<commit_msg>Add track album to data submitted to Last.fm (Issue #2)<commit_after>use std::time::{Duration, Instant};

use futures::{Future, BoxFuture, Async, Poll};
use futures::future;
use rustfm_scrobble;
use rustfm_scrobble::metadata::Scrobble;

use metadata::{Track, Artist, Album};
use session::Session;
use util::SpotifyId;

#[derive(Clone, Debug)]
pub struct ScrobblerConfig {
    pub api_key: String,
    pub api_secret: String,
    pub username: String,
    pub password: String
}

pub struct Scrobbler {
    config: ScrobblerConfig,
    scrobbler: rustfm_scrobble::Scrobbler,

    session: Session,
    current_track_id: Option<SpotifyId>,
    current_track_start: Option<Instant>,
    current_track_meta: Option<Scrobble>,
    current_track_scrobbled: bool,

    auth_future: BoxFuture<(), rustfm_scrobble::ScrobblerError>,
    new_track_future: BoxFuture<(), ()>,
    now_playing_future: BoxFuture<(), ScrobbleError>,
    meta_fetch_future: BoxFuture<Scrobble, ScrobbleError>,
    scrobble_future: Option<BoxFuture<(), ScrobbleError>>
}

#[derive(Debug)]
pub struct ScrobbleError {
    msg: String
}

impl ScrobbleError {

    pub fn new(msg: String) -> ScrobbleError {
        ScrobbleError {
            msg: msg
        }
    }

}

impl Scrobbler {

    pub fn new(config: ScrobblerConfig, session: Session) -> Scrobbler {
        let mut scrobbler = Scrobbler {
            session: session,
            scrobbler: rustfm_scrobble::Scrobbler::new(config.api_key.clone(), config.api_secret.clone()),
            current_track_id: None,
            current_track_start: None,
            current_track_meta: None,
            current_track_scrobbled: false,
            auth_future: future::empty().boxed(),
            new_track_future: future::empty().boxed(),
            now_playing_future: future::empty().boxed(),
            meta_fetch_future: future::empty().boxed(),
            scrobble_future: None,
            config: config
        };

        scrobbler.start_auth();
        scrobbler
    }

    pub fn start_auth(&mut self) {
        self.auth_future = self.auth();
    }

    pub fn auth(&mut self) -> BoxFuture<(), rustfm_scrobble::ScrobblerError> {
        match self.scrobbler.authenticate(self.config.username.clone(), self.config.password.clone()) {
            Ok(_) => future::ok(()),
            Err(err) => future::err(err)
        }.boxed()
    }

    pub fn update_current_track(&mut self, track_id: SpotifyId) {
        // TODO: Doesn't understand when a track is played on repeat
        let mut new_track = false;

        match self.current_track_id {
            None => { 
                new_track = true;
            },
            Some(id) => {
                if id != track_id {
                    new_track = true;
                }
            }
        }

        if !new_track {
            return
        }

        if self.can_scrobble_track() {
            self.start_scrobble();
        }

        self.new_track_future = self.set_new_track(track_id);
    }

    pub fn set_new_track(&mut self, track_id: SpotifyId) -> BoxFuture<(), ()> {
        self.current_track_id = Some(track_id);
        self.current_track_start = Some(Instant::now());
        self.current_track_meta = None;
        self.current_track_scrobbled = false;

        future::ok(()).boxed()
    }

    pub fn get_track_meta(&mut self, track_id: SpotifyId) -> BoxFuture<Scrobble, ScrobbleError> {
        let metadata = self.session.metadata().clone();

        metadata.get::<Track>(track_id).and_then(move |track| {
            let track_name = track.clone().name;
            let artist = *track.artists.first().expect("No artists");
            metadata.clone().get::<Artist>(artist).map(|artist| (track_name, artist.name.clone(), track, metadata))
        }).and_then(move |(track_name, artist_name, track_meta, metadata)| {
            metadata.get::<Album>(track_meta.album).map(|album| (track_name, artist_name, album.name.clone()))
        }).map_err(move |err| {
            ScrobbleError::new(format!("{:?}", err).to_owned())
        }).and_then(move |(track, artist, album)| {
            future::ok(Scrobble::new(artist, track, album))
{'fraction_non_alphanumeric': 0.1283633670698593, 'fraction_numerical': 0.00012342631449024932, 'mean_word_length': 2.586985391766268, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 50, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3169646', 'n_tokens_mistral': 2546, 'n_tokens_neox': 2501, 'n_words': 490}