/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * 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 org.jetbrains.idea.svn.history;

import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MultiLineLabelUI;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.vcs.RepositoryLocation;
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
import com.intellij.openapi.vcs.changes.committed.*;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.util.NotNullFunction;
import com.intellij.util.messages.Topic;
import icons.SvnIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.actions.AbstractIntegrateChangesAction;
import org.jetbrains.idea.svn.actions.ShowSvnMapAction;
import org.jetbrains.idea.svn.dialogs.WCInfoWithBranches;
import org.jetbrains.idea.svn.integrate.ChangeListsMergerFactory;
import org.jetbrains.idea.svn.integrate.MergerFactory;
import org.jetbrains.idea.svn.integrate.SelectedChangeListsChecker;
import org.jetbrains.idea.svn.integrate.SelectedCommittedStuffChecker;
import org.jetbrains.idea.svn.mergeinfo.ListMergeStatus;
import org.jetbrains.idea.svn.mergeinfo.MergeInfoHolder;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RootsAndBranches implements CommittedChangeListDecorator {
  private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.RootsAndBranches");

  @NotNull private final SvnVcs myVcs;
  @NotNull private final Project myProject;
  private final DecoratorManager myManager;
  private final RepositoryLocation myLocation;
  private JPanel myPanel;
  private final Map<String, SvnMergeInfoRootPanelManual> myMergePanels;
  private final Map<String, MergeInfoHolder> myHolders;

  private boolean myHighlightingOn;
  private JPanel myPanelWrapper;
  private final MergePanelFiltering myStrategy;
  private final FilterOutMerged myFilterMerged;
  private final FilterOutNotMerged myFilterNotMerged;
  private final FilterOutAlien myFilterAlien;
  private final IntegrateChangeListsAction myIntegrateAction;
  private final IntegrateChangeListsAction myUndoIntegrateChangeListsAction;
  private JComponent myToolbarComponent;

  private boolean myDisposed;

  private final WcInfoLoader myDataLoader;

  public static final Topic<Runnable> REFRESH_REQUEST = new Topic<Runnable>("REFRESH_REQUEST", Runnable.class);

  private MergeInfoHolder getHolder(final String key) {
    final MergeInfoHolder holder = myHolders.get(key);
    if (holder != null) {
      return holder;
    }
    return myHolders.get(key.endsWith(File.separator) ? key.substring(0, key.length() - 1) : key + File.separator);
  }

  private SvnMergeInfoRootPanelManual getPanelData(final String key) {
    final SvnMergeInfoRootPanelManual panel = myMergePanels.get(key);
    if (panel != null) {
      return panel;
    }
    return myMergePanels.get(key.endsWith(File.separator) ? key.substring(0, key.length() - 1) : key + File.separator);
  }

  public RootsAndBranches(@NotNull SvnVcs vcs, @NotNull DecoratorManager manager, final RepositoryLocation location) {
    myVcs = vcs;
    myProject = vcs.getProject();
    myManager = manager;
    myLocation = location;

    myDataLoader = new WcInfoLoader(myVcs, myLocation);

    myMergePanels = new HashMap<String, SvnMergeInfoRootPanelManual>();
    myHolders = new HashMap<String, MergeInfoHolder>();

    myFilterMerged = new FilterOutMerged();
    myFilterNotMerged = new FilterOutNotMerged();
    myFilterAlien = new FilterOutAlien();
    myIntegrateAction = new IntegrateChangeListsAction(true);
    myUndoIntegrateChangeListsAction = new IntegrateChangeListsAction(false);

    myPanel = new JPanel(new GridBagLayout());
    createToolbar();
    final GridBagConstraints gb =
      new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(1, 1, 1, 1), 0, 0);
    gb.insets = new Insets(20, 1, 1, 1);
    myPanel.add(new JLabel("Loading..."), gb);
    
    myPanel.setPreferredSize(new Dimension(200, 60));

    myManager.install(this);

    myStrategy = new MergePanelFiltering(getPanel());
  }

  public IntegrateChangeListsAction getIntegrateAction() {
    return myIntegrateAction;
  }

  public IntegrateChangeListsAction getUndoIntegrateAction() {
    return myUndoIntegrateChangeListsAction;
  }

  public boolean isHighlightingOn() {
    return myHighlightingOn;
  }

  public void reloadPanels() {
    final Map<Couple<String>, SvnMergeInfoRootPanelManual.InfoHolder> states = new HashMap<Couple<String>, SvnMergeInfoRootPanelManual.InfoHolder>();
    for (Map.Entry<String, SvnMergeInfoRootPanelManual> entry : myMergePanels.entrySet()) {
      final String localPath = entry.getKey();
      final WCInfoWithBranches wcInfo = entry.getValue().getWcInfo();
      states.put(Couple.of(localPath, wcInfo.getUrl().toString()), entry.getValue().getInfo());
    }
    createPanels(myLocation, new Runnable() {
      public void run() {
        for (Map.Entry<String, SvnMergeInfoRootPanelManual> entry : myMergePanels.entrySet()) {
          final String localPath = entry.getKey();
          final WCInfoWithBranches wcInfo = entry.getValue().getWcInfo();
          final Couple<String> key = Couple.of(localPath, wcInfo.getUrl().toString());
          final SvnMergeInfoRootPanelManual.InfoHolder infoHolder = states.get(key);
          if (infoHolder !=  null) {
            entry.getValue().initSelection(infoHolder);
          }
        }
      }
    });
  }

  public void turnFromHereHighlighting() {
    myHighlightingOn = true;
    for (MergeInfoHolder holder : myHolders.values()) {
      holder.updateMixedRevisionsForPanel();
    }

    myManager.repaintTree();
  }

  public void turnOff() {
    myHighlightingOn = false;
    for (SvnMergeInfoRootPanelManual panelManual : myMergePanels.values()) {
      panelManual.setMixedRevisions(false);
    }

    myManager.repaintTree();
  }

  public Icon decorate(final CommittedChangeList list) {
    final ListMergeStatus status = getStatus(list, false);
    return (status == null) ? ListMergeStatus.ALIEN.getIcon() : status.getIcon();
  }
  
  private void createPanels(final RepositoryLocation location, final Runnable afterRefresh) {
    final Task.Backgroundable backgroundable = new Task.Backgroundable(myProject, "Subversion: loading working copies data..", false,
                                                                        BackgroundFromStartOption.getInstance()) {
      public void run(@NotNull final ProgressIndicator indicator) {
        indicator.setIndeterminate(true);
        final Map<String, SvnMergeInfoRootPanelManual> panels = new HashMap<String, SvnMergeInfoRootPanelManual>();
        final Map<String, MergeInfoHolder> holders = new HashMap<String, MergeInfoHolder>();
        final List<WCInfoWithBranches> roots = myDataLoader.loadRoots();
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            if (myDisposed) return;
            final JPanel mainPanel = prepareData(panels, holders, roots);

            myMergePanels.clear();
            myHolders.clear();
            myMergePanels.putAll(panels);
            myHolders.putAll(holders);
            
            if (myPanelWrapper != null) {
              myPanelWrapper.removeAll();
              if (myMergePanels.isEmpty()) {
                final JPanel emptyPanel = new JPanel(new GridBagLayout());
                final GridBagConstraints gb =
                  new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0);
                final JLabel label = new JLabel("No Subversion 1.5 working copies\nof 1.5 repositories in the project");
                label.setUI(new MultiLineLabelUI());
                emptyPanel.add(label, gb);
                gb.fill = GridBagConstraints.HORIZONTAL;
                myPanelWrapper.add(emptyPanel, gb);
              } else {
                for (MergeInfoHolder holder : myHolders.values()) {
                  holder.updateMixedRevisionsForPanel();
                }
                myPanelWrapper.add(mainPanel, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
              }
              myPanelWrapper.repaint();
            } else {
              myPanel = mainPanel;
            }
            if (afterRefresh != null) {
              afterRefresh.run();
            }
          }
        });
      }
    };
    ProgressManager.getInstance().run(backgroundable);
  }

  public void refreshByLists(final List<CommittedChangeList> committedChangeLists) {
    if (! committedChangeLists.isEmpty()) {
      final SvnChangeList svnList = (SvnChangeList) committedChangeLists.get(0);
      final String wcPath = svnList.getWcPath();
      if (wcPath != null) {
        final MergeInfoHolder holder = getHolder(wcPath);
        if (holder != null) {
          holder.refresh(true);
        }
      }
    }
  }

  private void createToolbar() {
    final DefaultActionGroup svnGroup = createActions();
    final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, svnGroup, true);
    myToolbarComponent = actionToolbar.getComponent();
  }

  private JPanel prepareData(final Map<String, SvnMergeInfoRootPanelManual> panels, final Map<String, MergeInfoHolder> holders,
                             List<WCInfoWithBranches> roots) {
      final JPanel mainPanel = new JPanel(new GridBagLayout());
      boolean onlyOneRoot = roots.size() == 1;
      final GridBagConstraints gb = new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL,
                                                           new Insets(0,0,0,0), 0, 0);
      mainPanel.add(myToolbarComponent, gb);
      ++ gb.gridy;

      for (final WCInfoWithBranches root : roots) {
        if (root == null) {
          continue;
        }
        final SvnMergeInfoRootPanelManual panel = new SvnMergeInfoRootPanelManual(myProject,
                                                                                  new NotNullFunction<WCInfoWithBranches, WCInfoWithBranches>() {
                                                                                    @NotNull
                                                                                    public WCInfoWithBranches fun(@NotNull WCInfoWithBranches wcInfoWithBranches) {
                                                                                      final WCInfoWithBranches newInfo =
                                                                                        myDataLoader.reloadInfo(wcInfoWithBranches);
                                                                                      if (newInfo == null) {
                                                                                        // reload all items
                                                                                        myProject.getMessageBus().syncPublisher(SvnVcs.WC_CONVERTED).run();
                                                                                        // do not reload right now
                                                                                        return wcInfoWithBranches;
                                                                                      }
                                                                                      return newInfo;
                                                                                    }
                                                                                  }, new Runnable() {
            public void run() {
              final MergeInfoHolder holder = getHolder(root.getPath());
              if (holder != null) {
                holder.refresh(false);
              }
            }
          }, onlyOneRoot, root);
        panels.put(root.getPath(), panel);
        holders.put(root.getPath(), createHolder(panel));

        final JPanel contentPanel = panel.getContentPanel();
        mainPanel.add(contentPanel, gb);
        ++ gb.gridy;
      }
      if (panels.size() == 1) {
        for (SvnMergeInfoRootPanelManual panel : panels.values()) {
          panel.setOnlyOneRoot(true);
        }
      }
      return mainPanel;
  }

  private DefaultActionGroup createActions() {
    final DefaultActionGroup svnGroup = new DefaultActionGroup();
    svnGroup.add(new HighlightFrom());
    svnGroup.add(myIntegrateAction);
    svnGroup.add(myUndoIntegrateChangeListsAction);
    svnGroup.add(new MarkAsMerged(true));
    svnGroup.add(new MarkAsMerged(false));
    svnGroup.add(myFilterMerged);
    svnGroup.add(myFilterNotMerged);
    svnGroup.add(myFilterAlien);
    svnGroup.add(new ShowSvnMapAction());
    svnGroup.add(new MyRefresh());
    return svnGroup;
  }

  @NotNull
  private MergeInfoHolder createHolder(@NotNull SvnMergeInfoRootPanelManual panel) {
    return new MergeInfoHolder(myProject, myManager, this, panel);
  }

  public JComponent getPanel() {
    myPanelWrapper = new JPanel(new GridBagLayout()) {
      @Override
      public Dimension getPreferredSize() {
        final Dimension oldSize = super.getPreferredSize();
        oldSize.width = 200;
        return oldSize;
      }
    };
    //myPanelWrapper.setPreferredSize(new Dimension(200, 800));
    final GridBagConstraints gb =
      new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
    myPanelWrapper.add(myPanel, gb);
    return ScrollPaneFactory
      .createScrollPane(myPanelWrapper, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
  }

  // todo refactor to get rid of duplicate code dealing with separators
  private String ensureEndsWithSeparator(final String wcPath) {
    return wcPath.endsWith(File.separator) ? wcPath : (wcPath + File.separator);
  }

  public void refresh() {
    final Map<String, CommittedChangeListsListener> refreshers = new HashMap<String, CommittedChangeListsListener>();

    for (Map.Entry<String, MergeInfoHolder> entry : myHolders.entrySet()) {
      final CommittedChangeListsListener refresher = entry.getValue().createRefresher(false);
      if (refresher != null) {
        refreshers.put(ensureEndsWithSeparator(entry.getKey()), refresher);
      }
    }

    if (! refreshers.isEmpty()) {
      myManager.reportLoadedLists(new CommittedChangeListsListener() {
        public void onBeforeStartReport() {
        }

        public boolean report(final CommittedChangeList list) {
          if (list instanceof SvnChangeList) {
            final SvnChangeList svnList = (SvnChangeList) list;
            final String wcPath = svnList.getWcPath();
            if (wcPath != null) {
              final CommittedChangeListsListener refresher = refreshers.get(ensureEndsWithSeparator(wcPath));
              if (refresher != null) {
                refresher.report(list);
              }
            }
          }
          return true;
        }

        public void onAfterEndReport() {
          for (CommittedChangeListsListener refresher : refreshers.values()) {
            refresher.onAfterEndReport();
          }
          myStrategy.notifyListener();
        }
      });
      myManager.repaintTree();
    }
  }


  private class MyRefresh extends AnAction {
    private MyRefresh() {
      super(SvnBundle.message("committed.changes.action.merge.highlighting.refresh.text"),
            SvnBundle.message("committed.changes.action.merge.highlighting.refresh.description"), AllIcons.Actions.Refresh);
    }

    @Override
    public void update(final AnActionEvent e) {
      for (MergeInfoHolder holder : myHolders.values()) {
        if (holder.refreshEnabled(false)) {
          e.getPresentation().setEnabled(true);
          return;
        }
      }
      e.getPresentation().setEnabled(false);
    }

    public void actionPerformed(final AnActionEvent e) {
      final Presentation presentation = e.getPresentation();
      presentation.setEnabled(false);

      refresh();
    }
  }

  private class HighlightFrom extends ToggleAction {
    @Override
    public void update(final AnActionEvent e) {
      super.update(e);
      final Presentation presentation = e.getPresentation();
      presentation.setIcon(SvnIcons.ShowIntegratedFrom);
      presentation.setText(SvnBundle.message("committed.changes.action.enable.merge.highlighting"));
      presentation.setDescription(SvnBundle.message("committed.changes.action.enable.merge.highlighting.description.text"));
    }

    public boolean isSelected(final AnActionEvent e) {
      return myHighlightingOn;
    }

    public void setSelected(final AnActionEvent e, final boolean state) {
      if (state) {
        turnFromHereHighlighting();
      } else {
        turnOff();
      }
    }
  }

  private abstract class CommonFilter extends ToggleAction {
    private boolean mySelected;
    private final Icon myIcon;

    protected CommonFilter(final Icon icon, final String text) {
      super(text);
      myIcon = icon;
    }

    @Override
    public void update(final AnActionEvent e) {
      super.update(e);
      final Presentation presentation = e.getPresentation();
      presentation.setEnabled(myHighlightingOn);
      presentation.setIcon(myIcon);
      presentation.setText(getTemplatePresentation().getText());
    }

    public boolean isSelected(final AnActionEvent e) {
      return mySelected;
    }

    public void setSelected(final AnActionEvent e, final boolean state) {
      mySelected = state;
      myStrategy.notifyListener();
    }
  }

  private class FilterOutMerged extends CommonFilter {
    private FilterOutMerged() {
      super(SvnIcons.FilterIntegrated, SvnBundle.message("tab.repository.merge.panel.filter.plus"));
    }
  }

  private class FilterOutNotMerged extends CommonFilter {
    private FilterOutNotMerged() {
      super(SvnIcons.FilterNotIntegrated, SvnBundle.message("tab.repository.merge.panel.filter.minus"));
    }
  }

  private class FilterOutAlien extends CommonFilter {
    private FilterOutAlien() {
      super(SvnIcons.FilterOthers, SvnBundle.message("tab.repository.merge.panel.filter.others"));
    }
  }

  private boolean mergeEnabled(final List<CommittedChangeList> listsList, final boolean forMerge) {
    if ((listsList == null) || (listsList.isEmpty())) {
      return false;
    }
    for (CommittedChangeList list : listsList) {
      if (! mergeEnabled(list, forMerge)) {
        return false;
      }
    }
    return true;
  }

  private boolean mergeEnabled(final CommittedChangeList list, final boolean forMerge) {
    final ListMergeStatus mergeStatus = getStatus(list, true);
    if ((mergeStatus == null) || (ListMergeStatus.ALIEN.equals(mergeStatus))) {
      return false;
    } else if (ListMergeStatus.REFRESHING.equals(mergeStatus)) {
      return true;
    }
    if (forMerge) {
      return ListMergeStatus.NOT_MERGED.equals(mergeStatus);
    }
    return ListMergeStatus.MERGED.equals(mergeStatus);
  }

  private class MarkAsMerged extends AbstractIntegrateChangesAction<SelectedChangeListsChecker> {
    private final String myText;
    private final String myDescription;
    private final boolean myMarkAsMerged;

    private MarkAsMerged(boolean markAsMerged) {
      super(false);
      myMarkAsMerged = markAsMerged;
      myText = message("action.mark.list.as.%s.text");
      myDescription = message("action.mark.list.as.%s.description");
    }

    @NotNull
    protected MergerFactory createMergerFactory(SelectedChangeListsChecker checker) {
      return new ChangeListsMergerFactory(checker.getSelectedLists(), true, !myMarkAsMerged, false);
    }

    @NotNull
    protected SelectedChangeListsChecker createChecker() {
      return new SelectedChangeListsChecker();
    }

    @Override
    protected void updateWithChecker(AnActionEvent e, SelectedCommittedStuffChecker checker) {
      final Presentation presentation = e.getPresentation();
      presentation.setIcon(myMarkAsMerged ? SvnIcons.MarkAsMerged : SvnIcons.MarkAsNotMerged);
      presentation.setText(myText);
      presentation.setDescription(myDescription);
      presentation.setEnabled(presentation.isEnabled() && mergeEnabled(checker.getSelectedLists(), myMarkAsMerged));
    }
    
    @Nullable
    protected String getSelectedBranchUrl(SelectedCommittedStuffChecker checker) {
      final SvnMergeInfoRootPanelManual data = getPanelData(checker.getSelectedLists());
      if (data != null) {
        return data.getBranch().getUrl();
      }
      return null;
    }

    @Nullable
    protected String getSelectedBranchLocalPath(SelectedCommittedStuffChecker checker) {
      final SvnMergeInfoRootPanelManual data = getPanelData(checker.getSelectedLists());
      if (data != null) {
        return data.getLocalBranch();
      }
      return null;
    }

    protected String getDialogTitle() {
      return myText;
    }

    @NotNull
    private String message(@NotNull String key) {
      return SvnBundle.message(String.format(key, myMarkAsMerged ? "merged" : "not.merged"));
    }
  }

  private class IntegrateChangeListsAction extends AbstractIntegrateChangesAction<SelectedChangeListsChecker> {
    private final boolean myIntegrate;

    public IntegrateChangeListsAction(boolean integrate) {
      super(false);
      myIntegrate = integrate;
    }

    @NotNull
    protected MergerFactory createMergerFactory(final SelectedChangeListsChecker checker) {
      return new ChangeListsMergerFactory(checker.getSelectedLists(), false, !myIntegrate, false);
    }

    @NotNull
    protected SelectedChangeListsChecker createChecker() {
      return new SelectedChangeListsChecker();
    }

    @Override
    protected void updateWithChecker(AnActionEvent e, SelectedCommittedStuffChecker checker) {
      if (myIntegrate) {
        e.getPresentation().setIcon(SvnIcons.IntegrateToBranch);
      }
      else {
        e.getPresentation().setIcon(SvnIcons.UndoIntegrateToBranch);
        e.getPresentation().setText(SvnBundle.message("undo.integrate.to.branch"));
        e.getPresentation().setDescription(SvnBundle.message("undo.integrate.to.branch.description"));
      }
    }

    protected String getSelectedBranchUrl(SelectedCommittedStuffChecker checker) {
      final SvnMergeInfoRootPanelManual data = getPanelData(checker.getSelectedLists());
      if (data != null && data.getBranch() != null) {
        return data.getBranch().getUrl();
      }
      return null;
    }

    protected String getSelectedBranchLocalPath(SelectedCommittedStuffChecker checker) {
      final SvnMergeInfoRootPanelManual data = getPanelData(checker.getSelectedLists());
      if (data != null) {
        return data.getLocalBranch();
      }
      return null;
    }

    protected String getDialogTitle() {
      return !myIntegrate ? SvnBundle.message("undo.integrate.to.branch.dialog.title") : null;
    }
  }

  private SvnMergeInfoRootPanelManual getPanelData(final List<CommittedChangeList> listsList) {
    for (CommittedChangeList list : listsList) {
      if (! (list instanceof SvnChangeList)) {
        return null;
      }
      final SvnChangeList svnList = (SvnChangeList) list;
      final String wcPath = svnList.getWcPath();
      if (wcPath == null) {
        continue;
      }
      return getPanelData(wcPath);
    }
    return null;
  }

  @Nullable
  public ListMergeStatus getStatus(final CommittedChangeList list, final boolean ignoreEnabled) {
    if (! (list instanceof SvnChangeList)) {
      return null;
    }

    final SvnChangeList svnList = (SvnChangeList) list;
    final String wcPath = svnList.getWcPath();
    MergeInfoHolder holder = null;
    if (wcPath == null) {
      for (Map.Entry<String, SvnMergeInfoRootPanelManual> entry : myMergePanels.entrySet()) {
        final SvnMergeInfoRootPanelManual panelManual = entry.getValue();
        if ((panelManual.getBranch() != null) && (panelManual.getBranch().getUrl() != null) &&
            svnList.allPathsUnder(panelManual.getBranch().getUrl())) {
          holder = getHolder(entry.getKey());
        }
      }
    } else {
      holder = getHolder(wcPath);
    }
    if (holder != null) {
      return holder.check(list, ignoreEnabled);
    }
    return null;
  }

  public MergePanelFiltering getStrategy() {
    return myStrategy;
  }

  public boolean strategyInitialized() {
    return myStrategy.isInitialized();
  }

  private class MergePanelFiltering implements ChangeListFilteringStrategy {
    private final JComponent myPanel;
    private ChangeListener myListener;
    private boolean myInitialized;
    private final static String ourKey = "MERGE_PANEL";

    public MergePanelFiltering(final JComponent panel) {
      myPanel = panel;
    }

    public boolean isInitialized() {
      return myInitialized;
    }

    public JComponent getFilterUI() {
      if (! myInitialized) {
        createPanels(myLocation, null);
      }
      myInitialized = true;
      return myPanel;
    }

    @Override
    public CommittedChangesFilterKey getKey() {
      return new CommittedChangesFilterKey(ourKey, CommittedChangesFilterPriority.MERGE);
    }

    public void setFilterBase(final List<CommittedChangeList> changeLists) {
    }

    public void addChangeListener(final ChangeListener listener) {
      myListener = listener;
    }

    public void removeChangeListener(final ChangeListener listener) {
      myListener = null;
    }

    public void resetFilterBase() {
    }

    public void appendFilterBase(List<CommittedChangeList> changeLists) {
    }

    @NotNull
    public List<CommittedChangeList> filterChangeLists(final List<CommittedChangeList> changeLists) {
      if ((! myFilterAlien.isSelected(null)) && (! myFilterNotMerged.isSelected(null)) && (! myFilterMerged.isSelected(null))) {
        return changeLists;
      }

      final List<CommittedChangeList> result = new ArrayList<CommittedChangeList>();
      for (CommittedChangeList list : changeLists) {
        final ListMergeStatus status = getStatus(list, true);
        if (ListMergeStatus.REFRESHING.equals(status)) {
          result.add(list);
        } else if ((status == null) || ListMergeStatus.ALIEN.equals(status)) {
          if (! myFilterAlien.isSelected(null)) {
            result.add(list);
          }
        } else if (ListMergeStatus.MERGED.equals(status) || ListMergeStatus.COMMON.equals(status)) {
          if (! myFilterMerged.isSelected(null)) {
            result.add(list);
          }
        } else {
          // not merged
          if (! myFilterNotMerged.isSelected(null)) {
            result.add(list);
          }
        }
      }
      return result;
    }

    public void notifyListener() {
      if (myListener != null) {
        myListener.stateChanged(new ChangeEvent(this));
      }
      myManager.repaintTree();
    }
  }

  public void fireRepaint() {
    myManager.repaintTree();
  }

  public void dispose() {
    myDisposed = true;
  }
}
