# Copyright 2002 by Katharine Lindner.  All rights reserved.
# This code is part of the Biopython distribution and governed by its
# license.  Please see the LICENSE file that should have been included
# as part of this package.

"""handles true random numbers supplied from the the web server of fourmilab. Based on
atmospheric noise.  The motivation is to support biosimulations that rely on random numbers.
"""

import os
import sys
import string
from urllib import FancyURLopener
from urllib import urlencode

from Bio.SGMLExtractor import SGMLExtractorHandle


def hex_convert( text ):
    num_val = 0L
    text = text.lower()
    for letter in text:
        hex_digit = string.hexdigits.find( letter )
        if( hex_digit < 0 ):
            raise ValueError
        num_val = ( num_val * 16 ) + hex_digit
    return num_val

def byte_concat( text ):
    val = 0
    numbytes = len( text )
    for i in range( 0, numbytes ):
        val = val * 256
        val = val + ord( text[ i ] )

    return val

class HotCache:

    def __init__( self  ):
#        self.url = 'http://www.fourmilab.ch/cgi-bin/uncgi/Hotbits?num=5000&min=1&max=6&col=1'
        self.url = 'http://www.random.org/cgi-bin/randbyte?'
        self.query = { 'nbytes' : 128, 'fmt' : 'h' }
        self.fill_hot_cache()

    def fill_hot_cache( self ):
        bases   = [ 'a', 'g', 'c', 't' ]
        url = self.url + urlencode( self.query )
        url_opener = FancyURLopener( )
        fh = url_opener.open( url )
        hot_rand_handle = SGMLExtractorHandle( fh, [ 'pre', ] )

        hot_cache = fh.read()
        self.hot_cache = hot_cache
        fh.close()
        return self.hot_cache

    def next_num( self, num_digits = 4 ):
        cache = self.hot_cache
        numbytes = num_digits / 2
        if( len( cache ) % numbytes != 0 ):
            print 'len_cache is %d' % len( cache )
            raise ValueError
        if( cache == '' ):
            self.fill_hot_cache()
            cache = self.hot_cache
        hexdigits = cache[ :numbytes ]
        self.hot_cache = cache[ numbytes: ]
        return byte_concat( hexdigits )



class HotRandom:

    def __init__( self ):
        self.hot_cache = HotCache( )

    def hot_rand( self, high, low = 0 ):
        span = high - low
        val = self.hot_cache.next_num()
        val = ( span * val ) >> 16
        val = val + low
        return val


if( __name__ == '__main__' ):
    hot_random = HotRandom()
    for j in range ( 0, 130 ):
        print hot_random.hot_rand( 25 )
    nums = [ '0000', 'abcd', '1234', '5555', '4321', 'aaaa', 'ffff' ]
    for num in nums:
        print hex_convert( num )



