public class ProfileManager {

    public static final String JS_SUBSCRIBED_PODCASTS = "subscribedPodcasts";
    public static final String JS_SUBSCRIBED_STATIONS = "subscribedStations";
    public static final String JS_RECENT_STATIONS = "recentStations";

    public static ProfileManager profileManager;
    private IOperationFinishWithDataCallback profileSavedCallback;


    public static class ProfileUpdateEvent {
        public String updateListType;
        public MediaItem mediaItem;
        public boolean isRemoved;

        public ProfileUpdateEvent(String updateListType, MediaItem mediaItem, boolean isRemoved) {
            this.updateListType = updateListType;
            this.mediaItem = mediaItem;
            this.isRemoved = isRemoved;
        }
    }

    private ProfileManager() {

    }

    public synchronized static ProfileManager getInstance() {
        if (profileManager == null) {
            profileManager = new ProfileManager();
        }
        return profileManager;
    }

    public void setProfileSavedCallback(IOperationFinishWithDataCallback profileSavedCallback) {
        this.profileSavedCallback = profileSavedCallback;
    }

    private ArrayList<Long> getMediaListIds(String mediaType, String listType) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String[] args1 = {mediaType, listType};
        ArrayList<Long> ids = new ArrayList<>();
        String table = mediaType.equals(MediaListItem.TYPE_RADIO) ? "radio_stations" : "podcasts";
        Cursor cursor = database.rawQuery("SELECT p.id FROM " + table + " as p\n" +
                "LEFT JOIN media_list as ml\n" +
                "ON p.id = ml.media_id\n" +
                "WHERE ml.media_type = ? and ml.list_type = ?", args1);
        while (cursor.moveToNext()) {
            ids.add(cursor.getLong(cursor.getColumnIndex("id")));
        }
        cursor.close();
        return ids;
    }

    private ArrayList<Podcast> getPodcastsForMediaType(String mediaType) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        ArrayList<Long> ids = getMediaListIds(MediaListItem.TYPE_PODCAST,
                mediaType.equals(MediaListItem.DOWNLOADED) ? MediaListItem.SUBSCRIBED : MediaListItem.DOWNLOADED);
        ArrayList<Podcast> podcasts = new ArrayList<>();
        String[] args2 = {MediaListItem.TYPE_PODCAST, mediaType};
        Cursor cursor = database.rawQuery("SELECT p.* FROM podcasts as p\n" +
                "LEFT JOIN media_list as ml\n" +
                "ON p.id = ml.media_id\n" +
                "WHERE ml.media_type = ? and ml.list_type = ? " +
                "ORDER BY id DESC", args2);
        while (cursor.moveToNext()) {
            Podcast podcast = Podcast.withCursor(cursor);
            if (mediaType.equals(MediaListItem.DOWNLOADED)) {
                podcast.isDownloaded = true;
                if (ids.contains(podcast.id)) {
                    podcast.isSubscribed = true;
                }
            } else if (mediaType.equals(MediaListItem.SUBSCRIBED)) {
                podcast.isSubscribed = true;
                if (ids.contains(podcast.id)) {
                    podcast.isDownloaded = true;
                }
            }
            podcasts.add(podcast);
        }
        cursor.close();

        Podcast.syncWithDb(podcasts);

        return podcasts;
    }

    private ArrayList<RadioItem> getRadioStationsForMediaType(String mediaType) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        ArrayList<Long> ids = getMediaListIds(MediaListItem.TYPE_RADIO,
                mediaType.equals(MediaListItem.RECENT) ? MediaListItem.SUBSCRIBED : MediaListItem.RECENT);
        ArrayList<RadioItem> radioItems = new ArrayList<>();
        String[] args2 = {MediaListItem.TYPE_RADIO, mediaType};

        Cursor cursor = database.rawQuery("SELECT r.* FROM radio_stations as r\n" +
                "LEFT JOIN media_list as ml\n" +
                "ON r.id = ml.media_id\n" +
                "WHERE ml.media_type = ? and ml.list_type = ? " +
                "ORDER BY id DESC", args2);

        while (cursor.moveToNext()) {
            RadioItem radioItem = RadioItem.withCursor(cursor);
            if (mediaType.equals(MediaListItem.RECENT)) {
                radioItem.isRecent = true;
                if (ids.contains(radioItem.id)) {
                    radioItem.isSubscribed = true;
                }
            } else if (mediaType.equals(MediaListItem.SUBSCRIBED)) {
                radioItem.isSubscribed = true;
                if (ids.contains(radioItem.id)) {
                    radioItem.isRecent = true;
                }
            }
            radioItems.add(radioItem);
        }
        cursor.close();
        return radioItems;
    }

    private void addTrack(MediaItem mediaItem, Track track, String listType) {
        if (mediaItem instanceof Podcast && track instanceof Episode) {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            boolean isNotifyChanges = false;
            Podcast podcast = (Podcast) mediaItem;
            Episode episode = (Episode) track;

            //1. Save episode/podcast if needed
            if (!podcast.isExistsInDb) {
                podcast.save();
            }
            if (!episode.isExistsInDb) {
                episode.save(podcast.id);
            }

            //2. Add podcast to media_list if needed
            if ((!podcast.isDownloaded && listType.equals(MediaListItem.DOWNLOADED)) ||
                    (!podcast.hasNewEpisodes && listType.equals(MediaListItem.NEW))) {
                ContentValues values = new ContentValues();
                values.put("media_id", mediaItem.id);
                values.put("media_type", MediaListItem.TYPE_PODCAST);
                values.put("list_type", listType);
                database.insert("media_list", null, values);
                isNotifyChanges = true;
            }

            //3. Create podcasts_episodes_rel
            if ((!episode.isDownloaded && listType.equals(MediaListItem.DOWNLOADED)) ||
                    (!episode.isNew && listType.equals(MediaListItem.NEW))) {
                ContentValues values = new ContentValues();
                values.put("podcast_id", podcast.id);
                values.put("episode_id", episode.id);
                values.put("type", listType);
                database.insert("podcasts_episodes_rel", null, values);
            }

            //4. Update objects
            if (listType.equals(MediaListItem.DOWNLOADED)) {
                podcast.isDownloaded = true;
                episode.isDownloaded = true;
                if (isNotifyChanges) {
                    notifyChanges(new ProfileUpdateEvent(MediaListItem.DOWNLOADED, mediaItem, false));
                }
            } else if (listType.equals(MediaListItem.NEW)) {
                podcast.hasNewEpisodes = true;
                episode.isNew = true;
                if (!Podcast.hasEpisodeWithTitle(podcast, episode)) {
                    podcast.getEpisodes().add(episode);
                }
                if (Looper.myLooper() == Looper.getMainLooper()) {
                    notifyChanges(new ProfileUpdateEvent(MediaListItem.NEW, mediaItem, false));
                }
            }
        }
    }

    private void removeTrack(MediaItem mediaItem, Track track, String listType) {
        if (mediaItem instanceof Podcast && track instanceof Episode) {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            Podcast podcast = (Podcast) mediaItem;
            Episode episode = (Episode) track;

            //Remove reletionships -> then remove episode
            String args[] = {String.valueOf(podcast.id), String.valueOf(episode.id), listType};
            database.delete("podcasts_episodes_rel", "podcast_id = ? AND episode_id = ? AND type = ?", args);

            if (listType.equals(MediaListItem.DOWNLOADED)) {
                File episodeFile = new File(episode.getAudeoUrl());
                episodeFile.delete();
                episode.isDownloaded = false;
                if (!episode.isNew) {
                    episode.remove();
                }
            } else if (listType.equals(MediaListItem.NEW)) {
                episode.isNew = false;
                if (!episode.isDownloaded) {
                    episode.remove();
                }
            }

            if (listType.equals(MediaListItem.DOWNLOADED) && podcast.getDownloadedEpisodsCount() == 0) {
                String args2[] = {String.valueOf(podcast.id), MediaListItem.TYPE_PODCAST, MediaListItem.DOWNLOADED};
                database.delete("media_list", "media_id = ? AND media_type = ? AND list_type = ?", args2);
                podcast.isDownloaded = false;
                notifyChanges(new ProfileUpdateEvent(MediaListItem.DOWNLOADED, mediaItem, true));
            } else if (listType.equals(MediaListItem.NEW) && podcast.getNewEpisodsCount() == 0) {
                podcast.hasNewEpisodes = false;
                String args2[] = {String.valueOf(podcast.id), MediaListItem.TYPE_PODCAST, MediaListItem.NEW};
                database.delete("media_list", "media_id = ? AND media_type = ? AND list_type = ?", args2);
            }

            if (listType.equals(MediaListItem.NEW) && Looper.myLooper() == Looper.getMainLooper()) {
                //If we are in main thread and were changes in count of new episodes -> notify provider
                notifyChanges(new ProfileUpdateEvent(MediaListItem.NEW, mediaItem, false));
            }

        }
    }

    public ArrayList<Podcast> getDownloadedPodcasts() {
        return getPodcastsForMediaType(MediaListItem.DOWNLOADED);
    }

    public ArrayList<Podcast> getSubscribedPodcasts() {
        return getPodcastsForMediaType(MediaListItem.SUBSCRIBED);
    }

    public ArrayList<RadioItem> getSubscribedRadioItems() {
        return getRadioStationsForMediaType(MediaListItem.SUBSCRIBED);
    }

    public ArrayList<RadioItem> getRecentRadioItems() {
        return getRadioStationsForMediaType(MediaListItem.RECENT);
    }

    public int getTrackPosition(Track track) {
        int trackPostion = -1;
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String args[] = {track.getTitle()};
        Cursor cursor = database.rawQuery("SELECT position FROM tracks_positions WHERE track_name = ?", args);
        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            trackPostion = cursor.getInt(cursor.getColumnIndex("position"));
        }
        return trackPostion;
    }

    public void addSubscribedMediaItem(MediaItem mediaItem) {
        if (!mediaItem.isSubscribed) {
            String mediaType = MediaListItem.TYPE_RADIO;
            if (mediaItem instanceof Podcast) {
                mediaType = MediaListItem.TYPE_PODCAST;
                Podcast podcast = ((Podcast) mediaItem);
                if (!mediaItem.isExistsInDb) {
                    podcast.save();
                }
                Feed.removeFeed(podcast.getFeedUrl());
                Feed.saveAsFeed(podcast.getFeedUrl(), podcast.getEpisodes(), true);
            } else if (mediaItem instanceof RadioItem) {
                if (!mediaItem.isExistsInDb) {
                    ((RadioItem) mediaItem).save();
                }
            }
            ContentValues values = new ContentValues();
            values.put("media_id", mediaItem.id);
            values.put("media_type", mediaType);
            values.put("list_type", MediaListItem.SUBSCRIBED);
            UpodsApplication.getDatabaseManager().getWritableDatabase().insert("media_list", null, values);
            mediaItem.isSubscribed = true;

            if (mediaItem instanceof Podcast && getSubscribedPodcasts().size() == 1) {
                UpodsApplication.setAlarmManagerTasks();
            }
        }
        notifyChanges(new ProfileUpdateEvent(MediaListItem.SUBSCRIBED, mediaItem, false));
    }

    public void addRecentMediaItem(MediaItem mediaItem) {
        if (mediaItem instanceof RadioItem) {
            if (!((RadioItem) mediaItem).isRecent) {
                if (!mediaItem.isExistsInDb) {
                    ((RadioItem) mediaItem).save();
                }
                ContentValues values = new ContentValues();
                values.put("media_id", mediaItem.id);
                values.put("media_type", MediaListItem.TYPE_RADIO);
                values.put("list_type", MediaListItem.RECENT);
                UpodsApplication.getDatabaseManager().getWritableDatabase().insert("media_list", null, values);
                ((RadioItem) mediaItem).isRecent = true;
                notifyChanges(new ProfileUpdateEvent(MediaListItem.RECENT, mediaItem, false));
            }
        }
    }

    public void addNewTrack(MediaItem mediaItem, Track track) {
        addTrack(mediaItem, track, MediaListItem.NEW);
    }

    public void addDownloadedTrack(MediaItem mediaItem, Track track) {
        addTrack(mediaItem, track, MediaListItem.DOWNLOADED);
    }

    public void saveTrackPosition(Track track, int position) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("track_name", track.getTitle());
        values.put("position", position);
        database.replace("tracks_positions", null, values);
    }

    public void removeNewTrack(MediaItem mediaItem, Track track) {
        removeTrack(mediaItem, track, MediaListItem.NEW);
    }

    public void removeDownloadedTrack(MediaItem mediaItem, Track track) {
        removeTrack(mediaItem, track, MediaListItem.DOWNLOADED);
    }

    public void removeRecentMediaItem(MediaItem mediaItem) {
        if (mediaItem instanceof RadioItem) {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            String args[] = {String.valueOf(mediaItem.id), MediaListItem.TYPE_RADIO, MediaListItem.RECENT};
            database.delete("media_list", "media_id = ? AND media_type = ? AND list_type = ?", args);
            ((RadioItem) mediaItem).isRecent = false;

            //Remove media item from DB if it doesn't use
            if (!((RadioItem) mediaItem).isSubscribed) {
                String args2[] = {String.valueOf(mediaItem.id)};
                database.delete("radio_stations", "id = ?", args2);
            }
        }
        notifyChanges(new ProfileUpdateEvent(MediaListItem.SUBSCRIBED, mediaItem, true));
    }

    public void removeSubscribedMediaItem(MediaItem mediaItem) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        String type = MediaListItem.TYPE_RADIO;
        if (mediaItem instanceof Podcast) {
            type = MediaListItem.TYPE_PODCAST;
        }
        mediaItem.isSubscribed = false;
        String args[] = {String.valueOf(mediaItem.id), type, MediaListItem.SUBSCRIBED};
        database.delete("media_list", "media_id = ? AND media_type = ? AND list_type = ?", args);

        //Remove media item from DB if it doesn't use
        if (mediaItem instanceof RadioItem && !((RadioItem) mediaItem).isRecent) {
            String args2[] = {String.valueOf(mediaItem.id)};
            database.delete("radio_stations", "id = ?", args2);
        } else if (mediaItem instanceof Podcast && !((Podcast) mediaItem).isDownloaded && !((Podcast) mediaItem).hasNewEpisodes) {
            String args2[] = {String.valueOf(mediaItem.id)};
            database.delete("podcasts", "id = ?", args2);
        }

        notifyChanges(new ProfileUpdateEvent(MediaListItem.SUBSCRIBED, mediaItem, true));
    }

    public void notifyChanges(ProfileUpdateEvent updateEvent) {
        if (profileSavedCallback != null) {
            profileSavedCallback.operationFinished(updateEvent);
        }
    }

    private void initSubscribedPodcasts(JSONArray jSubscribedPodcasts) {
        try {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            ArrayList<Podcast> subscribedPodcasts = new ArrayList<>();
            for (int i = 0; i < jSubscribedPodcasts.length(); i++) {
                Podcast podcast = new Podcast(jSubscribedPodcasts.getJSONObject(i));
                subscribedPodcasts.add(podcast);
            }

            if (subscribedPodcasts.isEmpty()) {
                return;
            }

            Podcast.syncWithDb(subscribedPodcasts);
            for (Podcast podcast : subscribedPodcasts) {
                if (!podcast.isSubscribed) {
                    addSubscribedMediaItem(podcast);
                }
            }
            ArrayList<String> ids = MediaItem.getIds(subscribedPodcasts);
            if (ids.size() == 0) {
                return;
            }
            ArrayList<String> args = new ArrayList<>();
            args.add(MediaListItem.TYPE_PODCAST);
            args.add(MediaListItem.SUBSCRIBED);
            args.addAll(ids);
            database.delete("media_list", "media_type = ? AND list_type = ? AND media_id not in (" + SQLdatabaseManager.makePlaceholders(ids.size()) + ")",
                    args.toArray(new String[args.size()]));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initRadioStations(JSONArray jStations, String listType) {
        try {
            SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
            ArrayList<RadioItem> radioStations = new ArrayList<>();
            for (int i = 0; i < jStations.length(); i++) {
                RadioItem radioItem = new RadioItem(jStations.getJSONObject(i));
                radioStations.add(radioItem);
            }

            if (radioStations.isEmpty()) {
                return;
            }

            RadioItem.syncWithDb(radioStations);
            for (RadioItem radioItem : radioStations) {
                if (!radioItem.isSubscribed && listType.equals(MediaListItem.SUBSCRIBED)) {
                    addSubscribedMediaItem(radioItem);
                } else if (!radioItem.isRecent && listType.equals(MediaListItem.RECENT)) {
                    addRecentMediaItem(radioItem);
                }
            }
            ArrayList<String> ids = MediaItem.getIds(radioStations);

            if (ids.size() == 0) {
                return;
            }

            ArrayList<String> args = new ArrayList<>();
            args.add(MediaListItem.TYPE_RADIO);
            args.add(listType);
            args.addAll(ids);
            database.delete("media_list", "media_type = ? AND list_type = ? AND media_id not in (" + SQLdatabaseManager.makePlaceholders(ids.size()) + ")",
                    args.toArray(new String[args.size()]));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void readFromJson(JSONObject rootProfile) {
        try {
            //Read from JSON -> syncWithDB -> save all which not exists -> remove all which not in JSON
            // -> UI will be notified inside init... functions

            initSubscribedPodcasts(rootProfile.getJSONArray(JS_SUBSCRIBED_PODCASTS));
            initRadioStations(rootProfile.getJSONArray(JS_SUBSCRIBED_STATIONS), MediaListItem.SUBSCRIBED);
            initRadioStations(rootProfile.getJSONArray(JS_RECENT_STATIONS), MediaListItem.RECENT);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }


    public JSONObject getAsJson() {
        JSONObject rootProfile = new JSONObject();
        try {
            rootProfile.put(JS_RECENT_STATIONS, RadioItem.toJsonArray(getRecentRadioItems()));
            rootProfile.put(JS_SUBSCRIBED_STATIONS, RadioItem.toJsonArray(getSubscribedRadioItems()));
            rootProfile.put(JS_SUBSCRIBED_PODCASTS, Podcast.toJsonArray(getSubscribedPodcasts()));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return rootProfile;
    }

} public class UpodsApplication extends Application {

    private static final int CHECK_NEW_EPISODS_INTENT_CODE = 2302;

    private static final String TAG = "UpodsApplication";
    private static Context applicationContext;
    private static SQLdatabaseManager databaseManager;
    private static boolean isLoaded;

    @Override
    public void onCreate() {
        isLoaded = false;
        applicationContext = getApplicationContext();
        YandexMetrica.activate(getApplicationContext(), Config.YANDEX_METRICS_API_KEY);
        YandexMetrica.enableActivityAutoTracking(this);
        FacebookSdk.sdkInitialize(applicationContext);
        LoginMaster.getInstance().init();
        new Prefs.Builder()
                .setContext(this)
                .setMode(ContextWrapper.MODE_PRIVATE)
                .setPrefsName(getPackageName())
                .setUseDefaultSharedPreference(true).build();

        super.onCreate();
    }

    /**
     * All resources which can be loaded not in onCreate(), should be load here,
     * this function called while splash screen is active
     */
    public static void initAllResources() {
        if (!isLoaded) {
            databaseManager = new SQLdatabaseManager(applicationContext);
            SettingsManager.getInstace().init();
            Category.initPodcastsCatrgories();
            SimpleCacheManager.getInstance().removeExpiredCache();
            runMainService();
            setAlarmManagerTasks();
            isLoaded = true;
        }
    }

    private static void runMainService() {
        applicationContext.startService(new Intent(applicationContext, MainService.class));
    }

    public static void setAlarmManagerTasks() {
        setAlarmManagerTasks(applicationContext);
    }

    public static void setAlarmManagerTasks(Context context) {
        try {
            if (ProfileManager.getInstance().getSubscribedPodcasts().size() == 0) {
                return;
            }
            AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            Intent intent = new Intent(context, NetworkTasksService.class);
            intent.setAction(NetworkTasksService.ACTION_CHECK_FOR_NEW_EPISODS);
            PendingIntent alarmIntent = PendingIntent.getService(context, CHECK_NEW_EPISODS_INTENT_CODE, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);

            if (SettingsManager.getInstace().getBooleanSettingsValue(SettingsManager.JS_NOTIFY_EPISODES)) {
                long interval = SettingsManager.getInstace().getIntSettingsValue(SettingsManager.JS_PODCASTS_UPDATE_TIME);
                //interval = TimeUnit.MINUTES.toMillis(60);
                alarmMgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime(),
                        interval, alarmIntent);
                Logger.printInfo(TAG, "Alarm managers - Episods check for updates task added");
            } else {
                alarmIntent.cancel();
                alarmMgr.cancel(alarmIntent);
                Logger.printInfo(TAG, "Alarm managers - Episods check for updates task canceled");
            }
        } catch (Exception e) {
            Logger.printInfo(TAG, "Alarm managers - can't set alarm manager");
            e.printStackTrace();
        }
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
    }

    public static Context getContext() {
        return applicationContext;
    }

    public static SQLdatabaseManager getDatabaseManager() {
        return databaseManager;
    }
} public interface IOperationFinishCallback {

    void operationFinished();
} public interface IPlayerStateListener {

    void onStateChanged(UniversalPlayer.State state);
} public abstract class MediaItem extends SQLModel implements IMediaItemView {

    /**
     * Created by Alon Zilberman on 10/23/15.
     * Use it to transfer MediaItems and tracks in one object
     */
    public static class MediaItemBucket {
        public MediaItem mediaItem;
        public Track track;
    }

    protected String name;
    protected String coverImageUrl;
    protected float score;


    public boolean isSubscribed;

    public MediaItem() {
    }

    public String getCoverImageUrl() {
        return coverImageUrl;
    }

    public String getName() {
        return name;
    }

    public String getSubHeader() {
        return "";
    }

    public String getBottomHeader() {
        return "";
    }

    public boolean hasTracks() {
        return false;
    }

    public String getDescription() {
        return "";
    }

    public String getAudeoLink() {
        return null;
    }

    public String getBitrate() {
        return "";
    }


    public void syncWithDB(){

    }

    public void syncWithMediaItem(MediaItem updatedMediaItem) {
        this.id = updatedMediaItem.id;
        this.isSubscribed = updatedMediaItem.isSubscribed;
        this.isExistsInDb = updatedMediaItem.isExistsInDb;
    }

    public static boolean hasMediaItemWithName(ArrayList<? extends MediaItem> mediaItems, MediaItem mediaItemToCheck) {
        for (MediaItem mediaItem : mediaItems) {
            if (mediaItem.getName().equals(mediaItemToCheck.getName())) {
                return true;
            }
        }
        return false;
    }

    public static MediaItem getMediaItemByName(ArrayList<? extends MediaItem> mediaItems, MediaItem mediaItemTarget) {
        return getMediaItemByName(mediaItems, mediaItemTarget.getName());
    }

    public static MediaItem getMediaItemByName(ArrayList<? extends MediaItem> mediaItems, String targetName) {
        for (MediaItem mediaItem : mediaItems) {
            if (GlobalUtils.safeTitleEquals(mediaItem.getName(), targetName)) {
                return mediaItem;
            }
        }
        return null;
    }

    public static ArrayList<String> getIds(ArrayList<? extends MediaItem> mediaItems) {
        ArrayList<String> ids = new ArrayList<>();
        for (MediaItem mediaItem : mediaItems) {
            ids.add(String.valueOf(mediaItem.id));
        }
        return ids;
    }

    public float getScore(){
        return score;
    }
} public class Podcast extends MediaItem {
    public static final String TABLE = "podcasts";
    private static String PODCAST_LOG = "PODCAST";
    protected String name;
    protected String censoredName;
    protected String artistName;
    protected String feedUrl;
    protected String releaseDate;
    protected String explicitness;
    protected String trackCount;
    protected String country;
    protected String genre;
    protected String description;

    protected ArrayList<Episode> episodes;

    public boolean isDownloaded;
    public boolean hasNewEpisodes;

    public Podcast() {
        super();
        this.episodes = new ArrayList<>();
        this.censoredName = "";
        this.artistName = "";
        this.feedUrl = "";
        this.releaseDate = "";
        this.explicitness = "";
        this.trackCount = "0";
        this.genre = "";
        this.description = "";
        this.score = 4.5f;
    }

    public Podcast(String name, String feedUrl) {
        this();
        this.name = name.replace("\n", "").trim();
        this.feedUrl = feedUrl;
    }


    public Podcast(JSONObject jsonItem) {
        this();
        try {
            this.description = jsonItem.has("description") ? jsonItem.getString("description") : "";

            if (jsonItem.has("episodes")) {
                JSONArray jepisodes = jsonItem.getJSONArray("episodes");
                for (int i = 0; i < jepisodes.length(); i++) {
                    episodes.add(new Episode(jepisodes.getJSONObject(i)));
                }
            }

            if (jsonItem.has("kind")) { //Itunes
                this.id = jsonItem.has("trackId") ? jsonItem.getInt("trackId") : 0;
                this.name = jsonItem.has("collectionName") ? jsonItem.getString("collectionName") : "";
                this.name = name.replace("\n", "").trim();
                this.censoredName = jsonItem.has("collectionCensoredName") ? jsonItem.getString("collectionCensoredName") : "";
                this.artistName = jsonItem.has("artistName") ? jsonItem.getString("artistName") : "";
                this.feedUrl = jsonItem.has("feedUrl") ? jsonItem.getString("feedUrl") : "";
                this.coverImageUrl = jsonItem.has("artworkUrl600") ? jsonItem.getString("artworkUrl600") : "";
                if (this.coverImageUrl.isEmpty()) {
                    this.coverImageUrl = jsonItem.has("artworkUrl100") ? jsonItem.getString("artworkUrl100") : "";
                }
                if (this.coverImageUrl.isEmpty()) {
                    this.coverImageUrl = jsonItem.has("artworkUrl60") ? jsonItem.getString("artworkUrl60") : "";
                }
                this.country = jsonItem.has("country") ? jsonItem.getString("country") : "";
                this.releaseDate = jsonItem.has("releaseDate") ? jsonItem.getString("releaseDate") : "";
                this.explicitness = jsonItem.has("collectionExplicitness") ? jsonItem.getString("collectionExplicitness") : "";
                this.trackCount = jsonItem.has("trackCount") ? jsonItem.getString("trackCount") : "";
                this.genre = jsonItem.has("primaryGenreName") ? jsonItem.getString("primaryGenreName") : "";
                this.score = 4.5f;
            } else {//Our backend
                this.id = jsonItem.has("id") ? jsonItem.getInt("id") : 0;
                this.name = jsonItem.has("name") ? jsonItem.getString("name") : "";
                this.name = name.replace("\n", "").trim();
                this.censoredName = jsonItem.has("censored_name") ? jsonItem.getString("censored_name") : "";
                this.artistName = jsonItem.has("artist_name") ? jsonItem.getString("artist_name") : "";
                this.feedUrl = jsonItem.has("feed_url") ? jsonItem.getString("feed_url") : "";
                this.coverImageUrl = jsonItem.has("image_url") ? jsonItem.getString("image_url") : "";
                this.country = jsonItem.has("country") ? jsonItem.getString("country") : "";
                this.releaseDate = jsonItem.has("release_date") ? jsonItem.getString("release_date") : "";
                this.explicitness = jsonItem.has("explicitness") ? jsonItem.getString("explicitness") : "";
                this.trackCount = jsonItem.has("track_count") ? jsonItem.getString("track_count") : "";
                this.score = jsonItem.has("score") ? (float) jsonItem.getDouble("score") : 4.5f;
                if (jsonItem.has("genres") && jsonItem.getJSONArray("genres").length() > 0) {
                    this.genre = jsonItem.getJSONArray("genres").getString(0);
                }
                if (jsonItem.has("episodes")) {
                    JSONArray jsonEpisodes = jsonItem.getJSONArray("episodes");
                    for (int i = 0; i < jsonEpisodes.length(); i++) {
                        this.episodes.add(new Episode(jsonEpisodes.getJSONObject(i)));
                    }
                }
            }
            this.name = this.name.replace("\n", "").trim();
        } catch (Exception e) {
            Logger.printError(PODCAST_LOG, "Can't parse podcast from json");
            e.printStackTrace();
        }
    }

    public Podcast(Podcast podcast) {
        this.name = podcast.getName().replace("\n", "").trim();
        this.censoredName = podcast.getCensoredName();
        this.artistName = podcast.getArtistName();
        this.feedUrl = podcast.getFeedUrl();
        this.coverImageUrl = podcast.getCoverImageUrl();
        this.releaseDate = podcast.getReleaseDate();
        this.explicitness = podcast.getExplicitness();
        this.trackCount = podcast.getTrackCount();
        this.country = podcast.getCountry();
        this.genre = podcast.getGenre();
        this.id = podcast.id;
        this.isExistsInDb = podcast.isExistsInDb;
        this.isSubscribed = podcast.isSubscribed;
        this.isDownloaded = podcast.isDownloaded;
        this.hasNewEpisodes = podcast.hasNewEpisodes;
        this.score = podcast.score;
        this.episodes = new ArrayList<Episode>(podcast.episodes);
    }


    public void setName(String name) {
        this.name = name;
    }

    public String getCensoredName() {
        return censoredName;
    }

    public String getFeedUrl() {
        return feedUrl;
    }

    public String getArtistName() {
        return artistName;
    }

    public String getReleaseDate() {
        return releaseDate;
    }

    public String getTrackCount() {
        return trackCount;
    }

    public void setTrackCount(String trackCount) {
        this.trackCount = trackCount;
    }

    public String getExplicitness() {
        return explicitness;
    }

    public String getCountry() {
        return country;
    }

    public String getGenre() {
        return genre;
    }

    public ArrayList<Episode> getEpisodes() {
        return episodes;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setTracks(ArrayList<? extends Track> tracks) {
        if (this.episodes != null) {
            this.episodes.clear();
        }
        this.episodes = (ArrayList<Episode>) tracks;
    }

    public ArrayList<? extends Track> getTracks() {
        return this.episodes;
    }

    public String getTracksFeed() {
        return this.feedUrl;
    }

    public Track getSelectedTrack() {
        for (Episode episode : episodes) {
            if (episode.isSelected) {
                return episode;
            }
        }
        return null;
    }

    public int getNewEpisodsCount() {
        int count = 0;
        for (Episode episode : episodes) {
            if (episode.isNew) {
                count++;
            }
        }
        return count;
    }

    public int getDownloadedEpisodsCount() {
        int count = 0;
        for (Episode episode : episodes) {
            if (episode.isDownloaded) {
                count++;
            }
        }
        return count;
    }

    public String getDownloadedDirectory() {
        for (Episode episode : episodes) {
            if (episode.isDownloaded) {
                File podcastFolder = new File(episode.getAudeoUrl());
                return podcastFolder.getParentFile().getPath();
            }
        }
        return null;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getCoverImageUrl() {
        return coverImageUrl != null && coverImageUrl.isEmpty() ? null : coverImageUrl;
    }

    @Override
    public String getSubHeader() {
        return this.artistName;
    }

    @Override
    public String getBottomHeader() {
        return this.country;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public String getAudeoLink() {
        for (Episode episode : episodes) {
            if (episode.isSelected) {
                return episode.getAudeoUrl();
            }
        }
        return episodes.get(0).getAudeoUrl();
    }

    @Override
    public String getBitrate() {
        return "";
    }

    @Override
    public boolean hasTracks() {
        return true;
    }

    public long save() {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("censored_name", censoredName);
        values.put("artist_name", artistName);
        values.put("description", description);
        values.put("feed_url", feedUrl);
        values.put("cover_image_url", coverImageUrl);
        values.put("release_date", releaseDate);
        values.put("explicitness", explicitness);
        values.put("country", country);
        values.put("genre", genre);
        values.put("track_count", trackCount);
        values.put("score", score);

        id = database.insert(TABLE, null, values);
        isExistsInDb = true;

        return id;
    }

    public JSONObject toJSON() {
        JSONObject podcast = new JSONObject();
        try {
            podcast.put("id", this.id);
            podcast.put("name", this.name);
            podcast.put("censored_name", this.censoredName);
            podcast.put("artist_name", this.artistName);
            podcast.put("feed_url", this.feedUrl);
            podcast.put("image_url", this.coverImageUrl);
            podcast.put("country", this.country);
            podcast.put("release_date", this.releaseDate);
            podcast.put("explicitness", this.explicitness);
            podcast.put("track_count", this.trackCount);
            podcast.put("genre", this.genre);
            podcast.put("description", this.description);
            podcast.put("score", this.score);
            //podcast.put("episodes", Episode.toJSONArray(this.episodes, true));
        } catch (JSONException e) {
            Logger.printError(PODCAST_LOG, "Can't convert podcast to json");
            e.printStackTrace();
        }
        return podcast;
    }

    public void selectTrack(Track track) {
        for (Episode episode : episodes) {
            episode.isSelected = episode.equals(track) ? true : false;
        }
    }

    public static boolean isExistsWithName(String name) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String[] args = {name};
        Cursor cursor = database.rawQuery("SELECT * from podcasts where name = ? LIMIT 1", args);
        cursor.moveToFirst();
        return cursor.getCount() > 0;
    }

    public static JSONArray toJsonArray(ArrayList<Podcast> podcasts) {
        JSONArray jsonPodcasts = new JSONArray();
        for (Podcast podcast : podcasts) {
            jsonPodcasts.put(podcast.toJSON());
        }
        return jsonPodcasts;
    }

    public static Podcast withCursor(Cursor cursor) {
        Podcast podcast = new Podcast();
        podcast.isExistsInDb = true;
        podcast.id = cursor.getLong(cursor.getColumnIndex("id"));
        podcast.name = cursor.getString(cursor.getColumnIndex("name"));
        podcast.censoredName = cursor.getString(cursor.getColumnIndex("censored_name"));
        podcast.artistName = cursor.getString(cursor.getColumnIndex("artist_name"));
        podcast.description = cursor.getString(cursor.getColumnIndex("description"));
        podcast.feedUrl = cursor.getString(cursor.getColumnIndex("feed_url"));
        podcast.coverImageUrl = cursor.getString(cursor.getColumnIndex("cover_image_url"));
        podcast.releaseDate = cursor.getString(cursor.getColumnIndex("release_date"));
        podcast.explicitness = cursor.getString(cursor.getColumnIndex("explicitness"));
        podcast.country = cursor.getString(cursor.getColumnIndex("country"));
        podcast.genre = cursor.getString(cursor.getColumnIndex("genre"));
        podcast.trackCount = cursor.getString(cursor.getColumnIndex("track_count"));
        podcast.score = cursor.getFloat(cursor.getColumnIndex("score"));

        podcast.episodes.addAll(Episode.withPodcastId(podcast.id));

        return podcast;
    }

    public static ArrayList<Podcast> withJsonArray(JSONArray jsonPodcastsItems) {
        ArrayList<Podcast> items = new ArrayList<Podcast>();
        try {
            for (int i = 0; i < jsonPodcastsItems.length(); i++) {
                JSONObject podcastItem = (JSONObject) jsonPodcastsItems.get(i);
                items.add(new Podcast(podcastItem));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return items;
    }

    @Override
    public void syncWithMediaItem(MediaItem updatedMediaItem) {
        super.syncWithMediaItem(updatedMediaItem);
        Podcast updatedPodcast = (Podcast) updatedMediaItem;
        this.hasNewEpisodes = updatedPodcast.hasNewEpisodes;
        this.isDownloaded = updatedPodcast.isDownloaded;
        this.episodes.clear();
        this.episodes.addAll(updatedPodcast.episodes);
    }

    @Override
    public void syncWithDB() {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        String args[] = {String.valueOf(id), MediaListItem.TYPE_PODCAST};
        Cursor cursor = database.rawQuery("SELECT * FROM media_list WHERE media_id = ? AND media_type = ?", args);

        this.isDownloaded = false;
        this.isSubscribed = false;
        this.hasNewEpisodes = false;
        while (cursor.moveToNext()) {
            String listType = cursor.getString(cursor.getColumnIndex("list_type"));
            if (listType.equals(MediaListItem.NEW)) {
                this.hasNewEpisodes = true;
            } else if (listType.equals(MediaListItem.SUBSCRIBED)) {
                this.isSubscribed = true;
            } else if (listType.equals(MediaListItem.DOWNLOADED)) {
                this.isDownloaded = true;
            }
        }
        cursor.close();

        ArrayList<Episode> existingEpisodes = Episode.withPodcastId(id);
        episodes.clear();

        for (Episode episode : existingEpisodes) {
            Episode podcastEpisode = Episode.getEpisodByTitle(episodes, episode.getTitle());
            if (podcastEpisode == null) {
                episodes.add(episode);
            } else {
                podcastEpisode.isDownloaded = episode.isDownloaded;
                podcastEpisode.isNew = episode.isNew;
            }
        }

    }

    public static void syncWithDb(ArrayList<Podcast> podcasts) {
        ArrayList<MediaListItem> listItems = MediaListItem.withMediaType(MediaListItem.TYPE_PODCAST);
        for (MediaListItem listItem : listItems) {
            Podcast podcast = (Podcast) MediaItem.getMediaItemByName(podcasts, listItem.mediaItemName);
            if (podcast != null) {
                podcast.id = listItem.id;
                podcast.isExistsInDb = true;
                if (listItem.listType.equals(MediaListItem.SUBSCRIBED)) {
                    podcast.isSubscribed = true;
                } else if (listItem.listType.equals(MediaListItem.DOWNLOADED)) {
                    podcast.isDownloaded = true;
                } else if (listItem.listType.equals(MediaListItem.NEW)) {
                    podcast.hasNewEpisodes = true;
                }
                ArrayList<Episode> existingEpisodes = Episode.withPodcastId(podcast.id);
                for (Episode episode : existingEpisodes) {
                    Episode podcastEpisode = Episode.getEpisodByTitle(podcast.episodes, episode.getTitle());
                    if (podcastEpisode == null) {
                        podcast.episodes.add(episode);
                    } else {
                        podcastEpisode.isDownloaded = episode.isDownloaded;
                        podcastEpisode.isNew = episode.isNew;
                    }
                }
            }
        }
    }


    public static void syncNewEpisodes(ArrayList<Podcast> podcasts) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String args[] = {MediaListItem.TYPE_PODCAST, MediaListItem.NEW};
        Cursor cursor = database.rawQuery("SELECT p.id, p.name, m.list_type FROM podcasts as p " +
                "LEFT JOIN media_list as m ON p.id = m.media_id " +
                "WHERE m.media_type =  ? AND m.list_type =  ?", args);
        while (cursor.moveToNext()) {
            long id = cursor.getLong(cursor.getColumnIndex("id"));
            for (Podcast podcast : podcasts) {
                if (podcast.id == id) {
                    podcast.hasNewEpisodes = true;
                    ArrayList<Episode> existingEpisodes = Episode.withPodcastId(podcast.id);
                    podcast.getEpisodes().addAll(existingEpisodes);
                }
            }
        }
        cursor.close();
    }

    public static boolean hasEpisodeWithTitle(Podcast podcast, Episode episodeToCheck) {
        for (Episode episode : podcast.getEpisodes()) {
            if (GlobalUtils.safeTitleEquals(episode.getTitle(), episodeToCheck.getTitle())) {
                return true;
            }
        }
        return false;
    }
} public class RadioItem extends MediaItem {

    public static final String TABLE = "radio_stations";
    private final static String RADIO_LOG = "RADIO_LOG";

    protected Set<StreamUrl> streamUrls;
    protected StreamUrl selectedStreamUrl;
    protected String bannerImageUrl;
    protected String description;
    protected String website;
    protected String facebook;
    protected String twitter;
    protected String country;
    protected String genre;

    //Not in DB
    public boolean isRecent;

    public RadioItem() {
        super();
        this.name = "";
        this.description = "";
        this.selectedStreamUrl = null;
        this.streamUrls = new HashSet<>();
        this.coverImageUrl = "";
        this.bannerImageUrl = "";
        this.website = "";
        this.facebook = "";
        this.twitter = "";
        this.country = "";
        this.genre = "";
        this.score = 4.5f;
    }

    public RadioItem(String name, StreamUrl streamUrl, String coverImageUrl) {
        this();
        this.name = name;
        this.name = this.name.replace("\n", "").trim();
        this.streamUrls.add(streamUrl);
        this.coverImageUrl = coverImageUrl;
    }

    public RadioItem(RadioItem item) {
        this.id = item.id;
        this.name = item.name;
        this.name = this.name.replace("\n", "").trim();
        this.selectedStreamUrl = item.selectedStreamUrl;
        this.streamUrls = new HashSet<>(item.streamUrls);
        this.coverImageUrl = item.coverImageUrl;
        this.bannerImageUrl = item.bannerImageUrl;
        this.description = item.description;
        this.website = item.website;
        this.facebook = item.facebook;
        this.twitter = item.twitter;
        this.country = item.country;
        this.isExistsInDb = item.isExistsInDb;
        this.isRecent = item.isRecent;
        this.isSubscribed = item.isSubscribed;
        this.score = item.score;
    }

    public long save() {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("description", description);
        values.put("website", website);
        values.put("facebook", facebook);
        values.put("twitter", twitter);
        values.put("cover_image_url", coverImageUrl);
        values.put("country", country);
        values.put("genre", genre);
        values.put("score", score);

        id = database.insert(TABLE, null, values);
        isExistsInDb = true;
        StreamUrl.saveSet(streamUrls, id);

        return id;
    }

    public RadioItem(JSONObject jsonItem) {
        try {
            this.streamUrls = new HashSet<>();
            this.id = jsonItem.has("id") ? jsonItem.getInt("id") : 0;
            this.name = jsonItem.has("name") ? jsonItem.getString("name") : "";
            this.name = name.replace("\n", "").trim();
            this.description = jsonItem.has("description") ? jsonItem.getString("description") : "";
            this.website = jsonItem.has("website") ? jsonItem.getString("website") : "";
            this.facebook = jsonItem.has("facebook") ? jsonItem.getString("facebook") : "";
            this.twitter = jsonItem.has("twitter") ? jsonItem.getString("twitter") : "";
            this.country = jsonItem.has("country") ? jsonItem.getString("country") : "";
            this.score = jsonItem.has("score") ? (float) jsonItem.getDouble("score") : 4.5f;
            this.selectedStreamUrl = jsonItem.has("selectedStreamUrl") ? new StreamUrl(jsonItem.getJSONObject("selectedStreamUrl")) : null;

            if (jsonItem.has("streamUrls") && jsonItem.getJSONArray("streamUrls").length() > 0) {
                JSONArray jsonStreamUrls = jsonItem.getJSONArray("streamUrls");
                for (int i = 0; i < jsonStreamUrls.length(); i++) {
                    this.streamUrls.add(new StreamUrl(jsonStreamUrls.getJSONObject(i)));
                }
            }

            if (!jsonItem.has("covers")) {//from profile
                this.coverImageUrl = jsonItem.has("cover_image_url") ? jsonItem.getString("cover_image_url") : "";
                this.bannerImageUrl = jsonItem.has("banner_image_url") ? jsonItem.getString("banner_image_url") : "";
                this.genre = jsonItem.has("genre") ? jsonItem.getString("genre") : "";

            } else {//from backend
                if (jsonItem.has("covers") && jsonItem.getJSONArray("covers").length() > 0) {
                    this.coverImageUrl = jsonItem.getJSONArray("covers").getString(0);
                }
                if (jsonItem.has("banners") && jsonItem.getJSONArray("banners").length() > 0) {
                    this.bannerImageUrl = jsonItem.getJSONArray("banners").getString(0);
                }
                if (jsonItem.has("genres") && jsonItem.getJSONArray("genres").length() > 0) {
                    this.genre = jsonItem.getJSONArray("genres").getString(0);
                }

            }
            this.name = this.name.replace("\n", "").trim();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public String getAudeoLink() {
        if (selectedStreamUrl != null) {
            return selectedStreamUrl.getUrl();
        }
        return StreamUrl.getBestStreamUrl(streamUrls).getUrl();
    }

    @Override
    public String getBitrate() {
        if (selectedStreamUrl != null) {
            return selectedStreamUrl.getBitrate();
        }
        return StreamUrl.getBestStreamUrl(streamUrls).getBitrate();
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getCoverImageUrl() {
        return coverImageUrl != null && coverImageUrl.isEmpty() ? null : coverImageUrl;
    }

    @Override
    public String getSubHeader() {
        return this.country;
    }

    @Override
    public String getDescription() {
        return description != null ? description : "";
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getFacebook() {
        return facebook;
    }

    public void setFacebook(String facebook) {
        this.facebook = facebook;
    }

    public String getTwitter() {
        return twitter;
    }

    public String getCountry() {
        return country;
    }

    public String getBannerImageUrl() {
        return bannerImageUrl;
    }


    public JSONObject toJSON() {
        JSONObject radioItem = new JSONObject();
        try {
            radioItem.put("id", this.id);
            radioItem.put("name", this.name);
            radioItem.put("cover_image_url", this.coverImageUrl);
            radioItem.put("banner_image_url", this.bannerImageUrl);
            radioItem.put("description", this.description);
            radioItem.put("website", this.website);
            radioItem.put("facebook", this.facebook);
            radioItem.put("twitter", this.twitter);
            radioItem.put("country", this.country);
            radioItem.put("genre", this.genre);
            radioItem.put("score", this.score);
            if (this.selectedStreamUrl != null) {
                radioItem.put("selectedStreamUrl", this.selectedStreamUrl.toJSON());
            }
            JSONArray jsonStramUrls = new JSONArray();
            for (StreamUrl streamUrl : streamUrls) {
                jsonStramUrls.put(streamUrl.toJSON());
            }
            radioItem.put("streamUrls", jsonStramUrls);
        } catch (JSONException e) {
            Logger.printError(RADIO_LOG, "Can't convert radio to json");
            e.printStackTrace();
        }
        return radioItem;
    }

    /**
     * Fixes current RadioItem link (formail, isAlive) if needed.
     *
     * @param operationFinishSecsuessCallback
     * @param operationFinishFailCallback
     */
    public void fixAudeoLinks(final IOperationFinishCallback operationFinishSecsuessCallback, final IOperationFinishCallback operationFinishFailCallback) {
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                boolean hasAtleastOneUrl = false;
                if (selectedStreamUrl != null) {
                    if (!GlobalUtils.isUrlReachable(selectedStreamUrl.getUrl())) {
                        selectedStreamUrl = null;
                    } else {
                        operationFinishSecsuessCallback.operationFinished();
                        return;
                    }
                }
                for (StreamUrl streamUrl : streamUrls) {
                    final String currentUrl = streamUrl.getUrl();
                    if (GlobalUtils.isUrlReachable(currentUrl)) {
                        if (currentUrl.matches("(.+\\.m3u$)|(.+\\.pls$)")) {
                            try {
                                String newUrl = MediaUtils.extractMp3FromFile(currentUrl);
                                StreamUrl.replaceUrl(streamUrls, newUrl, currentUrl);
                            } catch (Exception e) {
                                Logger.printInfo(RADIO_LOG, "Error with fetching radio url from file");
                                e.printStackTrace();
                                continue;
                            }
                        }
                        hasAtleastOneUrl = true;
                        streamUrl.isAlive = true;
                    } else {
                        streamUrl.isAlive = false;
                    }
                }
                if (hasAtleastOneUrl) {
                    operationFinishSecsuessCallback.operationFinished();
                } else {
                    operationFinishFailCallback.operationFinished();
                }
            }
        });
    }


    public String[] getAvailableStreams() {
        Set<String> availableStreams = StreamUrl.getAllAvailableStreams(streamUrls);
        List<String> list = new ArrayList<String>(availableStreams);
        Collections.sort(list, new Comparator<String>() {
            public int compare(String a, String b) {
                return Integer.parseInt(b) - Integer.parseInt(a);
            }
        });
        return list.toArray(new String[availableStreams.size()]);
    }

    public int getSelectedStreamAsNumber(String[] availableStreams) {
        if (selectedStreamUrl != null) {
            for (int i = 0; i < availableStreams.length; i++) {
                if (availableStreams[i].equals(selectedStreamUrl.getBitrate())) {
                    return i;
                }
            }
        } else {
            for (int i = 0; i < availableStreams.length; i++) {
                if (availableStreams[i].equals(getBitrate())) {
                    return i;
                }
            }
        }
        return 0;
    }

    public void selectStreamUrl(String quality) {
        for (StreamUrl streamUrl : streamUrls) {
            if (streamUrl.isAlive && streamUrl.hasBitrate() && streamUrl.getBitrate().equals(quality)) {
                selectedStreamUrl = streamUrl;
                return;
            }

        }

    }

    public boolean hasMultiplyStreams() {
        return getAvailableStreams().length > 1;
    }

    @Override
    public void syncWithDB() {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        String args[] = {String.valueOf(id), MediaListItem.TYPE_RADIO};
        Cursor cursor = database.rawQuery("SELECT * FROM media_list WHERE media_id = ? AND media_type = ?", args);

        this.isSubscribed = false;
        this.isRecent = false;
        while (cursor.moveToNext()) {
            String listType = cursor.getString(cursor.getColumnIndex("list_type"));
            if (listType.equals(MediaListItem.RECENT)) {
                this.isRecent = true;
            } else if (listType.equals(MediaListItem.SUBSCRIBED)) {
                this.isSubscribed = true;
            }
        }
    }

    @Override
    public void syncWithMediaItem(MediaItem updatedMediaItem) {
        super.syncWithMediaItem(updatedMediaItem);
        this.isRecent = ((RadioItem) updatedMediaItem).isRecent;
    }

    public static JSONArray toJsonArray(ArrayList<RadioItem> radioItems) {
        JSONArray jsonRadioItems = new JSONArray();
        for (RadioItem radioItem : radioItems) {
            jsonRadioItems.put(radioItem.toJSON());
        }
        return jsonRadioItems;
    }

    public static ArrayList<IMediaItemView> withOnlyBannersHeader() {
        ArrayList<IMediaItemView> items = new ArrayList<IMediaItemView>();
        items.add(new BannersLayoutItem());
        return items;
    }

    public static ArrayList<RadioItem> withJsonArray(JSONArray jsonRadioItems, Context mContext) {
        ArrayList<RadioItem> items = new ArrayList<RadioItem>();
        try {
            for (int i = 0; i < jsonRadioItems.length(); i++) {
                JSONObject jsonRadionItem = (JSONObject) jsonRadioItems.get(i);
                items.add(new RadioItem(jsonRadionItem));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return items;
    }

    public static void syncWithDb(ArrayList<RadioItem> radioItems) {
        ArrayList<MediaListItem> listItems = MediaListItem.withMediaType(MediaListItem.TYPE_RADIO);
        for (MediaListItem listItem : listItems) {
            MediaItem mediaItem = MediaItem.getMediaItemByName(radioItems, listItem.mediaItemName);
            if (mediaItem != null) {
                mediaItem.id = listItem.id;
                mediaItem.isExistsInDb = true;
                if (listItem.listType.equals(MediaListItem.SUBSCRIBED)) {
                    mediaItem.isSubscribed = true;
                } else if (listItem.listType.equals(MediaListItem.RECENT)) {
                    ((RadioItem) mediaItem).isRecent = true;
                }
            }
        }
    }

    public static RadioItem withCursor(Cursor cursor) {
        RadioItem radioItem = new RadioItem();
        radioItem.isExistsInDb = true;
        radioItem.id = cursor.getLong(cursor.getColumnIndex("id"));
        radioItem.name = cursor.getString(cursor.getColumnIndex("name"));
        radioItem.website = cursor.getString(cursor.getColumnIndex("website"));
        radioItem.facebook = cursor.getString(cursor.getColumnIndex("facebook"));
        radioItem.description = cursor.getString(cursor.getColumnIndex("description"));
        radioItem.twitter = cursor.getString(cursor.getColumnIndex("twitter"));
        radioItem.coverImageUrl = cursor.getString(cursor.getColumnIndex("cover_image_url"));
        radioItem.country = cursor.getString(cursor.getColumnIndex("country"));
        radioItem.genre = cursor.getString(cursor.getColumnIndex("genre"));
        radioItem.score = cursor.getFloat(cursor.getColumnIndex("score"));

        radioItem.streamUrls.addAll(StreamUrl.withRaioItemId(radioItem.id));

        return radioItem;
    }
} public abstract class Track extends SQLModel {

    protected String title;
    protected String audeoUrl;

    protected boolean isSelected;

    public Track() {
        this.title = "";
        this.audeoUrl = "";
        this.isSelected = false;
    }

    public abstract String getDuration();

    public abstract String getDate();

    public abstract String getTitle();

    public abstract String getAudeoUrl();

    public abstract String getSubTitle();

    public abstract String getInfo();

    public void setTitle(String title) {
        this.title = title;
    }

    public void setAudeoUrl(String audeoUrl) {
        this.audeoUrl = audeoUrl;
    }
} public class Analytics {


    //Sliding menu
    public static final String SLIDING_MENU_SETTINGS_CLICK = "sliding_menu_settings_click";
    public static final String SLIDING_MENU_PODCASTS_CLICK = "sliding_menu_podcasts_click";
    public static final String SLIDING_MENU_RADIO_CLICK = "sliding_menu_radio_click";
    public static final String SLIDING_MENU_PROFILE_CLICK = "sliding_profile_click";
    public static final String SLIDING_MENU_HELP_CLICK = "sliding_menu_help_click";
    public static final String SLIDING_MENU_RATE_CLICK = "sliding_menu_rate_click";
    public static final String SLIDING_MENU_ABOUT_CLICK = "sliding_about_click";

    //Main screen
    public static final String FEATURED_BUTTON_MORE = "featured_btn_more_click";

    //Middle screen
    public static final String MIDDLE_SCREEN_UNSUBSCRIBE = "middle_screen_subscribe";
    public static final String MIDDLE_SCREEN_SUBSCRIBE = "middle_screen_unsubscribe";
    public static final String MIDDLE_SCREEN_PLAY_RADIO = "middle_screen_play_radio";
    public static final String MIDDLE_SCREEN_PLAY_EPISODE = "middle_screen_play_episode";
    public static final String MIDDLE_SCREEN_DOWNLOAD_EPISODE = "middle_screen_download_episode";
    public static final String MIDDLE_SCREEN_CANCELL_DOWNLOAD_EPISODE = "middle_screen_cancel_download";
    public static final String MIDDLE_SCREEN_ABOUT_PODCAST = "middle_screen_about_podcast";
    public static final String MIDDLE_SCREEN_OPEN_ON_DISK = "middle_screen_open_disk";
    public static final String MIDDLE_REMOVE_EPISODE = "middle_screen_remove_episode";
    public static final String MIDDLE_REMOVE_ALL_EPISODES = "middle_screen_rempve_all";

    //Playlist
    public static final String PLAYLIST_TOGGLE = "playlist_toggle";
    public static final String PLAYLIST_ITEM_CLICK = "playlist_item_click";

    //Player
    public static final String PLAYER_STREAM_QUALITY = "player_stream_quality";
    public static final String PLAYER_STREAM_INFO = "player_stream_info";
    public static final String PLAYER_SUBSCRIBE = "player_subscribe";
    public static final String PLAYER_UNSUBSCRIBE = "player_unsubscribe";

    public static String PLAY_PREFIX = "play_";
} public class GlobalUtils {

    public static boolean safeTitleEquals(String title1, String title2) {
        if (title1 == null || title2 == null) {
            return false;
        }
        //replaceAll("[^a-z,A-Z,0-9,.,+,-, ,!,?,_,:,;,=]", "");
        title1 = title1.trim().replace("'","");
        title2 = title2.trim().replace("'","");;
        return title1.equals(title2);
    }

    public static String getCurrentDateTimeUS() {
        DateFormat df = new SimpleDateFormat("d MMM, HH:mm", Locale.US);
        String date = df.format(Calendar.getInstance().getTime());
        Logger.printInfo("Sync date", date);
        return date;
    }

    public static String parserDateToMonth(String date) {
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss Z", Locale.US);
            Date inputDate = dateFormat.parse(date);
            dateFormat = new SimpleDateFormat("MMM\ndd", Locale.US);
            date = dateFormat.format(inputDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    public static String getCleanFileName(String str) {
        str = str.toLowerCase();
        return str.replaceAll("[^a-zA-Z0-9]+", "_");
    }

    public static boolean deleteDirectory(File path) {
        if (path.exists()) {
            File[] files = path.listFiles();
            if (files == null) {
                return true;
            }
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    deleteDirectory(files[i]);
                } else {
                    files[i].delete();
                }
            }
        }
        return (path.delete());
    }

    public static boolean isInternetConnected() {
        ConnectivityManager cm = (ConnectivityManager) UpodsApplication.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm.getActiveNetworkInfo() != null;
    }

    /**
     * Returns a list with all links contained in the input
     */
    public static List<String> extractUrls(String text) {
        String lines[] = text.split("\\s+");
        List<String> containedUrls = new ArrayList<String>();
        String urlRegex = "((https?|ftp|gopher|telnet|file):((//)|(\\\\))+[\\w\\d:#@%/;$()~_?\\+-=\\\\\\.&]*)";
        Pattern pattern = Pattern.compile(urlRegex, Pattern.CASE_INSENSITIVE);

        for (String line : lines) {
            Matcher urlMatcher = pattern.matcher(line);
            while (urlMatcher.find()) {
                containedUrls.add(line.substring(urlMatcher.start(0), urlMatcher.end(0)));
            }
        }
        return containedUrls;
    }

    public static boolean isUrlReachable(String urlToCheck) {
        try {
            URL url = new URL(urlToCheck);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(1000);
            int code = connection.getResponseCode();
            if (code == 200) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static void rateApp(Context context) {
        Uri uri = Uri.parse("market://details?id=" + context.getPackageName());
        Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri);

        goToMarket.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
        try {
            context.startActivity(goToMarket);
        } catch (ActivityNotFoundException e) {
            context.startActivity(new Intent(Intent.ACTION_VIEW,
                    Uri.parse("http://play.google.com/store/apps/details?id=" + context.getPackageName())));
        }
    }

    public static boolean isPackageInstalled(String packagename) {
        PackageManager pm = UpodsApplication.getContext().getPackageManager();
        try {
            pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    public static String upperCase(String str) {
        StringBuilder rackingSystemSb = new StringBuilder(str.toLowerCase());
        rackingSystemSb.setCharAt(0, Character.toUpperCase(rackingSystemSb.charAt(0)));
        str = rackingSystemSb.toString();
        return str;
    }

} public class Logger {

    private static boolean isTurnedOn = false;

    public static void printInfo(String tag, String text) {
        if (isTurnedOn) {
            Log.i(tag, text);
        }
    }

    public static void printInfo(String tag, int text) {
        if (isTurnedOn) {
            Log.i(tag, String.valueOf(text));
        }
    }

    public static void printInfo(String tag, boolean text) {
        if (isTurnedOn) {
            Log.i(tag, String.valueOf(text));
        }
    }


    public static void printError(String tag, String text) {
        if (isTurnedOn) {
            Log.i(tag, text);
        }
    }
} public class MediaUtils {

    private static final String LOG_TAG = "MediaUtils";

    public static String extractMp3FromFile(String m3uUrl) throws IOException, JSONException {
        Request request = new Request.Builder().url(m3uUrl).build();
        String response = BackendManager.getInstance().sendSimpleSynchronicRequest(request);
        List<String> allURls = GlobalUtils.extractUrls(response);
        String mp3Url = allURls.size() > 0 ? allURls.get(0) : "";
        Logger.printInfo(LOG_TAG, "Extracted from file urls: " + allURls.toString());
        return mp3Url;
    }

    public static String formatMsToTimeString(int millis) {
        return String.format("%02d:%02d",
                MILLISECONDS.toMinutes(millis),
                MILLISECONDS.toSeconds(millis) -
                        TimeUnit.MINUTES.toSeconds(MILLISECONDS.toMinutes(millis))
        );
    }

    public static long timeStringToLong(String timeString) {
        if (timeString.matches("^[0-9]{3,5}$")) {//Time was given in seconds
            return Long.valueOf(timeString) * 1000;
        }
        if (timeString.matches("[0-9]{2}:[0-9]{2}")) {
            timeString = "00:" + timeString;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date date = null;
        long ms;
        try {
            date = sdf.parse("1970-01-01 " + timeString);
            ms = date.getTime();
        } catch (ParseException e) {
            ms = FragmentPlayer.DEFAULT_RADIO_DURATIO;
            e.printStackTrace();
        }
        return ms;
    }

    public static int calculateNextTrackNumber(Direction direction, int currentTrackNumber, int tracksSize) {
        int changeTo = 0;
        if (direction == Direction.LEFT) {
            changeTo = currentTrackNumber - 1;
            if (changeTo < 0) {
                changeTo = tracksSize - 1;
            }
        } else {
            changeTo = currentTrackNumber + 1;
            if (changeTo >= tracksSize) {
                changeTo = 0;
            }
        }
        return changeTo;
    }

    public static boolean isVideoUrl(String link) {
        return link.matches(".*\\.(mpeg|avi|mp4|3gp|webm|mkv|ogg|wma)$");
    }

    public static IOperationFinishCallback getPlayerFailCallback(final Activity activity, final MediaItem playableMediaItem) {
        return new IOperationFinishCallback() {
            @Override
            public void operationFinished() {
                UniversalPlayer.getInstance().releasePlayer();
                StringBuilder mesStringBuilder = new StringBuilder();
                mesStringBuilder.append(activity.getString(R.string.cant_play));
                mesStringBuilder.append(" ");
                mesStringBuilder.append(playableMediaItem instanceof Podcast
                        ? activity.getString(R.string.podcast_small) : activity.getString(R.string.radio_station));
                mesStringBuilder.append(":( ");
                mesStringBuilder.append(activity.getString(R.string.please_try_later));
                DialogFragmentMessage dialogFragmentMessage = new DialogFragmentMessage();
                dialogFragmentMessage.setMessage(mesStringBuilder.toString());
                dialogFragmentMessage.setTitle(activity.getString(R.string.oops));
                dialogFragmentMessage.setOnOkClicked(new IOperationFinishCallback() {
                    @Override
                    public void operationFinished() {
                        activity.onBackPressed();
                        UniversalPlayer.getInstance().releasePlayer();
                    }
                });
                ((IFragmentsManager) activity).showDialogFragment(dialogFragmentMessage);
            }
        };
    }

} public enum Direction {

    LEFT, RIGHT
} public class DefaultNotificationPanel extends PlayerNotificationPanel {
    private static final int COVER_IMAGE_SIZE = UIHelper.dpToPixels(50);
    private static int NOTIFICATION_ID = 12;
    private PendingIntent playIntent;
    private PendingIntent pauseIntent;
    private PendingIntent forwardIntent;
    private PendingIntent backwardIntent;

    public DefaultNotificationPanel(Context mContext, MediaItem playableMediaItem) {
        super(mContext, playableMediaItem);
        this.currentState = UniversalPlayer.State.PLAYING;
        this.nBuilder = new NotificationCompat.Builder(mContext);
        this.nBuilder.setContentTitle(playableMediaItem.getName());
        this.nBuilder.setContentInfo(playableMediaItem.getName());
        this.nBuilder.setContentText(playableMediaItem.getName());
        this.nBuilder.setSmallIcon(R.drawable.ic_pause_white);
        this.nBuilder.setAutoCancel(false);
        this.nBuilder.setOngoing(true);

        Intent intentOpen = new Intent(mContext, ActivityPlayer.class);
        PendingIntent piOpen = PendingIntent.getActivity(mContext, 0, intentOpen, 0);

        this.nBuilder.setContentIntent(piOpen);
        this.nBuilder.setOngoing(false);

        this.remoteView = new RemoteViews(mContext.getPackageName(), R.layout.player_notification);
        this.remoteView.setTextViewText(R.id.tvPlayerTitleNtBar, playableMediaItem.getName());
        this.remoteView.setTextViewText(R.id.tvPlayerSubTitleNtBar, playableMediaItem.getSubHeader());
        setListeners();

        this.nBuilder.setContent(remoteView);
        Notification notification = nBuilder.build();
        this.nManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        this.nManager.notify(NOTIFICATION_ID, notification);

        if (playableMediaItem.getCoverImageUrl() != null) {
            Glide.with(mContext)
                    .load(playableMediaItem.getCoverImageUrl())
                    .asBitmap()
                    .into(new NotificationTarget(
                            mContext,
                            remoteView,
                            R.id.imgPlayerNtBar,
                            notification,
                            NOTIFICATION_ID));
        } else {
            final LetterBitmap letterBitmap = new LetterBitmap(mContext);
            Bitmap letterTile = letterBitmap.getLetterTile(playableMediaItem.getName(), playableMediaItem.getName(), COVER_IMAGE_SIZE, COVER_IMAGE_SIZE);
            remoteView.setImageViewBitmap(R.id.imgPlayerNtBar, letterTile);
            this.nManager.notify(NOTIFICATION_ID, notification);
        }
    }

    @Override
    protected void setListeners() {
        Intent plIntent = new Intent(mContext, MainBroadcastRecivier.class);
        plIntent.setAction(UniversalPlayer.INTENT_ACTION_PLAY);
        playIntent = PendingIntent.getBroadcast(mContext, 0, plIntent, 0);

        Intent pIntent = new Intent(mContext, MainBroadcastRecivier.class);
        pIntent.setAction(UniversalPlayer.INTENT_ACTION_PAUSE);
        pauseIntent = PendingIntent.getBroadcast(mContext, 0, pIntent, 0);

        Intent fIntent = new Intent(mContext, MainBroadcastRecivier.class);
        fIntent.setAction(UniversalPlayer.INTENT_ACTION_FORWARD);
        forwardIntent = PendingIntent.getBroadcast(mContext, 0, fIntent, 0);

        Intent bIntent = new Intent(mContext, MainBroadcastRecivier.class);
        bIntent.setAction(UniversalPlayer.INTENT_ACTION_BACKWARD);
        backwardIntent = PendingIntent.getBroadcast(mContext, 0, bIntent, 0);

        remoteView.setOnClickPendingIntent(R.id.btnPlayNtBar, pauseIntent);
        remoteView.setOnClickPendingIntent(R.id.btnRewindLeftNtBar, backwardIntent);
        remoteView.setOnClickPendingIntent(R.id.btnRewindRightNtBar, forwardIntent);
        super.setListeners();
    }

    @Override
    public void updateNotificationStatus(UniversalPlayer.State state) {
        if (state == UniversalPlayer.State.PLAYING) {
            remoteView.setImageViewResource(R.id.btnPlayNtBar, R.drawable.ic_pause_white);
            nBuilder.setOngoing(true);
        } else {
            remoteView.setImageViewResource(R.id.btnPlayNtBar, R.drawable.ic_play_white);
            nBuilder.setOngoing(false);
        }
        remoteView.setOnClickPendingIntent(R.id.btnPlayNtBar, state == UniversalPlayer.State.PLAYING ? pauseIntent : playIntent);
        remoteView.setOnClickPendingIntent(R.id.btnRewindLeftNtBar, backwardIntent);
        remoteView.setOnClickPendingIntent(R.id.btnRewindRightNtBar, forwardIntent);
        nBuilder.setSmallIcon(state == UniversalPlayer.State.PLAYING ? R.drawable.ic_pause_white : R.drawable.ic_play_white_24dp);
        nManager.notify(NOTIFICATION_ID, nBuilder.build());
        super.updateNotificationStatus(state);
    }
} public abstract class PlayerNotificationPanel {


    protected static int NOTIFICATION_ID;

    protected Context mContext;
    protected NotificationManager nManager;
    protected NotificationCompat.Builder nBuilder;
    protected RemoteViews remoteView;
    protected MediaItem mediaItem;

    protected UniversalPlayer.State currentState;

    public PlayerNotificationPanel(Context mContext, MediaItem mediaItem) {
        this.mContext = mContext;
        this.mediaItem = mediaItem;
    }

    protected void setListeners() {

    }

    public void notificationCancel() {
        nManager.cancel(NOTIFICATION_ID);
    }

    public void updateNotificationStatus(UniversalPlayer.State state) {
        currentState = state;
    }

    public UniversalPlayer.State getCurrentState() {
        return currentState;
    }
}
import android.app.NotificationManager;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.webkit.URLUtil;
import com.chickenkiller.upods2.controllers.app.ProfileManager;
import com.chickenkiller.upods2.controllers.app.UpodsApplication;
import com.chickenkiller.upods2.interfaces.IOperationFinishCallback;
import com.chickenkiller.upods2.interfaces.IPlayerStateListener;
import com.chickenkiller.upods2.models.MediaItem;
import com.chickenkiller.upods2.models.Podcast;
import com.chickenkiller.upods2.models.RadioItem;
import com.chickenkiller.upods2.models.Track;
import com.chickenkiller.upods2.utils.Analytics;
import com.chickenkiller.upods2.utils.GlobalUtils;
import com.chickenkiller.upods2.utils.Logger;
import com.chickenkiller.upods2.utils.MediaUtils;
import com.chickenkiller.upods2.utils.enums.Direction;
import com.chickenkiller.upods2.views.DefaultNotificationPanel;
import com.chickenkiller.upods2.views.PlayerNotificationPanel;
import com.yandex.metrica.YandexMetrica;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.Media;
import org.videolan.libvlc.MediaPlayer;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
package com.chickenkiller.upods2.controllers.player;





/**
 * Created by Alon Zilberman on 7/29/15.
 */
public class UniversalPlayer implements MediaPlayer.EventListener {


    private static final long RECONNECT_RATE = 5000;

    public enum State {PLAYING, PAUSED, END_REACHED}

    public static final String INTENT_ACTION_PLAY = "com.chickenkiller.upods2.player.PLAY";
    public static final String INTENT_ACTION_PAUSE = "com.chickenkiller.upods2.player.PAUSE";
    public static final String INTENT_ACTION_FORWARD = "com.chickenkiller.upods2.player.FORWARD";
    public static final String INTENT_ACTION_BACKWARD = "com.chickenkiller.upods2.player.BACKWARD";
    private static final String PLAYER_LOG = "UniversalPlayer";

    private static UniversalPlayer universalPlayer;

    private LibVLC mLibVLC = null;
    private MediaPlayer mediaPlayer = null;

    private IOperationFinishCallback onAutonomicTrackChangeCallback;
    private IOperationFinishCallback onPlayingFailedCallback;
    private IPlayerStateListener playerStateListener;

    private MediaItem mediaItem;
    private PlayerNotificationPanel notificationPanel;

    private TimerTask autoReconector;

    public boolean isLinkReadyForPlaying;
    public boolean isPrepaired;
    public boolean isInErrorState;

    private UniversalPlayer() {
    }

    public static synchronized UniversalPlayer getInstance() {
        if (universalPlayer == null) {
            universalPlayer = new UniversalPlayer();
        }
        return universalPlayer;
    }

    /**
     * Checks if given type of media item is supported, if yes copies it and save instance in player.
     *
     * @param mediaItem
     */
    public void setMediaItem(MediaItem mediaItem) {
        if (isCurrentMediaItem(mediaItem)) {
            return;
        }
        YandexMetrica.reportEvent(Analytics.PLAY_PREFIX + mediaItem.getName());
        if (mediaItem instanceof RadioItem) {
            this.mediaItem = new RadioItem((RadioItem) mediaItem);
        } else if (mediaItem instanceof Podcast) {
            this.mediaItem = new Podcast((Podcast) mediaItem);
        } else {
            throw new RuntimeException("Unsupported type of MediaItem");
        }
    }

    /**
     * Checks if given type of media item is supported, if yes copies it and save instance in player.
     *
     * @param needReset - if true will reset the player before changing the mediaItem
     * @param mediaItem
     */
    public void setMediaItem(MediaItem mediaItem, boolean needReset) {
        if (needReset && !isCurrentMediaItem(mediaItem)) {
            resetPlayer();
        }
        setMediaItem(mediaItem);
    }

    public void setOnPlayingFailedCallback(IOperationFinishCallback onPlayingFailedCallback) {
        this.onPlayingFailedCallback = onPlayingFailedCallback;
    }

    public void setPlayerStateListener(IPlayerStateListener playerStateListener) {
        this.playerStateListener = playerStateListener;
    }

    public void setOnAutonomicTrackChangeCallback(IOperationFinishCallback onAutonomicTrackChangeCallback) {
        this.onAutonomicTrackChangeCallback = onAutonomicTrackChangeCallback;
    }

    /**
     * Main prepare method. Prepares media item to be played.
     */
    public void prepare() {
        if (mediaItem == null) {
            throw new RuntimeException("MediaItem is not set. Call setMediaItem before prepare.");
        }

        try {
            isInErrorState = false;

            if (!isLinkReadyForPlaying && !prepareLinkForPlaying()) {
                return;
            }

            String audeoLink = mediaItem.getAudeoLink();

            if (audeoLink.isEmpty()) {
                throw new IllegalArgumentException("Link for playing is empty");
            }

            Logger.printInfo(PLAYER_LOG, "Trying to play: " + audeoLink);