/**************************************************************************
 OmegaT - Computer Assisted Translation (CAT) tool 
          with fuzzy matching, translation memory, keyword search, 
          glossaries, and translation leveraging into updated projects.

 Copyright (C) 2013 Alex Buloichik, Aaron Madlon-Kay
               Home page: http://www.omegat.org/
               Support center: http://groups.yahoo.com/group/OmegaT/

 This file is part of OmegaT.

 OmegaT 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.

 OmegaT 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

package org.omegat.util;

import java.util.ArrayDeque;
import java.util.Map;
import java.util.TreeMap;

import org.omegat.filters3.Tag;

/**
 * This class handles inline tags, i.e. helps to replace all tags into
 * shortcuts. It handles bpt,ept,it tags numeration.
 * 
 * @author Alex Buloichik (alex73mail@gmail.com)
 * @author Aaron Madlon-Kay
 */
public class InlineTagHandler {
    /** map of 'i' attributes to tag numbers */
    Map<String, Integer> pairTags = new TreeMap<String, Integer>();
    Map<String, ArrayDeque<Integer>> pairedOtherTags = new TreeMap<String, ArrayDeque<Integer>>();
    Map<String, Integer> shortcutLetters = new TreeMap<String, Integer>();
    String currentI;
    String currentPos;
    int tagIndex;
    int otherTagShortcutLetter;

    /**
     * Reset stored info for process new part of XML.
     */
    public void reset() {
        pairTags.clear();
        pairedOtherTags.clear();
        currentI = null;
        currentPos = null;
        tagIndex = 0;
    }

    /**
     * Handle "bpt" tag start for TMX. OmegaT internal tag number
     * will be based off the x attr (if provided).
     * 
     * @param i TMX i attribute value
     * @param x TMX x attribute value (can be null)
     */
    public void startBPT(String i, String x) {
        if (i == null) {
            throw new RuntimeException("Wrong index in inline tag");
        }
        currentI = i;
        int index = tagIndex++;
        try {
            // If a value for the @x attr was provided, base the tag
            // number off of it for matching purposes.
            // Subtract 1 because OmegaT 0-indexes tags, while TMX
            // seems to start at 1 (though the spec only says it must be
            // unique for each <bpt> in the segment, so we clip to 0
            // to prevent negative tag numbers).
            index = Math.max(0, Integer.parseInt(x) - 1);
        } catch (Exception ex) {
            // Ignore
        }
        pairTags.put(currentI, index);
    }
    
    /**
     * Handle "bpt" tag start. Identifier will be first non-null
     * attribute in provided attributes. OmegaT internal tag number
     * will be its index in the list of tags in the segment (starting
     * with 0).
     * 
     * @param attributeValues
     *            attributes to identify pairs
     */
    public void startBPT(String... attributeValues) {
        currentI = nvl(attributeValues);
        pairTags.put(currentI, tagIndex++);
    }

    /**
     * Store shortcut letter for current 'i' value.
     * 
     * @param letter
     *            letter to store
     */
    public void setTagShortcutLetter(int letter) {
        if (letter != 0) {
            shortcutLetters.put(currentI, letter);
        }
    }

    /**
     * Get stored shortcut letter for current 'i' value.
     * 
     * @return
     */
    public int getTagShortcutLetter() {
        Integer c = shortcutLetters.get(currentI);
        return c != null ? c : 0;
    }

    /**
     * Store shortcut letter for current other tag.
     * 
     * @param letter
     *            letter to store
     */
    public void setOtherTagShortcutLetter(int letter) {
        otherTagShortcutLetter = letter;
    }

    /**
     * Get stored shortcut letter for current other tag.
     * 
     * @return
     */
    public int getOtherTagShortcutLetter() {
        return otherTagShortcutLetter;
    }

    /**
     * Handle "ept" tag start.
     * 
     * @param attributeValues
     *            attributes to identify pairs
     */
    public void startEPT(String... attributeValues) {
        currentI = nvl(attributeValues);
    }

    /**
     * Handle other tag start.
     */
    public void startOTHER() {
        currentI = null;
    }

    /**
     * Handle "bpt" tag end.
     * 
     * @return shortcut index
     */
    public Integer endBPT() {
        return pairTags.get(currentI);
    }

    /**
     * Handle "ept" tag end.
     * 
     * @return shortcut index
     */
    public Integer endEPT() {
        return pairTags.get(currentI);
    }

    /**
     * Handle other tag end.
     * 
     * @return shortcut index
     */
    public int endOTHER() {
        int result = tagIndex;
        tagIndex++;
        return result;
    }

    /**
     * Handle paired tag end.
     * 
     * @return shortcut index
     */
    public int paired(String tagName, Tag.Type tagType) {
        int result;
        switch (tagType) {
        case BEGIN:
            result = tagIndex;
            cacheTagIndex(tagName, result);
            tagIndex++;
            return result;
        case END:
            Integer index = getCachedTagIndex(tagName);
            if (index == null) {
                index = tagIndex;
                tagIndex++;
            }
            return index;
        case ALONE:
            result = tagIndex;
            tagIndex++;
            return result;
        default:
            throw new RuntimeException("Impossible tag type");
        }
    }

    private void cacheTagIndex(String tagName, int result) {
        ArrayDeque<Integer> cache = pairedOtherTags.get(tagName);
        if (cache == null) {
            cache = new ArrayDeque<Integer>();
            pairedOtherTags.put(tagName, cache);
        }
        cache.addFirst(result);
    }

    private Integer getCachedTagIndex(String tagName) {
        ArrayDeque<Integer> cache = pairedOtherTags.get(tagName);
        if (cache == null) {
            return null;
        }
        return cache.pollFirst();
    }

    /**
     * Remember current begin/end mark of "it" tag.
     */
    public void setCurrentPos(String currentPos) {
        this.currentPos = currentPos;
    }

    /**
     * Returns current begin/end mark of "it" tag.
     */
    public String getCurrentPos() {
        return currentPos;
    }

    private String nvl(String... attributeValues) {
        String result = StringUtil.nvl(attributeValues);
        if (result == null) {
            throw new RuntimeException("Wrong index in inline tag");
        }
        return result;
    }
}
