public class ActivityPlayer extends BasicActivity implements IToolbarHolder, Toolbar.OnMenuItemClickListener {

    public static final String ACTIVITY_STARTED_FROM = "startedFrom";
    public static final String ACTIVITY_STARTED_FROM_IN_DEPTH = "startedFromDepth"; //used if activity was started from depth fragment i.e search

    private MediaItem currentMediaItem;
    private Toolbar toolbar;
    private ActionMenuItemView itemFavorites;
    private FragmentPlayer fragmentPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);

        toolbar = (Toolbar) findViewById(R.id.toolbar_player);
        toolbar.setTitleTextColor(getResources().getColor(R.color.white));
        toolbar.inflateMenu(R.menu.menu_activity_player);
        toolbar.setOnMenuItemClickListener(this);
        toolbar.setVisibility(View.VISIBLE);
        itemFavorites = (ActionMenuItemView) toolbar.findViewById(R.id.action_favorites_player);

        currentMediaItem = UniversalPlayer.getInstance().getPlayingMediaItem();
        if (currentMediaItem == null) {
            Toast.makeText(this, getString(R.string.error_cant_play), Toast.LENGTH_SHORT).show();
            onBackPressed();
            return;
        }

        if (getFragmentManager().getBackStackEntryCount() == 0) {
            if (currentMediaItem instanceof Podcast && MediaUtils.isVideoUrl(currentMediaItem.getAudeoLink())) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                FragmentVideoPlayer fragmentVideoPlayer = new FragmentVideoPlayer();
                fragmentVideoPlayer.setOnPlayingFailedCallback(MediaUtils.getPlayerFailCallback(this, currentMediaItem));
                showFragment(R.id.fl_window, fragmentVideoPlayer, FragmentVideoPlayer.TAG);
            } else {
                fragmentPlayer = new FragmentPlayer();
                fragmentPlayer.setPlayableItem(currentMediaItem);
                showFragment(R.id.fl_window, fragmentPlayer, FragmentMainFeatured.TAG);
            }
        }
    }

    @Override
    public void onBackPressed() {
        if (getFragmentManager().getBackStackEntryCount() > 1 || getLatestFragmentTag().equals(FragmentPlayer.TAG)) {
            getFragmentManager().popBackStack();
        } else {
            getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
            Intent myIntent = new Intent(this, ActivityMain.class);
            if (getIntent().hasExtra(ACTIVITY_STARTED_FROM)) {
                myIntent.putExtra(ACTIVITY_STARTED_FROM, getIntent().getIntExtra(ACTIVITY_STARTED_FROM, -1));
                myIntent.putExtra(ACTIVITY_STARTED_FROM_IN_DEPTH, getIntent().getIntExtra(ACTIVITY_STARTED_FROM_IN_DEPTH, -1));
            }
            startActivity(myIntent);
            overridePendingTransition(R.anim.slide_in_top, R.anim.slide_out_top);
            finish();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public Toolbar getToolbar() {
        return toolbar;
    }


    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        if (contextMenuType == ContextMenuType.PLAYER_SETTINGS) {
            inflater.inflate(R.menu.menu_basic_sceleton, menu);
            if (currentMediaItem instanceof RadioItem) {
                menu.add(getString(R.string.select_stream_quality));
                menu.add(getString(R.string.stream_info));
            } else {
                menu.add(getString(R.string.show_notes));
            }
        }
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        MediaItem mediaItem = UniversalPlayer.getInstance().getPlayingMediaItem();
        if (item.getItemId() == R.id.action_favorites_player) {
            if (mediaItem.isSubscribed) {
                itemFavorites.setIcon(getResources().getDrawable(R.drawable.ic_heart_white_24dp));
                ProfileManager.getInstance().removeSubscribedMediaItem(mediaItem);
                Toast.makeText(this, getString(R.string.removed_from_favorites), Toast.LENGTH_SHORT).show();
                YandexMetrica.reportEvent(Analytics.PLAYER_UNSUBSCRIBE);
            } else {
                itemFavorites.setIcon(getResources().getDrawable(R.drawable.ic_heart_black_24dp));
                ProfileManager.getInstance().addSubscribedMediaItem(mediaItem);
                Toast.makeText(this, getString(R.string.added_to_favorites), Toast.LENGTH_SHORT).show();
                YandexMetrica.reportEvent(Analytics.PLAYER_SUBSCRIBE);
            }
        } else if (item.getItemId() == R.id.action_player_settings) {
            openContextMenu(findViewById(R.id.action_player_settings), ContextMenuType.PLAYER_SETTINGS, null, null);
        }
        return false;
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        if (item.getTitle().equals(getString(R.string.select_stream_quality))) {
            YandexMetrica.reportEvent(Analytics.PLAYER_STREAM_QUALITY);
            ContextMenuHelper.selectRadioStreamQuality(this, fragmentPlayer, (RadioItem) currentMediaItem);
        } else if (item.getTitle().equals("Show notes")) {
            DialogFragmentTrackInfo dialogFragmentTrackInfo = new DialogFragmentTrackInfo();
            dialogFragmentTrackInfo.setTrack(((Podcast) UniversalPlayer.getInstance().getPlayingMediaItem()).getSelectedTrack());
            dialogFragmentTrackInfo.enableStream = false;
            showDialogFragment(dialogFragmentTrackInfo);
        } else if (item.getTitle().equals(getString(R.string.stream_info))) {
            ContextMenuHelper.showStreamInfoDialog(this);
            YandexMetrica.reportEvent(Analytics.PLAYER_STREAM_INFO);
        }
        return super.onContextItemSelected(item);
    }

    public static void openWithIntent(Activity activity) {
        //Detect if starting from search -> if yes from which type
        if (FragmentSearch.isActive) {
            if (ActivityMain.lastFragmentType == MediaItemType.PODCAST.ordinal()) {
                ActivityMain.lastChildFragmentType = MediaItemType.PODCAST_SEARCH.ordinal();
            } else if (ActivityMain.lastFragmentType == MediaItemType.RADIO.ordinal()) {
                ActivityMain.lastChildFragmentType = MediaItemType.RADIO_SEARCH.ordinal();
            }
        }

        Intent myIntent = new Intent(activity, ActivityPlayer.class);
        myIntent.putExtra(ActivityPlayer.ACTIVITY_STARTED_FROM, ActivityMain.lastFragmentType);
        myIntent.putExtra(ActivityPlayer.ACTIVITY_STARTED_FROM_IN_DEPTH, ActivityMain.lastChildFragmentType);
        activity.startActivity(myIntent);
        activity.overridePendingTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom);
        activity.finish();
        ActivityMain.lastChildFragmentType = -1;
        ActivityMain.lastChildFragmentType = -1;
    }
} public class MediaItemsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public static final int HEADER = 1;
    public static final int ITEM = 2;
    public static final int BANNERS_LAYOUT = 3;
    public static final int ROUNDED_BUTTONS = 4;
    private static final int MAX_CONTENT_LEVEL = 2; //count of items to load (banner, main cards)
    private static final int COVER_IMAGE_SIZE = UIHelper.dpToPixels(80);

    private int itemLayout;
    private int titleLayout;
    private int roundedButtonsLayout;
    private int currentContentLevel;

    private List<IMediaItemView> items;
    private Context mContext;
    private IFragmentsManager fragmentsManager;
    private IContentLoadListener iContentLoadListener;
    private IOperationFinishWithDataCallback iRoundButtonClicked;
    private MediaItemType mediaItemType;

    private static class ViewHolderCardItem extends RecyclerView.ViewHolder {
        public ImageViewSquare imgSquare;
        public TextView tvSquareTitle;
        public TextView tvItemStatus;
        public TextView tvItemCount;
        public TextView tvSquareSubTitle;
        public RatingBar rbMediaItem;
        public CardView cvSquare;

        public ViewHolderCardItem(View view) {
            super(view);
            this.imgSquare = (ImageViewSquare) view.findViewById(R.id.imgSquare);
            this.tvItemStatus = (TextView) view.findViewById(R.id.tvItemStatus);
            this.tvSquareTitle = (TextView) view.findViewById(R.id.tvSquareTitle);
            this.tvSquareSubTitle = (TextView) view.findViewById(R.id.tvSquareSubTitle);
            this.tvItemCount = (TextView) view.findViewById(R.id.tvItemCount);
            this.rbMediaItem = (RatingBar) view.findViewById(R.id.rbMediaItem);
            this.cvSquare = (CardView) view;
            Context context = view.getContext();
            LayerDrawable stars = (LayerDrawable) rbMediaItem.getProgressDrawable();
            stars.getDrawable(2).setColorFilter(context.getResources().getColor(R.color.starFullySelected), PorterDuff.Mode.SRC_ATOP);
            stars.getDrawable(1).setColorFilter(context.getResources().getColor(R.color.starPartiallySelected), PorterDuff.Mode.SRC_ATOP);
            stars.getDrawable(0).setColorFilter(context.getResources().getColor(R.color.starNotSelected), PorterDuff.Mode.SRC_ATOP);
        }

        public void setCardClickListener(View.OnClickListener cardClickListener) {
            cvSquare.setOnClickListener(cardClickListener);
        }

    }

    private static class ViewHolderMediaItemTitle extends RecyclerView.ViewHolder {
        public TextView tvMediaCardTitle;
        public TextView tvMediaCardSubTitle;
        public Button btnMediaTitleMore;

        public ViewHolderMediaItemTitle(View view) {
            super(view);
            this.tvMediaCardTitle = (TextView) view.findViewById(R.id.tvMediaCardTitle);
            this.tvMediaCardSubTitle = (TextView) view.findViewById(R.id.tvMediaCardSubTitle);
            this.btnMediaTitleMore = (Button) view.findViewById(R.id.btnMediaTitleMore);
        }

    }

    private static class ViewHolderRoundedButtons extends RecyclerView.ViewHolder {

        public ViewHolderRoundedButtons(View view, final IOperationFinishWithDataCallback iRoundButtonClicked) {
            super(view);
            if (iRoundButtonClicked != null) {
                view.findViewById(R.id.btnRoundedGenres).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        iRoundButtonClicked.operationFinished(RoundedButtonsLayoutItem.ROUND_BTN_GENRES);
                    }
                });
                view.findViewById(R.id.btnRoundedCountries).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        iRoundButtonClicked.operationFinished(RoundedButtonsLayoutItem.ROUND_BTN_COUNTRIES);
                    }
                });

                view.findViewById(R.id.btnRoundedLanguages).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        iRoundButtonClicked.operationFinished(RoundedButtonsLayoutItem.ROUND_BTN_LAGUAGES);
                    }
                });
            }
        }

    }

    public MediaItemsAdapter(Context mContext, int itemLayout, List<IMediaItemView> items) {
        super();
        this.items = items;
        this.itemLayout = itemLayout;
        this.mContext = mContext;
        this.mediaItemType = MediaItemType.DEFAULT;
    }

    public MediaItemsAdapter(Context mContext, int itemLayout, int titleLayout, ArrayList<IMediaItemView> items) {
        this(mContext, itemLayout, items);
        this.titleLayout = titleLayout;
    }

    public MediaItemsAdapter(Context mContext, int itemLayout, int titleLayout) {
        this(mContext, itemLayout, new ArrayList<IMediaItemView>());
        this.titleLayout = titleLayout;
    }

    public void setRoundedButtonsLayout(int roundedButtonsLayout) {
        this.roundedButtonsLayout = roundedButtonsLayout;
    }

    public void setFragmentsManager(IFragmentsManager fragmentsManager) {
        this.fragmentsManager = fragmentsManager;
    }

    public void setiRoundButtonClicked(IOperationFinishWithDataCallback iRoundButtonClicked) {
        this.iRoundButtonClicked = iRoundButtonClicked;
    }

    public void setMediaItemType(MediaItemType mediaItemType) {
        this.mediaItemType = mediaItemType;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = null;
        RecyclerView.ViewHolder viewHolder = null;
        if (viewType == BANNERS_LAYOUT) {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_banners_layout, parent, false);
            IContentLoadListener contentLoadListener = new IContentLoadListener() {
                @Override
                public void onContentLoaded() {
                    notifyContentLoadingStatus();
                }
            };
            viewHolder = new ViewHolderBannersLayout(view, fragmentsManager, contentLoadListener);
            currentContentLevel = 0;
        } else if (viewType == ITEM) {
            view = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
            viewHolder = new ViewHolderCardItem(view);
        } else if (viewType == ROUNDED_BUTTONS) {
            view = LayoutInflater.from(parent.getContext()).inflate(roundedButtonsLayout, parent, false);
            viewHolder = new ViewHolderRoundedButtons(view, iRoundButtonClicked);
        } else {
            view = LayoutInflater.from(parent.getContext()).inflate(titleLayout, parent, false);
            viewHolder = new ViewHolderMediaItemTitle(view);
        }
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof ViewHolderCardItem) {//Card
            bindCardViewHolder(holder, position);
        } else if (holder instanceof ViewHolderMediaItemTitle) {//Title
            bindTitleViewHolder(holder, position);
        }
    }

    private void bindTitleViewHolder(RecyclerView.ViewHolder holder, int position) {
        MediaItemTitle currentItem = (MediaItemTitle) items.get(position);
        ViewHolderMediaItemTitle holderMediaItemTitle = ((ViewHolderMediaItemTitle) holder);

        holderMediaItemTitle.tvMediaCardTitle.setText(currentItem.getTitle());
        if (currentItem.getSubTitle().isEmpty()) {
            holderMediaItemTitle.tvMediaCardSubTitle.setVisibility(View.GONE);
            RelativeLayout.LayoutParams llp = (RelativeLayout.LayoutParams) holderMediaItemTitle.tvMediaCardTitle.getLayoutParams();
            // Left // Top // Right // Bottom
            llp.setMargins(UIHelper.dpToPixels(15), UIHelper.dpToPixels(10), UIHelper.dpToPixels(15), UIHelper.dpToPixels(3));
            holderMediaItemTitle.tvMediaCardTitle.setLayoutParams(llp);
        } else {
            holderMediaItemTitle.tvMediaCardSubTitle.setVisibility(View.VISIBLE);
            holderMediaItemTitle.tvMediaCardSubTitle.setText(currentItem.getSubTitle());
        }

        if (!currentItem.showButton) {
            holderMediaItemTitle.btnMediaTitleMore.setVisibility(View.GONE);
        } else {
            holderMediaItemTitle.btnMediaTitleMore.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    YandexMetrica.reportEvent(Analytics.FEATURED_BUTTON_MORE);
                    Toast.makeText(mContext, R.string.coming_soon, Toast.LENGTH_SHORT).show();
                }
            });
        }
        holder.itemView.setTag(currentItem);
    }

    private void bindCardViewHolder(RecyclerView.ViewHolder holder, int position) {
        MediaItem currentItem = (MediaItem) items.get(position);
        ViewHolderCardItem viewHolderCardItem = (ViewHolderCardItem) holder;

        //Cover
        if (currentItem.getCoverImageUrl() == null) {
            final LetterBitmap letterBitmap = new LetterBitmap(mContext);
            Bitmap letterTile = letterBitmap.getLetterTile(currentItem.getName(), currentItem.getName(), COVER_IMAGE_SIZE, COVER_IMAGE_SIZE);
            viewHolderCardItem.imgSquare.setImageBitmap(letterTile);
        } else {
            Glide.with(mContext).load(currentItem.getCoverImageUrl()).centerCrop()
                    .crossFade().into(viewHolderCardItem.imgSquare);
        }

        viewHolderCardItem.tvSquareTitle.setText(currentItem.getName());
        if (viewHolderCardItem.tvSquareSubTitle != null) {
            viewHolderCardItem.tvSquareSubTitle.setText(currentItem.getSubHeader());
        }
        if (viewHolderCardItem.tvItemStatus != null) {
            initStatusBlock(viewHolderCardItem, currentItem);
        }
        if (viewHolderCardItem.tvItemCount != null) {
            initCountBlock(viewHolderCardItem, currentItem);
        }
        if (viewHolderCardItem.rbMediaItem != null) {
            viewHolderCardItem.rbMediaItem.setRating(currentItem.getScore());
        }

        holder.itemView.setTag(currentItem);
        viewHolderCardItem.setCardClickListener(getCardClickListener(position));
    }

    /**
     * Inits status block (icon + text, i.e downloaded)
     */
    private void initStatusBlock(ViewHolderCardItem holder, MediaItem currentItem) {
        if (mediaItemType == MediaItemType.PODCAST_DOWNLOADED && ((Podcast) currentItem).isDownloaded) {
            holder.tvItemStatus.setText(R.string.downloaded);
            holder.tvItemStatus.setVisibility(View.VISIBLE);

        } else {
            holder.tvItemStatus.setVisibility(View.GONE);
        }
    }

    /**
     * Inits cpunt block (number in circle, i.e new episodes)
     */
    private void initCountBlock(ViewHolderCardItem holder, MediaItem currentItem) {
        if (mediaItemType == MediaItemType.PODCAST_FAVORITE && currentItem instanceof Podcast &&
                ((Podcast) currentItem).getNewEpisodsCount() > 0) {//only for podcasts in favorites
            long newEpisodesCount = ((Podcast) currentItem).getNewEpisodsCount();
            holder.tvItemCount.setText(String.valueOf(newEpisodesCount));
            holder.tvItemCount.setVisibility(View.VISIBLE);
        } else {
            holder.tvItemCount.setVisibility(View.GONE);
        }
    }

    private View.OnClickListener getCardClickListener(final int position) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                FragmentMediaItemDetails fragmentMediaItemDetails = new FragmentMediaItemDetails();
                if (items.get(position) instanceof MediaItem && !fragmentsManager.hasFragment(FragmentMediaItemDetails.TAG)) {
                    InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Activity.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
                    fragmentMediaItemDetails.setPlayableItem((MediaItem) items.get(position));
                    fragmentMediaItemDetails.setMediaItemType(mediaItemType);
                    fragmentsManager.showFragment(R.id.fl_window, fragmentMediaItemDetails, FragmentMediaItemDetails.TAG,
                            IFragmentsManager.FragmentOpenType.OVERLAY, IFragmentsManager.FragmentAnimationType.BOTTOM_TOP);
                }
            }
        };
    }

    private View.OnClickListener getCardMenuClickListener(final int position) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (items.get(position) instanceof MediaItem && mContext instanceof IContextMenuManager) {
                    ((IContextMenuManager) mContext).openContextMenu(view, ContextMenuType.CARD_DEFAULT, items.get(position), null);
                }
            }
        };
    }

    @Override
    public int getItemViewType(int position) {
        if (items.get(position) instanceof BannersLayoutItem)
            return BANNERS_LAYOUT;
        if (items.get(position) instanceof MediaItemTitle)
            return HEADER;
        if (items.get(position) instanceof RoundedButtonsLayoutItem)
            return ROUNDED_BUTTONS;
        return ITEM;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public void addItems(ArrayList<IMediaItemView> items) {
        this.items.addAll(items);
        this.notifyDataSetChanged();
    }

    public void clearCurrentContentLevel() {
        currentContentLevel = 0;
    }

    public void clearItems() {
        this.items.clear();
    }

    public IMediaItemView getItemAt(int position) {
        return this.items.get(position);
    }

    public boolean removeMediaItem(MediaItem removedMediaItem) {
        int i = 0;
        for (IMediaItemView mediaItemView : items) {
            if (mediaItemView instanceof MediaItem
                    && GlobalUtils.safeTitleEquals(((MediaItem) mediaItemView).getName(), removedMediaItem.getName())) {
                items.remove(i);
                return true;
            }
            i++;
        }
        return false;
    }

    public void setContentLoadListener(IContentLoadListener contentLoadListener) {
        this.iContentLoadListener = contentLoadListener;
    }

    public void addItem(MediaItem mediaItem) {
        items.add(1, mediaItem); // Because 0 is title
    }

    /**
     * Function to synchronize adapter content loading . Call it every time, when part of content was loaded.
     * When all content will be loaded  IContentLoadListener callback will fire.
     */
    public void notifyContentLoadingStatus() {
        currentContentLevel++;
        if (currentContentLevel == MAX_CONTENT_LEVEL && iContentLoadListener != null) {
            iContentLoadListener.onContentLoaded();
        }
    }

    public boolean containsMediaItem(MediaItem mediaItem) {
        for (IMediaItemView mediaItemView : items) {
            if (mediaItemView instanceof MediaItem && GlobalUtils.safeTitleEquals(mediaItem.getName(), ((MediaItem) mediaItemView).getName())) {
                return true;
            }
        }
        return false;
    }

    public void updateMediaItem(MediaItem updatedMediaItem) {
        /*for (IMediaItemView mediaItemView : items) {
            if (mediaItemView instanceof MediaItem
                    && GlobalUtils.safeTitleEquals(((MediaItem) mediaItemView).getName(), updatedMediaItem.getName())) {
                ((MediaItem) mediaItemView).syncWithMediaItem(updatedMediaItem);
            }
        }*/

        //There is nothing to update here.
    }
} 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 SettingsManager {

    private static final String TAG = "SettingsManager";
    private static final String JS_SETTINGS = "settings";


    private static final int DEFAULT_PODCAST_UPDATE_TIME = 24; //hours
    private static final boolean DEFAULT_NOTIFY_EPISODS = true;
    private static final String DEFAULT_START_SCREEN = "rs_featured";

    private static SettingsManager settingsManager;

    public static final String TOPS_RU_LANGUAGE = "ru";
    public static final String TOPS_ENG_LANGUAGE = "en";

    public static final String PREFS_LAST_CLOUD_SYNC = "last_sync";

    public static final String JS_PODCASTS_UPDATE_TIME = "podcasts_update_time";
    public static final String JS_START_SCREEN = "start_screen";
    public static final String JS_NOTIFY_EPISODES = "notify_episods";
    public static final String JS_STREAM_QUALITY = "stream_quality";
    public static final String JS_TOPS_LANGUAGE = "tops_lng";

    public static final String DEFAULT_STREAM_QUALITY = "high";

    private SettingsManager() {
        super();
    }

    public static synchronized SettingsManager getInstace() {
        if (settingsManager == null) {
            settingsManager = new SettingsManager();
        }
        return settingsManager;
    }

    public void init() {
        readSettings(); //Call it here in order to create empty settings if not exist
    }

    public void readSettings(JSONObject jsonObject) {
        Prefs.putString(JS_SETTINGS, jsonObject.toString());
        readSettings();
    }

    private JSONObject readSettings() {
        try {
            if (Prefs.getString(JS_SETTINGS, null) == null) {
                JSONObject settingsObject = new JSONObject();
                settingsObject.put(JS_START_SCREEN, DEFAULT_START_SCREEN);
                settingsObject.put(JS_NOTIFY_EPISODES, DEFAULT_NOTIFY_EPISODS);
                settingsObject.put(JS_PODCASTS_UPDATE_TIME, DEFAULT_PODCAST_UPDATE_TIME);
                settingsObject.put(JS_STREAM_QUALITY, DEFAULT_STREAM_QUALITY);
                settingsObject.put(JS_TOPS_LANGUAGE, Locale.getDefault().getLanguage());

                Prefs.putString(JS_TOPS_LANGUAGE, Locale.getDefault().getLanguage());
                Prefs.putString(JS_SETTINGS, settingsObject.toString());
            }
            return new JSONObject(Prefs.getString(JS_SETTINGS, null));
        } catch (JSONException e) {
            Logger.printInfo(TAG, "Can't read settings");
            e.printStackTrace();
        }
        return null;
    }

    public int getIntSettingsValue(String key) {
        try {
            return readSettings().getInt(key);
        } catch (JSONException e) {
            Logger.printInfo(TAG, "Can't read value with key: " + key + " from json settings");
            e.printStackTrace();
            return -1;
        }
    }

    public boolean getBooleanSettingsValue(String key) {
        try {
            return readSettings().getBoolean(key);
        } catch (JSONException e) {
            Logger.printInfo(TAG, "Can't read value with key: " + key + " from json settings");
            e.printStackTrace();
            return false;
        }
    }

    public String getStringSettingValue(String key) {
        try {
            return readSettings().getString(key);
        } catch (JSONException e) {
            Logger.printInfo(TAG, "Can't read value with key: " + key + " from json settings");
            e.printStackTrace();
            return "";
        }
    }

    public String getPareSettingValue(String settingsKey, String pareKey) {
        try {
            JSONObject rootObject = readSettings();
            if (rootObject.has(settingsKey)) {
                JSONArray streamsArray = readSettings().getJSONArray(settingsKey);
                for (int i = 0; i < streamsArray.length(); i++) {
                    if (streamsArray.getJSONObject(i).has(pareKey)) {
                        return streamsArray.getJSONObject(i).getString(pareKey);
                    }
                }
            }
            return "";
        } catch (JSONException e) {
            Logger.printInfo(TAG, "Can't read value with key: " + settingsKey + " from json settings");
            e.printStackTrace();
            return "";
        }
    }


    public void saveStreamQualitySelection(MediaItem mediaItem, String quality) {
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("media_item_name", mediaItem.getName());
        values.put("quality", quality);
        database.replace("streams_quality", null, values);
    }

    public String getStreamQuality(MediaItem mediaItem) {
        String streamQuality = null;
        SQLiteDatabase database = UpodsApplication.getDatabaseManager().getReadableDatabase();
        String args[] = {mediaItem.getName()};
        Cursor cursor = database.rawQuery("SELECT quality FROM streams_quality WHERE media_item_name = ?", args);
        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            streamQuality = cursor.getString(cursor.getColumnIndex("quality"));
        }
        return streamQuality;
    }

    /**
     * Automatically saves settings
     *
     * @param key
     * @param value
     */
    public void putSettingsValue(String key, Object value) {
        try {
            JSONObject settingsObject = readSettings();
            if (value instanceof Integer) {
                settingsObject.put(key, (int) value);
            } else if (value instanceof Boolean) {
                settingsObject.put(key, (boolean) value);
            } else if (value instanceof String) {
                settingsObject.put(key, value);
            } else if (value instanceof Pair) {
                String pairKey = (String) ((Pair) value).first;
                String pairValue = (String) ((Pair) value).second;
                JSONObject streamForItem = new JSONObject();
                JSONArray streamsArray = settingsObject.has(key) ? settingsObject.getJSONArray(key) : new JSONArray();

                JSONArray newStreamsArray = new JSONArray();
                streamForItem.put(pairKey, pairValue);
                for (int i = 0; i < streamsArray.length(); i++) {
                    if (!streamsArray.getJSONObject(i).has(pairKey)) {
                        newStreamsArray.put(streamsArray.getJSONObject(i));
                    }
                }
                newStreamsArray.put(streamForItem);
                settingsObject.put(key, newStreamsArray);
            }
            saveSettings(settingsObject);
        } catch (JSONException e) {
            Logger.printInfo(TAG, "Can't put value for key: " + key + " to json settings");
            e.printStackTrace();
        }
    }


    public void initSettingsFromPreferences() {
        putSettingsValue(JS_NOTIFY_EPISODES, Prefs.getBoolean(JS_NOTIFY_EPISODES, DEFAULT_NOTIFY_EPISODS));
        putSettingsValue(JS_START_SCREEN, Prefs.getString(JS_START_SCREEN, DEFAULT_START_SCREEN));
        putSettingsValue(JS_STREAM_QUALITY, Prefs.getString(JS_STREAM_QUALITY, DEFAULT_STREAM_QUALITY));
        putSettingsValue(JS_TOPS_LANGUAGE, Prefs.getString(JS_TOPS_LANGUAGE, Locale.getDefault().getLanguage()));

        String pUpdateTime = Prefs.getString(JS_PODCASTS_UPDATE_TIME, String.valueOf(DEFAULT_PODCAST_UPDATE_TIME));
        putSettingsValue(JS_PODCASTS_UPDATE_TIME, Integer.valueOf(pUpdateTime));
    }

    public JSONObject getAsJson() {
        return readSettings();
    }


    public void saveSettings(JSONObject settingsObject) {
        if (settingsObject != null) {
            Prefs.putString(JS_SETTINGS, settingsObject.toString());
        }
    }

} public class BackendManager {

    public enum TopType {

        MAIN_FEATURED("main_featured"), MAIN_FEATURED_RU("main_featured_ru"), MAIN_BANNER("main_banner"),
        MAIN_PODCAST("podcasts_featured"), MAIN_PODCAST_RU("podcasts_featured_ru");

        private String name;

        TopType(String name) {
            this.name = name;
        }

        public String getStringValue() {
            return name;
        }
    }

    private static final String TAG = "BackendManager";
    private static long SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MB

    private final OkHttpClient client;
    private static BackendManager backendManager;
    private ArrayList<QueueTask> searchQueue;

    private class QueueTask {
        public Request request;
        public IRequestCallback iRequestHandler;

        public QueueTask(Request request, IRequestCallback uiUpdater) {
            this.request = request;
            this.iRequestHandler = uiUpdater;
        }
    }

    private BackendManager() {
        super();
        File cacheDir = UpodsApplication.getContext().getDir("upods_cache", Context.MODE_PRIVATE);
        Cache cache = new Cache(cacheDir, SIZE_OF_CACHE);
        this.client = new OkHttpClient.Builder()
                .cache(cache).addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        if (GlobalUtils.isInternetConnected()) {
                            request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build();
                        } else {
                            request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build();
                        }
                        return chain.proceed(request);
                    }
                }).build();
    }

    public static synchronized BackendManager getInstance() {
        if (backendManager == null) {
            backendManager = new BackendManager();
            backendManager.searchQueue = new ArrayList<>();
        }
        return backendManager;
    }

    /**
     * Wrapper for simple backend queries
     *
     * @param request   - OKHttp request
     * @param uiUpdater - INetworkUIupdater to update UI
     */
    private void sendRequest(final Request request, final IRequestCallback uiUpdater) {
        try {
            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                    uiUpdater.onRequestFailed();
                    searchQueueNextStep();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    try {
                        final JSONObject jResponse = new JSONObject(response.body().string());
                        uiUpdater.onRequestSuccessed(jResponse);
                        //SimpleCacheManager.getInstance().cacheUrlOutput(request.url().toString(), jResponse.toString());
                        searchQueueNextStep();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

            });
        } catch (Exception e) {
            e.printStackTrace();
            uiUpdater.onRequestFailed();
            searchQueueNextStep();
        }
    }

    private void sendRequest(final Request request, final ISimpleRequestCallback uiUpdater) {
        try {
            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                    uiUpdater.onRequestFailed();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    try {
                        String strResponse = response.body().string();
                        //SimpleCacheManager.getInstance().cacheUrlOutput(request.url().toString(), strResponse);
                        uiUpdater.onRequestSuccessed(strResponse);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            uiUpdater.onRequestFailed();
        }
    }


    private void sendRequest(QueueTask queueTask) {
        sendRequest(queueTask.request, queueTask.iRequestHandler);
    }

    private void searchQueueNextStep() {
        if (!searchQueue.isEmpty()) {
            searchQueue.remove(searchQueue.get(0));
            if (!searchQueue.isEmpty()) {
                sendRequest(searchQueue.get(0));
            }
        }
    }

    /**
     * Simple HTTP GET request
     *
     * @param url       - any url
     * @param uiUpdater ISimpleRequestHandler
     */
    public void sendRequest(String url, final ISimpleRequestCallback uiUpdater) {
        Request request = new Request.Builder().url(url).build();
        sendRequest(request, uiUpdater);
    }

    /**
     * Simple HTTP GET request
     *
     * @param url       - any url
     * @param uiUpdater IRequestHandler
     */
    public void sendRequest(String url, final IRequestCallback uiUpdater) {
        Request request = new Request.Builder().url(url).build();
        sendRequest(request, uiUpdater);
    }

    public void loadTops(TopType topType, String topLink, final IRequestCallback uiUpdater) {
        Request request = new Request.Builder()
                .url(topLink + topType.getStringValue())
                .build();
        sendRequest(request, uiUpdater);
    }

    public void doSearch(String query, final IRequestCallback uiUpdater) {
        Request request = new Request.Builder()
                .url(query)
                .build();
        if (searchQueue.isEmpty()) {
            searchQueue.add(new QueueTask(request, uiUpdater));
            sendRequest(request, uiUpdater);
        } else {
            searchQueue.add(new QueueTask(request, uiUpdater));
        }
    }

    public JSONObject sendSynchronicRequest(Request request) throws IOException, JSONException {
        Response response = client.newCall(request).execute();
        final JSONObject jResponse = new JSONObject(response.body().string());
        return jResponse;
    }

    public String sendSimpleSynchronicRequest(Request request) throws IOException, JSONException {
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    public void clearSearchQueue() {
        searchQueue.clear();
    }

    public ArrayList<Episode> fetchEpisodes(String feedUrl, Podcast podcast) {
        try {
            Request request = new Request.Builder().url(feedUrl).build();
            String response = BackendManager.getInstance().sendSimpleSynchronicRequest(request);
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            XMLReader xr = sp.getXMLReader();
            EpisodesXMLHandler episodesXMLHandler = new EpisodesXMLHandler();
            xr.setContentHandler(episodesXMLHandler);

            //TODO could be encoding problem
            InputSource inputSource = new InputSource(new StringReader(response));
            xr.parse(inputSource);
            ArrayList<Episode> parsedEpisodes = episodesXMLHandler.getParsedEpisods();
            if (podcast != null) {
                podcast.setDescription(episodesXMLHandler.getPodcastSummary());
            }
            return parsedEpisodes;
        } catch (Exception e) {
            Logger.printError(TAG, "Can't fetch episodes for url: " + feedUrl);
            e.printStackTrace();
            return new ArrayList<Episode>();
        }
    }

    public ArrayList<Episode> fetchEpisodes(String feedUrl) {
        return fetchEpisodes(feedUrl, null);
    }

} public interface IContentLoadListener {

    void onContentLoaded();
} public interface IFragmentsManager {

    enum FragmentOpenType {REPLACE, OVERLAY}

    enum FragmentAnimationType {DEFAULT, BOTTOM_TOP}


    void showFragment(int id, Fragment fragment, String tag, FragmentOpenType openType, FragmentAnimationType animationType);

    void showFragment(int id, Fragment fragment, String tag);

    void hideFragment(Fragment fragment);

    void showDialogFragment(DialogFragment dialogFragment);

    boolean hasFragment(String tag);

    String getLatestFragmentTag();
} public interface IMediaItemView {
} public interface IRequestCallback {

    void onRequestSuccessed(JSONObject jResponse);

    void onRequestFailed();
} public interface ISlidingMenuHolder{

    SlidingMenu getSlidingMenu();

    void setSlidingMenuHeader(String itemName);
} public interface IToolbarHolder {

    Toolbar getToolbar();

} public class MediaItemTitle implements IMediaItemView {

    private String title;
    private String subTitle;
    public boolean showButton;

    public String getTitle() {
        return title;
    }

    public MediaItemTitle(String title) {
        super();
        this.subTitle = "";
        this.showButton = true;
        this.title = title;
    }

    public MediaItemTitle(String title, String subTitle) {
        this.title = title;
        this.subTitle = subTitle;
        this.showButton = false;
    }

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

    public String getSubTitle() {
        return subTitle;
    }

    public void setSubTitle(String subTitle) {
        this.subTitle = subTitle;
    }
} 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 ServerApi {

    public static final String DOMAIN;

    public static final String RADIO_TOP;
    public static final String RADIO_SEARCH;
    public static final String RADIO_COUNTRIES;
    public static final String RADIO_LANGUAGES;
    public static final String RADIO_CATEGORIES;
    public static final String RADIO_BY_CATEGORIES;
    public static final String RADIO_BY_LANGUAGE;
    public static final String RADIO_BY_COUNTRY;

    public static final String PODCASTS_TOP;
    public static final String PODCAST_CATEGORIES;
    public static final String PODCAST_SEARCH;
    public static final String PODCAST_SEARCH_PARAM;
    public static final String PODCASTS_BY_CATEGORY;

    public static final String USER_SYNC;
    public static final String STREAM_INFO;

    static {
        DOMAIN = "https://upods.xyz";
        RADIO_TOP = DOMAIN + "/upods/api/v1.0/radio-stations/top?type=";
        RADIO_SEARCH = DOMAIN + "/upods/api/v1.0/radio-stations/search?query=";
        RADIO_CATEGORIES = DOMAIN + "/upods/api/v1.0/music-genres?type=all";
        RADIO_LANGUAGES = DOMAIN + "/upods/api/v1.0/radio-stations/languages";
        RADIO_COUNTRIES = DOMAIN + "/upods/api/v1.0/radio-stations/countries";
        RADIO_BY_CATEGORIES = DOMAIN + "/upods/api/v1.0/radio-stations/genre?id=";
        RADIO_BY_COUNTRY = DOMAIN + "/upods/api/v1.0/radio-stations/by-country?country=";
        RADIO_BY_LANGUAGE = DOMAIN + "/upods/api/v1.0/radio-stations/by-lng?lng=";

        PODCASTS_TOP = DOMAIN + "/upods/api/v1.0/podcasts/top?type=";
        PODCAST_CATEGORIES = DOMAIN + "/upods/api/v1.0/podcasts-categories?type=all";
        PODCAST_SEARCH = "https://itunes.apple.com/search?term=";
        PODCAST_SEARCH_PARAM = "&entity=podcast";
        PODCASTS_BY_CATEGORY = DOMAIN + "/upods/api/v1.0/podcasts/category?id=";

        USER_SYNC = DOMAIN + "/upods/api/v1.0/user/sync";
        STREAM_INFO = DOMAIN + "/upods/api/v1.0/stream/info?link=";
    }
} public enum MediaItemType {

    DEFAULT, PODCAST, RADIO, PODCAST_DOWNLOADED, RADIO_SUBSCRIBED, RADIO_RECENT, PODCAST_FAVORITE, RADIO_SEARCH, PODCAST_SEARCH
} public class AutofitRecyclerView extends RecyclerView {
    private GridLayoutManager manager;
    private int columnWidth = -1;
    private int spanCount;

    public AutofitRecyclerView(Context context) {
        super(context);
        init(context, null);
    }

    public AutofitRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public AutofitRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        if (attrs != null) {
            int[] attrsArray = {
                    android.R.attr.columnWidth
            };
            TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
            columnWidth = array.getDimensionPixelSize(0, -1);
            array.recycle();
        }
        manager = new GridLayoutManager(getContext(), 1);
        setLayoutManager(manager);
    }

    public void setSpanSizeLookup(GridLayoutManager.SpanSizeLookup spanSizeLookup) {
        manager.setSpanSizeLookup(spanSizeLookup);
    }

    public int getSpanCount() {
        return spanCount;
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        super.onMeasure(widthSpec, heightSpec);
        if (columnWidth > 0) {
            spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
            manager.setSpanCount(spanCount);
        }
    }

    protected int getTotalSpan(View view, RecyclerView parent) {
        return manager.getSpanCount();
    }
} public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {

    private int spanCount;
    private int spacing;
    private boolean includeEdge;
    private int cardItemType;
    private int itemsTypesCount;

    public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
        this.spanCount = spanCount;
        this.spacing = spacing;
        this.includeEdge = includeEdge;
        this.cardItemType = -1;
        this.itemsTypesCount = 1;
    }

    public void setGridItemType(int cardItemType) {
        this.cardItemType = cardItemType;
    }

    public void setItemsTypesCount(int itemsTypesCount) {
        this.itemsTypesCount = itemsTypesCount;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view); // item position
        if ((cardItemType == -1 || parent.getAdapter().getItemViewType(position) == cardItemType) && spanCount != 0) {
            position -= (itemsTypesCount - 1);
            int column = position % spanCount; // item column

            if (includeEdge) {
                outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
                outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)

                if (position < spanCount) { // top edge
                    outRect.top = spacing;
                }
                outRect.bottom = spacing; // item bottom
            } else {
                outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
                outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f /    spanCount) * spacing)
                if (position >= spanCount) {
                    outRect.top = spacing; // item top
                }
            }
        }
    }
}
import android.app.Fragment;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import com.chickenkiller.upods2.R;
import com.chickenkiller.upods2.activity.ActivityPlayer;
import com.chickenkiller.upods2.controllers.adaperts.MediaItemsAdapter;
import com.chickenkiller.upods2.controllers.app.ProfileManager;
import com.chickenkiller.upods2.controllers.app.SettingsManager;
import com.chickenkiller.upods2.controllers.internet.BackendManager;
import com.chickenkiller.upods2.interfaces.IContentLoadListener;
import com.chickenkiller.upods2.interfaces.IFragmentsManager;
import com.chickenkiller.upods2.interfaces.IMediaItemView;
import com.chickenkiller.upods2.interfaces.IRequestCallback;
import com.chickenkiller.upods2.interfaces.ISlidingMenuHolder;
import com.chickenkiller.upods2.interfaces.IToolbarHolder;
import com.chickenkiller.upods2.models.MediaItemTitle;
import com.chickenkiller.upods2.models.Podcast;
import com.chickenkiller.upods2.utils.ServerApi;
import com.chickenkiller.upods2.utils.enums.MediaItemType;
import com.chickenkiller.upods2.views.AutofitRecyclerView;
import com.chickenkiller.upods2.views.GridSpacingItemDecoration;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
package com.chickenkiller.upods2.fragments;





/**
 * Created by Alon Zilberman on 7/10/15.
 */
public class FragmentPodcastFeatured extends Fragment implements IContentLoadListener {

    public static final String TAG = "podcasts_featured";
    public static final int MEDIA_ITEMS_TYPES_COUNT = 2;

    private AutofitRecyclerView rvMain;
    private MediaItemsAdapter mediaItemsAdapter;
    private ProgressBar pbLoadingFeatured;
    private LinearLayout lnInternetError;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ((IToolbarHolder) getActivity()).getToolbar().setVisibility(View.VISIBLE);

        //Init fragments views
        View view = inflater.inflate(R.layout.fragment_podcasts_featured, container, false);
        lnInternetError = (LinearLayout) view.findViewById(R.id.lnInternetError);
        pbLoadingFeatured = (ProgressBar) view.findViewById(R.id.pbLoadingFeatured);
        rvMain = (AutofitRecyclerView) view.findViewById(R.id.rvMain);

        //Toolbar
        if (getActivity() instanceof IToolbarHolder) {
            MenuItem searchMenuItem = ((IToolbarHolder) getActivity()).getToolbar().getMenu().findItem(R.id.action_search);
            searchMenuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem menuItem) {
                    FragmentSearch fragmentSearch = new FragmentSearch();
                    fragmentSearch.setSearchType(MediaItemType.PODCAST);
                    ((IFragmentsManager) getActivity()).showFragment(R.id.fl_content, fragmentSearch, FragmentSearch.TAG);
                    return false;
                }
            });
        }
        ((IToolbarHolder) getActivity()).getToolbar().setTitle(R.string.podcasts_main);
        ((ISlidingMenuHolder) getActivity()).setSlidingMenuHeader(getString(R.string.podcasts));

        //Featured adapter
        mediaItemsAdapter = new MediaItemsAdapter(getActivity(), R.layout.card_media_item_vertical,
                R.layout.media_item_title);
        if (getActivity() instanceof IFragmentsManager) {
            mediaItemsAdapter.setFragmentsManager((IFragmentsManager) getActivity());
        }
        mediaItemsAdapter.setContentLoadListener(this);

        //Featured recycle view
        rvMain.setHasFixedSize(true);
        rvMain.setAdapter(mediaItemsAdapter);
        rvMain.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                int viewType = mediaItemsAdapter.getItemViewType(position);
                return (viewType != MediaItemsAdapter.HEADER && viewType != MediaItemsAdapter.BANNERS_LAYOUT) ?
                        1 : rvMain.getSpanCount();
            }
        });
        mediaItemsAdapter.notifyContentLoadingStatus();
        rvMain.setVisibility(View.INVISIBLE);

        //Load tops from remote server
        showTops();

        //Open search fragment backed from other activity which was started from search
        if (getActivity().getIntent().hasExtra(ActivityPlayer.ACTIVITY_STARTED_FROM_IN_DEPTH)) {
            FragmentSearch.openFromIntent(getActivity());
        }
        return view;
    }

    private void showTops() {
        String topLang = SettingsManager.getInstace().getStringSettingValue(SettingsManager.JS_TOPS_LANGUAGE);
        BackendManager.TopType topType = topLang.equals(SettingsManager.TOPS_RU_LANGUAGE) ? BackendManager.TopType.MAIN_PODCAST_RU