/*
 * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This source code is provided to illustrate the usage of a given feature
 * or technique and has been deliberately simplified. Additional steps
 * required for a production-quality application, such as security checks,
 * input validation and proper error handling, might not be present in
 * this sample code.
 */


/*
 */

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.*;

import java.util.*;
import java.util.regex.*;

/**
 * RangeMenu.java
 *
 * @author Shinsuke Fukuda
 * @author Ankit Patel [Conversion to Swing - 01/07/30]
 */

/// Custom made choice menu that holds data for unicode range

public final class RangeMenu extends JComboBox implements ActionListener {

    private static final int[][] UNICODE_RANGES = getUnicodeRanges();
    private static final String[] UNICODE_RANGE_NAMES = getUnicodeRangeNames();

    private boolean useCustomRange = false;
    private int[] customRange = { 0x0000, 0x007f };

    /// Custom range dialog variables
    private final JDialog customRangeDialog;
    private final JTextField customRangeStart = new JTextField( "0000", 4 );
    private final JTextField customRangeEnd   = new JTextField( "007F", 4 );
    private final int CUSTOM_RANGE_INDEX = UNICODE_RANGE_NAMES.length - 1;

    /// Parent Font2DTest Object holder
    private final Font2DTest parent;

    public static final int SURROGATES_AREA_INDEX = 91;

    public RangeMenu( Font2DTest demo, JFrame f ) {
        super();
        parent = demo;

        for ( int i = 0; i < UNICODE_RANGE_NAMES.length; i++ )
          addItem( UNICODE_RANGE_NAMES[i] );

        setSelectedIndex( 0 );
        addActionListener( this );

        /// Set up custom range dialog...
        customRangeDialog = new JDialog( f, "Custom Unicode Range", true );
        customRangeDialog.setResizable( false );

        JPanel dialogTop = new JPanel();
        JPanel dialogBottom = new JPanel();
        JButton okButton = new JButton("OK");
        JLabel from = new JLabel( "From:" );
        JLabel to = new JLabel("To:");
        Font labelFont = new Font( "dialog", Font.BOLD, 12 );
        from.setFont( labelFont );
        to.setFont( labelFont );
        okButton.setFont( labelFont );

        dialogTop.add( from );
        dialogTop.add( customRangeStart );
        dialogTop.add( to );
        dialogTop.add( customRangeEnd );
        dialogBottom.add( okButton );
        okButton.addActionListener( this );

        customRangeDialog.getContentPane().setLayout( new BorderLayout() );
        customRangeDialog.getContentPane().add( "North", dialogTop );
        customRangeDialog.getContentPane().add( "South", dialogBottom );
        customRangeDialog.pack();
    }

    /// Return the range that is currently selected

    public int[] getSelectedRange() {
        if ( useCustomRange ) {
            int startIndex, endIndex;
            String startText, endText;
            String empty = "";
            try {
                startText = customRangeStart.getText().trim();
                endText = customRangeEnd.getText().trim();
                if ( startText.equals(empty) && !endText.equals(empty) ) {
                    endIndex = Integer.parseInt( endText, 16 );
                    startIndex = endIndex - 7*25;
                }
                else if ( !startText.equals(empty) && endText.equals(empty) ) {
                    startIndex = Integer.parseInt( startText, 16 );
                    endIndex = startIndex + 7*25;
                }
                else {
                    startIndex = Integer.parseInt( customRangeStart.getText(), 16 );
                    endIndex = Integer.parseInt( customRangeEnd.getText(), 16 );
                }
            }
            catch ( Exception e ) {
                /// Error in parsing the hex number ---
                /// Reset the range to what it was before and return that
                customRangeStart.setText( Integer.toString( customRange[0], 16 ));
                customRangeEnd.setText( Integer.toString( customRange[1], 16 ));
                return customRange;
            }

            if ( startIndex < 0 )
              startIndex = 0;
            if ( endIndex > 0xffff )
              endIndex = 0xffff;
            if ( startIndex > endIndex )
              startIndex = endIndex;

            customRange[0] = startIndex;
            customRange[1] = endIndex;
            return customRange;
        }
        else
          return UNICODE_RANGES[ getSelectedIndex() ];
    }

    /// Function used by loadOptions in Font2DTest main panel
    /// to reset setting and range selection
    public void setSelectedRange( String name, int start, int end ) {
        setSelectedItem( name );
        customRange[0] = start;
        customRange[1] = end;
        parent.fireRangeChanged();
    }

    /// ActionListener interface function
    /// ABP
    /// moved JComboBox event code into this fcn from
    /// itemStateChanged() method. Part of change to Swing.
    public void actionPerformed( ActionEvent e ) {
        Object source = e.getSource();

        if ( source instanceof JComboBox ) {
                String rangeName = (String)((JComboBox)source).getSelectedItem();

                if ( rangeName.equals("Custom...") ) {
                    useCustomRange = true;
                    customRangeDialog.setLocationRelativeTo(parent);
                    customRangeDialog.show();
                }
                else {
                  useCustomRange = false;
                }
                parent.fireRangeChanged();
        }
        else if ( source instanceof JButton ) {
                /// Since it is only "OK" button that sends any action here...
                customRangeDialog.hide();
        }
    }

    private static int[][] getUnicodeRanges() {
        List<Integer> ranges = new ArrayList<>();
        ranges.add(0);
        Character.UnicodeBlock currentBlock = Character.UnicodeBlock.of(0);
        for (int cp = 0x000001; cp < 0x110000; cp++ ) {
            Character.UnicodeBlock ub = Character.UnicodeBlock.of(cp);
            if (currentBlock == null) {
                if (ub != null) {
                    ranges.add(cp);
                    currentBlock = ub;
                }
            } else {  // being in some unicode range
                if (ub == null) {
                    ranges.add(cp - 1);
                    currentBlock = null;
                } else if (cp == 0x10ffff) {  // end of last block
                    ranges.add(cp);
                } else if (! ub.equals(currentBlock)) {
                    ranges.add(cp - 1);
                    ranges.add(cp);
                    currentBlock = ub;
                }
            }
        }
        ranges.add(0x00);  // for user defined range.
        ranges.add(0x7f);  // for user defined range.

        int[][] returnval = new int[ranges.size() / 2][2];
        for (int i = 0 ; i < ranges.size() / 2 ; i++ ) {
            returnval[i][0] = ranges.get(2*i);
            returnval[i][1] = ranges.get(2*i + 1);
        }
        return returnval;
    }

    private static String[] getUnicodeRangeNames() {
        String[] names = new String[UNICODE_RANGES.length];
        for (int i = 0 ; i < names.length ; i++ ) {
            names[i] = titleCase(
                Character.UnicodeBlock.of(UNICODE_RANGES[i][0]).toString());
        }
        names[names.length - 1] = "Custom...";
        return names;
    }

    private static String titleCase(String str) {
        str = str.replaceAll("_", " ");
        Pattern p = Pattern.compile("(^|\\W)([a-z])");
        Matcher m = p.matcher(str.toLowerCase(Locale.ROOT));
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, m.group(1) + m.group(2).toUpperCase(Locale.ROOT));
        }
        m.appendTail(sb);
        return sb.toString().replace("Cjk", "CJK").replace("Nko", "NKo");
    }
}
