/*
 **********************************************************************
 * Copyright (c) 2006-2007, Google and others.  All Rights Reserved.
 **********************************************************************
 * Author: Mark Davis
 **********************************************************************
 */
package org.unicode.cldr.util;

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.unicode.cldr.util.Dictionary.Matcher;
import org.unicode.cldr.util.Dictionary.Matcher.Status;

public class DictionaryStringByteConverter extends StringByteConverter {
    private final Dictionary<String> dictionary;
    private final Matcher<String> matcher;
    private final StringByteConverter byteMaker;
    private final StringBuilder buffer = new StringBuilder();
    private final int maxBytesPerChar;
    private Matcher<CharSequence> backMatcher = null;

    public Dictionary<String> getDictionary() {
        return dictionary;
    }

    public DictionaryStringByteConverter(Dictionary<String> dictionary, StringByteConverter byteMaker) {
        super();
        this.dictionary = dictionary;
        matcher = dictionary.getMatcher();
        matcher.setText(buffer);
        this.byteMaker = byteMaker;
        int mBytesPerChar = 0;
        for (Iterator<Entry<CharSequence, String>> m = dictionary.getMapping(); m.hasNext();) {
            Entry<CharSequence, String> entry = m.next();
            // System.out.println("** " + key + "\t\t" + value);
            int bytesPerChar = entry.getValue().length() * byteMaker.getMaxBytesPerChar(); // all bytes are generated
            // from last char
            if (mBytesPerChar < bytesPerChar) {
                mBytesPerChar = bytesPerChar;
            }
        }
        maxBytesPerChar = mBytesPerChar;
    }

    @Override
    public int getMaxBytesPerChar() {
        return maxBytesPerChar;
    }

    @Override
    public int toBytes(char ch, byte[] output, int bytePosition) {
        buffer.append(ch);
        return toBytes(output, bytePosition, true);
    }

    @Override
    public int toBytes(byte[] output, int bytePosition) {
        return toBytes(output, bytePosition, false);
    }

    public int toBytes(byte[] output, int bytePosition, boolean stopOnFinalPartial) {
        // keep converting until the buffer is empty, or unless we get a PARTIAL at the end
        while (buffer.length() != 0) {
            matcher.setText(buffer); // reset the matcher to the start
            // find last, best status
            Status status = Status.NONE;
            int bestEnd = 0;
            String bestValue = null;
            main: while (true) {
                Status tempStatus = matcher.next();
                switch (tempStatus) {
                case NONE:
                    break main;
                case PARTIAL:
                    // if the partial is at the end, then wait for more input
                    if (stopOnFinalPartial && matcher.getMatchEnd() == buffer.length()) {
                        if (true) matcher.nextUniquePartial(); // for debugging
                        return bytePosition;
                    }
                    continue; // otherwise ignore
                default:
                    // MATCH
                    status = tempStatus;
                    bestEnd = matcher.getMatchEnd();
                    bestValue = matcher.getMatchValue();
                    break;
                }
            }
            // we've now come out, and have either MATCH or not
            // so replace what we came up with, and continue
            switch (status) {
            case MATCH:
                bytePosition = byteMaker.toBytes(bestValue, output, bytePosition);
                buffer.delete(0, bestEnd);
                break;
            default:
                bytePosition = byteMaker.toBytes(buffer.charAt(0), output, bytePosition);
                buffer.delete(0, 1);
                break;
            }
        }
        return bytePosition;
    }

    @Override
    public Appendable fromBytes(byte[] input, int byteStart, int byteLength, Appendable result) {
        // first convert from bytes
        StringBuffer internal = new StringBuffer();
        byteMaker.fromBytes(input, byteStart, byteLength, internal);
        // then convert using dictionary
        if (backMatcher == null) {
            Map<CharSequence, CharSequence> back = new TreeMap<CharSequence, CharSequence>(
                Dictionary.CHAR_SEQUENCE_COMPARATOR);
            for (Iterator<Entry<CharSequence, String>> m = dictionary.getMapping(); m.hasNext();) {
                Entry<CharSequence, String> entry = m.next();
                if (entry.getValue().length() != 0) {
                    if (!back.containsKey(entry.getValue())) {// may lose info
                        back.put(entry.getValue(), entry.getKey());
                    }
                }
            }
            backMatcher = new StateDictionaryBuilder<CharSequence>().make(back).getMatcher();
        }
        backMatcher.setText(internal).convert(result);
        return result;
    }
}