/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

====================================================================== */

package org.bodington.util;



/**
 * This was invented for use with MCQResponse database record so that 5
 * true/false/dont knows can be packed into one byte storage in a way that
 * maps 5 true/false options in the same format as before. Combinations of
 * flags that have one or more 'dont know' values are mapped so that one or
 * more of the upper three bits are set.
 * @author  Administrator
 */
public class PackedThreeOptionFlags
{
    
    static int[] lookup_table=null;
    static int[] reverse_lookup_table=null;
    
    int flags;

    /** sets up lookup tables so that flag sets that don't contain
     * third options are stored as simple one bit per flag format */
    private static synchronized void init()
    {
        lookup_table = new int[243];
        reverse_lookup_table = new int[243];
        int indices[] = new int[5];
        int i;
        boolean binary;
        
        int[] temp = lookup_table;
        
        int value;
        int binary_value=0;
        int tertiary_value=32;
        int input=0;
        for ( indices[4]=0; indices[4]<3; indices[4]++ )
            for ( indices[3]=0; indices[3]<3; indices[3]++ )
                for ( indices[2]=0; indices[2]<3; indices[2]++ )
                    for ( indices[1]=0; indices[1]<3; indices[1]++ )
                        for ( indices[0]=0; indices[0]<3; indices[0]++ )
                        {
                            // binary set of flags?
                            binary=true;
                            for ( i=0; i<5; i++ )
                                binary = binary && (indices[i]<2);
                                
                            lookup_table[input++] = binary?binary_value++:tertiary_value++;
                        }
        
        // do reverse look up
        for ( value=0; value<243; value++ )
        {
            for ( i=0; i<243; i++ )
                if ( lookup_table[i] == value )
                {
                    reverse_lookup_table[value] = i;
                    break;
                }
        }
        
    }
    
    /** Creates a new instance of FlagTest */
    public PackedThreeOptionFlags()
    {
        flags=0;
    }
    
    public byte getPackedFlags()
    {
        if ( lookup_table == null )
            init();
        return (byte)lookup_table[flags];
    }
    
    public void setPackedFlags( byte f )
    {
        if ( reverse_lookup_table == null )
            init();
        flags = reverse_lookup_table[((int)f)&0xff];
    }
    
    public int getFlag( int index )
    {
        int i, power=1;
        if ( index<0 || index>4 )
            throw new IndexOutOfBoundsException( "Flag index out of bounds: " + index );
        int working_flags = flags;
        for ( i=0; i<index; i++ )
            power *= 3;
        return (working_flags/power) % 3;
    }
    
    public void setFlag( int index, int value )
    {
        int i, power=1;
        
        if ( index<0 || index>4 )
            throw new IndexOutOfBoundsException( "Flag index out of bounds: " + index );
        if ( value<0 || value>2 )
            throw new IndexOutOfBoundsException( "Flag value out of bounds: " + value );
        
        int working_flags = flags;
        for ( i=0; i<index; i++ )
            power *= 3;
        
        flags =
        (
            // flags above the one indexed
            ((working_flags/(power*3))*(power*3)) +
            // plus value in question
            (value*power) +
            //plus flags below the one indexed
            (working_flags%power)
        );        
    }
} 
