package com.marshalchen.common.uimodule.tileView.tileview.detail;
import android.graphics.Rect;
import com.marshalchen.common.uimodule.tileView.tileview.tiles.selector.TileSetSelector;
import com.marshalchen.common.uimodule.tileView.tileview.tiles.selector.TileSetSelectorMinimalUpScale;
import java.util.HashSet;
public class DetailManager {
private static final double PRECISION = 6;
private static final double DECIMAL = Math.pow( 10, PRECISION );
private DetailLevelSet detailLevels = new DetailLevelSet();
private HashSet<DetailLevelEventListener> detailLevelEventListeners = new HashSet<DetailLevelEventListener>();
private HashSet<DetailLevelSetupListener> detailLevelSetupListeners = new HashSet<DetailLevelSetupListener>();
private double scale = 1;
private double historicalScale;
private DetailLevel currentDetailLevel;
private int width;
private int height;
private int scaledWidth;
private int scaledHeight;
private boolean detailLevelLocked = false;
private int padding = 0;
private Rect viewport = new Rect();
private Rect computedViewport = new Rect();
private DetailLevelPatternParser detailLevelPatternParser = new DetailLevelPatternParserDefault();
private static double getAtPrecision( double s ) {
return Math.round( s * DECIMAL ) / DECIMAL;
}
public DetailManager(){
update( true );
}
public double getScale() {
return scale;
}
public void setScale( double s ) {
// round to PRECISION decimal places
// DEBUG: why are we rounding still?
s = getAtPrecision( s );
// is it changed?
boolean changed = ( scale != s );
// set it
scale = s;
// update computed values
update( changed );
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
// DEBUG: needed? maybe use ZPL's width and height...?
public int getScaledWidth(){
return scaledWidth;
}
public int getScaledHeight(){
return scaledHeight;
}
public void setSize( int w, int h ) {
width = w;
height = h;
update( true );
}
/**
* "pads" the viewport by the number of pixels passed. e.g., setPadding( 100 ) instructs the
* DetailManager to interpret it's actual viewport offset by 100 pixels in each direction (top, left,
* right, bottom), so more tiles will qualify for "visible" status when intersections are calculated.
* @param pixels (int) the number of pixels to pad the viewport by
*/
public void setPadding( int pixels ) {
padding = pixels;
updateComputedViewport();
}
public void updateViewport( int left, int top, int right, int bottom ) {
viewport.set( left, top, right, bottom );
updateComputedViewport();
}
private void updateComputedViewport() {
computedViewport.set( viewport );
computedViewport.top -= padding;
computedViewport.left -= padding;
computedViewport.bottom += padding;
computedViewport.right += padding;
}
public Rect getViewport() {
return viewport;
}
public Rect getComputedViewport() {
return computedViewport;
}
public DetailLevelPatternParser getDetailLevelPatternParser() {
return detailLevelPatternParser;
}
public void setDetailLevelPatternParser( DetailLevelPatternParser parser ) {
detailLevelPatternParser = parser;
}
private void update( boolean changed ){
// has there been a change in tile sets?
boolean detailLevelChanged = false;
// if detail level is locked, do not change tile sets
if(!detailLevelLocked){
// get the most appropriate detail level for the current scale
DetailLevel matchingLevel = detailLevels.find( getScale() );
// if one is found (if any tile sets are registered)
if(matchingLevel != null){
// is it the same as the one being used?
detailLevelChanged = !matchingLevel.equals( currentDetailLevel );
// update current detail level
currentDetailLevel = matchingLevel;
}
}
// update scaled values
scaledWidth = (int) ( getWidth() * getScale() );
scaledHeight = (int) ( getHeight() * getScale() );
// broadcast scale change
if( changed ) {
for ( DetailLevelEventListener listener : detailLevelEventListeners ) {
listener.onDetailScaleChanged( getScale() );
}
}
// if there's a change in detail, update appropriate values
if ( detailLevelChanged ) {
// notify all interested parties
for ( DetailLevelEventListener listener : detailLevelEventListeners ) {
listener.onDetailLevelChanged();
}
}
}
public void lockDetailLevel(){
detailLevelLocked = true;
}
public void unlockDetailLevel(){
detailLevelLocked = false;
}
public void addDetailLevelEventListener( DetailLevelEventListener l ) {
detailLevelEventListeners.add( l );
}
public void removeDetailLevelEventListener( DetailLevelEventListener l ) {
detailLevelEventListeners.remove( l );
}
public void addDetailLevelSetupListener( DetailLevelSetupListener l ) {
detailLevelSetupListeners.add( l );
}
public void removeDetailLevelSetupListener( DetailLevelSetupListener l ) {
detailLevelSetupListeners.remove( l );
}
private void addDetailLevel( DetailLevel detailLevel ) {
detailLevels.addDetailLevel( detailLevel );
update( false );
for ( DetailLevelSetupListener listener : detailLevelSetupListeners ) {
listener.onDetailLevelAdded();
}
}
public void addDetailLevel( float scale, String pattern, String downsample ) {
DetailLevel detailLevel = new DetailLevel( this, scale, pattern, downsample );
addDetailLevel( detailLevel );
}
public void addDetailLevel( float scale, String pattern, String downsample, int tileWidth, int tileHeight ) {
DetailLevel detailLevel = new DetailLevel( this, scale, pattern, downsample, tileWidth, tileHeight );
addDetailLevel( detailLevel );
}
public void resetDetailLevels(){
detailLevels.clear();
update( false );
}
public DetailLevel getCurrentDetailLevel() {
return currentDetailLevel;
}
public double getCurrentDetailLevelScale(){
if(currentDetailLevel != null ) {
return currentDetailLevel.getScale();
}
return 1;
}
public double getHistoricalScale(){
return historicalScale;
}
public void saveHistoricalScale(){
historicalScale = scale;
}
public TileSetSelector getTileSetSelector() {
return this.detailLevels.getTileSetSelector();
}
/**
* Set the tile selection method, defaults to {@link TileSetSelectorMinimalUpScale}
*
* @param selector
*/
public void setTileSetSelector(TileSetSelector selector) {
this.detailLevels.setTileSetSelector(selector);
}
}