/**
* 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.session;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.java.dev.spellcast.utilities.LockableListModel;
import net.java.dev.spellcast.utilities.SortedListModel;
import net.sourceforge.kolmafia.AdventureResult;
import net.sourceforge.kolmafia.KoLConstants;
import net.sourceforge.kolmafia.KoLmafia;
import net.sourceforge.kolmafia.RequestThread;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.persistence.ItemDatabase;
import net.sourceforge.kolmafia.request.DisplayCaseRequest;
import net.sourceforge.kolmafia.request.GenericRequest;
import net.sourceforge.kolmafia.utilities.CharacterEntities;
import net.sourceforge.kolmafia.utilities.StringUtilities;
public abstract class DisplayCaseManager
{
private static final GenericRequest SHELF_REORDER = new GenericRequest( "managecollection.php" );
private static final Pattern OPTION_PATTERN =
Pattern.compile( "</tr><tr[^>]*><td>(.*?)(?: \\((\\d+)\\))?</td>.*?javascript:addform\\((\\d+), (\\d+)\\)" );
private static final Pattern SHELVES_PATTERN = Pattern.compile( "<script>.*?var shelves = \\{(.*?)\\};.*?</script>", Pattern.DOTALL );
private static final Pattern SHELF_PATTERN = Pattern.compile( "\"(\\d+)\":\"([^\"]*)\"" );
private static final LockableListModel headers = new LockableListModel();
private static final LockableListModel shelves = new LockableListModel();
public static boolean collectionRetrieved = false;
public static final void clearCache()
{
KoLConstants.collection.clear();
DisplayCaseManager.collectionRetrieved = false;
DisplayCaseManager.headers.clear();
DisplayCaseManager.shelves.clear();
}
public static final LockableListModel getHeaders()
{
return DisplayCaseManager.headers;
}
public static final String getHeader( final int shelf )
{
return (String) DisplayCaseManager.headers.get( shelf );
}
public static final LockableListModel getShelves()
{
return DisplayCaseManager.shelves;
}
public static final void move( final Object[] moving, final int sourceShelf, final int destinationShelf )
{
// In order to take advantage of the utilities of the
// Collections interface, place everything inside of
// a list first.
List movingList = new ArrayList();
AdventureResult []items = new AdventureResult[ moving.length ];
for ( int i = 0; i < moving.length; ++i )
{
Object item = moving[ i ];
movingList.add( item );
items[i] = (AdventureResult)item;
}
// Use the removeAll() and addAll() methods inside of
// the Collections interface.
( (SortedListModel) DisplayCaseManager.shelves.get( sourceShelf ) ).removeAll( movingList );
( (SortedListModel) DisplayCaseManager.shelves.get( destinationShelf ) ).addAll( movingList );
RequestThread.postRequest( new DisplayCaseRequest( items, destinationShelf ) );
KoLmafia.updateDisplay( "Display case updated." );
}
public static final void reorder( final String[] headers )
{
headers[ 0 ] = "-none-";
// Unfortunately, if there are deleted shelves, the
// shelves cannot be re-ordered directly. What has
// to happen is that the number of deleted shelves
// needs to be created with some dummy name and then
// deleted afterwards.
boolean containsDeletedShelf = false;
boolean[] deleted = new boolean[ headers.length ];
for ( int i = 0; i < headers.length; ++i )
{
deleted[ i ] = headers[ i ].equals( "(Deleted Shelf)" );
containsDeletedShelf |= deleted[ i ];
}
for ( int i = 0; i < deleted.length; ++i )
{
if ( deleted[ i ] )
{
DisplayCaseManager.SHELF_REORDER.addFormField( "action", "newshelf" );
DisplayCaseManager.SHELF_REORDER.addFormField( "pwd" );
DisplayCaseManager.SHELF_REORDER.addFormField( "shelfname", "Deleted Shelf " + i );
RequestThread.postRequest( DisplayCaseManager.SHELF_REORDER );
}
}
// Determine where the headers are in the existing
// list of headers to find out where the shelf contents
// should be stored after the update.
List shelforder = new ArrayList();
for ( int i = 0; i < headers.length; ++i )
{
shelforder.add( DisplayCaseManager.shelves.get( DisplayCaseManager.headers.indexOf( headers[ i ] ) ) );
}
// Save the lists to the server and update the display
// on theto reflect the change.
DisplayCaseManager.save( shelforder );
// Redelete the previously deleted shelves so that the
// user isn't stuck with shelves they aren't going to use.
DisplayCaseManager.SHELF_REORDER.clearDataFields();
DisplayCaseManager.SHELF_REORDER.addFormField( "action", "modifyshelves" );
DisplayCaseManager.SHELF_REORDER.addFormField( "pwd" );
for ( int i = 1; i < headers.length; ++i )
{
DisplayCaseManager.SHELF_REORDER.addFormField( "newname" + i, headers[ i ] );
if ( deleted[ i ] )
{
DisplayCaseManager.SHELF_REORDER.addFormField( "delete" + i, "on" );
}
}
RequestThread.postRequest( DisplayCaseManager.SHELF_REORDER );
RequestThread.postRequest( new DisplayCaseRequest() );
KoLmafia.updateDisplay( "Display case updated." );
}
private static final void save( final List shelfOrder )
{
int elementCounter = 0;
SortedListModel currentShelf;
// In order to ensure that all data is saved with no
// glitches server side, all items submit their state.
// Store the data in two parallel arrays.
int size = KoLConstants.collection.size();
int[] newShelves = new int[ size ];
AdventureResult[] newItems = new AdventureResult[ size ];
// Iterate through each shelf and place the item into
// the parallel arrays.
for ( int i = 0; i < shelfOrder.size(); ++i )
{
currentShelf = (SortedListModel) shelfOrder.get( i );
for ( int j = 0; j < currentShelf.size(); ++j, ++elementCounter )
{
newShelves[ elementCounter ] = i;
newItems[ elementCounter ] = (AdventureResult) currentShelf.get( j );
}
}
// Once the parallel arrays are properly initialized,
// send the update request to the server.
RequestThread.postRequest( new DisplayCaseRequest( newItems, newShelves ) );
}
public static final void update( final String data )
{
DisplayCaseManager.updateShelves( data );
ArrayList<AdventureResult> items = new ArrayList<AdventureResult>();
ArrayList [] shelves = new ArrayList[ DisplayCaseManager.shelves.size() ];
for ( int i = 0; i < shelves.length; ++i )
{
shelves[ i ] = new ArrayList<AdventureResult>();
}
Matcher optionMatcher = DisplayCaseManager.OPTION_PATTERN.matcher( data );
while ( optionMatcher.find() )
{
int itemId = StringUtilities.parseInt( optionMatcher.group( 3 ) );
if ( ItemDatabase.getItemName( itemId ) == null )
{
// Do not register new items discovered in your
// display case, since descid is not available
//
// String itemName = optionMatcher.group( 1 );
// ItemDatabase.registerItem( itemId, itemName );
continue;
}
String countString = optionMatcher.group( 2 );
int itemCount = countString == null ? 1 : StringUtilities.parseInt( countString );
AdventureResult item = ItemPool.get( itemId, itemCount );
int shelf = StringUtilities.parseInt( optionMatcher.group( 4 ) );
items.add( item );
shelves[ shelf ].add( item );
}
KoLConstants.collection.addAll( items );
for ( int i = 0; i < DisplayCaseManager.shelves.size(); ++i )
{
( (SortedListModel) DisplayCaseManager.shelves.get( i ) ).addAll( shelves[ i ] );
}
// Finally, we can account for Golden Mr. A's in your display case
InventoryManager.countGoldenMrAccesories();
DisplayCaseManager.collectionRetrieved = true;
}
private static final void updateShelves( final String data )
{
DisplayCaseManager.clearCache();
Matcher caseMatcher = DisplayCaseManager.SHELVES_PATTERN.matcher( data );
if ( caseMatcher.find() )
{
Matcher shelfMatcher = DisplayCaseManager.SHELF_PATTERN.matcher( caseMatcher.group(1) );
while ( shelfMatcher.find() )
{
int shelf = StringUtilities.parseInt( shelfMatcher.group( 1 ) );
String name = CharacterEntities.unescape( shelfMatcher.group( 2 ) );
for ( int i = DisplayCaseManager.headers.size(); i < shelf; ++i )
{
DisplayCaseManager.headers.add( "(Deleted Shelf)" );
}
DisplayCaseManager.headers.add( name );
}
}
if ( DisplayCaseManager.headers.size() == 0 )
{
DisplayCaseManager.headers.add( "-none-" );
}
for ( int i = 0; i < DisplayCaseManager.headers.size(); ++i )
{
DisplayCaseManager.shelves.add( new SortedListModel() );
}
}
}