contract LuckyNumberService is LuckyNumber, Mortal, Random {
    
    // Initialize state +.+.+.
    function LuckyNumberService() {
        owned();
        // defaults
        cost = 20000000000000000; // 0.02 ether // 20 finney
        max = 15; // generate number between 1 and 15
        waitTime = 3; // 3 blocks
    }

    // Let owner customize defauts.
    // Allow the owner to set max.
    function setMax(uint256 _max)
    onlyOwner
    public
    returns (bool) {
        max = _max;
        return true;
    }

    // Allow the owner to set waitTime. (in blocks)
    function setWaitTime(uint8 _waitTime)
    onlyOwner
    public
    returns (bool) {
        waitTime = _waitTime;
        return true;
    }

    // Allow the owner to set cost.
    function setCost(uint256 _cost)
    onlyOwner
    public
    returns (bool) {
        cost = _cost;
        return true;
    }

    // Allow the owner to set a transaction proxy
    // which can perform value exchanges on behalf of this contract.
    // (unrelated to the requestProxy which is not whiteList)
    function enableProxy(address _proxy)
    onlyOwner
    public
    returns (bool) {
        whiteList[_proxy] = true;
        return whiteList[_proxy];
    }

    function removeProxy(address _proxy)
    onlyOwner
    public
    returns (bool) {
        delete whiteList[_proxy];
        return true;
    }

    // Allow the owner to cash out the holdings of this contract.
    function withdraw(address _recipient, uint256 _balance)
    onlyOwner
    public
    returns (bool) {
        _recipient.transfer(_balance);
        return true;
    }

    // Assume that simple transactions are trying to request a number,
    // unless it is from the owner.
    function () payable public {
        assert(msg.sender != owner);
        requestNumber(msg.sender, max, waitTime);
    }
    
    // Request a Number ... *~>
    function requestNumber(address _requestor, uint256 _max, uint8 _waitTime)
    payable 
    public {
        // external requirement: 
        // value must exceed cost
        // unless address is whitelisted
        if (!whiteList[msg.sender]) {
            require(!(msg.value < cost));
        }

        // internal requirement: 
        // request address must not have pending number
        assert(!checkNumber(_requestor));
        // set pending number
        pendingNumbers[_requestor] = PendingNumber({
            requestProxy: tx.origin, // requestProxy: original address that kicked off the transaction
            renderedNumber: 0,
            max: max,
            originBlock: block.number,
            waitTime: waitTime
        });
        if (_max > 1) {
            pendingNumbers[_requestor].max = _max;
        }
        // max 250 wait to leave a few blocks
        // for the reveal transction to occur
        // and write from the pending numbers block
        // before it expires
        if (_waitTime > 0 && _waitTime < 250) {
            pendingNumbers[_requestor].waitTime = _waitTime;
        }
        EventLuckyNumberRequested(_requestor, pendingNumbers[_requestor].max, pendingNumbers[_requestor].originBlock, pendingNumbers[_requestor].waitTime, pendingNumbers[_requestor].requestProxy);
    }

    // Reveal your number ... *~>
    // Only requestor or proxy can generate the number
    function revealNumber(address _requestor)
    public
    payable {
        assert(_canReveal(_requestor, msg.sender));
        // waitTime has passed, render this requestor's number.
        _revealNumber(_requestor);
    }

    // Internal implementation of revealNumber().
    function _revealNumber(address _requestor) 
    internal {
        uint256 luckyBlock = _revealBlock(_requestor);
        // 
        // TIME LIMITATION ~> should handle in user interface
        // blocks older than (currentBlock - 256) 
        // "expire" and read the same hash as most recent valid block
        // 
        uint256 luckyNumber = getRandomFromBlockHash(luckyBlock, pendingNumbers[_requestor].max);

        // set new values
        pendingNumbers[_requestor].renderedNumber = luckyNumber;
        // event
        EventLuckyNumberRevealed(_requestor, pendingNumbers[_requestor].originBlock, pendingNumbers[_requestor].renderedNumber, pendingNumbers[_requestor].requestProxy);
        // zero out wait blocks since this is now inactive (for state management)
        pendingNumbers[_requestor].waitTime = 0;
    }

    function canReveal(address _requestor)
    public
    constant
    returns (bool, uint, uint, address, address) {
        return (_canReveal(_requestor, msg.sender), _remainingBlocks(_requestor), _revealBlock(_requestor), _requestor, msg.sender);
    }

    function _canReveal(address _requestor, address _proxy) 
    internal
    constant
    returns (bool) {
        // check for pending number request
        if (checkNumber(_requestor)) {
            // check for no remaining blocks to be mined
            // must wait for `pendingNumbers[_requestor].waitTime` to be excceeded
            if (_remainingBlocks(_requestor) == 0) {
                // check for ownership
                if (pendingNumbers[_requestor].requestProxy == _requestor || pendingNumbers[_requestor].requestProxy == _proxy) {
                    return true;
                }
            }
        }
        return false;
    }

    function _remainingBlocks(address _requestor)
    internal
    constant
    returns (uint) {
        uint256 revealBlock = add(pendingNumbers[_requestor].originBlock, pendingNumbers[_requestor].waitTime);
        uint256 remainingBlocks = 0;
        if (revealBlock > block.number) {
            remainingBlocks = sub(revealBlock, block.number);
        }
        return remainingBlocks;
    }

    function _revealBlock(address _requestor)
    internal
    constant
    returns (uint) {
        // add wait block time
        // to creation block time
        // then subtract 1
        return add(pendingNumbers[_requestor].originBlock, pendingNumbers[_requestor].waitTime);
    }


    function getNumber(address _requestor)
    public
    constant
    returns (uint, uint, uint, address) {
        return (pendingNumbers[_requestor].renderedNumber, pendingNumbers[_requestor].max, pendingNumbers[_requestor].originBlock, _requestor);
    }

    // is a number request pending for the address
    function checkNumber(address _requestor)
    public
    constant
    returns (bool) {
        if (pendingNumbers[_requestor].renderedNumber == 0 && pendingNumbers[_requestor].waitTime > 0) {
            return true;
        }
        return false;
    }
// 0xMMWKkk0KN/>HBBi/MASSa/DANTi/LANTen.MI.MI.MI.M+.+.+.M->MMWNKOkOKWJ.J.J.M*~+>
}
/**
 * Lucky Number Service *~+>
 * Any contract or address can make a request from this implementation
 * on behalf of any other address as a requestProxy.
 */
function setWaitTime(uint8 _waitTime)
onlyOwner
public
returns (bool) {
    waitTime = _waitTime;
    return true;
}

// Allow the owner to set waitTime. (in blocks)