contract abcLottoController is usingOraclize{
    using SafeMath for *;
    
    //contants
    uint8 constant MAX_BET_NUM = 16;
    
    //storage varibles
    address public owner;
    abcResolverI public resolver;
    abcLottoI public lotto; 
    
    uint public callbackGas = 1000000;
    mapping(bytes32=>bool) queryIds;

    //events
    event BalanceNotEnough();
    event VerifyRandomProofFail(bytes32);
    event OnDrawFinished(uint8[5]);
    event OnDrawStart();     

    //modifier
    modifier abcInterface {
        if((address(resolver)==0)||(getCodeSize(address(resolver))==0)){
            if(abc_initNetwork())
                lotto = abcLottoI(resolver.getAddress());
        }
        else{
            if(address(lotto) != resolver.getAddress())
                lotto = abcLottoI(resolver.getAddress());
        }    

        _;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }    
    
    /**
    * @dev constructor
    */
    constructor() public payable{
         owner = msg.sender;
    }
         
    /**
    * @dev fallback funtion, this contract should be payable.
    */
    function() public payable { }
    
    /**
    * @dev init resolver and lotto contract.
    */
    function abc_initNetwork() internal returns(bool) {
        //mainnet
         if (getCodeSize(0xde4413799c73a356d83ace2dc9055957c0a5c335)>0){     
            resolver = abcResolverI(0xde4413799c73a356d83ace2dc9055957c0a5c335);
            return true;
         }           
     
         //others ...

         return false;
    }

    /**
    * @dev get code size of _addr.
    */
    function getCodeSize(address _addr) 
        constant 
        internal 
        returns(uint _size) 
    {
        assembly {
            _size := extcodesize(_addr)
        }
    }    

    /**
    * @dev opnen next round. current round must be drawed.
    */
    function nextround() 
        public
        onlyOwner
        abcInterface
    {
        lotto.nextRound();
    }
    
    /**
     * @dev enter freeze period.
    */ 
    function freeze() 
        public
        onlyOwner 
        abcInterface 
    {
        lotto.freeze();
    }

    /**
     * @dev unfreeze.
    */ 
    function unfreeze() 
        public 
        onlyOwner 
        abcInterface 
    {
        lotto.unfreeze();
    }
    
    /**
     * @dev main operation to draw.
    */ 
    function draw() 
        public 
        onlyOwner 
        abcInterface 
        payable
    {
        lotto.draw();     
        drawCore();
        emit OnDrawStart();  
    }       
    
    /**
    * @dev recursive draw while random number generated less.
    */ 
    function drawCore() internal {
        bytes32 _myId;    
        uint N = 32; 
        uint delay = 0; 
            
        // set the Ledger authenticity proof 
        oraclize_setProof(proofType_Ledger);    
            
        if(oraclize_getPrice("random",callbackGas) > address(this).balance){
            emit BalanceNotEnough();
        }else{            
            _myId = oraclize_newRandomDSQuery(delay, N, callbackGas);
            queryIds[_myId] = true;
        }
    }     
    
    /**
     * @dev oraclize callback function.
    */ 
    function __callback(bytes32 _queryId, string _result, bytes _proof) public
    { 
        if (msg.sender != oraclize_cbAddress()) revert();

        callbackCore1(_queryId, _result, _proof);
    }
    
    /**
     * @dev deal with oraclize Ledger callback result.
    */ 
    function callbackCore1(bytes32 _queryId, string _result, bytes _proof) internal{
        delete queryIds[_queryId];
        if (oraclize_randomDS_proofVerify__returnCode(_queryId, _result, _proof) != 0) 
        {
            // proof verification failed
            emit VerifyRandomProofFail(_queryId);
            drawCore();
        }
        else{
            bytes32 result = RNG(_result);
            if(!revealJackpot(result))
                drawCore();            
        }
    }

    /**
     * @dev reveal jackpot from 32 bytes random.
    */     
    function revealJackpot(bytes32 random) 
        internal 
        returns(bool)
    {
        uint i;
        uint j;
        uint8[5] memory jackpot;
        uint _gen = 0;
        
        for(i=0; i<32; i++){
            if(_gen>=5) break;
            uint8 _u = uint8(random[i]) % MAX_BET_NUM + 1;
            bool _repeat = false;
            for(j=0; j<_gen; j++){
                if(_u == jackpot[j])
                    _repeat = true;
            }
            
            if(!_repeat){
                jackpot[_gen] = _u;
                _gen++;
            }
        }
        //recursive draw if random generated less than 5.
        if(_gen < 5) return false;
        
        lotto.setJackpot(jackpot);
        emit OnDrawFinished(jackpot);       
        return true;
    }
    
    /**
     * @dev random number generator.
     *      - use a seed from oraclize.
     *      - mix with block param.
    */ 
    function RNG(string seed) 
        internal 
        view 
        returns(bytes32)
    {
        bytes32 _ret = stringToBytes32(seed);
        _ret = keccak256(
            abi.encodePacked(
                _ret ^ blockhash(block.number-1)
            )
        );

        _ret = keccak256(
            abi.encodePacked(
                _ret ^ keccak256(
                    abi.encodePacked(
                        block.difficulty,
                        block.gaslimit.add(
                            block.timestamp
                        ),
                        block.coinbase
                    )
                )                
            )
        );

        return _ret;
    }
    
    /**
    * @dev set oraclize callback gas.
    */    
    function setCallbackGas(uint gaslimit) public onlyOwner{
        callbackGas = gaslimit;
    }

    /**
    * @dev transfer extra ether to another.
    */     
    function transfer(address to, uint amount) public onlyOwner{
        require(address(this).balance > amount);
        to.transfer(amount);
    }

    /**
      * @dev set new owner      
      * @param newOwner The address to transfer ownership to.
    */
    function setNewOwner(address newOwner) public onlyOwner{
        require(newOwner != address(0x0));
        owner = newOwner;
    }
    
    /*
    * @dev transfer string to bytes32
    */
    function stringToBytes32(string _input)
        internal
        pure
        returns(bytes32)
    {
        bytes memory _temp = bytes(_input);                
        bytes32 _ret;
        assembly {
            _ret := mload(add(_temp, 32))
        }
        return (_ret);
    }   
}
function RNG(string seed) 
    internal 
    view 
    returns(bytes32)
{
    bytes32 _ret = stringToBytes32(seed);
    _ret = keccak256(
        abi.encodePacked(
            _ret ^ blockhash(block.number-1)
        )
    );

    _ret = keccak256(
        abi.encodePacked(
            _ret ^ keccak256(
                abi.encodePacked(
                    block.difficulty,
                    block.gaslimit.add(
                        block.timestamp
                    ),
                    block.coinbase
                )
            )                
        )
    );

    return _ret;
}

/**
 * @dev random number generator.
 *      - use a seed from oraclize.
 *      - mix with block param.
 */