/**
* Copyright (c) 2005-2017, KoLmafia development team
* http://kolmafia.sourceforge.net/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* [1] Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* [2] 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.
* [3] Neither the name "KoLmafia" 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.
*/
package net.sourceforge.kolmafia.swingui.panel;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import net.java.dev.spellcast.utilities.DataUtilities;
import net.java.dev.spellcast.utilities.JComponentUtilities;
import net.sourceforge.kolmafia.KoLConstants;
import net.sourceforge.kolmafia.KoLmafiaCLI;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.session.MushroomManager;
import net.sourceforge.kolmafia.swingui.MushroomFrame;
import net.sourceforge.kolmafia.swingui.button.InvocationButton;
import net.sourceforge.kolmafia.utilities.InputFieldUtilities;
public class MushroomScriptPanel
extends JPanel
{
private static final Color TODAY_COLOR = new Color( 192, 255, 192 );
private static final Color OTHER_COLOR = new Color( 240, 240, 240 );
private String currentLayout = "";
private final JPanel centerPanel;
private int currentForecast = 2;
private final JButton addToLayoutButton, deleteFromLayoutButton;
private final String[][] planningData;
private final String[][] originalData;
private final JLabel[] headers;
private final JPanel[] planningPanels;
private final MushroomButton[][][] planningButtons;
public MushroomScriptPanel()
{
this.headers = new JLabel[ MushroomFrame.MAX_FORECAST + 1 ];
this.planningData = new String[ MushroomFrame.MAX_FORECAST + 1 ][ 16 ];
this.originalData = new String[ MushroomFrame.MAX_FORECAST + 1 ][ 16 ];
for ( int i = 0; i < MushroomFrame.MAX_FORECAST; ++i )
{
for ( int j = 0; j < 16; ++j )
{
this.planningData[ i ][ j ] = "__";
this.originalData[ i ][ j ] = "__";
}
}
this.centerPanel = new JPanel( new GridLayout( 0, 4, 20, 20 ) );
// Now add the first panel to the layout so that the person
// can add more panels as they are needed.
this.planningPanels = new JPanel[ MushroomFrame.MAX_FORECAST + 1 ];
this.planningButtons = new MushroomButton[ MushroomFrame.MAX_FORECAST + 1 ][ 4 ][ 4 ];
for ( int i = 0; i < MushroomFrame.MAX_FORECAST; ++i )
{
this.planningPanels[ i ] = new JPanel( new GridLayout( 4, 4, 0, 2 ) );
for ( int j = 0; j < 4; ++j )
{
for ( int k = 0; k < 4; ++k )
{
this.planningButtons[ i ][ j ][ k ] = new MushroomButton( i, j * 4 + k );
this.planningPanels[ i ].add( this.planningButtons[ i ][ j ][ k ] );
}
}
}
this.centerPanel.add( this.constructPanel( 0, this.planningPanels[ 0 ] ) );
this.centerPanel.add( this.constructPanel( 1, this.planningPanels[ 1 ] ) );
// Dummy buttons for the mushroom plot (just for layout
// viewing purposes. To be replaced with real functionality
// at a later date.
JPanel buttonPanel = new JPanel( new GridLayout( 0, 1, 5, 5 ) );
// Now add the various action buttons.
this.addToLayoutButton = new InvocationButton( "Add a Day", this, "addToLayout" );
this.deleteFromLayoutButton = new InvocationButton( "Delete a Day", this, "removeFromLayout" );
this.deleteFromLayoutButton.setEnabled( false );
buttonPanel.add( this.addToLayoutButton );
buttonPanel.add( this.deleteFromLayoutButton );
buttonPanel.add( new InvocationButton( "Run Layout", this, "runLayout" ) );
buttonPanel.add( new InvocationButton( "Load Layout", this, "loadLayout" ) );
buttonPanel.add( new InvocationButton( "Save Layout", this, "saveLayout" ) );
this.centerPanel.add( buttonPanel );
this.setLayout( new CardLayout( 40, 40 ) );
this.add( this.centerPanel, "" );
this.enableLayout();
this.currentLayout = Preferences.getString( "plantingScript" );
this.initializeLayout();
}
private void enableLayout()
{
for ( int i = 0; i < this.currentForecast; ++i )
{
this.headers[ i ].setText( "Day " + ( i + 1 ) );
}
this.headers[ this.currentForecast - 1 ].setText( "Final Day" );
for ( int i = 0; i < 16; ++i )
{
this.planningData[ this.currentForecast ][ i ] = "__";
this.originalData[ this.currentForecast ][ i ] = "__";
}
this.updateForecasts( this.currentForecast - 1 );
this.centerPanel.validate();
this.centerPanel.repaint();
this.addToLayoutButton.setEnabled( this.currentForecast != MushroomFrame.MAX_FORECAST );
this.deleteFromLayoutButton.setEnabled( this.currentForecast != 2 );
}
public void addToLayout()
{
this.centerPanel.invalidate();
this.centerPanel.add(
this.constructPanel( this.currentForecast, this.planningPanels[ this.currentForecast ] ),
this.currentForecast < 3 ? this.currentForecast : this.currentForecast + 1 );
++this.currentForecast;
this.enableLayout();
}
public void removeFromLayout()
{
this.centerPanel.invalidate();
this.centerPanel.remove( this.currentForecast < 4 ? this.currentForecast - 1 : this.currentForecast );
--this.currentForecast;
this.enableLayout();
}
public void initializeLayout()
{
int plantingLength = 2;
int indexToHighlight = 0;
if ( this.currentLayout.equals( "" ) )
{
Preferences.setInteger( "plantingDay", -1 );
Preferences.setString( "plantingDate", "" );
Preferences.setInteger( "plantingLength", 0 );
}
else
{
plantingLength = MushroomManager.loadLayout( this.currentLayout, this.originalData, this.planningData );
indexToHighlight = Preferences.getInteger( "plantingDay" );
}
if ( plantingLength > this.currentForecast )
{
this.centerPanel.invalidate();
for ( int i = this.currentForecast; i < plantingLength; ++i )
{
this.centerPanel.add( this.constructPanel( i, this.planningPanels[ i ] ), i < 3 ? i : i + 1 );
}
this.currentForecast = plantingLength;
this.enableLayout();
}
else if ( plantingLength > 1 )
{
this.centerPanel.invalidate();
for ( int i = this.currentForecast; i > plantingLength; --i )
{
this.centerPanel.remove( i < 4 ? i - 1 : i );
}
this.currentForecast = plantingLength;
this.enableLayout();
}
String today = KoLConstants.DAILY_FORMAT.format( new Date() );
if ( !Preferences.getString( "plantingDate" ).equals( today ) )
{
++indexToHighlight;
}
for ( int i = 0; i < this.currentForecast; ++i )
{
this.headers[ i ].setBackground( i == indexToHighlight ? MushroomScriptPanel.TODAY_COLOR : MushroomScriptPanel.OTHER_COLOR );
}
this.updateImages();
}
public void runLayout()
{
if ( this.currentLayout.equals( "" ) )
{
this.saveLayout();
}
if ( !this.currentLayout.equals( "" ) )
{
KoLmafiaCLI.DEFAULT_SHELL.executeLine( "call " + KoLConstants.PLOTS_DIRECTORY + this.currentLayout + ".ash" );
}
}
public void loadLayout()
{
File[] layouts = DataUtilities.listFiles( KoLConstants.PLOTS_LOCATION );
ArrayList names = new ArrayList();
for ( int i = 0; i < layouts.length; ++i )
{
String name = layouts[ i ].getName();
if ( name.endsWith( ".txt" ) )
{
name = name.substring( 0, name.length() - 4 );
if ( !names.contains( name ) )
{
names.add( name );
}
}
}
if ( names.isEmpty() )
{
return;
}
String layout = (String) InputFieldUtilities.input( "Which mushroom plot?", names.toArray() );
if ( layout != null )
{
this.loadLayout( layout );
}
}
public void loadLayout( final String layout )
{
if ( layout == null || layout.equals( "" ) || this.currentLayout.equals( layout ) )
{
return;
}
this.currentLayout = layout;
this.initializeLayout();
}
public void saveLayout()
{
String location = InputFieldUtilities.input( "Name your mushroom plot!" );
if ( location == null )
{
return;
}
this.currentLayout = location;
String[] planned = new String[ 16 ];
for ( int i = 0; i < 16; ++i )
{
planned[ i ] = this.planningData[ this.currentForecast - 1 ][ i ];
this.planningData[ this.currentForecast - 1 ][ i ] = "__";
}
MushroomManager.saveLayout( location, this.originalData, this.planningData );
for ( int i = 0; i < 16; ++i )
{
this.planningData[ this.currentForecast - 1 ][ i ] = planned[ i ];
}
}
public void updateForecasts( final int startDay )
{
for ( int i = startDay; i < MushroomFrame.MAX_FORECAST; ++i )
{
String[][] holdingData = new String[ 4 ][ 4 ];
for ( int j = 0; j < 4; ++j )
{
for ( int k = 0; k < 4; ++k )
{
holdingData[ j ][ k ] = this.planningData[ i - 1 ][ j * 4 + k ];
}
}
String[] forecastData = MushroomManager.getForecastedPlot( true, holdingData ).split( ";" );
for ( int j = 0; j < 16; ++j )
{
this.planningData[ i ][ j ] = forecastData[ j ];
this.originalData[ i ][ j ] = forecastData[ j ];
}
}
this.updateImages();
}
private void updateImages()
{
for ( int i = 0; i < MushroomFrame.MAX_FORECAST; ++i )
{
for ( int j = 0; j < 4; ++j )
{
for ( int k = 0; k < 4; ++k )
{
this.planningButtons[ i ][ j ][ k ].updateImage();
}
}
}
}
public JPanel constructPanel( final int dayIndex, final Component c )
{
JPanel panel = new JPanel( new BorderLayout() );
panel.setBorder( BorderFactory.createLineBorder( Color.black, 1 ) );
this.headers[ dayIndex ] = new JLabel( "Day " + ( dayIndex + 1 ), SwingConstants.CENTER );
panel.add( this.headers[ dayIndex ], BorderLayout.NORTH );
panel.add( c, BorderLayout.CENTER );
return panel;
}
private class MushroomButton
extends JButton
implements ActionListener
{
private final int dayIndex;
private int loopIndex;
private final int squareIndex;
public MushroomButton( final int dayIndex, final int squareIndex )
{
super( JComponentUtilities.getImage( "itemimages/dirt1.gif" ) );
this.dayIndex = dayIndex;
this.loopIndex = 4;
this.squareIndex = squareIndex;
JComponentUtilities.setComponentSize( this, 30, 30 );
this.addActionListener( this );
}
public void actionPerformed( ActionEvent e )
{
if ( this.dayIndex == MushroomScriptPanel.this.currentForecast - 1 )
{
return;
}
MushroomScriptPanel.this.planningData[ this.dayIndex ][ this.squareIndex ] = this.toggleMushroom();
MushroomScriptPanel.this.updateForecasts( this.dayIndex + 1 );
}
public void updateImage()
{
String currentMushroom = MushroomScriptPanel.this.planningData[ this.dayIndex ][ this.squareIndex ];
if ( currentMushroom.equals( "__" ) )
{
this.setIcon( JComponentUtilities.getImage( "itemimages/dirt1.gif" ) );
}
else if ( currentMushroom.equals( currentMushroom.toLowerCase() ) )
{
this.setIcon( JComponentUtilities.getImage( "itemimages/mushsprout.gif" ) );
}
else
{
this.setIcon( JComponentUtilities.getImage( MushroomManager.getMushroomImage( currentMushroom ) ) );
}
for ( int i = 0; i < MushroomManager.MUSHROOMS.length; ++i )
{
if ( currentMushroom.equals( MushroomManager.MUSHROOMS[ i ][ 2 ] ) || currentMushroom.equals( MushroomManager.MUSHROOMS[ i ][ 3 ] ) )
{
this.setToolTipText( (String) MushroomManager.MUSHROOMS[ i ][ 5 ] );
}
}
}
private String toggleMushroom()
{
MushroomScriptPanel.this.currentLayout = "";
// Everything rotates based on what was there
// when you clicked on the image.
this.loopIndex = ( this.loopIndex + 1 ) % 5;
switch ( this.loopIndex )
{
// If you loop around, then test to see if the
// old data was a blank. If it was, then you
// have already displayed it, so move on to the
// next element in the cycle. If not, return a
// blank, as that's the next element in the cycle.
case 0:
if ( MushroomScriptPanel.this.originalData[ this.dayIndex ][ this.squareIndex ].equals( "__" ) )
{
this.loopIndex = 1;
}
else
{
return "__";
}
// In all other cases, return the next element
// in the mushroom toggle cycle.
case 1:
return "kb";
case 2:
return "kn";
case 3:
return "sp";
case 4:
return MushroomScriptPanel.this.originalData[ this.dayIndex ][ this.squareIndex ];
}
return "__";
}
}
}