// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
 *******************************************************************************
 * Copyright (C) 1996-2010, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.dev.demo.translit;

import java.awt.event.KeyEvent;

import com.ibm.icu.dev.demo.impl.DumbTextComponent;
import com.ibm.icu.text.ReplaceableString;
import com.ibm.icu.text.Transliterator;

/**
 * A subclass of {@link DumbTextComponent} that passes key events through
 * a {@link com.ibm.icu.text.Transliterator}.
 *
 * @author Alan Liu
 */
public class TransliteratingTextComponent extends DumbTextComponent {

    /**
     * For serialization
     */
    private static final long serialVersionUID = -8672128213174154047L;

    private static boolean DEBUG = false;

    private Transliterator translit = null;
    
    // NOTE: DISABLE THE START AND CURSOR UNTIL WE CAN GET IT TO WORK AT ALL

    // Index into getText() where the start of transliteration is.
    // As we commit text during transliteration, we advance
    // this.
    //private int start = 0;

    // Index into getText() where the cursor is; cursor >= start
    //private int cursor = 0;

//    private static final String COPYRIGHT =
//        "\u00A9 IBM Corporation 1999. All rights reserved.";

    /**
     * Constructor.
     */
    public TransliteratingTextComponent() {
        super();
        /*
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // We get an ActionEvent only when the selection changes
                resetTransliterationStart();
            }
        });
        */
    }

    /**
     * {@link DumbTextComponent} API.  Framework method that is called
     * when a <code>KeyEvent</code> is received.  This implementation
     * runs the new character through the current
     * <code>Transliterator</code>, if one is set, and inserts the
     * transliterated text into the buffer.
     */
    protected void handleKeyTyped(KeyEvent e) {
        char ch = e.getKeyChar();
        
        if (translit == null) {
            setKeyStart(-1);
            super.handleKeyTyped(e);
            return;
        }

        transliterate(ch, false);
    }
    
    public void flush() {
        if (translit != null) transliterate('\uFFFF', true);
    }
    
    
    protected void transliterate(char ch, boolean flush) {

        // ------------------------------------------------------------
        // The following case motivates the two lines that recompute
        // start and cursor below.

        //      "     "   
        // a b c q r|s t u m m
        // 0 1 2 3 4 5 6 7 8 9
        //       0 1 2

        // start 3, cursor 5, sel 6 -> { 0, 3, 2 }
        // : new int[] { 0, sel - start, cursor - start };
        
        // sz>99|9

        //      "     {   "
        // a b c q r 9 9|9 t u m m
        // 0 1 2 3 4 5 6 7 8 9 a b
        //       0 1 2 3 4

        // { 3, 5, 4 } -> start 6, cursor 7, sel 8
        // : start += index[0];
        // : cursor = start + index[2] - index[0];
        // ------------------------------------------------------------

        // Need to save start because calls to replaceRange will update
        // start and cursor.
        //int saveStart = start;

        int end = flush ? getSelectionEnd() : getSelectionStart();
        String sourceText = getText().substring(0,end);
        ReplaceableString buf = new ReplaceableString(sourceText);
        /*buf.replace(0, 1, getText().substring(start,
                                              getSelectionStart()));*/

        Transliterator.Position index = new Transliterator.Position();
        index.contextLimit = buf.length();
        index.contextStart = 0;
        index.start = getKeyStart();
        if (index.start == -1) index.start = getSelectionStart();
        index.limit = buf.length();

      //  StringBuffer log = null;
        if (DEBUG) {
            System.out.println("Transliterator: " + translit.getID());
            System.out.println("From:\t" + '"' + buf.toString() + '"'
                + "; {cs: " + index.contextStart
                + ", s: " + index.start
                + ", l: " + index.limit
                + ", cl: " + index.contextLimit 
                + "}" + "; '" + ch + "'"
                + " " + getKeyStart()
            );
        }

        if (flush) {
            translit.finishTransliteration(buf, index);
        } else {
            translit.transliterate(buf, index, ch);
        }
        
        if (DEBUG) {
            System.out.println("To:\t" + '"' + buf.toString() + '"'
                + "; {cs: " + index.contextStart
                + ", s: " + index.start
                + ", l: " + index.limit
                + ", cl: " + index.contextLimit 
                + "}"
                );
            System.out.println();
        }
        /*
        buf.replace(buf.length(), buf.length(), String.valueOf(ch));
        translit.transliterate(buf);
        */
        
        String result = buf.toString();
        //if (result.equals(sourceText + ch)) return;
        
        replaceRange(result, 0, getSelectionEnd());
        setKeyStart(index.start);
        
        // At this point start has been changed by the callback to
        // resetTransliteratorStart() via replaceRange() -- so use our
        // local copy, saveStart.

        // The START index is zero-based.  On entry to transliterate(),
        // it was zero.  We can therefore just add it to our original
        // getText()-based index value of start (in saveStart) to get
        // the new getText()-based start.
//        start = saveStart + index.contextStart;

        // Make the cursor getText()-based.  The CURSOR index is zero-based.
//        cursor = start + index.start - index.contextStart;

/*
        if (DEBUG) {
            String out = buf.toString();
            log.append(out.substring(0, index.contextStart)).
                append('{').
                append(out.substring(index.contextStart, index.start)).
                append('|').
                append(out.substring(index.start)).
                append('"');
            log.append(", {" + index.contextStart + ", " + index.contextLimit + ", " + index.start + "}, ");
//            log.append("start " + start + ", cursor " + cursor);
            log.append(", sel " + getSelectionStart());
            System.out.println(escape(log.toString()));
        }
        */
    }
    
    /**
     * Set the {@link com.ibm.icu.text.Transliterator} and direction to
     * use to process incoming <code>KeyEvent</code>s.
     * @param t the {@link com.ibm.icu.text.Transliterator} to use
     */
    public void setTransliterator(Transliterator t) {
        /*
        if (translit != t) { // [sic] pointer compare ok; singletons
            resetTransliterationStart();
        }
        */
        translit = t;
    }

    public Transliterator getTransliterator() {
        return translit;
    }

    /**
     * Reset the start point at which transliteration begins.  This
     * needs to be done when the user moves the cursor or when the
     * current {@link com.ibm.icu.text.Transliterator} is changed. 
     */
     /*
    private void resetTransliterationStart() {
        start = getSelectionStart();
        cursor = start;
    }
    */

    /**
     * Escape non-ASCII characters as Unicode.
     * JUST FOR DEBUGGING OUTPUT.
     */
    public static final String escape(String s) {
        StringBuffer buf = new StringBuffer();
        for (int i=0; i<s.length(); ++i) {
            char c = s.charAt(i);
            if (c >= ' ' && c <= 0x007F) {
                if (c == '\\') {
                    buf.append("\\\\"); // That is, "\\"
                } else {
                    buf.append(c);
                }
            } else {
                buf.append("\\u");
                if (c < 0x1000) {
                    buf.append('0');
                    if (c < 0x100) {
                        buf.append('0');
                        if (c < 0x10) {
                            buf.append('0');
                        }
                    }
                }
                buf.append(Integer.toHexString(c));
            }
        }
        return buf.toString();
    }
}
