/*
 Copyright (c) 2007 Eric J. Feminella  <eric@ericfeminella.com>
 All rights reserved.

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is furnished
 to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 @internal
 */

package com.ericfeminella.utils
{
    /**
     *
     * All static utility class which provides an API for converting
     * numberic data types to their 4, 8, 16 or 32-bit binary 
     * equivalents, respectively
     *
     */
    public final class BinaryConversion
    {
        /**
         *
         * Defines a 4-bit nibble / half-octet binary conversion
         * representation: 2^4 (16 possible combinations, 0-15)
         *
         */
        public static const FOUR_BIT:int       = 4;

        /**
         *
         * Defines an 8-bit, 1 byte / octet binary conversion
         * representation: 2^8 (256 possible combinations, 0-255)
         *
         */
        public static const EIGHT_BIT:int      = 8;

        /**
         *
         * Defines a 16-bit binary conversion representation:
         * 2^16 (65,536 possible combinations, 0-65535)
         *
         */
        public static const SIXTEEN_BIT:int    = 16;

        /**
         *
         * Defines a 32-bit (binary conversion representation:
         * 2^32 (4,294,967,295 possible combinations, 0-4294967295)
         *
         */
        public static const THIRTY_TWO_BIT:int = 32;

        /**
         *
         * Converts the specified Number to it's 4, 8, 16 or 32-bit
         * binary equivalent
         *
         * <p>
         * The following example demonstrates a typical use case in
         * which a composite number (10) is converted to it's
         * 16-bit binary equivalent
         * </p>
         *
         * <pre>
         *
         * import com.ericfeminella.utils.BinaryConversion;
         *
         * trace( BinaryConversion.convert(10, BinaryConversion.FOUR_BIT) );
         * trace( BinaryConversion.convert(10, BinaryConversion.EIGHT_BIT) );
         * trace( BinaryConversion.convert(10, BinaryConversion.SIXTEEN_BIT) );
         * trace( BinaryConversion.convert(10, BinaryConversion.THIRTY_TWO_BIT) );
         *
         * //outputs:
         * //1010
         * //00001010
         * //0000000000001010
         * //00000000000000000000000000001010
         *
         * </pre>
         *
         * @param  Decimal which is to be converted to binary
         * @param  bit conversion type; valid values are 4, 8, 16 or 32
         * @return binary conversion as String
         *
         */
        public static function convert(value:Number, conversion:int) : String
        {
            if ( isValidRange( conversion ) )
            {
                if ( isValidConversion( value, conversion ) )
                {
                    var binary:String;

                    for (var i:int = 0; i < conversion; i++)
                    {
                        var leastSignificantBit:Number = value & 1;
                        binary = (leastSignificantBit ? 1 : 0) + binary;
                        value >>= 1;
                    }
                    return binary.toString();
                }
                else
                {
                    throw new Error( "Can not convert value: " + value + " to " + conversion + "-bit binary" );
                }
            }
            else
            {
                throw new Error( "Invalid range specified: " + conversion + "-bit conversion is not valid, only 4, 8, 16 and 32 bit conversions are supported" );
            }
        }

        /**
         *
         * @private
         *
         * Determines if the bit type specified is supported
         *
         * <p>
         * Valid conversion types are 4, 8, 16, and 32 which are
         * defined as constants in BinaryConversion. Below is a
         * list of the constants which define valid conversions
         * </p>
         *
         * <p>
         *  <ul>
         *     <li>BinaryConversion.FOUR_BIT</li>
         *     <li>BinaryConversion.EIGHT_BIT</li>
         *     <li>BinaryConversion.SIXTEEN_BIT</li>
         *     <li>BinaryConversion.THIRTY_TWO_BIT</li>
         *   </ul>
         * </p>
         *
         * @param  the specific binary conversion type
         * @return true of the conversion is valid, otherwise false
         *
         */
        private static function isValidRange(conversion:int) :  Boolean
        {
            var isValid:Boolean = false;

            switch( conversion )
            {
                case BinaryConversion.FOUR_BIT :
                case BinaryConversion.EIGHT_BIT :
                case BinaryConversion.SIXTEEN_BIT :
                case BinaryConversion.THIRTY_TWO_BIT :
                   isValid = true;
                   break;
            }
            return isValid;
        }

        /**
         *
         * @private
         *
         * Determines if the maximum value for a given binary conversion
         * has been exceeded.
         *
         * <p>
         * 4-bit conversions allow up to 16 possible permutations.
         * Thus integers between the range of 0-15 are valid values
         * which can be converted to a 4-bit binary equivalent
         * </p>
         *
         * <p>
         * 8-bit conversions allow up to 256 possible permutations.
         * Thus a range of 0-255 are valid values which can be
         * converted to an 8-bit binary equivalent
         * </p>
         *
         * <p>
         * 16-bit conversions allow up to 65,536 possible permutations.
         * Thus a range of 0-65535 are valid values which can be
         * converted to a 16-bit binary equivalent
         * </p>
         *
         * <p>
         * 32-bit conversions allow up to 4,294,967,296 possible permutations.
         * Thus a range of 0-4294967295 are valid values which can be
         * converted to a 32-bit binary equivalent
         * </p>
         *
         * @param  specified value in which to convert
         * @param  binary conversion type to convert decimal
         * @return true if conversion is valid, otherwise false
         *
         */
        private static function isValidConversion(value:Number, conversion:int) : Boolean
        {
            var isValid:Boolean = false;

            switch( conversion )
            {
                case BinaryConversion.FOUR_BIT :
                   if (value <= 15)
                   {
                       isValid = true;
                   }
                   break;
                case BinaryConversion.EIGHT_BIT :
                   if (value <= 255)
                   {
                       isValid = true;
                   }
                   break
                case BinaryConversion.SIXTEEN_BIT :
                   if (value <= 65535)
                   {
                       isValid = true;
                   }
                   break
                case BinaryConversion.THIRTY_TWO_BIT :
                   if (value <= 4294967295)
                   {
                       isValid = true;
                   }
                   break;
            }
            return isValid;
        }
    }
}