package rabbitescape.ui.swing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.Stack;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter;
import static rabbitescape.engine.util.Util.*;
import static rabbitescape.engine.i18n.Translation.*;
import static rabbitescape.ui.swing.SwingConfigSetup.*;
import rabbitescape.engine.CompletedLevelWinListener;
import rabbitescape.engine.IgnoreLevelWinListener;
import rabbitescape.engine.LevelWinListener;
import rabbitescape.engine.MultiLevelWinListener;
import rabbitescape.engine.config.Config;
import rabbitescape.engine.config.ConfigKeys;
import rabbitescape.engine.config.ConfigTools;
import rabbitescape.engine.config.TapTimer;
import rabbitescape.engine.err.RabbitEscapeException;
import rabbitescape.engine.menu.AboutText;
import rabbitescape.engine.menu.ByNameConfigBasedLevelsCompleted;
import rabbitescape.engine.menu.LevelMenuItem;
import rabbitescape.engine.menu.LevelsCompleted;
import rabbitescape.engine.menu.LevelsList;
import rabbitescape.engine.menu.LoadLevelsList;
import rabbitescape.engine.menu.Menu;
import rabbitescape.engine.menu.MenuDefinition;
import rabbitescape.engine.menu.MenuItem;
import rabbitescape.engine.util.RealFileSystem;
import rabbitescape.engine.util.Util.IdxObj;
import rabbitescape.render.BitmapCache;
import rabbitescape.render.androidlike.Sound;
public class MenuUi
{
public static class UnknownMenuItemType extends RabbitEscapeException
{
private static final long serialVersionUID = 1L;
public final String name;
public final MenuItem.Type type;
public UnknownMenuItemType( MenuItem item )
{
this.name = item.name;
this.type = item.type;
}
}
private class ButtonListener implements ActionListener
{
private final MenuItem item;
public ButtonListener( MenuItem item )
{
this.item = item;
}
@Override
public void actionPerformed( ActionEvent event )
{
switch ( item.type )
{
case MENU:
{
SwingUtilities.invokeLater( new Runnable()
{
@Override
public void run()
{
stack.add( item.menu );
placeMenu();
}
} );
return;
}
case ABOUT:
{
about();
return;
}
case LEVEL:
{
level( (LevelMenuItem)item );
return;
}
case LOAD:
{
chooseLevel();
return;
}
case GITHUB_ISSUE:
{
chooseIssue();
return;
}
case QUIT:
{
frame.exit();
return;
}
case DEMO:
{
return;
}
default:
{
throw new UnknownMenuItemType( item );
}
}
}
}
private static final Color backgroundColor = Color.WHITE;
private static final Color buttonColor = Color.LIGHT_GRAY;
private final RealFileSystem fs;
private final PrintStream out;
private final Locale locale;
private final BitmapCache<SwingBitmap> bitmapCache;
private final Stack<Menu> stack;
private final Config uiConfig;
private final MainJFrame frame;
private final Sound sound;
private final JPanel menuPanel;
private final LevelsCompleted levelsCompleted;
private SideMenu sidemenu;
public MenuUi(
RealFileSystem fs,
PrintStream out,
Locale locale,
BitmapCache<SwingBitmap> bitmapCache,
Config uiConfig,
MainJFrame frame,
Sound sound
)
{
this.fs = fs;
this.out = out;
this.locale = locale;
this.bitmapCache = bitmapCache;
this.stack = new Stack<>();
this.uiConfig = uiConfig;
this.frame = frame;
this.sound = sound;
this.menuPanel = new JPanel( new GridBagLayout() );
LevelsList levelsList = LoadLevelsList.load(
MenuDefinition.allLevels );
this.levelsCompleted = new ByNameConfigBasedLevelsCompleted(
uiConfig, levelsList );
stack.push(
MenuDefinition.mainMenu( this.levelsCompleted, levelsList, true )
);
init();
}
public void init()
{
Container contentPane = frame.getContentPane();
contentPane.setLayout( new BorderLayout( 4, 4 ) );
sidemenu = new SideMenu(
contentPane,
bitmapCache,
new Dimension( 32, 32 ),
uiConfig,
backgroundColor
);
JScrollPane scrollPane = new JScrollPane( menuPanel );
contentPane.add( scrollPane, BorderLayout.CENTER );
contentPane.setBackground( backgroundColor );
scrollPane.setBackground( backgroundColor );
scrollPane.getVerticalScrollBar().setUnitIncrement( 16 );
menuPanel.setBackground( backgroundColor );
placeMenu();
frame.setBoundsFromConfig();
frame.setTitle( t( "Rabbit Escape" ) );
frame.pack();
frame.setVisible( true );
initListeners();
}
private void uninit()
{
frame.getContentPane().removeAll();
}
public void placeMenu()
{
Menu menu = stack.lastElement();
menuPanel.removeAll();
Dimension buttonSize = new Dimension( 300, 40 );
JLabel label = new JLabel( t( menu.intro ) );
label.setHorizontalAlignment( SwingConstants.CENTER );
label.setForeground( Color.RED );
label.setPreferredSize( buttonSize );
menuPanel.add( label, constraints( 0 ) );
for ( IdxObj<MenuItem> item : enumerate1( menu.items ) )
{
if ( item.object.hidden && !TapTimer.matched )
{
continue;
}
JButton button = new JButton(
t( item.object.name, item.object.nameParams ) );
button.setBackground( buttonColor );
button.addActionListener( new ButtonListener( item.object ) );
button.setVisible( true );
button.setEnabled( item.object.enabled || TapTimer.matched );
button.setPreferredSize( buttonSize );
menuPanel.add( button, constraints( item.index ) );
}
sound.setMusic( "tryad-let_them_run" );
frame.repaint();
frame.revalidate();
}
public void refreshEnabledItems()
{
Menu menu = stack.lastElement();
menu.refresh();
}
private GridBagConstraints constraints( int i )
{
return new GridBagConstraints(
0,
i,
1,
1,
1.0,
1.0,
GridBagConstraints.CENTER,
GridBagConstraints.NONE,
new Insets( 10, 10, 10, 10 ),
0,
0
);
}
private void chooseIssue()
{
GitHubIssueDialog id = new GitHubIssueDialog( frame );
String world = id.getWorld();
if( null == world )
{
return; // User clicked cancel, or selected issue with no world
}
String path = ConfigTools.getString(
uiConfig, ConfigKeys.CFG_LOAD_LEVEL_PATH );
File nameCandidate = new File ( path + File.separator + id.generateFilename() + ".rel" );
int version = 0;
String filename = id.generateFilename();
while( nameCandidate.exists() )
{
nameCandidate = new File (
path + File.separator + filename + "." + ( version++ ) + ".rel"
);
}
PrintWriter out;
try
{
out = new PrintWriter( nameCandidate );
out.print( world );
out.close();
playLevel( nameCandidate.getAbsolutePath(), new IgnoreLevelWinListener() );
}
catch ( FileNotFoundException e ) /// @TODO fix exception handling
{
e.printStackTrace();
}
}
private void chooseLevel()
{
String filename = chooseLevelFilename();
if ( filename == null )
{
return; // The user cancelled or closed the dialog
}
playLevel( filename, new IgnoreLevelWinListener() );
}
private String chooseLevelFilename()
{
String path = ConfigTools.getString(
uiConfig, ConfigKeys.CFG_LOAD_LEVEL_PATH );
final JFileChooser fc = new JFileChooser();
final FileNameExtensionFilter relFilter = new FileNameExtensionFilter(
t( "Rabbit Escape Level (*.rel)" ),
"rel"
);
fc.setDialogTitle( t( "Open a level file" ) );
fc.setCurrentDirectory( new File( path ) );
fc.addChoosableFileFilter( relFilter );
fc.setFileFilter( relFilter );
int chooserVal = fc.showOpenDialog( frame );
if ( JFileChooser.APPROVE_OPTION != chooserVal )
{
return null; // The user cancelled or closed the dialog
}
String filename = fc.getSelectedFile().getAbsolutePath();
ConfigTools.setString(
uiConfig,
ConfigKeys.CFG_LOAD_LEVEL_PATH,
fc.getCurrentDirectory().getAbsolutePath()
);
uiConfig.save();
return filename;
}
private void level( final LevelMenuItem item )
{
playLevel( item.fileName, winListeners( item ) );
}
private void playLevel(
final String filename, final LevelWinListener levelWinListener )
{
new SwingWorker<Void, Void>()
{
@Override
protected Void doInBackground() throws Exception
{
uninit();
new SwingSingleGameEntryPoint(
fs,
out,
locale,
bitmapCache,
uiConfig,
frame,
sound,
MenuUi.this,
SwingGameLaunch.NOT_DEMO_MODE,
false
).launchGame(
new String[] { filename },
levelWinListener
);
return null;
}
}.execute();
}
protected LevelWinListener winListeners( LevelMenuItem item )
{
return new MultiLevelWinListener(
new CompletedLevelWinListener(
item.levelsDir, item.levelNumber, levelsCompleted ),
new UpdateSwingMenuLevelWinListener( this )
);
}
private void about()
{
JTextPane text = new JTextPane();
text.setText( t( AboutText.text ) );
text.setBackground( null );
JOptionPane.showMessageDialog(
frame,
text,
t( "About Rabbit Escape" ),
JOptionPane.INFORMATION_MESSAGE
);
}
private void back()
{
stack.pop();
if ( stack.empty() )
{
frame.exit();
}
else
{
SwingUtilities.invokeLater(
new Runnable()
{
@Override
public void run()
{
placeMenu();
}
}
);
}
}
private void setMuted( boolean muted )
{
ConfigTools.setBool( uiConfig, CFG_MUTED, muted );
uiConfig.save();
sound.mute( muted );
}
private void initListeners()
{
sidemenu.mute.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent evt )
{
setMuted( sidemenu.mute.isSelected() );
}
} );
MenuTools.clickOnKey( sidemenu.mute, "mute", KeyEvent.VK_M );
sidemenu.back.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
back();
}
} );
MenuTools.clickOnKey( sidemenu.back, "back", KeyEvent.VK_ESCAPE );
sidemenu.exit.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent evt )
{
frame.exit();
}
} );
MenuTools.clickOnKey( sidemenu.exit, "quit", KeyEvent.VK_Q );
}
}