/*
 
* Copyright (c) 2016, Metron, Inc.
 
* All rights reserved.
 
*
 
* Redistribution and use in source and binary forms, with or without
 
* modification, are permitted provided that the following conditions are met:
 
*
     
* Redistributions of source code must retain the above copyright
 
*
       
notice, this list of conditions and the following disclaimer.
 
*
     
* 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.
 
*
     
* Neither the name of Metron, Inc. 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 METRON, INC. 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 com.metsci.glimpse.dnc;

import static com.jogamp.common.nio.Buffers.newDirectByteBuffer;
import static com.metsci.glimpse.dnc.util.DncMiscUtils.nextPowerOfTwo;
import static com.metsci.glimpse.gl.util.GLUtils.genTexture;
import static com.metsci.glimpse.util.GeneralUtils.ints;
import static java.lang.Math.min;
import static javax.media.opengl.GL.GL_CLAMP_TO_EDGE;
import static javax.media.opengl.GL.GL_NEAREST;
import static javax.media.opengl.GL.GL_TEXTURE_2D;
import static javax.media.opengl.GL.GL_TEXTURE_MAG_FILTER;
import static javax.media.opengl.GL.GL_TEXTURE_MIN_FILTER;
import static javax.media.opengl.GL.GL_TEXTURE_WRAP_S;
import static javax.media.opengl.GL.GL_TEXTURE_WRAP_T;
import static javax.media.opengl.GL.GL_UNPACK_ALIGNMENT;
import static javax.media.opengl.GL.GL_UNSIGNED_BYTE;
import static javax.media.opengl.GL2ES3.GL_R8UI;
import static javax.media.opengl.GL2ES3.GL_RED_INTEGER;

import java.nio.ByteBuffer;

import javax.media.opengl.GL;

import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;

public class IndexSetTexture
{

    
private static byte TRUE = 1;
    
private static byte FALSE = 0;


    
protected final IntSet hSet;
    
protected boolean hSetChanged;

    
protected IntSet dSet;
    
protected ByteBuffer dBuffer;
    
protected int dRowLength;
    
protected int dRowCount;
    
protected int dTextureHandle;


    
public IndexSetTexture( )
    
{
        
this.hSet = new IntOpenHashSet( );
        
this.hSetChanged = true;

        
this.dSet = null;
        
this.dBuffer = null;
        
this.dRowLength = 0;
        
this.dRowCount = 0;
        
this.dTextureHandle = 0;
    
}

    
public void set( IntCollection indices )
    
{
        
if ( !hSet.isEmpty( ) )
        
{
            
hSet.clear( );
            
hSetChanged = true;
        
}

        
if ( hSet.addAll( indices ) )
        
{
            
hSetChanged = true;
        
}
    
}

    
public void bind( GL gl, int indexCount, int textureMaxRowLength )
    
{

        
// Prepare dBuffer
        
boolean dBufferChanged = false;
        
if ( dSet == null || dBuffer == null || dRowLength * dRowCount < indexCount || dRowLength > textureMaxRowLength )
        
{
            
dRowLength = min( textureMaxRowLength, nextPowerOfTwo( indexCount ) );
            
dRowCount = ( indexCount + dRowLength - 1 ) / dRowLength;

            
dBuffer = newDirectByteBuffer( dRowLength * dRowCount );
            
for ( int i = 0; dBuffer.hasRemaining( ); i++ )
            
{
                
dBuffer.put( hSet.contains( i ) ? TRUE : FALSE );
            
}
            
dBuffer.flip( );
            
dBufferChanged = true;

            
dSet = new IntOpenHashSet( hSet );
            
hSetChanged = false;
        
}
        
else if ( hSetChanged )
        
{
            
for ( IntIterator it = dSet.iterator( ); it.hasNext( ); )
            
{
                
int index = it.nextInt( );
                
if ( !hSet.contains( index ) )
                
{
                    
dBuffer.put( index, FALSE );
                    
dBufferChanged = true;
                
}
            
}

            
for ( IntIterator it = hSet.iterator( ); it.hasNext( ); )
            
{
                
int index = it.nextInt( );
                
if ( dBuffer.get( index ) != TRUE )
                
{
                    
dBuffer.put( index, TRUE );
                    
dBufferChanged = true;
                
}
            
}

            
dSet.clear( );
            
dSet.addAll( hSet );
            
hSetChanged = false;
        
}


        
// Prepare dTextureHandle
        
boolean dTextureHandleChanged = false;
        
if ( dTextureHandle == 0 )
        
{
            
dTextureHandle = genTexture( gl );
            
gl.glBindTexture( GL_TEXTURE_2D, dTextureHandle );
            
gl.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
            
gl.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
            
gl.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
            
gl.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

            
dTextureHandleChanged = true;
        
}
        
else
        
{
            
gl.glBindTexture( GL_TEXTURE_2D, dTextureHandle );
        
}


        
// Push data to the device
        
if ( dBufferChanged || dTextureHandleChanged )
        
{
            
gl.glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
            
gl.glTexImage2D( GL_TEXTURE_2D, 0, GL_R8UI, dRowLength, dRowCount, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dBuffer );
        
}

    
}

    
public void freeDeviceResources( GL gl )
    
{
        
if ( dTextureHandle != 0 )
        
{
            
gl.glDeleteTextures( 1, ints( dTextureHandle ), 0 );
        
}

        
dSet = null;
        
dBuffer = null;
        
dRowLength = 0;
        
dRowCount = 0;
        
dTextureHandle = 0;
    
}

}