package jp.ac.dendai.cdl.mori.wikie.util;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.ac.dendai.cdl.mori.wikie.WikIE;

import org.apache.commons.lang.StringUtils;

/**
 * リンク等に関するメソッドをまとめたクラス
 * @author Mori
 *
 */
public class WLinkUtils {
    /**
     * 使用するWNormalizer
     */
    private WNormalizer normalizer;

    public static final String[] REGEX_FOR_RELATED = {"<!--.*-->","<div +.*div>","<.*?>",
                                                      "\\{\\{.*?\\}\\}" , "==.*"};

                                                      public static final String[] REGEX_COMMON = { "\\[\\[[^]]*?\\|", "\\[\\[",
                                                          "\\]\\]", "''+" };

    public WLinkUtils(WNormalizer normalizer) {
        this.normalizer = normalizer;
    }

    /**
     * 二重括弧[[]]の中身を取得する
     * @param text text要素
     * @return 二重括弧[[]]の中身をStringで格納したList
     */
    public List<String> getDoubleBracket(String text) {
        List<String> result = new ArrayList<String>();
        text = text.replaceAll("<nowiki>.*?</nowiki>", "");
        Pattern pattern = Pattern.compile("\\[\\[(.+?)\\]\\]");
        Matcher matcher = pattern.matcher(text);
        while (matcher.find()) {
            String entry = matcher.group(1).trim();
            if (StringUtils.isNotBlank(entry)) {
                result.add(entry);
            }
        }
        return result;
    }

    public List<String> getDoubleBracket2(String text) {
        List<String> result = new ArrayList<String>();
        text = text.replaceAll("<nowiki>.*?</nowiki>", "");
        Pattern pattern = Pattern.compile("\\{\\{(.+?)[\n|\\}]", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(text);
        while (matcher.find()) {
            String entry = matcher.group(1).trim();
            if (StringUtils.isNotBlank(entry)) {
                result.add(entry);
            }
        }
        return result;
    }

    public List<WTitle> getTemplate(String text) {
        List<WTitle> result = new ArrayList<WTitle>();
        for (String link : getDoubleBracket2(text)) {
            try {
                if (link.charAt(0) != ':') {
                    WTitle linkEntry = normalizer.normalize(StringUtils.splitPreserveAllTokens(link, "\\|")[0]);
                    result.add(linkEntry);
                }
            }
            catch (ArrayIndexOutOfBoundsException e) {
            }
        }
        return result;
    }

    /**
     * Category付けのリンクを取得する。
     * @param text text要素
     * @return Category付けされているCategoryページのWEntry型オブジェクト
     */
    public Set<WTitle> getCategory(String text) {
        Set<WTitle> result = new LinkedHashSet<WTitle>();
        for (String link : getDoubleBracket(text)) {
            try {
                if (link.charAt(0) != ':') {
                    WTitle linkEntry = normalizer.normalize(StringUtils.splitPreserveAllTokens(link, "\\|")[0]);
                    if (linkEntry.getNsNumber() == WikIE.CATEGORY_NS_NUM &&
                    StringUtils.isBlank(linkEntry.getLang()) &&
                    StringUtils.isBlank(linkEntry.getProject()) &&
                    StringUtils.isNotBlank(linkEntry.toString())) {
                        result.add(linkEntry);
                    }
                }
            }
            catch (ArrayIndexOutOfBoundsException e) {
            }
        }
        return result;
    }

    /**
     * Wiki内リンクを取得する。
     * @param text text要素
     * @return　WLink型オブジェクトのList
     */
    public List<WLink> getLink(String text) {
        List<WLink> result = new ArrayList<WLink>();
        Iterator<String> linkItr = getDoubleBracket(text).iterator();
        while (linkItr.hasNext()) {
            WLink wLink = new WLink();
            String anchor[] = linkItr.next().split("\\|");
            if (anchor.length <= 0) {
                continue;
            }
            if (anchor.length == 2) {
                wLink.setAnchorText(anchor[1].trim());
            }
            String section[] = anchor[0].split("#");
            if (section.length == 2) {
                wLink.setSection(WNormalizer.decodeSectionLink(section[1].trim()));
            }
            WTitle entry = normalizer.normalize(section[0].trim());
            if (entry.getNsNumber() == WikIE.ARTICLE_NS_NUM ||
            entry.getNsNumber() == WikIE.CATEGORY_NS_NUM && section[0].charAt(0) == ':') {
                if (StringUtils.isBlank(entry.getLang()) && StringUtils.isBlank(entry.getProject())) {
                    wLink.setTitle(entry);
                    result.add(wLink);
                }
            }
        }
        return result;
    }

    public Set<WLink> getLinkSet(String text) {
        return new HashSet<WLink>(getLink(text));
    }

    /**
     * 言語間リンクを取得する。
     * @param text text要素
     * @return　WLink型オブジェクトのList
     */
    public List<WLink> getInterLangLink(String text) {
        List<WLink> result = new ArrayList<WLink>();
        Iterator<String> linkItr = getDoubleBracket(text).iterator();
        while (linkItr.hasNext()) {
            String link = linkItr.next();
            if (link.indexOf(":") > 0) {
                WTitle entry = normalizer.normalize(link);
                if (StringUtils.isNotBlank(entry.getLang()) && StringUtils.isBlank(entry.getProject())) {
                    result.add(new WLink(entry, "", ""));
                }
            }
        }
        return result;
    }

    public Set<String> getInterLangLinkPrefix(String text) {
        Set<String> result = new HashSet<String>();
        Iterator<String> linkItr = getDoubleBracket(text).iterator();
        while (linkItr.hasNext()) {
            String link = linkItr.next();
            if (link.indexOf(":") > 0) {
                WTitle entry = normalizer.normalize(link);
                if (StringUtils.isNotBlank(entry.getLang())) {
                    result.add(entry.getLang());
                }
            }
        }
        return result;
    }

    /**
     * エントリの種別を判定する。
     * @param entry 調べたいエントリのWEntry
     * @param text そのエントリのtext要素
     * @return 普通の記事ならWikIE.LEAF<br>
     *         リダイレクトならWikIE.REDIRECT<br>
     *         カテゴリならWikIE.NODE<br>
     *         それ以外ならWikIE.OTHER
     */
    public String getKind(WTitle entry, String text) {
        int ns = entry.getNsNumber();
        String kind = WikIE.OTHER;
        if (ns == WikIE.ARTICLE_NS_NUM) {
            WTitle redirect = isRedirect(text);
            if (redirect != null) {
                kind = WikIE.REDIRECT;
            }
            else {
                kind = WikIE.LEAF;
            }
        }
        else if (ns == WikIE.CATEGORY_NS_NUM){
            kind = WikIE.NODE;
        }
        else if (ns == WikIE.TEMPLATE_NS_NUM){
            kind = WikIE.TEMPLATE;
        }
        return kind;
    }

    /**
     * エントリの種別を判定する。
     * @param ns 調べたいエントリの名前空間番号
     * @param text そのエントリのtext要素
     * @return 普通の記事ならWikIE.LEAF<br>
     *         リダイレクトならWikIE.REDIRECT<br>
     *         カテゴリならWikIE.NODE<br>
     *         それ以外ならWikIE.OTHER
     */
    public String getKind(int ns, String text) {
        String kind = WikIE.OTHER;
        if (ns == WikIE.ARTICLE_NS_NUM) {
            WTitle redirect = isRedirect(text);
            if (redirect != null) {
                kind = WikIE.REDIRECT;
            }
            else {
                kind = WikIE.LEAF;
            }
        }
        else if (ns == WikIE.CATEGORY_NS_NUM){
            kind = WikIE.NODE;
        }
        return kind;
    }

    /**
     * Blikiライブラリを使ってエントリがリダイレクトかどうか判定する。
     * @param text text要素
     * @return リダイレクトならリダイレクト先エントリのWEntry<br>
     *         リダイレクトでないならnull
     */
    public WTitle isRedirect(String text) {
        text = text.trim().split("\n")[0];
        Pattern pattern = Pattern.compile("# *REDIRECT.*?\\[\\[(.+?)\\]\\]", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(text);
        if (matcher.find()) {
            String target = matcher.group(1);
            if (target.contains("#")) {
                target = target.substring(0, target.indexOf("#"));
            }
            if (target.contains("|")) {
                target = target.substring(0, target.indexOf("|"));
            }
            return normalizer.normalize(target);
        }
        return null;
    }

    public List<String> getRelatedItemList(String text, String seeAlsoSection) {
        List<String> relatedItemList = new ArrayList<String>();

        text = text.replaceAll("[\r\n]", "").replaceAll("<!--.*?-->", "");

        Pattern pattern = Pattern.compile("[^\\s]=+[\\s]*?" + seeAlsoSection + "[\\s]*?=+");
        Matcher matcher = pattern.matcher(text);

        int relatedStart = 0;
        if (matcher.find()) {
            relatedStart = matcher.end();
        }

        if (relatedStart > 0) {
            int relatedEnd = getRelatedEnd(text, relatedStart);

            if (relatedEnd > 0 && relatedStart < relatedEnd) {
                String relatedStr = text.substring(relatedStart, relatedEnd);

//              relatedStr = relatedStr.replace("\n", " ");
                relatedStr = replaceUselessString(relatedStr, REGEX_FOR_RELATED);

//              for (String relatedItem : replaceUselessString(relatedStr,
//                      REGEX_COMMON).split("\\*")) {
                for (String relatedItem : relatedStr.split("\\*")) {
//              for(String relatedItem : getDoubleBracket(relatedStr)){
                    List<String> itemList = getDoubleBracket(relatedItem.trim());
                    if(!itemList.isEmpty()){
                        for(String item : itemList){
                            item = item.trim();
                            if(!item.equals("")){
                                String[] itemArray = item.split("\\|");
                                for(String splitItem:itemArray){
                                    relatedItemList.add(splitItem);
                                }
                            }
                        }
                    } else {
                        Pattern relatedPattern = Pattern.compile("[ ].{1,10}$");
                        Matcher relatedMatcher = relatedPattern.matcher(relatedItem);
                        if(relatedMatcher.find()){
                            String st = relatedMatcher.group().trim();
//                          if(!st.equals("")){
                                relatedItemList.add(st);
//                          }
                        }
                    }
//                  relatedItem = relatedItem.trim();
//                  if (relatedItem.length() > 0) {
//                      relatedItemList.add(relatedItem);
//                  }
                }
            }
        }

        return relatedItemList;
    }

    /**
     * 関連項目の終了インデックスを取得
     * @param text
     * @param fromIndex
     * @return
     */
    private static int getRelatedEnd(String text, int fromIndex) {
        int relatedEnd = text.indexOf("={2}\\p{Space}.*\\p{Space}={2}\\u000A", fromIndex);

        if (relatedEnd < 0) {
            relatedEnd = getCategoryStart(text);
        }

        return relatedEnd;
    }

    /**
     * カテゴリの開始インデックスを取得
     * @param text
     * @return
     */
    private static int getCategoryStart(String text) {
        Pattern pattern = Pattern.compile("\\[\\[Category:|\\[\\[category:");
        Matcher matcher = pattern.matcher(text);

        if (matcher.find()) {
            return matcher.start();
        }

        return -1;
    }

    /**
     * 引数regexesの正規表現にマッチする引数strの文字列を空白に置換
     *
     * @param str
     * @param regexes
     * @return
     */
    public static String replaceUselessString(String str, String[] regexes) {
        for (String regex : regexes) {
            str = str.replaceAll(regex, "");
        }

        return str;
    }


    public boolean isNormalEntry(WTitle entry) {
        return StringUtils.isBlank(entry.getLang()) && StringUtils.isBlank(entry.getProject());
    }

    public static void main(String[] args) {
        WLinkUtils utils = new WLinkUtils(new WNormalizer(null, null, null));
        System.out.println(utils.isRedirect("#REDIRECT [[昭帝 (漢)|昭帝]]　"));
    }
}
