content: /*
   This file is part of Subsonic.
  
   Subsonic is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
  
   Subsonic is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License
   along with Subsonic.  If not, see <http://www.gnu.org/licenses/>.
  
   Copyright 2010 (C) Sindre Mehus
   */
  package net.nullsum.audinaut.fragments;
  
  import android.content.SharedPreferences;
  import android.media.audiofx.BassBoost;
  import android.media.audiofx.Equalizer;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.ContextMenu;
  import android.view.LayoutInflater;
  import android.view.MenuItem;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.CheckBox;
  import android.widget.LinearLayout;
  import android.widget.SeekBar;
  import android.widget.TextView;
  
  import net.nullsum.audinaut.R;
  import net.nullsum.audinaut.audiofx.EqualizerController;
  import net.nullsum.audinaut.audiofx.LoudnessEnhancerController;
  import net.nullsum.audinaut.service.DownloadService;
  import net.nullsum.audinaut.util.Constants;
  import net.nullsum.audinaut.util.Util;
  
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * Created by Scott on 10/27/13.
   */
  public class EqualizerFragment extends SubsonicFragment {
      private static final String TAG = EqualizerFragment.class.getSimpleName();
  
      private static final int MENU_GROUP_PRESET = 100;
  
      private final Map<Short, SeekBar> bars = new HashMap<>();
      private SeekBar bassBar;
      private SeekBar loudnessBar;
      private EqualizerController equalizerController;
      private Equalizer equalizer;
      private BassBoost bass;
      private LoudnessEnhancerController loudnessEnhancer;
      private short masterLevel = 0;
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
          rootView = inflater.inflate(R.layout.equalizer, container, false);
  
          try {
              DownloadService service = DownloadService.getInstance();
              equalizerController = service.getEqualizerController();
              equalizer = equalizerController.getEqualizer();
              bass = equalizerController.getBassBoost();
              loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
  
              initEqualizer();
          } catch (Exception e) {
              Log.e(TAG, "Failed to initialize EQ", e);
              Util.toast(context, "Failed to initialize EQ");
              context.onBackPressed();
          }
  
          final View presetButton = rootView.findViewById(R.id.equalizer_preset);
          registerForContextMenu(presetButton);
          presetButton.setOnClickListener(view -> presetButton.showContextMenu());
  
          CheckBox enabledCheckBox = rootView.findViewById(R.id.equalizer_enabled);
          enabledCheckBox.setChecked(equalizer.getEnabled());
          enabledCheckBox.setOnCheckedChangeListener((compoundButton, b) -> {
              try {
                  setEqualizerEnabled(b);
              } catch (Exception e) {
                  Log.e(TAG, "Failed to set EQ enabled", e);
                  Util.toast(context, "Failed to set EQ enabled");
                  context.onBackPressed();
              }
          });
  
          setTitle(R.string.equalizer_label);
          setSubtitle(null);
  
          return rootView;
      }
  
      @Override
      public void onPause() {
          super.onPause();
  
          try {
              equalizerController.saveSettings();
  
              if (!equalizer.getEnabled()) {
                  equalizerController.release();
              }
          } catch (Exception e) {
              Log.w(TAG, "Failed to release controller", e);
          }
      }
  
      @Override
      public void onResume() {
          super.onResume();
          equalizerController = DownloadService.getInstance().getEqualizerController();
          equalizer = equalizerController.getEqualizer();
          bass = equalizerController.getBassBoost();
      }
  
      @Override
      public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
          super.onCreateContextMenu(menu, view, menuInfo);
          if (!primaryFragment) {
              return;
          }
  
          short currentPreset;
          try {
              currentPreset = equalizer.getCurrentPreset();
          } catch (Exception x) {
              currentPreset = -1;
          }
  
          for (short preset = 0; preset < equalizer.getNumberOfPresets(); preset++) {
              MenuItem menuItem = menu.add(MENU_GROUP_PRESET, preset, preset, equalizer.getPresetName(preset));
              if (preset == currentPreset) {
                  menuItem.setChecked(true);
              }
          }
          menu.setGroupCheckable(MENU_GROUP_PRESET, true, true);
      }
  
      @Override
      public boolean onContextItemSelected(MenuItem menuItem) {
          short preset = (short) menuItem.getItemId();
          for (int i = 0; i < 10; i++) {
              try {
                  equalizer.usePreset(preset);
                  i = 10;
              } catch (UnsupportedOperationException e) {
                  equalizerController.release();
                  equalizer = equalizerController.getEqualizer();
                  bass = equalizerController.getBassBoost();
                  loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
              }
          }
          updateBars(false);
          return true;
      }
  
      private void setEqualizerEnabled(boolean enabled) {
          SharedPreferences prefs = Util.getPreferences(context);
          SharedPreferences.Editor editor = prefs.edit();
          editor.putBoolean(Constants.PREFERENCES_EQUALIZER_ON, enabled);
          editor.apply();
          for (int i = 0; i < 10; i++) {
              try {
                  equalizer.setEnabled(enabled);
                  updateBars(true);
                  i = 10;
              } catch (UnsupportedOperationException e) {
                  equalizerController.release();
                  equalizer = equalizerController.getEqualizer();
                  bass = equalizerController.getBassBoost();
                  loudnessEnhancer = equalizerController.getLoudnessEnhancerController();
              }
          }
      }
  
      private void updateBars(boolean changedEnabled) {
          try {
              boolean isEnabled = equalizer.getEnabled();
              short minEQLevel = equalizer.getBandLevelRange()[0];
              short maxEQLevel = equalizer.getBandLevelRange()[1];
              for (Map.Entry<Short, SeekBar> entry : bars.entrySet()) {
                  short band = entry.getKey();
                  SeekBar bar = entry.getValue();
                  bar.setEnabled(isEnabled);
                  if (band >= (short) 0) {
                      short setLevel;
                      if (changedEnabled) {
                          setLevel = (short) (equalizer.getBandLevel(band) - masterLevel);
                          if (isEnabled) {
                              bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
                          } else {
                              bar.setProgress(-minEQLevel);
                          }
                      } else {
                          bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
                          setLevel = (short) (equalizer.getBandLevel(band) + masterLevel);
                      }
                      if (setLevel < minEQLevel) {
                          setLevel = minEQLevel;
                      } else if (setLevel > maxEQLevel) {
                          setLevel = maxEQLevel;
                      }
                      equalizer.setBandLevel(band, setLevel);
                  } else if (!isEnabled) {
                      bar.setProgress(-minEQLevel);
                  }
              }
  
              bassBar.setEnabled(isEnabled);
              if (loudnessBar != null) {
                  loudnessBar.setEnabled(isEnabled);
              }
              if (changedEnabled && !isEnabled) {
                  bass.setStrength((short) 0);
                  bassBar.setProgress(0);
                  if (loudnessBar != null) {
                      loudnessEnhancer.setGain(0);
                      loudnessBar.setProgress(0);
                  }
              }
  
              if (!isEnabled) {
                  masterLevel = 0;
                  SharedPreferences prefs = Util.getPreferences(context);
                  SharedPreferences.Editor editor = prefs.edit();
                  editor.putInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, masterLevel);
                  editor.apply();
              }
          } catch (Exception e) {
              Log.e(TAG, "Failed to update bars");
          }
      }
  
      private void initEqualizer() {
          LinearLayout layout = rootView.findViewById(R.id.equalizer_layout);
  
          final short minEQLevel = equalizer.getBandLevelRange()[0];
          final short maxEQLevel = equalizer.getBandLevelRange()[1];
  
          // Setup Pregain
          SharedPreferences prefs = Util.getPreferences(context);
          masterLevel = (short) prefs.getInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, 0);
          initPregain(layout, minEQLevel, maxEQLevel);
  
          for (short i = 0; i < equalizer.getNumberOfBands(); i++) {
              final short band = i;
  
              View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
              TextView freqTextView = bandBar.findViewById(R.id.equalizer_frequency);
              final TextView levelTextView = bandBar.findViewById(R.id.equalizer_level);
              SeekBar bar = bandBar.findViewById(R.id.equalizer_bar);
  
              freqTextView.setText((equalizer.getCenterFreq(band) / 1000) + " Hz");
  
              bars.put(band, bar);
              bar.setMax(maxEQLevel - minEQLevel);
              short level = equalizer.getBandLevel(band);
              if (equalizer.getEnabled()) {
                  level = (short) (level - masterLevel);
              }
              bar.setProgress(level - minEQLevel);
              bar.setEnabled(equalizer.getEnabled());
              updateLevelText(levelTextView, level);
  
              bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                  @Override
                  public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                      try {
                          short level = (short) (progress + minEQLevel);
                          if (fromUser) {
                              equalizer.setBandLevel(band, (short) (level + masterLevel));
                          }
                          updateLevelText(levelTextView, level);
                      } catch (Exception e) {
                          Log.e(TAG, "Failed to change equalizer", e);
                      }
                  }
  
                  @Override
                  public void onStartTrackingTouch(SeekBar seekBar) {
                  }
  
                  @Override
                  public void onStopTrackingTouch(SeekBar seekBar) {
                  }
              });
              layout.addView(bandBar);
          }
  
          LinearLayout specialLayout = rootView.findViewById(R.id.special_effects_layout);
  
          // Setup bass booster
          View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
          TextView freqTextView = bandBar.findViewById(R.id.equalizer_frequency);
          final TextView bassTextView = bandBar.findViewById(R.id.equalizer_level);
          bassBar = bandBar.findViewById(R.id.equalizer_bar);
  
          freqTextView.setText(R.string.equalizer_bass_booster);
          bassBar.setEnabled(equalizer.getEnabled());
          short bassLevel = 0;
          if (bass.getEnabled()) {
              bassLevel = bass.getRoundedStrength();
          }
          bassTextView.setText(context.getResources().getString(R.string.equalizer_bass_size, bassLevel));
          bassBar.setMax(1000);
          bassBar.setProgress(bassLevel);
          bassBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
              @Override
              public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                  try {
                      bassTextView.setText(context.getResources().getString(R.string.equalizer_bass_size, progress));
                      if (fromUser) {
                          if (progress > 0) {
                              if (!bass.getEnabled()) {
                                  bass.setEnabled(true);
                              }
                              bass.setStrength((short) progress);
                          } else if (progress == 0 && bass.getEnabled()) {
                              bass.setStrength((short) progress);
                              bass.setEnabled(false);
                          }
                      }
                  } catch (Exception e) {
                      Log.w(TAG, "Error on changing bass: ", e);
                  }
              }
  
              @Override
              public void onStartTrackingTouch(SeekBar seekBar) {
  
              }
  
              @Override
              public void onStopTrackingTouch(SeekBar seekBar) {
  
              }
          });
          specialLayout.addView(bandBar);
  
          if (loudnessEnhancer != null && loudnessEnhancer.isAvailable()) {
              // Setup loudness enhancer
              bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
              freqTextView = bandBar.findViewById(R.id.equalizer_frequency);
              final TextView loudnessTextView = bandBar.findViewById(R.id.equalizer_level);
              loudnessBar = bandBar.findViewById(R.id.equalizer_bar);
  
              freqTextView.setText(R.string.equalizer_voice_booster);
              loudnessBar.setEnabled(equalizer.getEnabled());
              int loudnessLevel = 0;
              if (loudnessEnhancer.isEnabled()) {
                  loudnessLevel = (int) loudnessEnhancer.getGain();
              }
              loudnessBar.setProgress(loudnessLevel / 100);
              loudnessTextView.setText(context.getResources().getString(R.string.equalizer_db_size, loudnessLevel / 100));
              loudnessBar.setMax(15);
              loudnessBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                  @Override
                  public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                      try {
                          loudnessTextView.setText(context.getResources().getString(R.string.equalizer_db_size, progress));
                          if (fromUser) {
                              if (progress > 0) {
                                  if (!loudnessEnhancer.isEnabled()) {
                                      loudnessEnhancer.enable();
                                  }
                                  loudnessEnhancer.setGain(progress * 100);
                              } else if (progress == 0 && loudnessEnhancer.isEnabled()) {
                                  loudnessEnhancer.setGain(progress * 100);
                                  loudnessEnhancer.disable();
                              }
                          }
                      } catch (Exception e) {
                          Log.w(TAG, "Error on changing loudness: ", e);
                      }
                  }
  
                  @Override
                  public void onStartTrackingTouch(SeekBar seekBar) {
  
                  }
  
                  @Override
                  public void onStopTrackingTouch(SeekBar seekBar) {
  
                  }
              });
              specialLayout.addView(bandBar);
          }
      }
  
      private void initPregain(LinearLayout layout, final short minEQLevel, final short maxEQLevel) {
          View bandBar = LayoutInflater.from(context).inflate(R.layout.equalizer_bar, null);
          TextView freqTextView = bandBar.findViewById(R.id.equalizer_frequency);
          final TextView levelTextView = bandBar.findViewById(R.id.equalizer_level);
          SeekBar bar = bandBar.findViewById(R.id.equalizer_bar);
  
          freqTextView.setText("Master");
  
          bars.put((short) -1, bar);
          bar.setMax(maxEQLevel - minEQLevel);
          bar.setProgress(masterLevel - minEQLevel);
          bar.setEnabled(equalizer.getEnabled());
          updateLevelText(levelTextView, masterLevel);
  
          bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
              @Override
              public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                  try {
                      masterLevel = (short) (progress + minEQLevel);
                      if (fromUser) {
                          SharedPreferences prefs = Util.getPreferences(context);
                          SharedPreferences.Editor editor = prefs.edit();
                          editor.putInt(Constants.PREFERENCES_EQUALIZER_SETTINGS, masterLevel);
                          editor.apply();
                          for (short i = 0; i < equalizer.getNumberOfBands(); i++) {
                              short level = (short) ((bars.get(i).getProgress() + minEQLevel) + masterLevel);
                              equalizer.setBandLevel(i, level);
                          }
                      }
                      updateLevelText(levelTextView, masterLevel);
                  } catch (Exception e) {
                      Log.e(TAG, "Failed to change equalizer", e);
                  }
              }
  
              @Override
              public void onStartTrackingTouch(SeekBar seekBar) {
              }
  
              @Override
              public void onStopTrackingTouch(SeekBar seekBar) {
              }
          });
          layout.addView(bandBar);
      }
  
      private void updateLevelText(TextView levelTextView, short level) {
          levelTextView.setText((level > 0 ? "+" : "") + context.getResources().getString(R.string.equalizer_db_size, level / 100));
      }
  }
  