content: /*
   * Copyright (C) 2010-2014 The MPDroid Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.namelessdev.mpdroid;
  
  import com.namelessdev.mpdroid.MPDroidActivities.MPDroidActivity;
  import com.namelessdev.mpdroid.adapters.SeparatedListAdapter;
  import com.namelessdev.mpdroid.helpers.MPDAsyncHelper.AsyncExecListener;
  import com.namelessdev.mpdroid.library.SimpleLibraryActivity;
  import com.namelessdev.mpdroid.tools.Tools;
  import com.namelessdev.mpdroid.views.SearchResultDataBinder;
  
  import org.a0z.mpd.exception.MPDException;
  import org.a0z.mpd.item.Album;
  import org.a0z.mpd.item.Artist;
  import org.a0z.mpd.item.Item;
  import org.a0z.mpd.item.Music;
  
  import android.app.SearchManager;
  import android.content.Intent;
  import android.os.Bundle;
  import android.os.Parcelable;
  import android.provider.SearchRecentSuggestions;
  import android.support.annotation.StringRes;
  import android.support.v4.app.FragmentTransaction;
  import android.support.v4.view.PagerAdapter;
  import android.support.v4.view.ViewPager;
  import android.support.v7.app.ActionBar;
  import android.util.Log;
  import android.view.ContextMenu;
  import android.view.Menu;
  import android.view.MenuItem;
  import android.view.MenuItem.OnMenuItemClickListener;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.AdapterView;
  import android.widget.AdapterView.AdapterContextMenuInfo;
  import android.widget.AdapterView.OnItemClickListener;
  import android.widget.ListAdapter;
  import android.widget.ListView;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  public class SearchActivity extends MPDroidActivity implements OnMenuItemClickListener,
          AsyncExecListener, OnItemClickListener, ActionBar.TabListener {
  
      public static final int ADD = 0;
  
      public static final int ADD_PLAY = 2;
  
      public static final int ADD_REPLACE = 1;
  
      public static final int ADD_REPLACE_PLAY = 3;
  
      public static final int GOTO_ALBUM = 4;
  
      public static final int MAIN = 0;
  
      public static final int PLAYLIST = 3;
  
      private static final String PLAY_SERVICES_ACTION_SEARCH
              = "com.google.android.gms.actions.SEARCH_ACTION";
  
      private static final String TAG = "SearchActivity";
  
      private final ArrayList<Album> mAlbumResults;
  
      private final ArrayList<Artist> mArtistResults;
  
      private final ArrayList<Music> mSongResults;
  
      protected int mJobID = -1;
  
      protected View mLoadingView;
  
      protected View mNoResultAlbumsView;
  
      protected View mNoResultArtistsView;
  
      protected View mNoResultSongsView;
  
      protected ViewPager mPager;
  
      @StringRes
      private int mAddString;
  
      @StringRes
      private int mAddedString;
  
      private ListView mListAlbums = null;
  
      private View mListAlbumsFrame = null;
  
      private ListView mListArtists = null;
  
      private View mListArtistsFrame = null;
  
      private ListView mListSongs = null;
  
      private View mListSongsFrame = null;
  
      private String mSearchKeywords = null;
  
      private ActionBar.Tab mTabAlbums;
  
      private ActionBar.Tab mTabArtists;
  
      private ActionBar.Tab mTabSongs;
  
      public SearchActivity() {
          super();
          mAddString = R.string.addSong;
          mAddedString = R.string.songAdded;
          mArtistResults = new ArrayList<>();
          mAlbumResults = new ArrayList<>();
          mSongResults = new ArrayList<>();
      }
  
      protected void add(final Artist artist, final Album album, final boolean replace,
              final boolean play) {
          String note = null;
  
          try {
              if (artist == null) {
                  final Artist albumArtist = album.getArtist();
  
                  mApp.oMPDAsyncHelper.oMPD.add(album, replace, play);
                  if (albumArtist != null) {
                      note = albumArtist.getName() + " - " + album.getName();
                  }
              } else if (album == null) {
                  mApp.oMPDAsyncHelper.oMPD.add(artist, replace, play);
                  note = artist.getName();
              }
          } catch (final IOException | MPDException e) {
              Log.e(TAG, "Failed to add.", e);
          }
  
          if (note != null) {
              Tools.notifyUser(mAddedString, note);
          }
      }
  
      protected void add(final Music music, final boolean replace, final boolean play) {
          try {
              mApp.oMPDAsyncHelper.oMPD.add(music, replace, play);
              Tools.notifyUser(R.string.songAdded, music.getTitle(), music.getName());
          } catch (final IOException | MPDException e) {
              Log.e(TAG, "Failed to add.", e);
          }
      }
  
      protected void add(final Object object, final boolean replace, final boolean play) {
          setContextForObject(object);
          if (object instanceof Music) {
              add((Music) object, replace, play);
          } else if (object instanceof Artist) {
              add((Artist) object, null, replace, play);
          } else if (object instanceof Album) {
              add(null, (Album) object, replace, play);
          }
      }
  
      @Override
      public void asyncExecSucceeded(final int jobID) {
          if (mJobID == jobID) {
              updateFromItems();
          }
      }
  
      protected void asyncUpdate() {
          final String finalSearch = mSearchKeywords.toLowerCase();
  
          List<Music> arrayMusic = null;
  
          try {
              arrayMusic = mApp.oMPDAsyncHelper.oMPD.search("any", finalSearch);
          } catch (final IOException | MPDException e) {
              Log.e(TAG, "MPD search failure.", e);
  
          }
  
          if (arrayMusic == null) {
              return;
          }
  
          mArtistResults.clear();
          mAlbumResults.clear();
          mSongResults.clear();
  
          String tmpValue;
          boolean valueFound;
          for (final Music music : arrayMusic) {
              if (music.getTitle() != null && music.getTitle().toLowerCase().contains(finalSearch)) {
                  mSongResults.add(music);
              }
              valueFound = false;
              Artist artist = music.getAlbumArtistAsArtist();
              if (artist == null || artist.isUnknown()) {
                  artist = music.getArtistAsArtist();
              }
              if (artist != null) {
                  final String name = artist.getName();
                  if (name != null) {
                      tmpValue = name.toLowerCase();
                      if (tmpValue.contains(finalSearch)) {
                          for (final Artist artistItem : mArtistResults) {
                              final String artistItemName = artistItem.getName();
                              if (artistItemName != null &&
                                      artistItemName.equalsIgnoreCase(tmpValue)) {
                                  valueFound = true;
                              }
                          }
                          if (!valueFound) {
                              mArtistResults.add(artist);
                          }
                      }
                  }
              }
  
              valueFound = false;
              final Album album = music.getAlbumAsAlbum();
              if (album != null) {
                  final String albumName = album.getName();
                  if (albumName != null) {
                      tmpValue = albumName.toLowerCase();
                      if (tmpValue.contains(finalSearch)) {
                          for (final Album albumItem : mAlbumResults) {
                              final String albumItemName = albumItem.getName();
                              if (albumItemName.equalsIgnoreCase(tmpValue)) {
                                  valueFound = true;
                              }
                          }
                          if (!valueFound) {
                              mAlbumResults.add(album);
                          }
                      }
                  }
              }
          }
  
          Collections.sort(mArtistResults);
          Collections.sort(mAlbumResults);
          Collections.sort(mSongResults, Music.COMPARE_WITHOUT_TRACK_NUMBER);
  
          runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  mTabArtists.setText(
                          getString(R.string.artists) + " (" + mArtistResults.size() + ')');
                  mTabAlbums.setText(getString(R.string.albums) + " (" + mAlbumResults.size() + ')');
                  mTabSongs.setText(getString(R.string.songs) + " (" + mSongResults.size() + ')');
              }
          });
      }
  
      @Override
      protected void onCreate(final Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          setContentView(R.layout.search_results);
  
          final SearchResultsPagerAdapter adapter = new SearchResultsPagerAdapter();
          final ActionBar actionBar = getSupportActionBar();
  
          mPager = (ViewPager) findViewById(R.id.pager);
          mPager.setAdapter(adapter);
          mPager.setOnPageChangeListener(
                  new ViewPager.SimpleOnPageChangeListener() {
                      @Override
                      public void onPageSelected(final int position) {
                          // When swiping between pages, select the corresponding tab.
                          actionBar.setSelectedNavigationItem(position);
                      }
                  });
  
          actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
  
          mTabArtists = actionBar.newTab()
                  .setText(R.string.artists)
                  .setTabListener(this);
          actionBar.addTab(mTabArtists);
  
          mTabAlbums = actionBar.newTab()
                  .setText(R.string.albums)
                  .setTabListener(this);
          actionBar.addTab(mTabAlbums);
  
          mTabSongs = actionBar.newTab()
                  .setText(R.string.songs)
                  .setTabListener(this);
          actionBar.addTab(mTabSongs);
  
          mListArtistsFrame = findViewById(R.id.list_artists_frame);
          mNoResultArtistsView = mListArtistsFrame.findViewById(R.id.no_artist_result);
          mListArtists = (ListView) mListArtistsFrame.findViewById(android.R.id.list);
          mListArtists.setOnItemClickListener(this);
  
          mListAlbumsFrame = findViewById(R.id.list_albums_frame);
          mNoResultAlbumsView = mListAlbumsFrame.findViewById(R.id.no_album_result);
          mListAlbums = (ListView) mListAlbumsFrame.findViewById(android.R.id.list);
          mListAlbums.setOnItemClickListener(this);
  
          mListSongsFrame = findViewById(R.id.list_songs_frame);
          mNoResultSongsView = mListSongsFrame.findViewById(R.id.no_song_result);
          mListSongs = (ListView) mListSongsFrame.findViewById(android.R.id.list);
          mListSongs.setOnItemClickListener(this);
  
          mLoadingView = findViewById(R.id.loadingLayout);
          mLoadingView.setVisibility(View.VISIBLE);
  
          final Intent queryIntent = getIntent();
          final String queryAction = queryIntent.getAction();
  
          if (Intent.ACTION_SEARCH.equals(queryAction) || PLAY_SERVICES_ACTION_SEARCH
                  .equals(queryAction)) {
              mSearchKeywords = queryIntent.getStringExtra(SearchManager.QUERY).trim();
              final SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
                      SearchRecentProvider.AUTHORITY, SearchRecentProvider.MODE);
              suggestions.saveRecentQuery(mSearchKeywords, null);
          } else {
              return; // Bye !
          }
  
          setTitle(getTitle() + " : " + mSearchKeywords);
  
          registerForContextMenu(mListArtists);
          registerForContextMenu(mListAlbums);
          registerForContextMenu(mListSongs);
  
          updateList();
          actionBar.setDisplayHomeAsUpEnabled(true);
      }
  
      @Override
      public void onCreateContextMenu(final ContextMenu menu, final View v,
              final ContextMenu.ContextMenuInfo menuInfo) {
          super.onCreateContextMenu(menu, v, menuInfo);
  
          final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
  
          switch (mPager.getCurrentItem()) {
              case 0:
                  final Artist artist = mArtistResults.get((int) info.id);
                  menu.setHeaderTitle(artist.mainText());
                  setContextForObject(artist);
                  break;
              case 1:
                  final Album album = mAlbumResults.get((int) info.id);
                  menu.setHeaderTitle(album.mainText());
                  setContextForObject(album);
                  break;
              case 2:
                  final Music music = mSongResults.get((int) info.id);
                  final MenuItem gotoAlbumItem = menu
                          .add(Menu.NONE, GOTO_ALBUM, 0, R.string.goToAlbum);
                  gotoAlbumItem.setOnMenuItemClickListener(this);
                  menu.setHeaderTitle(music.mainText());
                  setContextForObject(music);
                  break;
              default:
                  break;
          }
  
          final MenuItem addItem = menu.add(Menu.NONE, ADD, 0, getString(mAddString));
          final MenuItem addAndReplaceItem = menu.add(Menu.NONE, ADD_REPLACE, 0,
                  R.string.addAndReplace);
          final MenuItem addReplacePlayItem = menu.add(Menu.NONE, ADD_REPLACE_PLAY,
                  0, R.string.addAndReplacePlay);
          final MenuItem addAndPlayItem = menu.add(Menu.NONE, ADD_PLAY, 0, R.string.addAndPlay);
  
          addItem.setOnMenuItemClickListener(this);
          addAndReplaceItem.setOnMenuItemClickListener(this);
          addReplacePlayItem.setOnMenuItemClickListener(this);
          addAndPlayItem.setOnMenuItemClickListener(this);
      }
  
      @Override
      public boolean onCreateOptionsMenu(final Menu menu) {
          super.onCreateOptionsMenu(menu);
          getMenuInflater().inflate(R.menu.mpd_searchmenu, menu);
          return true;
      }
  
      @Override
      public void onDestroy() {
          mApp.oMPDAsyncHelper.removeAsyncExecListener(this);
          super.onDestroy();
      }
  
      @Override
      public void onItemClick(final AdapterView<?> parent, final View view, final int position,
              final long id) {
          final Object selectedItem = parent.getAdapter().getItem(position);
          if (selectedItem instanceof Music) {
              add((Music) selectedItem, false, false);
          } else if (selectedItem instanceof Artist) {
              final Intent intent = new Intent(this, SimpleLibraryActivity.class);
              intent.putExtra("artist", (Parcelable) selectedItem);
              startActivityForResult(intent, -1);
          } else if (selectedItem instanceof Album) {
              final Intent intent = new Intent(this, SimpleLibraryActivity.class);
              intent.putExtra("album", (Parcelable) selectedItem);
              startActivityForResult(intent, -1);
          }
      }
  
      @Override
      public boolean onMenuItemClick(final MenuItem item) {
          final AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
          final List<? extends Item> targetArray;
          switch (mPager.getCurrentItem()) {
              case 1:
                  targetArray = mAlbumResults;
                  break;
              case 2:
                  targetArray = mSongResults;
                  break;
              case 0:
              default:
                  targetArray = mArtistResults;
                  break;
          }
          final Object selectedItem = targetArray.get((int) info.id);
          if (item.getItemId() == GOTO_ALBUM) {
              if (selectedItem instanceof Music) {
                  final Music music = (Music) selectedItem;
                  final Intent intent = new Intent(this, SimpleLibraryActivity.class);
                  final Parcelable artist = new Artist(music.getAlbumArtistOrArtist());
                  intent.putExtra("artist", artist);
                  intent.putExtra("album", music.getAlbumAsAlbum());
                  startActivityForResult(intent, -1);
              }
          } else {
              mApp.oMPDAsyncHelper.execAsync(new Runnable() {
                  @Override
                  public void run() {
                      boolean replace = false;
                      boolean play = false;
                      switch (item.getItemId()) {
                          case ADD_REPLACE_PLAY:
                              replace = true;
                              play = true;
                              break;
                          case ADD_REPLACE:
                              replace = true;
                              break;
                          case ADD_PLAY:
                              play = true;
                              break;
                          default:
                              break;
                      }
                      add(selectedItem, replace, play);
                  }
              });
          }
          return false;
      }
  
      @Override
      public boolean onOptionsItemSelected(final MenuItem item) {
          boolean handled = true;
  
          switch (item.getItemId()) {
              case R.id.menu_search:
                  onSearchRequested();
                  break;
              case android.R.id.home:
                  final Intent intent = new Intent(this, MainMenuActivity.class);
                  intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                  startActivity(intent);
                  break;
              default:
                  handled = false;
                  break;
          }
  
          return handled;
      }
  
      @Override
      public void onStart() {
          super.onStart();
          mApp.setActivity(this);
      }
  
      @Override
      public void onStop() {
          super.onStop();
          mApp.unsetActivity(this);
      }
  
      @Override
      public void onTabReselected(final ActionBar.Tab tab, final FragmentTransaction ft) {
      }
  
      @Override
      public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction ft) {
          mPager.setCurrentItem(tab.getPosition());
      }
  
      @Override
      public void onTabUnselected(final ActionBar.Tab tab, final FragmentTransaction ft) {
      }
  
      private void setContextForObject(final Object object) {
          if (object instanceof Music) {
              mAddString = R.string.addSong;
              mAddedString = R.string.songAdded;
          } else if (object instanceof Artist) {
              mAddString = R.string.addArtist;
              mAddedString = R.string.artistAdded;
          } else if (object instanceof Album) {
              mAddString = R.string.addAlbum;
              mAddedString = R.string.albumAdded;
          }
      }
  
      /**
       * This updates a specific ListView for search results.
       *
       * @param listView      The ListView to update with search results.
       * @param resultList    The List of results to enter into the ListView.
       * @param noResultsView The View to hide if there are no results.
       */
      private void update(final ListView listView, final List<? extends Item> resultList,
              final View noResultsView) {
          final ListAdapter separatedListAdapter = new SeparatedListAdapter(this,
                  R.layout.search_list_item,
                  new SearchResultDataBinder(),
                  resultList);
  
          listView.setAdapter(separatedListAdapter);
  
          try {
              listView.setEmptyView(noResultsView);
              mLoadingView.setVisibility(View.GONE);
          } catch (final RuntimeException e) {
              Log.e(TAG, "Failed to update items.", e);
          }
      }
  
      /**
       * Update the view from the items list if items is set.
       */
      public void updateFromItems() {
          update(mListArtists, mArtistResults, mNoResultArtistsView);
          update(mListAlbums, mAlbumResults, mNoResultAlbumsView);
          update(mListSongs, mSongResults, mNoResultSongsView);
      }
  
      public void updateList() {
          mApp.oMPDAsyncHelper.addAsyncExecListener(this);
          mJobID = mApp.oMPDAsyncHelper.execAsync(new Runnable() {
              @Override
              public void run() {
                  asyncUpdate();
              }
          });
      }
  
      class SearchResultsPagerAdapter extends PagerAdapter {
  
          @Override
          public void destroyItem(final ViewGroup container, final int position,
                  final Object object) {
              container.removeView((View) object);
          }
  
          @Override
          public int getCount() {
              return 3;
          }
  
          @Override
          public Object instantiateItem(final ViewGroup container, final int position) {
  
              final View v;
              switch (position) {
                  case 1:
                      v = mListAlbumsFrame;
                      break;
                  case 2:
                      v = mListSongsFrame;
                      break;
                  case 0:
                  default:
                      v = mListArtistsFrame;
                      break;
              }
              if (v.getParent() == null) {
                  mPager.addView(v);
              }
              return v;
          }
  
          @Override
          public boolean isViewFromObject(final View arg0, final Object arg1) {
              return arg0.equals(arg1);
          }
      }
  
  }
  