
//========================================
//    Package
//========================================
package trick.common.ui;

//========================================
//    Imports
//========================================
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.DefaultComboBoxModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.ListSelectionModel;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

import org.jdesktop.swingx.JXLabel;
import org.jdesktop.swingx.JXTitledPanel;

import trick.common.TrickApplication;
import trick.common.utils.TrickColors;


/**
 * Utilities class for UI.
 *
 * @author Hong Chen
 * @since Trick 7
 */
public class UIUtils 
{
    
    //========================================
    //    Public data
    //========================================
    public final static String DOCUMENT_STYLE_REGULAR                        = "regular";
    public final static String DOCUMENT_STYLE_REGULAR_ITALIC                 = "regularItalic";
    public final static String DOCUMENT_STYLE_REGULAR_BOLD_BLUE              = "regularBoldBlue";
    public final static String DOCUMENT_STYLE_REGULAR_BOLD_BLUE_SHADE        = "regularBoldBlueShade";
    public final static String DOCUMENT_STYLE_REGULAR_LARGE_BOLD_BLUE_SHADE  = "regularLargeBoldBlueShade";
    public final static String DOCUMENT_STYLE_REGULAR_LARGE_BOLD_BLUE        = "regularLargeBoldBlue";
    public final static String DOCUMENT_STYLE_REGULAR_SMALL                  = "regularSmall";
    
    // For numbers that only needs to 2 fraction points.
    public static final NumberFormat TWO_FRACTION_FORMATTER = new DecimalFormat("0.00");
    
    public final static int CANCEL_OPTION = 0;
    public final static int OK_OPTION = 1;
    
    
    //========================================
    //    Protected data
    //========================================
    
    
    //========================================
    //    Constructors
    //========================================
    
    
    //========================================
    //    Set/Get methods
    //========================================
    /**
     * Gets the value of HOME environment variable.
     * 
     * @return The value of HOME.
     */
    public static String getUserHome() 
    {
        return System.getenv("HOME");
    }
    
    /**
     * Gets the value of TRICK_USER_HOME environment variable.
     * 
     * @return The value of TRICK_USER_HOME. If it is not defined, return 
     *            the value of HOME.
     */
    public static String getTrickUserHome()  
    {
        String ret = System.getenv("TRICK_USER_HOME");
        if (ret == null) 
        {
            ret = getUserHome();
        }
        return ret;
    }
    
    /**
     * Gets the value of TRICK_HOME environment variable.
     * 
     * @return The value of TRICK_HOME. If it is not defined, return 
     *            the value of HOME. 
     */
    public static String getTrickHome() 
    {
        String ret = System.getenv("TRICK_HOME");
        if (ret == null) 
        {
            ret = getUserHome();
        }
        return ret;
    }
    
    /**
     * Gets the text of $TRICK_HOME/bin.
     * 
     * @return The text of $TRICK_HOME/bin. If it is not defined, return 
     *         the text of $HOME/bin. 
     */
    public static String getTrickBin() 
    {
        String ret = System.getenv("TRICK_HOME");
        if (ret == null) 
        {
            ret = getUserHome();
        }
        ret = ret + File.separator + "bin";
        return ret;
    }
    
    /**
     * Gets the value of TRICK_VER environment variable.
     * 
     * @return The value of TRICK_VER. 
     */
    public static String getTrickVersion() 
    {
        return System.getenv("TRICK_VER");
    }
    
    /**
     * Gets the value of TRICK_HOST_CPU environment variable.
     * 
     * @return The value of TRICK_HOST_CPU.
     */
    public static String getTrickHostCPU() 
    {
        return System.getenv("TRICK_HOST_CPU");
    }
    
    /**
     * Gets the value of TRICK_LOGO environment variable.
     * 
     * @return The value of TRICK_LOGO.
     */
    public static String getTrickLogo() 
    {
        return System.getenv("TRICK_LOGO");
    }
    
    /**
     * Gets the value of TRICK_LOGO_STEP environment variable.
     * 
     * @return The value of TRICK_LOGO_STEP.
     */
    public static String getTrickLogoStep() 
    {
        return System.getenv("TRICK_LOGO_STEP");
    }
    
    //========================================
    //    Methods
    //========================================
    
    /**
     * Gets html hex for a {@link Color}.
     * 
     * @param c    An instance of {@link Color} which the hex number is getting from.
     * 
     * @return "#" followed by exactly 6 hex digits.
     * 
     */
    public final static String colorToHTMLHex(Color c) 
    {
        String s = Integer.toHexString( c.getRGB() & 0xffffff );
            if ( s.length() < 6 ) 
            { // pad on left with zeros
                s = "000000".substring( 0, 6 - s.length() ) + s;
            }
        return '#' + s;
    }
    
    /**
     * Gets the color based on the 6-digit html hex string or color name.
     * 
     * @param colorStrOrCode    The 6-digit hex leading by "#" in string or a supported name of a color.
     * 
     * @return An instance of {@link Color}.
     */
    public final static Color getColorFromHTMLHex(String colorStrOrCode) 
    {
        if (colorStrOrCode == null || "".equals(colorStrOrCode)) 
        {
            return null;
        }
        Color c = null;
        try 
        {
            c = Color.decode(colorStrOrCode);
        } 
        catch (NumberFormatException nfe) 
        {
            c = TrickColors.getColor(colorStrOrCode);
        }
        return c;
    }
    
    /**
     * Checks to see if a JComboBox contains an object.
         * @param model combo box to check
         * @param obj object to test for
         * @return true or false
     */
    public static boolean comboBoxContains(DefaultComboBoxModel model, Object obj) 
    {
        for (int i = 0; i < model.getSize(); i++) 
        {
            if (obj.equals(model.getElementAt(i))) 
            {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Returns whether the mouse click is a right click.
     *
     * @param e a {@link MouseEvent} to test.
     *
     * @return <code>true</code> if right button was pressed, <code>false</code> otherwise.
     */
    public static boolean isRightMouseClick(MouseEvent e) 
    {
        boolean    rval = false;
        if(e.getButton() == 3) 
        {
            rval = true;
        }

        return rval;
    }
    
    /**
     * Check to see if the mouse click is double click.
     * 
     * @param e a {@link MouseEvent} to test.
     * 
     * @return <code>true</code> if is double-clicking, <code>false</code> otherwise.
     */
    public static boolean isDoubleClick(MouseEvent e) 
    {
        boolean rval = false;
        
        if (e.getClickCount() == 2) 
        {
            rval = true;
        }
        
        return rval;
    }
    
    /**
     * Check to see if the specified directory has "log" files.
     * 
     * @param dir     A file directory.
     * 
     * @return <code>true</code> if the specified directory has log files, <code>false</code> otherwise.
     */
    public static boolean hasLogFile(File dir) 
    {
        return hasSpecifiedFile(dir, TrickFileFilter.LOG);
    }
    
    /**
     * Check to see if the specified directory has .header files.
     * 
     * @param dir     A file directory.
     * 
     * @return <code>true</code> if the specified directory has .header files, <code>false</code> otherwise.
     */
    public static boolean hasHeaderFile(File dir) 
    {
        return hasSpecifiedFile(dir, TrickFileFilter.HEADER);
    }
    
    /**
     * Check to see if the specified directory has .trk files.
     * 
     * @param dir     A file directory.
     * 
     * @return <code>true</code> if the specified directory has .trk files, <code>false</code> otherwise.
     */
    public static boolean hasTRKFile(File dir) 
    {
        return hasSpecifiedFile(dir, TrickFileFilter.TRK);
    }
    
    /**
     * Check to see if the specified directory has .csv files.
     * 
     * @param dir     A file directory.
     * 
     * @return <code>true</code> if the specified directory has .csv files, <code>false</code> otherwise.
     */
    public static boolean hasCSVFile(File dir) 
    {
        return hasSpecifiedFile(dir, TrickFileFilter.CSV);
    }
    
    /**
     * Helper method for checking to see if a particular directory has certain files.
     * 
     * @param dir    A directory.
     * @param type    The Trick file type. See {@link TrickFileFilter} for all supported types.
     * 
     * @return <code>true</code> if the specified directory has specified files, <code>false</code> otherwise.
     */
    private static boolean hasSpecifiedFile(File dir, int type) 
    {
        File[] tmp = dir.listFiles(new TrickFileFilter(type));    
        if (tmp == null || tmp.length < 1) 
        {
            return false;
        }
        return true;
    }
    
    /**
     * Returns a file's name without its extension. If no extension found, return
     * the original file name.
         * @param file file object to use.
         * @return file name
     */
    public static String getFileNameWithoutExtension(File file) 
    {
        String fileName = file.getName();
        int dotLoc = fileName.lastIndexOf('.');
        if (dotLoc > 0 && dotLoc <= fileName.length() - 2) 
        {
            return fileName.substring(0, dotLoc);
        }
        return fileName;
    }
    
    /**
     * Returns an array of abstract pathnames denoting the files that satisfy the specified filter.
     * 
     * @param dir    A directory.
     * @param type    The Trick file type. See {@link TrickFileFilter} for all supported types.
     * 
     * @return    An array of abstract pathnames denoting the files, <code>null</code>
     *             if the specified dir is null or is not a directory.
     */
    public static File[] getListFiles(File dir, int type) 
    {
        if (dir != null && dir.isDirectory()) 
        {
            return dir.listFiles(new TrickFileFilter(type));
        }
        return null;
    }
    
    /**
     * Returns an instance of {@link JXLabel} that displays trick small icon for different GUIs
     * with version information tool tip.
     * 
     * @return    An instance of {@link JXLabel} with its icon set to trick small icon and 
     *          tool tip set to the trick version.
     */
    public static JXLabel getSmallTrickIconLabel() 
    {
        JXLabel label = new JXLabel();    
        String desc = "Trick Version " + getTrickVersion();
        label.setToolTipText(desc);
        
        ImageIcon smallIcon = createImageIcon("trick_small.gif");
        if (smallIcon != null) 
        {
            smallIcon.setDescription(desc);
            label.setIcon(smallIcon);
        }
        return label;
    }
    
    /**
     * Search to see if an input string has the search string. 
     * The search string can have "*". If there is no wildcard, 
     * it simply checks if inputStr string contians the searchStr string.
     * 
     * @param inputStr - the input search string.
     * @param searchStr - the search string which searching is based upon.
     * 
     * @return - true if found, false otherwise.
     */
    public static boolean searchWithWildcard(String inputStr, String searchStr) 
    {
        StringBuffer patternBuf = new StringBuffer();
        if (searchStr.contains("*")) 
        {
            String[] parts = searchStr.split("\\*");
            for (String part : parts) 
            {
                patternBuf.append(".*(");
                patternBuf.append(part);
                patternBuf.append(")");
            }
            patternBuf.append(".*");
            
            Pattern pattern = Pattern.compile(patternBuf.toString());
            Matcher matcher = pattern.matcher(inputStr);
            return matcher.find();
        } 
        else 
        {
            return (inputStr.contains(searchStr));
        }
    }
    
    /**
     * Opens a directory only file chooser for a particular directory 
     * with specified dialog title.
     * 
     * @param chooserTitle    The title for the dialog.
     * @param dir            The directory for the file chooser.
     * @param parent        The parent component for opening the file chooser.
     * 
     * @return    The selected directory.
     */
    public static File chooseDir(String chooserTitle, String dir, Component parent) 
    {
        JFileChooser chooser = new JFileChooser();
        if (dir == null) 
        {
            chooser.setCurrentDirectory(new java.io.File("."));
        } 
        else 
        {
            File file = new java.io.File(dir);
            if (!file.exists()) 
            {
                file = new java.io.File(".");
            }
            chooser.setCurrentDirectory(file);            
        }
        chooser.setDialogTitle(chooserTitle);
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        
        int chooserState = chooser.showDialog(parent, "Ok");
        if (chooserState == JFileChooser.APPROVE_OPTION) 
        {
            return chooser.getSelectedFile();
        } 
        else 
        {
            return null;
        }
    }
    
    /**
     * Opens a file chooser and returns the selected file if available. 
     * 
     * @param dir                The directory from which a file will be selected.
     * @param fileName            The initial file name that is prompted in the file field.
     * @param fileNameExtension    The desired file extension.
     * @param parent The parent component that makes the call.
     * 
     * @return An instance of {@link File} if the selection is successful, <code>null</code> otherwise.
     */
    public static File chooseOpenFile(String dir, String fileName, String fileNameExtension, Component parent) 
    {
        JFileChooser chooser = new JFileChooser();
        File selectedFile = null;
        
        File fileDir = null;
        
        // if dir is not specified or the dir doesn't exist, use the current directory
        if (dir == null) 
        {
            fileDir = new File(".");
        } 
        else 
        {
            fileDir = new File(dir);
        }
        
        if (!fileDir.exists()) 
        {
            fileDir = new File(".");
        }
        
        chooser.setCurrentDirectory(fileDir);
        
        if (fileName != null) 
        {
            chooser.setSelectedFile(new java.io.File(fileName));
        }
        
        if (fileNameExtension != null) 
        {
            chooser.setFileFilter(new FileNameExtensionFilter(fileNameExtension+" file", fileNameExtension));
        }
        chooser.setDialogTitle("Open File");
        
        boolean isValid = true;
        do 
        {
            int chooserState = chooser.showDialog(parent, "Ok");
            if (chooserState == JFileChooser.APPROVE_OPTION) 
            {
                selectedFile = chooser.getSelectedFile();
                if (!selectedFile.exists()) 
                {
                    JOptionPane.showMessageDialog(parent, 
                                                    selectedFile.getName()+" does not exist. Please choose another file!", 
                                                    "File Not Found",                              
                                                    JOptionPane.WARNING_MESSAGE);
                } 
                else 
                {
                    isValid = false;
                }
            } 
            else 
            {
                return null;
            }
        } while (isValid);
                
        return selectedFile;
    }
    
    /**
     * Saves text to a specified file.
     * 
     * @param contents    The text contents that need to be saved.
     * @param file        The file which the text will be saved to.
     */
    public static void saveTextFile(String contents, File file) 
    {
        try 
        {
            PrintWriter out = new PrintWriter(new FileWriter(file));
            out.print(contents);
            out.close();
        } 
        catch (IOException ioe) 
        {
            
        }
    }
    
    /**
     * Opens a file chooser and let the user to either choose or specify a file for saving purpose.
     * 
     * @param dir                The directory which the file chooser works from initially.
     * @param initialFile        The file that is set initially.
     * @param fileNameExtension    The desired file extension.
     * @param parent The parent component that wants to open the file chooser.
     * 
     * @return    The specified the file, <code>null</code> if canceled.
     */
    public static File chooseSaveFile(String dir, String initialFile, String fileNameExtension, Component parent) 
    {
        JFileChooser chooser = new JFileChooser();
        File selectedFile = null;
        String selectedFileName = null;
        
        File fileDir = null;
        
        // if dir is not specified or the dir doesn't exist, use the current directory
        if (dir == null) 
        {
            fileDir = new File(".");
        } 
        else 
        {
            fileDir = new File(dir);
        }
        
        if (!fileDir.exists()) 
        {
            fileDir = new File(".");
        }
        
        chooser.setCurrentDirectory(fileDir);
        
        if (initialFile != null) 
        {
            chooser.setSelectedFile(new java.io.File(initialFile));
        }
        if (fileNameExtension != null) 
        {
            chooser.setFileFilter(new FileNameExtensionFilter(fileNameExtension + " file", fileNameExtension));
        }
        chooser.setDialogTitle("Save File");
        boolean isValid = true;
        do 
        {
            int chooserState = chooser.showDialog(parent, "Ok");
            if (chooserState == JFileChooser.APPROVE_OPTION) 
            {
                selectedFile = chooser.getSelectedFile();
                selectedFileName = selectedFile.getName();
                if (fileNameExtension != null) 
                {
                    // 1.append the specified file extension if there is no extension.
                    // 2.append the desired extension if there is an extension which is not the same as specified
                    if (selectedFileName.indexOf('.') == -1 || 
                       (selectedFileName.indexOf('.') != -1 && !selectedFileName.endsWith(fileNameExtension))) 
                    {
                        selectedFileName += "." + fileNameExtension;
                    }
                    selectedFile = new File(selectedFile.getParentFile(), selectedFileName);
                }
                if (selectedFile.exists()) 
                {                    
                    int choice = JOptionPane.showConfirmDialog(parent, 
                                                               selectedFileName + " already exists. Overwrite it?", 
                                                               "Overwrite File", 
                                                               JOptionPane.YES_NO_OPTION);
                    // If "Yes" is selected
                    if (choice == JOptionPane.YES_OPTION) 
                    {
                        isValid = false;
                    }
                } 
                else 
                {
                    isValid = false;
                }
            } 
            else 
            {
                return null;
            }
        } while (isValid);
            
        return selectedFile;
    }
    
    
    /**
     * Shows a warning dialog with specified message and title.
     * 
     * @param title        The title for the dialog window.
     * @param msg        The message text for the dialog window.
     * @param parent     The parent component that wants to open the dialog.
     * 
     * @return    The selected choice.
     */
    public static int showOkCancelDialog(String title, String msg, Component parent) 
    {
        Object[] options = new Object[2];
        options[OK_OPTION] = "Ok";
        options[CANCEL_OPTION] = "Cancel";
        int choice = JOptionPane.showOptionDialog(parent,
                                                  msg,
                                                  title,
                                                  JOptionPane.DEFAULT_OPTION,
                                                  JOptionPane.WARNING_MESSAGE,
                                                  null,
                                                  options,
                                                  options[CANCEL_OPTION]);        
        return choice;
    }
    
    /**
     * Shows a list input dialog. Forces to use a {@link JList} for user to input.
     * 
     * JOptionPane.showInputDialog with the same arguments as this method uses 
     * a component for user to input and it is upt to the UI to decide how best to represent the 
     * <code>selectionValues</code>. 
     * Normally, it's a {@link JComboBox} if less than 20 <code>selectionValues</code>, 
     * {@link JList} if equal or greater than 20 <code>selectionValues</code>, or 
     * {@link JTextField} if null <code>selectionValues</code>. 
         * @param parentComponent parent to use for dialog box
         * @param message message to show
         * @param title title of window
         * @param messageType type of message to show
         * @param icon icon to use
         * @param selectionValues list of values to use as choices
         * @param initialSelectionValue sets initial value of selection values
         * @return value of the input selection
         * @throws HeadlessException HeadlessException
     */
    public static Object showListInputDialog(Component parentComponent, Object message, String title, int messageType,
                                             Icon icon, Object[] selectionValues, Object initialSelectionValue) 
                                             throws HeadlessException 
    {
        JOptionPane pane = new JOptionPane();
        JList list = getSingleSelectionList(pane, selectionValues, initialSelectionValue);
        JScrollPane sp = new JScrollPane(list);
        list.ensureIndexIsVisible(list.getSelectedIndex());
        
        pane.setMessage(new Object[] { message, sp});
        pane.setMessageType(JOptionPane.QUESTION_MESSAGE);
        pane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
        
        JDialog dialog = pane.createDialog(parentComponent, title);
        
        dialog.setVisible(true);
        dialog.dispose();
        
        Object value = pane.getInputValue();
        
        // this choice is either JOptionPane.OK_OPTION or JOptionPane.CANCEL_OPTION,
        // if null, meaning the user closed the window without choosing anything
        Object choice = pane.getValue();
        
        if (choice == null || ((Integer)choice).intValue() == JOptionPane.CANCEL_OPTION) 
        {
            return initialSelectionValue;
        }
        
        if (value == JOptionPane.UNINITIALIZED_VALUE) 
        {
            return null;
        }
        
        return value;
    }
    
    /**
     * Helper method and only used by <code>showListInputDialog</code> method.
     */
    @SuppressWarnings("unchecked")
    private static JList getSingleSelectionList(final JOptionPane optionPane, Object[] values, Object initialValue) 
    {
        JList list = new JList(values);
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        if(initialValue != null) 
        {
            list.setSelectedValue(initialValue, true);
        }
        
        MouseListener mouseListener = new MouseAdapter() 
        {
            @Override
            public void mousePressed(MouseEvent e) 
            {
                JList list = (JList)e.getSource();
                int index = list.locationToIndex(e.getPoint());    
                optionPane.setInputValue(list.getModel().getElementAt(index));
                
                if (isDoubleClick(e)) 
                {                  
                    // close the pane once the user selects a value by double-clicking
                    optionPane.setValue(JOptionPane.CLOSED_OPTION);
                } 
            }
        };
        
        list.addMouseListener(mouseListener);
        
        return list;
    }
    
    /** 
     * Returns an {@link ImageIcon} for the specified full file path or
     * a file in common/resources.
     * 
     * @param fileName    The name or the full path of the image file.
     * 
     * @return    an instance of {@link ImageIcon} for the specified image, 
     *             null if the image file can't be found.
     */
    public static ImageIcon createImageIcon(String fileName) 
    {    
        ImageIcon imgIcon = null;
        
        // if the fileName is a full path
        if (fileName.indexOf(java.io.File.separator) != -1) 
        {
            imgIcon = new ImageIcon(fileName);
        } 
        else 
        {
            // if only a file name specified, try to find it at common resources folder
            URL imgURL = TrickApplication.class.getResource("resources" + java.io.File.separator + fileName);
            if (imgURL != null) 
            {
                imgIcon = new ImageIcon(imgURL);
            } 
        }
        
        if (imgIcon == null) 
        {
            System.err.println("Couldn't find file: " + fileName);            
        }
    
        return imgIcon;       
    }

    /**
     * Returns a {@link InputStream} for the specified file path or a file
     * in common/resources.
     * 
     * @param fileName    The name of a file in common/resources or a full file path.
     * 
     * @return    an instance of {@link InputStream} for the specified file, 
     *          null if the specified file can't be found.
     */
    public static InputStream getInputStreamForFile(String fileName) 
    {
        try 
        {        
            InputStream ins = null;
            // if the fileName is a full path
            if (fileName.indexOf(java.io.File.separator) != -1) 
            {
                ins = new FileInputStream(fileName);
            } 
            else 
            {
            // if only a file name, then find it at common resources area
                ins = TrickApplication.class.getResourceAsStream("resources" + java.io.File.separator + fileName);
            }
            return ins;
        } 
        catch (NullPointerException npe) 
        {
            return null;
        } 
        catch (FileNotFoundException fnfe) 
        {
            return null;
        }
    }
    
    /**
     * Creates titled panel with content view plus search capability if applicable.
     * 
     * @param title                The title of the panel.
     * @param contentComponent    The searchable component for the content. 
     * @param findPanel            The panel for find/search function. 
     *                          If <code>null</code>, then only a titled panel is created.
     * 
     * @return    A titled panel with find bar for search feature.
     */
    public static JPanel createSearchableTitledPanel(String title, 
                                                     JComponent contentComponent, 
                                                     JPanel findPanel) 
    {
        JXTitledPanel titledPanel = new JXTitledPanel();        
        titledPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(""));
        titledPanel.setTitle(title);
        titledPanel.add(new JScrollPane(contentComponent), BorderLayout.CENTER);
        titledPanel.setMinimumSize(new Dimension(300, 250));
        if (findPanel != null) 
        {
            titledPanel.add(findPanel, BorderLayout.SOUTH);
        }
        return titledPanel;
    }
    
    /**
     * Creates a JTextPane that displays detail information and the information
     * is StyledDocument.
     * 
     * @return a JTextPane that has different document styles.
     */
    public static JTextPane createReadingTextPane() 
    {
        JTextPane textPane = new JTextPane();
        textPane.setEditable(false);
        textPane.setPreferredSize(new Dimension(400, 200));
        StyledDocument document = textPane.getStyledDocument();
        addStylesToDocument(document);
        return textPane;
    }    
    
    /**
     * Adds styles to a StyledDocument as specified doc.
     * 
     * @param doc    a StyledDocument to add styles to.
     */
    private static void addStylesToDocument(StyledDocument doc) 
    {
        //Initialize some styles.
        Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);

        Style regular = doc.addStyle(DOCUMENT_STYLE_REGULAR, def);
        StyleConstants.setFontFamily(def, "SansSerif");
        

        Style s = doc.addStyle(DOCUMENT_STYLE_REGULAR_ITALIC, regular);
        StyleConstants.setItalic(s, true);

        s = doc.addStyle(DOCUMENT_STYLE_REGULAR_BOLD_BLUE, regular);
        StyleConstants.setBold(s, true);
        StyleConstants.setForeground(s, Color.blue);

        s = doc.addStyle(DOCUMENT_STYLE_REGULAR_SMALL, regular);
        StyleConstants.setFontSize(s, 10);

        s = doc.addStyle(DOCUMENT_STYLE_REGULAR_LARGE_BOLD_BLUE_SHADE, regular);
        StyleConstants.setFontSize(s, 14);
        StyleConstants.setBold(s, true);
        StyleConstants.setBackground(s, Color.lightGray);
        StyleConstants.setForeground(s, Color.blue);
        
        s = doc.addStyle(DOCUMENT_STYLE_REGULAR_LARGE_BOLD_BLUE, regular);
        StyleConstants.setFontSize(s, 14);
        StyleConstants.setBold(s, true);
        StyleConstants.setForeground(s, Color.blue);
        
        s = doc.addStyle(DOCUMENT_STYLE_REGULAR_BOLD_BLUE_SHADE, regular);
        StyleConstants.setBold(s, true);
        StyleConstants.setBackground(s, Color.lightGray);
        StyleConstants.setForeground(s, Color.blue);
        
    }
}
