import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.net.*;
import java.io.*;

public class Convert21 extends Applet implements TextListener, ActionListener, FocusListener, Runnable
{
    static final String VERSION = "Cyrillic Converter 2.1";
    static final char hex [] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

    TextArea lat;
    TextArea rus;
    TextField latName;
    TextField rusName;
    TextField urlField;
    TextField emailField;
    Button sendButton;
    Button clearButton;
    Button cancelButton;
    TextComponent statusLabel;
    Checkbox vmCheck;
    Checkbox vm2Check;
    Checkbox kkCheck;
    Checkbox dkCheck;

    static String [][] convTable = {
        {"A",  "\u0410"},
        {"B",  "\u0411"},
        {"V",  "\u0412"},
        {"G",  "\u0413"},
        {"D",  "\u0414"},
        {"E",  "\u0415"},
        {"ZH", "\u0416"},
        {"Zh", "\u0416"},
        {"Z",  "\u0417"},
        {"I",  "\u0418"},
        {"J",  "\u0419"},
        {"K",  "\u041A"},
        {"L",  "\u041B"},
        {"M",  "\u041C"},
        {"N",  "\u041D"},
        {"O",  "\u041E"},
        {"P",  "\u041F"},
        {"R",  "\u0420"},
        {"S",  "\u0421"},
        {"T",  "\u0422"},
        {"U",  "\u0423"},
        {"F",  "\u0424"},
        {"H",  "\u0425"},
        {"C",  "\u0426"}, // ts
//        {"TS", "\u0426"},
//        {"Ts", "\u0426"},
        {"CH", "\u0427"},
        {"Ch", "\u0427"},
        {"SH", "\u0428"},
        {"Sh", "\u0428"},
        {"SCH","\u0429"},
        {"SCh","\u0429"},
        {"Sch","\u0429"},
        {"^''","\u042A"}, // tverdy znak
        {"Y",  "\u042B"},
        {"^'", "\u042C"}, // miagki znak
        {"^E", "\u042D"}, // E oborotnoe
        {"YU", "\u042E"}, // E oborotnoe
        {"Yu", "\u042E"}, // E oborotnoe
        {"YA", "\u042F"}, // E oborotnoe
        {"Ya", "\u042F"}, // E oborotnoe

        {"a",  "\u0430"},
        {"b",  "\u0431"},
        {"v",  "\u0432"},
        {"g",  "\u0433"},
        {"d",  "\u0434"},
        {"e",  "\u0435"},
        {"zh", "\u0436"},
        {"z",  "\u0437"},
        {"i",  "\u0438"},
        {"j",  "\u0439"},
        {"k",  "\u043A"},
        {"l",  "\u043B"},
        {"m",  "\u043C"},
        {"n",  "\u043D"},
        {"o",  "\u043E"},
        {"p",  "\u043F"},
        {"r",  "\u0440"},
        {"s",  "\u0441"},
        {"t",  "\u0442"},
        {"u",  "\u0443"},
        {"f",  "\u0444"},
        {"h",  "\u0445"},
        {"c", "\u0446"},
//        {"ts", "\u0446"},
        {"ch", "\u0447"},
        {"sh", "\u0448"},
        {"sch","\u0449"},
        {"''", "\u044A"}, // tverdy znak
        {"y",  "\u044B"},
        {"'",  "\u044C"}, // miagki znak
        {"^e", "\u044D"}, // E oborotnoe
        {"yu", "\u044E"},
        {"ya", "\u044F"},

        {"YO", "\u0401"},
        {"Yo", "\u0401"},
        {"JO", "\u0401"},
        {"Jo", "\u0401"},
        {"IO", "\u0401"},
        {"Io", "\u0401"},
        {"yo", "\u0451"},
        {"jo", "\u0451"},

        {"W",  "\u0412"},
        {"w",  "\u0432"},

        {"JU", "\u042E"},
        {"Ju", "\u042E"},
        {"IU", "\u042E"},
        {"Iu", "\u042E"},
        {"ju", "\u044E"},

        {"JA", "\u042F"},
        {"Ja", "\u042F"},
        {"IA", "\u042F"},
        {"Ia", "\u042F"},
        {"ja", "\u044F"},

//        {"tsya", "\u0442\u0441\u044F"}, // tsya - bez myagkogo znaka
        {"shod", "\u0441\u0445\u043E\u0434"}, // s-h-od - bez sh

    };

    Hashtable lrHash;
    String lrVector [];
    int lrSuffixLen [];
    String [] rlVector; // starts with 0x400;
    int maxpatlen = 0;
    String trailer;
    static final int RUS_START = 0x400;

    void prepareTable ()
    {
        lrHash = new Hashtable ();
        lrVector = new String [256];
        lrSuffixLen = new int [256];
        for (int i = 0; i < convTable.length; i++) {
            int patlen = convTable [i][0].length ();
            int c = convTable [i][0].charAt (0);
            if (patlen > lrSuffixLen [c])
                lrSuffixLen [c] = patlen;

            if (patlen == 1)
                lrVector [c] = convTable [i][1];
            else
                lrHash.put (convTable [i][0], convTable [i][1]);

            if (patlen > maxpatlen)
                maxpatlen = patlen;
        }

        trailer = "";
        for (int i = 0; i < maxpatlen; i++)
            trailer += "`";

        rlVector = new String [256];
        for (int i = 0; i < convTable.length; i++) {
            char c = convTable [i][1].charAt (0);
            if (c < RUS_START || c >= RUS_START + rlVector.length)
                continue;
            if (rlVector [c - RUS_START] != null)
                continue;
            rlVector [c - RUS_START] = convTable [i][0];
        }
    }

    String latToRus (String s)
    {
        String d;
        int len = s.length ();
        StringBuffer m = new StringBuffer (len);
        int i = 0;
        s += trailer;
        while (i < len) {
            int c = s.charAt (i);
            if (c == '`') {
                ++ i;
                continue;
            }
            if (c == '\\') {
                if (i+1 < len) {
                    m.append (s.charAt (i+1));
                    i += 2;
                } else
                    ++ i;
                continue;
            }
            if (c == '|') {
                ++i;
                while (i < len) {
                    c = s.charAt (i);
                    if (c == '|') break;
                    m.append ((char) c);
                    ++ i;
                }
                ++ i;
                continue;
            }
            if (c >= lrSuffixLen.length) {
                m.append ((char)c);
                ++i;
                continue;
            }

            if (lrSuffixLen [c] == 1) {
                m.append (lrVector [c]);
                ++i;
                continue;
            }
            if (lrSuffixLen [c] == 0) {
                m.append ((char)c);
                ++i;
                continue;
            }
            d = null;
            int j;
            for (j = lrSuffixLen [c]; j > 1; j--) {
                d = (String) lrHash.get (s.substring (i, i+j));
                if (d != null)
                    break;
            }
            if (d != null) {
                m.append (d);
                i += j;
                continue;
            }
            if (lrVector [c] != null)
                m.append (lrVector [c]);
            else
                m.append ((char) c);
            ++ i;
        }
        return m.toString ();
    }

    // need this because StringBuffer.substring only appeared in JDK 1.2

    String bufSubstring (StringBuffer b, int start, int len)
    {
        char c [] = new char [len];
        b.getChars (start, start + len, c, 0);
        return new String (c);
    }

    private boolean spec_symbol (char c)
    {
        if (c < 'A') return true;
        if (c <= 'Z') return false;
        if (c == '^') return false;
        if (c == '`') return false;
        if (c < 'a') return true;
        if (c <= 'z') return false;
        if (c <= 0x80) return true;
        return false;
    }

    String rusToLat (String s)
    {
        StringBuffer m = new StringBuffer (s.length () * 2);
        int latcount = 0;
        int latstart = -1;
        for (int i = 0; i < s.length (); i++) {
            char c = s.charAt (i);
            int p = m.length ();
            if (spec_symbol (c)) {
                m.append (c);
                continue;
            }
            if (c < RUS_START || c >= RUS_START + rlVector.length || rlVector [c - RUS_START] == null) {
                if (latcount == 0) {
                    latstart = m.length ();
                    m.append ('\\');
                } else if (latcount == 1)
                    m.setCharAt(latstart, '|');
                m.append (c);
                ++ latcount;
                continue;
            } else {
                if (latcount > 1) {
                    m.append ('|');
                    latcount = 0;
                }
                m.append (rlVector [c - RUS_START]);
            }
            if (p > 0) {
                c = m.charAt (p-1);
                int len = lrSuffixLen [c];
                if (len > 1) {
                    for (int j = len; j > 1; j--) {
                        if (p+j-1 > m.length ())
                            continue;
                        if (lrHash.get (bufSubstring (m, p-1, j)) != null) {
                            m.insert (p, '`');
                            break;
                        }
                    }
                }
            }
        }
        if (latcount > 1)
            m.append ('|');
        return m.toString ();
    }


    public void init()
    {
        prepareTable ();
        Panel leftPanel = new Panel ();
        leftPanel.setLayout (new BorderLayout ());
        leftPanel.add (new Label ("Latin text"), BorderLayout.NORTH);

        Panel rightPanel = new Panel ();
        rightPanel.setLayout (new BorderLayout ());
        rightPanel.add (new Label ("Cyrillic text"), BorderLayout.NORTH);

        lat = new TextArea ("", 0, 0, TextArea.SCROLLBARS_VERTICAL_ONLY);
        rus = new TextArea ("", 0, 0, TextArea.SCROLLBARS_VERTICAL_ONLY);
        lat.setFont (new Font ("Arial", 0, 15));
        rus.setFont (new Font ("Arial", 0, 15));

        leftPanel.add (lat, BorderLayout.CENTER);
        rightPanel.add (rus, BorderLayout.CENTER);

        sendButton = new Button ("BOT TAK");
        sendButton.addActionListener (this);

        clearButton = new Button ("3AHOBO");
        clearButton.addActionListener (this);

        cancelButton = new Button ("Cancel");
        cancelButton.addActionListener (this);
        cancelButton.setEnabled(false);

        Panel topPanel = new Panel ();
        topPanel.setLayout (new GridLayout (1,2));
        topPanel.add (leftPanel);
        topPanel.add (rightPanel);

        Panel bottomPanel = new Panel ();
        bottomPanel.setLayout (new GridLayout (3, 1, 5, 5));

        Panel namesPanel = new Panel ();
        namesPanel.setLayout (new GridLayout (1, 2, 5, 0));

        latName = new TextField ();
        rusName = new TextField ();
        latName.setFont (new Font ("Arial", 0, 15));
        rusName.setFont (new Font ("Arial", 0, 15));
        namesPanel.add (latName);
        namesPanel.add (rusName);
        bottomPanel.add (namesPanel);

        Panel fieldsPanel = new Panel ();
        fieldsPanel.setLayout (new GridLayout (1, 2, 5, 0));

        urlField = new TextField ();
        emailField = new TextField ();

        Panel urlPanel = new Panel ();
        urlPanel.setLayout (new BorderLayout ());
        urlPanel.add (new Label ("URL:"), BorderLayout.WEST);
        urlPanel.add (urlField, BorderLayout.CENTER);
        fieldsPanel.add (urlPanel);
        Panel mailPanel = new Panel ();
        mailPanel.setLayout (new BorderLayout ());
        mailPanel.add (new Label ("e-mail:"), BorderLayout.WEST);
        mailPanel.add (emailField, BorderLayout.CENTER);
        fieldsPanel.add (mailPanel);
        bottomPanel.add (fieldsPanel);

        Panel buttonPanel = new Panel ();
        buttonPanel.setLayout (new BorderLayout (10, 0));
        Panel buttons = new Panel ();
        buttons.add (sendButton);
        buttons.add (clearButton);
        buttonPanel.add (buttons, BorderLayout.WEST);
        statusLabel = new TextField ("Status:");
        statusLabel.setEditable (false);
        statusLabel.setBackground(Color.lightGray);
        buttonPanel.add (statusLabel, BorderLayout.CENTER);
        Panel cancelPanel = new Panel ();
        cancelPanel.add (cancelButton);
        buttonPanel.add (cancelPanel, BorderLayout.EAST);
        bottomPanel.add (buttonPanel);

        Panel titlePanel = new Panel ();
        titlePanel.setLayout (new GridLayout (1,2));
        titlePanel.add (new Label (VERSION));
        Panel p = new Panel ();

        String defaultDestination = getParameter ("wheretosend");
    	if (defaultDestination == null)
            defaultDestination = "vm";
        defaultDestination = defaultDestination.toLowerCase ();
        if (! defaultDestination.equals ("vm") &&
            ! defaultDestination.equals ("vm2") &&
            ! defaultDestination.equals ("kk") &&
            ! defaultDestination.equals ("dk")
           )
            defaultDestination = "vm";

        CheckboxGroup cbg = new CheckboxGroup ();
        vmCheck  = new Checkbox ("VM", cbg, defaultDestination.equals ("vm"));
        vm2Check = new Checkbox ("VM2", cbg, defaultDestination.equals ("vm2"));
        kkCheck  = new Checkbox ("KK", cbg, defaultDestination.equals ("kk"));
        dkCheck  = new Checkbox ("DK", cbg, defaultDestination.equals ("dk"));
        p.add (vmCheck);
        p.add (vm2Check);
        p.add (kkCheck);
        p.add (dkCheck);
        titlePanel.add (p);
        add (titlePanel, BorderLayout.NORTH);

        setLayout (new BorderLayout ());
        add (titlePanel, BorderLayout.NORTH);
        add (topPanel, BorderLayout.CENTER);
        add (bottomPanel, BorderLayout.SOUTH);

        lat.addTextListener (this);
        rus.addTextListener (this);
        latName.addTextListener (this);
        rusName.addTextListener (this);
        lat.addFocusListener (this);
        rus.addFocusListener (this);
        latName.addFocusListener (this);
        rusName.addFocusListener (this);
    }

    Point posToCoord (String text, int pos)
    {
        if (pos < 0) pos = 0;
        if (pos > text.length ()) pos = text.length ();
        int linenum = 0;
        int last = 0;
        for (int i = 0; i < pos; i++) {
            if (text.charAt (i) == '\n') {
                ++ linenum;
                last = i+1;
            }
        }
        return new Point (pos - last, linenum);
    }

    int CoordToPos (String text, Point p)
    {
        if (p.y < 0) return 0;
        if (p.y == 0)
            return p.x;
        int n = text.length ();
        int linenum = 0;
        for (int i = 0; i < n; i++) {
            if (text.charAt (i) == '\n') {
                ++ linenum;
                if (linenum == p.y)
                    return i+1+p.x;
            }
        }
        return n;
    }

    Component focus = null;

    public void focusGained (FocusEvent e)
    {
        focus = (Component) e.getSource ();
    }

    public void focusLost(FocusEvent e)
    {
    }

    public void textValueChanged (TextEvent event)
    {
        if (focus != event.getSource ())
            return;

        try {
            if (event.getSource () == rus) {
                String rtext = rus.getText ();
                String ltext = rusToLat (rtext);
                int ruspos = rus.getCaretPosition();
                Point p = posToCoord (rtext, ruspos);
                p.x = rusToLat (rtext.substring(ruspos - p.x, ruspos)).length ();
                int latpos = CoordToPos (ltext, p);
                lat.setText (ltext);
                lat.setCaretPosition (latpos);
            }
            if (event.getSource () == lat) {
                String ltext = lat.getText ();
                String rtext = latToRus (ltext);
                int latpos = lat.getCaretPosition();
                Point p = posToCoord (ltext, latpos);
                p.x = latToRus (ltext.substring(latpos - p.x, latpos)).length ();
                int pos = CoordToPos (rtext, p);
                rus.setText (rtext);
                rus.setCaretPosition (pos);
            }

            if (event.getSource () == rusName) {
                latName.setText (rusToLat (rusName.getText ()));
            }

            if (event.getSource () == latName) {
                rusName.setText (latToRus (latName.getText ()));
            }
        } catch (Throwable e) {
            e.printStackTrace ();
        }
    }

    Thread postThread = null;
    boolean cancelled = false;
    String whereToSend;

    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource() == sendButton) {
            if (vmCheck.getState())
                startSending ("VM");
            else if (vm2Check.getState())
                startSending ("VM2");
            else if (kkCheck.getState())
                startSending ("KK");
            else if (dkCheck.getState())
                startSending ("DK");
        }
        if (e.getSource() == cancelButton) {
            cancelled = true;
        }
        if (e.getSource() == clearButton) {
            lat.setText ("");
            rus.setText ("");
        }
    }

    void startSending (String where)
    {
        if (rusName.getText ().trim ().equals ("")) {
            statusLabel.setText ("No name specified, can't send");
            statusLabel.setBackground (Color.red);
            return;
        }

        if (rus.getText ().trim ().equals ("")) {
            statusLabel.setText ("No text to send");
            statusLabel.setBackground (Color.red);
            return;
        }

        whereToSend = where;
        sendButton.setEnabled(false);
        cancelButton.setEnabled(true);
        cancelled = false;
        postThread = new Thread (this);
        postThread.start ();
    }

    public void run ()
    {
        String msgText   = toNetworkFormat (rus.getText ());
        String nameText  = toNetworkFormat (rusName.getText ());
        String urlText   = toNetworkFormat (urlField.getText ());
        String emailText = toNetworkFormat (emailField.getText ());

        String result;
        try {
            result = postText (whereToSend, msgText, nameText, urlText, emailText);
            if (result == null) {
                statusLabel.setText ("Cancelled");
                statusLabel.setBackground (Color.red);
            } else {
                statusLabel.setText ("Result: " + result);
                statusLabel.setBackground (Color.lightGray);
            }
        } catch (Exception ex) {
            ex.printStackTrace (System.out);
            result = ex.toString ();
            statusLabel.setText ("Error: " + result);
            statusLabel.setBackground (Color.red);
        }
        postThread = null;
        sendButton.setEnabled(true);
        cancelButton.setEnabled(false);
    }

    String postText (String where, String msgText, String nameText, String urlText, String emailText) throws Exception
    {
        statusLabel.setText ("Status: Opening " + where);
        statusLabel.setBackground (Color.yellow);
        //String url = getDocumentBase () + "sender.cgi";
        //commented out because Netscape and IE return different document bases
        String url = "http://izm.zamok.net/afr/converter2/sender.cgi";
        URL u = new URL (url);
        URLConnection con = (URLConnection) u.openConnection ();
        if (cancelled)
            return null;

        con.setDoOutput (true);
        OutputStream o = con.getOutputStream ();

        PrintStream p = new PrintStream (o);
        statusLabel.setText ("Status: Posting");
        statusLabel.setBackground (Color.green);

        p.print ("&name="+nameText);
        p.print ("&e-mail="+emailText);
        p.print ("&url="+urlText);

        if (whereToSend.equals ("KK"))
            p.print ("&sendtokk=yes");
        if (whereToSend.equals ("VM"))
            p.print ("&sendtovm=yes");
        if (whereToSend.equals ("DK"))
            p.print ("&sendtodk=yes");
        if (whereToSend.equals ("VM2"))
            p.print ("&sendtovm2=yes");

        p.print ("&comment="+msgText);
        p.println();
        p.close ();
        if (cancelled)
            return null;
        statusLabel.setText ("Status: Waiting for reply");
        statusLabel.setBackground (Color.green.brighter());

        InputStream i = con.getInputStream ();
        StringBuffer buf = new StringBuffer ();
        int c;
        int level = 0;
        while ((c = i.read ()) >= 0) {
            if (c >= ' ') {
                if (c == '<') ++ level;
                if (level == 0) buf.append ((char) c);
                if (level > 0 && c == '>') -- level;
            }
        }
        i.close ();
        return buf.toString ();
    }

    String toNetworkFormat (String src)
    {
        StringBuffer b = new StringBuffer (src.length()*2);
        for (int i = 0; i < src.length(); i++) {
            char c = src.charAt (i);
            switch (c) {
            case '%': b.append ("%25"); break;
            case ' ': b.append ("%20"); break;
            case '?': b.append ("%3F"); break;
            case '=': b.append ("%3D"); break;
            case ';': b.append ("%3B"); break;
            case '/': b.append ("%2F"); break;
            case '&': b.append ("%26"); break;
            case '+': b.append ("%2B"); break;
            case '\n': b.append ("%0D%0A"); break;
            default:
                if (c >= 0x410 && c <= 0x44F) {
                    c = (char) (c - 0x410 + 0xC0);
                } else if (c == 0x401)
                    c = 0xA8;
                else if (c == 0x451)
                    c = 0xB8;
                if (c >= 0x80) {
                    b.append ('%');
                    b.append (hex [(c >> 4) & 0x0F]);
                    b.append (hex [c & 0x0F]);
                } else
                    b.append (c);
            }
        }
        return b.toString ();
    }

    public void start() {
    }

    public void stop() {
    }

    public void destroy() {
    }

    public String getAppletInfo() {
        return "Cyrillic Converter";
    }

    public String[][] getParameterInfo() {
        return null;
    }
}
