/*
Tiamat - a role-playing game framework
Dialog.java - a dialog to display text and accept selections
 
 Copyright  (C) 2004-2005 Hugues Johnson
 
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package com.huguesjohnson.tiamat.graphics;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;

/** Dialog - a dialog to display text and accept selections
 * @author Hugues Johnson
 */
public class Dialog implements Drawable{

//begin private members
    private int textIndex; //index of the text to display
    private int x; //used to save the position of the dialog
    private int y; //used to save the position of the dialog
    private int x1; //used to save the position of the dialog
    private int y1; //used to save the position of the dialog
//end private members

//begin public properties
    //the text to display, each array element represents one “screen” of text, using forward() and back() methods scroll through the text
    private String[] text;
    /** Sets the array of text to display.
     * @param text The array of text to display, each element in the array represents a page of text.
     */
    public void setText(String[] text){
        int length=text.length;
        this.text=new String[length];
        for(int index=0;index<length;index++){
            this.text[index]=new String(text[index]);
        }
        this.textIndex=0;
    }
    /** Sets one page of text to display.
     * @param text The new text to display.
     * @param index The page number to replace.
     */
    public void setText(String text,int index){
        if((index>=0)&&(index<this.text.length)){
            this.text[index]=new String(text);
        }
    }
    /** Returns the array of text to display.
     * @return The array of text to display, each element in the array represents a page of text.
     */
    public String[] getText(){
        return(this.text);
    }
    /** Returns a single page of text.
     * @param index The page index to return.
     * @return The page of text at index, null if(page<0||page>=text.length)
     */
    public String getText(int index){
        if((index>=0)&&(index<this.text.length)){
            return(new String(this.text[index]));
        }
		return(null);
    }
    
    //if this dialog allows a user to select an option after all the text has been displayed the selections are stored here
    private String[] selections;
    /** Sets the selections to present after the last page of text.
     * @param selections An array of text selections.
     */
    public void setSelections(String[] selections){
        int length=selections.length;
        this.selections=new String[length];
        for(int index=0;index<length;index++){
            this.selections[index]=new String(selections[index]);
        }
    }
    /** Sets the selection at the specified index.
     * @param selection The new selection to display.
     * @param index The index of the selection to replace.
     */ 
    public void setSelection(String selection,int index){
        if((index>=0)&&(index<this.text.length)){
            this.selections[index]=new String(selection);
        }
    }
    /** Returns all the selections that will be displayed after the last page of text.
     *  @return All the selections that will be displayed after the last page of text.
     */
    public String[] getSelections(){
        return(this.selections);
    }
    /** Returns the specified selection.
     * @param index The index of the selection to return.
     * @return The selection at index, null if(index<0||index>selections.length)
     */
    public String getSelection(int index){
        if((index>=0)&&(index<this.selections.length)){
            return(new String(this.selections[index]));
        }
		return(null);
    }
    
    //the colors for this dialog
    private Theme style;
    /** Returns the Theme for this Dialog.
     * @return The Theme for this Dialog.
     */
    public Theme getStyle(){ return(this.style); }
    /** Sets the Theme for this Dialog.
     * @param style New Theme for the Dialog.
     */
    public void setStyle(Theme style){ this.style=style; }
    
    //where to draw stuff
    private transient Component canvas;
    /** Returns the canvas.
     * @return Canvas, where this Dialog is drawn.
     */
    public Component getCanvas(){ return(this.canvas); }
    /** Sets the canvas for this Dialog
     * @param canvas Where to the draw this Dialog.
     */
    public void setCanvas(Component canvas){ this.canvas=canvas; }
//end public properties

//begin constructors
    /** Constructor.
     * @param text Array of text pages to display.
     * @param canvas Where to draw the Dialog.
     * @param style The Theme for the Dialog.
     * @param parent The parent frame for the Dialog.
     */
    public Dialog(String[] text,Component canvas,Theme style,JFrame parent){
        this.parent=parent;
        this.setText(text);
        this.setCanvas(canvas);
        this.setStyle(style);
        //set defaults
        this.textIndex=0;
        this.selections=new String[0];
        this.x=this.y=0;
        this.x1=this.y1=-1;
        this.initDialog();
    }
    /** Constructor.
     * @param text Array of text pages to display.
     * @param x Left coordinate.
     * @param x1 Right coordinate.
     * @param y Top coordinate.
     * @param y1 Bottom coordinate.
     * @param canvas Where to draw the Dialog.
     * @param style The Theme for the Dialog.
     * @param parent The parent frame for the Dialog.
     */
    public Dialog(String[] text,int x,int y,int x1,int y1,Component canvas,Theme style,JFrame parent){
        this.parent=parent;
        this.setText(text);
        this.setCanvas(canvas);
        this.setStyle(style);
        this.x=x;
        this.y=y;
        this.x1=x1;
        this.y1=y1;
        //set defaults
        this.textIndex=0;
        this.selections=new String[0];
        this.initDialog();
    }
    /** Constructor.
     * @param text Array of text pages to display.
     * @param selections The selections to display after showing the last page of text.
     * @param canvas Where to draw the Dialog.
     * @param style The Theme for the Dialog.
     * @param parent The parent frame for the Dialog.
     */
    public Dialog(String[] text,String[] selections,Component canvas,Theme style,JFrame parent){
        this.parent=parent;
        this.setText(text);
        this.setSelections(selections);
        this.setCanvas(canvas);
        this.setStyle(style);
        //set defaults
        this.textIndex=0;
        this.x=this.y=0;
        this.x1=this.y1=-1;
        this.initDialog();
    }
    /** Constructor.
     * @param text Array of text pages to display.
     * @param selections The selections to display after showing the last page of text.
     * @param x Left coordinate.
     * @param x1 Right coordinate.
     * @param y Top coordinate.
     * @param y1 Bottom coordinate.
     * @param canvas Where to draw the Dialog.
     * @param style The Theme for the Dialog.
     * @param parent The parent frame for the Dialog.
     */
    public Dialog(String[] text,String[] selections,int x,int y,int x1,int y1,Component canvas,Theme style,JFrame parent){
        this.parent=parent;
        this.setText(text);
        this.setSelections(selections);
        this.setCanvas(canvas);
        this.setStyle(style);
        this.x=x;
        this.y=y;
        this.x1=x1;
        this.y1=y1;
        //set defaults
        this.textIndex=0;
        this.initDialog();
    }
//end constructors
    
//begin public methods
    /** Scrolls the text ahead one page, also causes redraw.
     * @return False if on the last page of text.
     */
    public boolean forward(){
        boolean success=true;
        if(this.textIndex>=(this.text.length-1)){
            success=false;
        } else{
            this.textIndex++;
            this.showText();
        }
        return(success);
    }
    
    /** Scrolls the text behind one “screen”, also causes redraw.
     * @return False if on the first page of text.
     */
    public boolean back(){
        boolean success=true;
        if(this.textIndex<=0){
            success=false;
        } else{
            this.textIndex--;
            this.showText();
        }
        return(success);
    }

    /** Hides the Dialog.
     */
    public void hide(){
        this.dialog.setVisible(false);
        //check if a selection was made or if this is a cancel
        int selectionIndex=this.getSelectionIndex();
        if(selectionIndex>=0){
            this.onSelection(selectionIndex);
        } else{
            this.onCancel();
        }
    }
    
    /** Event called when the Dialog is closed.
     */
    public void onCancel(){ }
    
    /** Event invoked when the player has confirmed a selection in the dialog.
     * @param index The index of the selection made (relative to the selections array).
     */
    public void onSelection(int index){ }
    
    /** Draws the Dialog.
     */
    public void draw(){
        if((this.text!=null)&&(this.text.length>0)&&(this.textIndex<this.text.length)){
            this.showText();
        } else if((this.selections!=null)&&(this.selections.length>0)){
            this.showSelections();
        }
        this.showDialog();
    }
    
    /** Draws the Dialog at the specified coordinates.
     * @param x The left x cooridinate.
     * @param y The top y cooridinate. 
     */
    public void draw(int x,int y){
        this.x=x;
        this.y=y;
        this.draw();
    }
    
    /** Draws the Dialog at the specified coordinates.
     * @param x1 The left x cooridinate.
     * @param x2 The right x cooridinate.
     * @param y1 The top y cooridinate. 
     * @param y2 The bottom y cooridinate. 
     */
    public void draw(int x1,int y1,int x2,int y2){
        this.x=x1;
        this.y=y1;
        this.x1=x2;
        this.y1=y2;
        this.draw();
    }
//end public methods
    
//begin private methods
    //these are here to handle UI and are not part of the standard framework specification
    private JDialog dialog; //the UI component being shown
    private ButtonGroup buttonGroup; //used to group selections
    private JRadioButton[] options; //used to display selections
    //private JTextArea textArea; //used to show text
    private JLabel textArea; 
    private JPanel contentPanel; //used to hold the other components
    private JFrame parent;
    
    /** Initializes the Dialog.
     */
    private void initDialog(){
        //setup the dialog
        this.dialog=new JDialog(this.parent);
        this.dialog.setUndecorated(true);
        this.dialog.setResizable(false);
        this.dialog.setModal(false);
        this.dialog.getContentPane().setBackground(this.style.getColorScheme().getBackColor());
        this.dialog.getContentPane().setForeground(this.style.getColorScheme().getForeColor());
        this.dialog.addKeyListener(new KeyListener(){
            public void keyPressed(KeyEvent keyEvent){ keyPressedEvent(keyEvent.getKeyCode()); }
            public void keyReleased(KeyEvent keyEvent){ }
            public void keyTyped(KeyEvent keyEvent){ }
        });
        //setup the content pane
        this.contentPanel=new JPanel();
        this.contentPanel.setForeground(this.style.getColorScheme().getForeColor());
        this.contentPanel.setBackground(this.style.getColorScheme().getBackColor());
        this.contentPanel.addKeyListener(new KeyListener(){
            public void keyPressed(KeyEvent keyEvent){ keyPressedEvent(keyEvent.getKeyCode()); }
            public void keyReleased(KeyEvent keyEvent){ }
            public void keyTyped(KeyEvent keyEvent){ }        
        });
        LineBorder outerLineBorder=(LineBorder)BorderFactory.createLineBorder(this.style.getColorScheme().getBorderColor());
        LineBorder insetLineBorder=(LineBorder)BorderFactory.createLineBorder(this.style.getColorScheme().getBorderInsetColor());
        Border border=BorderFactory.createCompoundBorder(insetLineBorder,outerLineBorder);
        this.contentPanel.setBorder(border);
        this.contentPanel.setLayout(new BorderLayout());
        this.dialog.getContentPane().add(this.contentPanel);
        //setup the text area
        this.textArea=new JLabel();
        this.textArea.setFocusable(false);
        this.textArea.setForeground(this.style.getColorScheme().getForeColor());
        this.textArea.setBackground(this.style.getColorScheme().getBackColor());
        this.textArea.addKeyListener(new KeyListener(){
            public void keyPressed(KeyEvent keyEvent){ keyPressedEvent(keyEvent.getKeyCode()); }
            public void keyReleased(KeyEvent keyEvent){ }
            public void keyTyped(KeyEvent keyEvent){ }
        });
    }
    
    /** Handles key press events.
     * @param keyCode the code for the key that was pressed.
     */
    void keyPressedEvent(int keyCode){
        switch(keyCode){
            case(KeyEvent.VK_ESCAPE):{
                this.hide();
                break;
            }
            case(KeyEvent.VK_ENTER):{
                if(this.forward()){
                    this.showDialog();
                } else{
                    if(this.selections.length>0){
                        int selectionIndex=this.getSelectionIndex();
                        if(selectionIndex>=0){
                            this.hide();
                            this.onSelection(selectionIndex);
                        } else{
                            this.showSelections();
                            this.showDialog();
                        }
                    } else{
                        this.hide();
                    }
                }
                break;
            }
            case(KeyEvent.VK_CONTROL):{
                if(this.forward()){
                    this.showDialog();
                } else{
                    if(this.selections.length>0){
                        int selectionIndex=this.getSelectionIndex();
                        if(selectionIndex>=0){
                            this.hide();
                            this.onSelection(selectionIndex);
                        } else{
                            this.showSelections();
                            this.showDialog();
                        }
                    } else{
                        this.hide();
                    }
                }
                break;
            }
            case(KeyEvent.VK_SPACE):{
                if(this.forward()){
                    this.showDialog();
                } else{
                    if(this.selections.length>0){
                        int selectionIndex=this.getSelectionIndex();
                        if(selectionIndex>=0){
                            this.hide();
                            this.onSelection(selectionIndex);
                        } else{
                            this.showSelections();
                            this.showDialog();
                        }
                    } else{
                        this.hide();
                    }
                }
                break;
            }                
            case(KeyEvent.VK_BACK_SPACE):{
                this.back();
                break;
            }
            case(KeyEvent.VK_UP):{
                if(this.options!=null){
                    int selectedIndex=this.getSelectionIndex();
                    if(selectedIndex>0){
                        this.options[selectedIndex-1].setSelected(true);
                    }
                }
                break;
            }
            case(KeyEvent.VK_DOWN):{
                if(this.options!=null){
                    int length=this.options.length-1;
                    int selectedIndex=this.getSelectionIndex();
                    if(selectedIndex<length){
                        this.options[selectedIndex+1].setSelected(true);
                    }
                }
                break;
            }

        }
    }
    
    /** Shows text in the Dialog.
     */
    private void showText(){
        this.contentPanel.removeAll();
        String currentText=new String("<html>"+this.text[this.textIndex].replaceAll("\n","<br>")+"</html>");
        this.textArea.setText(currentText);
        this.contentPanel.add(this.textArea,BorderLayout.CENTER);
        this.dialog.pack();
        if((this.x1>0)&&(this.y1>0)){
            this.dialog.setSize(this.x1,this.y1);
        } else{
            
        }
        if(!this.dialog.hasFocus()){
            this.dialog.requestFocus();
        }
    }
    
    /** Shows the selections.
     */
    private void showSelections(){
        this.buttonGroup=new ButtonGroup();
        int length=0;
        if(this.selections!=null){
            length=this.selections.length;
        }
        if(length<=0){ //check to prevent something wacky from happening if no selections
            this.hide();
        } else{
            this.contentPanel.removeAll();
            this.contentPanel.setLayout(new BoxLayout(this.contentPanel,BoxLayout.Y_AXIS));
            this.options=new JRadioButton[length];
            for(int index=0;index<length;index++){
                this.options[index]=new JRadioButton(this.selections[index]);
                this.options[index].addKeyListener(new KeyListener(){
                    public void keyPressed(KeyEvent keyEvent){ keyPressedEvent(keyEvent.getKeyCode()); }
                    public void keyReleased(KeyEvent keyEvent){ }
                    public void keyTyped(KeyEvent keyEvent){ }
                });
                this.options[index].setForeground(this.style.getColorScheme().getForeColor());
                this.options[index].setBackground(this.style.getColorScheme().getBackColor());
                this.options[index].setIcon(new ImageIcon(this.style.getRadioButtonImage()));
                this.options[index].setSelectedIcon(new ImageIcon(this.style.getRadioButtonSelectedImage()));
                this.buttonGroup.add(this.options[index]);
                this.contentPanel.add(this.options[index]);
            }
            this.options[0].setSelected(true);
            //this.contentPanel.add(this.buttonGroup,java.awt.BorderLayout.CENTER);
            this.dialog.invalidate();
            //this.dialog.pack();
            if(!this.dialog.hasFocus()){
                this.dialog.requestFocus();
            }
        }
    }
    
    /** Shows the Dialog.
     */
    private void showDialog(){
        this.dialog.pack();
        this.dialog.setLocation(this.x,this.y);
        if((this.x1>0)&&(this.y1>0)){
            this.dialog.setSize(this.x1,this.y1);
        } 
        this.dialog.setVisible(true);
    }
    
    /** Returns the index of the selection chosen by the user.
     * @return The index of the selection chosen by the user.
     */
    private int getSelectionIndex(){
        int selectionIndex=-1;
        if(this.options!=null){
            int length=this.options.length;
            int index=0;
            while((index<length)&&(selectionIndex==-1)){
                if(this.options[index].isSelected()){
                    selectionIndex=index;
                } else{
                    index++;
                }
            }
        }
        return(selectionIndex);
    }
//end private methods
}