
package de.ivu.elect.presentation.menu;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.gebietsbaum.entity.GebietMeta;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.presentation.menu.AbstractSubMenuEntry;
import de.ivu.elect.jee.presentation.menu.MenuBuilder;
import de.ivu.elect.jee.presentation.menu.MenuEntry;


import static de.ivu.elect.jee.presentation.menu.AbstractBaseMenuItemEntry.NO_ACTION_NO_REDIRECT;
import static de.ivu.elect.jee.presentation.menu.AbstractBaseMenuItemEntry.TARGET_EXTERN;
import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 *  on 08.06.2015.
 */
@RequestScoped
@Named(MainMenuSearch.COMPONENT_NAME)
public class MainMenuSearch implements Serializable {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "mainMenuSearch";

  private static final int LIMIT = 10;

  @Inject
  private transient Instance<MenuBuilder> menuBuilders;

  @Inject
  private transient MainMenuSearchGebietCache mainMenuSearchGebietCache;

  @Inject
  private transient GebietMeta gebietMeta;

  private String entry;

  private Map<String, AbstractMenuItemEntry> items;

  public MainMenuSearch() {
    super();
  }

  public String getEntry() {
    return entry;
  }

  public void setEntry(String entry) {
    this.entry = entry;
  }

  public List<String> onComplete(String value) {
    if (isBlank(value)) {
      return Collections.emptyList();
    }
    return limit(sort(filter(mappeItems().keySet(), value), value), LIMIT);
  }

  private Set<String> filter(Set<String> strings, String value) {
    return strings.stream()
        .filter(string -> matches(value, string))
        .collect(Collectors.toCollection(() -> new HashSet<>(strings.size())));
  }

  private boolean matches(String eingabe, String auswahl) {
    String[] eingabeSplitted = eingabe.trim().toLowerCase().split(" ");
    String auswahlLower = auswahl.toLowerCase();
    boolean einTreffer = false;
    for (String teil : eingabeSplitted) {
      if (StringUtils.isNotBlank(teil)) {
        if (!auswahlLower.contains(teil.trim())) {
          return false;
        }
        einTreffer = true;
      }
    }

    return einTreffer;
  }

  private List<String> sort(Set<String> stringSet, String eingabe) {
    List<String> strings = new ArrayList<>(stringSet);
    if (StringUtils.isBlank(eingabe)) {
      Collections.sort(strings);
    } else {
      String[] eingabeSplitted = eingabe.trim().toLowerCase().split(" ");

      strings.sort(new Comparator<String>() {

        @Override
        public int compare(String o1, String o2) {
          boolean sw1 = startWidth(o1);
          boolean sw2 = startWidth(o2);
          if (sw1) {
            if (!sw2) {
              return -1;
            }
          }
          if (sw2) {
            if (!sw1) {
              return 1;
            }
          }

          return o1.compareTo(o2);
        }

        private boolean startWidth(String string) {
          String stringLower = string.toLowerCase();
          return Arrays.stream(eingabeSplitted).anyMatch(s -> stringLower.startsWith(s.trim()));
        }
      });
    }

    return strings;

  }

  private List<String> limit(List<String> list, int limit) {
    if (list.size() > limit) {
      return list.subList(0, limit);
    }

    return list;
  }

  public void select() {
    if (isBlank(entry)) {
      return;
    }

    Map<String, AbstractMenuItemEntry> items = mappeItems();
    Set<String> filtered = filter(items.keySet(), entry);

    String prefix = createPrefix();
    if (filtered.size() == 1 || filtered.contains(entry) || filtered.contains(prefix + entry)) {

      String treffer = null;
      if (filtered.size() == 1) {
        treffer = filtered.iterator().next();
      } else if (filtered.contains(entry) || filtered.contains(prefix + entry)) {
        treffer = entry;
      }
      if (treffer == null) {
        return;
      }

      AbstractMenuItemEntry item = items.get(treffer);

      if (item != null) {

        String redirect = item.getTargetViewId();
        if (redirect == null) {
          redirect = item.doAction();
        }

        if (redirect != null && !NO_ACTION_NO_REDIRECT.equals(redirect)) {
          if (TARGET_EXTERN.equals(item.getTarget())) {
            FacesContextUtil.redirectToExternalUrl(redirect);
          } else {
            FacesContextUtil.redirectToView(redirect);
          }
        }
      } else {
        // gebiet:
        if (treffer.startsWith(prefix)) {
          String nameGebiet = treffer.substring(prefix.length());
          if (mainMenuSearchGebietCache.getGebietNamen().contains(nameGebiet)) {
            mainMenuSearchGebietCache.select(nameGebiet);
          }
        }
      }
    }
  }

  @Nonnull
  private String createPrefix() {
    String gebietBezeichnung = gebietMeta.getEinzahlMessage();
    return gebietBezeichnung + ": ";
  }

  private Map<String, AbstractMenuItemEntry> mappeItems() {
    if (items == null) {
      items = bestimmeItems();
    }
    return items;
  }

  private Map<String, AbstractMenuItemEntry> bestimmeItems() {
    Map<String, AbstractMenuItemEntry> result = new HashMap<>();

    for (MenuBuilder menuBuilder : menuBuilders) {
      mappe(result, menuBuilder.listMenuEntries());
    }

    String prefix = createPrefix();
    for (String gebietName : mainMenuSearchGebietCache.getGebietNamen()) {
      result.put(prefix + gebietName, null);
    }

    return result;
  }

  private void mappe(Map<String, AbstractMenuItemEntry> result, List<MenuEntry> menuEntriesSorted) {
    for (MenuEntry menuEntry : menuEntriesSorted) {
      if (menuEntry instanceof AbstractMenuItemEntry) {
        AbstractMenuItemEntry menuItemEntry = (AbstractMenuItemEntry) menuEntry;
        if (menuItemEntry.isRendered() && !menuItemEntry.isDisabled() && StringUtils.isBlank(menuItemEntry.getDialogName())) {
          result.put(menuItemEntry.getLabelFinal(), menuItemEntry);
        }
      }
      if (menuEntry instanceof AbstractSubMenuEntry) {
        AbstractSubMenuEntry subMenuEntry = (AbstractSubMenuEntry) menuEntry;
        mappe(result, subMenuEntry.getSubMenuEntries());
      }
    }
  }

  public boolean isMainMenuSearchEnabled(){
    return true;
  }
}
