/*
 * Copyright 2000-2009 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 com.intellij.ui.popup.list;

import com.intellij.openapi.ui.popup.ListPopupStep;
import com.intellij.openapi.ui.popup.ListSeparator;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.speedSearch.ElementFilter;
import com.intellij.ui.speedSearch.SpeedSearch;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.ArrayList;
import java.util.List;

public class ListPopupModel extends AbstractListModel {

  private final List<Object> myOriginalList;
  private final List<Object> myFilteredList = new ArrayList<Object>();

  private final ElementFilter myFilter;
  private final ListPopupStep myStep;

  private int myFullMatchIndex = -1;
  private int myStartsWithIndex = -1;
  private final SpeedSearch mySpeedSearch;

  public ListPopupModel(ElementFilter filter, SpeedSearch speedSearch, ListPopupStep step) {
    myFilter = filter;
    myStep = step;
    mySpeedSearch = speedSearch;
    myOriginalList = new ArrayList<Object>(step.getValues());
    rebuildLists();
  }

  public void deleteItem(final Object item) {
    final int i = myOriginalList.indexOf(item);
    if (i >= 0) {
      myOriginalList.remove(i);
      rebuildLists();
      fireContentsChanged(this, 0, myFilteredList.size());
    }
  }

  @Nullable
  public Object get(final int i) {
    if (i >= 0 && i < myFilteredList.size()) {
      return myFilteredList.get(i);
    }

    return null;
  }

  private void rebuildLists() {
    myFilteredList.clear();
    myFullMatchIndex = -1;
    myStartsWithIndex = -1;

    for (Object each : myOriginalList) {
      if (myFilter.shouldBeShowing(each)) {
        addToFiltered(each);
      }
    }
  }

  private void addToFiltered(Object each) {
    myFilteredList.add(each);
    String filterString = StringUtil.toUpperCase(mySpeedSearch.getFilter());
    String candidateString = StringUtil.toUpperCase(myStep.getTextFor(each));
    int index = myFilteredList.size() - 1;

    if (myFullMatchIndex == -1 && filterString.equals(candidateString)) {
      myFullMatchIndex = index;
    }

    if (myStartsWithIndex == -1 && candidateString.startsWith(filterString)) {
      myStartsWithIndex = index;
    }
  }

  public int getSize() {
    return myFilteredList.size();
  }

  public Object getElementAt(int index) {
    if (index >= myFilteredList.size()) {
      return null;
    }
    return myFilteredList.get(index);
  }

  public boolean isSeparatorAboveOf(Object aValue) {
    return getSeparatorAbove(aValue) != null;
  }

  public String getCaptionAboveOf(Object value) {
    ListSeparator separator = getSeparatorAbove(value);
    if (separator != null) {
      return separator.getText();
    }
    return "";
  }

  private ListSeparator getSeparatorAbove(Object value) {
    return myStep.getSeparatorAbove(value);
  }

  public void refilter() {
    rebuildLists();
    if (myFilteredList.isEmpty() && !myOriginalList.isEmpty()) {
      mySpeedSearch.noHits();
    }
    else {
      fireContentsChanged(this, 0, myFilteredList.size());
    }
  }

  public boolean isVisible(Object object) {
    return myFilteredList.contains(object);
  }

  public int getClosestMatchIndex() {
    return myFullMatchIndex != -1 ? myFullMatchIndex : myStartsWithIndex;
  }

}

@@ -883,16 +883,54 @@
 Search;%0A
+import com.intellij.util.ObjectUtils;%0A
 import o
@@ -1032,20 +1032,68 @@
 va.util.
-List
+HashMap;%0Aimport java.util.List;%0Aimport java.util.Map
 ;%0A%0Apubli
@@ -1457,16 +1457,112 @@
 dSearch;
+%0A  private final Map%3CObject, ListSeparator%3E mySeparators = new HashMap%3CObject, ListSeparator%3E();
 %0A%0A  publ
@@ -2261,24 +2261,50 @@
 st.clear();%0A
+    mySeparators.clear();%0A
     myFullMa
@@ -2343,24 +2343,64 @@
 ndex = -1;%0A%0A
+    ListSeparator lastSeparator = null;%0A
     for (Obj
@@ -2438,74 +2438,299 @@
     
-if (myFilter.shouldBeShowing(each)) %7B%0A        addToFiltered(each);
+lastSeparator = ObjectUtils.chooseNotNull(myStep.getSeparatorAbove(each), lastSeparator);%0A%0A      if (myFilter.shouldBeShowing(each)) %7B%0A        addToFiltered(each);%0A        if (lastSeparator != null) %7B%0A          mySeparators.put(each, lastSeparator);%0A          lastSeparator = null;%0A        %7D
 %0A   
@@ -3841,32 +3841,24 @@
 turn myS
-tep.getS
 eparator
 Above(va
@@ -3841,37 +3841,37 @@
 turn mySeparator
-Above
+s.get
 (value);%0A  %7D%0A%0A  

fix separators in list popup with speed-search