/**
   * Return the folder where the file will be placed
   *
   * @param chunk The chunk
   */
  private String BuildDirPath(ResponseSongFileChunk chunk) {
    String defaultPath = Environment.getExternalStorageDirectory() + "/ClementineMusic";
    String path = mSharedPref.getString(App.SP_DOWNLOAD_DIR, defaultPath);

    StringBuilder sb = new StringBuilder();
    sb.append(path);
    sb.append(File.separator);
    if (mIsPlaylist && mCreatePlaylistDir) {
      sb.append(Utilities.removeInvalidFileCharacters(mPlaylistName));
      sb.append(File.separator);
    }

    // Create artist/album subfolder only when we have no playlist
    // or user set the settings
    if (!mIsPlaylist || mIsPlaylist && mCreatePlaylistArtistDir) {
      // Append artist name
      if (chunk.getSongMetadata().getAlbumartist().length() == 0) {
        sb.append(Utilities.removeInvalidFileCharacters(chunk.getSongMetadata().getArtist()));
      } else {
        sb.append(Utilities.removeInvalidFileCharacters(chunk.getSongMetadata().getAlbumartist()));
      }

      // append album
      sb.append(File.separator);
      sb.append(Utilities.removeInvalidFileCharacters(chunk.getSongMetadata().getAlbum()));
    }

    return sb.toString();
  }
  /**
   * Build the filename
   *
   * @param chunk The SongFileChunk
   * @return /sdcard/Music/Artist/Album/file.mp3
   */
  private String BuildFilePath(ResponseSongFileChunk chunk) {
    StringBuilder sb = new StringBuilder();
    sb.append(BuildDirPath(chunk));
    sb.append(File.separator);
    sb.append(Utilities.removeInvalidFileCharacters(chunk.getSongMetadata().getFilename()));

    return sb.toString();
  }
  @Override
  protected DownloaderResult doInBackground(ClementineMessage... params) {
    if (mSharedPref.getBoolean(App.SP_WIFI_ONLY, false) && !Utilities.onWifi(mContext)) {
      return new DownloaderResult(DownloaderResult.DownloadResult.ONLY_WIFI);
    }

    // First create a connection
    if (!connect()) {
      return new DownloaderResult(DownloaderResult.DownloadResult.CONNECTION_ERROR);
    }

    // Start the download
    return startDownloading(params[0]);
  }
        @Override
        public void OnLibraryDownloadFinished(DownloaderResult result) {
          mProgressDialog.dismiss();
          App.libraryDownloader = null;

          if (result.getResult() == DownloadResult.SUCCESSFUL) {
            if (mLibrary != null) {
              mLibrary.closeDatabase();
            }
            mLibrary = new MyLibrary(getActivity());
            mLibrary.openDatabase();
            LibraryAdapter a =
                new LibraryAdapter(
                    getActivity(), mLibrary.getArtists(), mLibrary, MyLibrary.LVL_ARTIST);
            mAdapters.add(a);
            showList();
          } else {
            Utilities.ShowMessageDialog(
                getActivity(), R.string.library_download_error, result.getMessageId());
          }
        }
  /** Start the Download */
  private DownloaderResult startDownloading(ClementineMessage clementineMessage) {
    boolean downloadFinished = false;
    DownloaderResult result = new DownloaderResult(DownloadResult.SUCCESSFUL);
    File f = null;
    FileOutputStream fo = null;

    publishProgress(0);

    // Now request the songs
    mClient.sendRequest(clementineMessage);

    while (!downloadFinished) {
      // Check if the user canceled the process
      if (isCancelled()) {
        // Close the stream and delete the incomplete file
        try {
          if (fo != null) {
            fo.flush();
            fo.close();
          }
          if (f != null) {
            f.delete();
          }
        } catch (IOException e) {
        }
        Log.d(TAG, "isCancelled");
        break;
      }

      // Get the raw protocol buffer
      ClementineMessage message = mClient.getProtoc();

      if (message.isErrorMessage()) {
        result = new DownloaderResult(DownloadResult.CONNECTION_ERROR);
        break;
      }

      // Is the download forbidden?
      if (message.getMessageType() == MsgType.DISCONNECT) {
        result = new DownloaderResult(DownloadResult.FOBIDDEN);
        break;
      }

      // Ignore other elements!
      if (message.getMessageType() != MsgType.LIBRARY_CHUNK) {
        continue;
      }

      ResponseLibraryChunk chunk = message.getMessage().getResponseLibraryChunk();

      try {
        // Check if we need to create a new file
        if (f == null) {
          // Check if we have enougth free space
          // size times 2, because we optimise the table later and
          // need space for that too!
          if ((chunk.getSize() * 2) > Utilities.getFreeSpaceExternal()) {
            result = new DownloaderResult(DownloadResult.INSUFFIANT_SPACE);
            break;
          }
          f = mLibrary.getLibraryDb();

          // User wants to override files, so delete it here!
          // The check was already done in processSongOffer()
          if (f.exists()) {
            f.delete();
          }

          f.createNewFile();
          fo = new FileOutputStream(f);
        }

        // Write chunk to sdcard
        fo.write(chunk.getData().toByteArray());

        // Have we downloaded all chunks?
        if (chunk.getChunkCount() == chunk.getChunkNumber()) {
          fo.flush();
          fo.close();
          f = null;
          downloadFinished = true;
        }

        // Update notification
        updateProgress(chunk);
      } catch (IOException e) {
        result = new DownloaderResult(DownloaderResult.DownloadResult.NOT_MOUNTED);
        break;
      }
    }

    // Disconnect at the end
    mClient.disconnect(ClementineMessage.getMessage(MsgType.DISCONNECT));

    // Optimize library table
    if (mLibrary.getLibraryDb().exists()) {
      mLibrary.optimizeTable();
    }

    return result;
  }
  /** Start the Downlaod */
  private DownloaderResult startDownloading(ClementineMessage clementineMessage) {
    boolean downloadFinished = false;
    DownloaderResult result = new DownloaderResult(DownloadResult.SUCCESSFUL);
    File f = null;
    FileOutputStream fo = null;

    // Do we have a playlist?
    mIsPlaylist =
        (clementineMessage.getMessage().getRequestDownloadSongs().getDownloadItem()
            == DownloadItem.APlaylist);
    if (mIsPlaylist) {
      int id = clementineMessage.getMessage().getRequestDownloadSongs().getPlaylistId();
      mPlaylistName = App.mClementine.getPlaylistManager().getPlaylist(id).getName();
    }

    publishProgress(0);

    // Now request the songs
    mClient.sendRequest(clementineMessage);

    while (!downloadFinished) {
      // Check if the user canceled the process
      if (isCancelled()) {
        // Close the stream and delete the incomplete file
        try {
          if (fo != null) {
            fo.flush();
            fo.close();
          }
          if (f != null) {
            f.delete();
          }
        } catch (IOException e) {
        }

        break;
      }

      // Get the raw protocol buffer
      ClementineMessage message = mClient.getProtoc();

      // Check if an error occured
      if (message == null || message.isErrorMessage()) {
        result = new DownloaderResult(DownloadResult.CONNECTION_ERROR);
        break;
      }

      // Is the download forbidden?
      if (message.getMessageType() == MsgType.DISCONNECT) {
        result = new DownloaderResult(DownloadResult.FOBIDDEN);
        break;
      }

      // Download finished?
      if (message.getMessageType() == MsgType.DOWNLOAD_QUEUE_EMPTY) {
        break;
      }

      // Ignore other elements!
      if (message.getMessageType() != MsgType.SONG_FILE_CHUNK) {
        continue;
      }

      ResponseSongFileChunk chunk = message.getMessage().getResponseSongFileChunk();

      // If we received chunk no 0, then we have to decide wether to
      // accept the song offered or not
      if (chunk.getChunkNumber() == 0) {
        processSongOffer(chunk);

        // Update progress here to. If the first (and only) file exists and shall not be
        // overriten, the notification bar shows NULL.
        mCurrentSong = MySong.fromProtocolBuffer(chunk.getSongMetadata());
        publishProgress(mCurrentProgress);
        continue;
      }

      try {
        // Check if we need to create a new file
        if (f == null) {
          // Check if we have enougth free space
          if (chunk.getSize() > Utilities.getFreeSpaceExternal()) {
            result = new DownloaderResult(DownloadResult.INSUFFIANT_SPACE);
            break;
          }

          File dir = new File(BuildDirPath(chunk));
          f = new File(BuildFilePath(chunk));

          // User wants to override files, so delete it here!
          // The check was already done in processSongOffer()
          if (f.exists()) {
            f.delete();
          }

          dir.mkdirs();
          f.createNewFile();
          fo = new FileOutputStream(f);

          // File for download fragment
          mFileUri = Uri.fromFile(f);
        }

        // Write chunk to sdcard
        fo.write(chunk.getData().toByteArray());

        // Have we downloaded all chunks?
        if (chunk.getChunkCount() == chunk.getChunkNumber()) {
          // Index file
          MediaScannerConnection.scanFile(mContext, new String[] {f.getAbsolutePath()}, null, null);
          fo.flush();
          fo.close();
          f = null;
        }

        // Update notification
        updateProgress(chunk);
      } catch (IOException e) {
        result = new DownloaderResult(DownloaderResult.DownloadResult.NOT_MOUNTED);
        break;
      }
    }

    // Disconnect at the end
    mClient.disconnect(ClementineMessage.getMessage(MsgType.DISCONNECT));

    return result;
  }