contract YUPTimelock is Ownable {
    using SafeERC20 for StandardToken;
    using SafeMath for uint256;
    
    /** Contract events **/
    event IsLocked(uint256 _time);
    event IsClaiming(uint256 _time);
    event IsFinalized(uint256 _time);
    event Claimed(address indexed _to, uint256 _value);
    event ClaimedFutureUse(address indexed _to, uint256 _value);
    
    /** State variables **/
    enum ContractState { Locked, Claiming, Finalized }
    ContractState public state;
    uint256 constant D160 = 0x0010000000000000000000000000000000000000000;
    StandardToken public token;
    mapping(address => uint256) public allocations;
    mapping(address => bool) public claimed;                //indicates whether beneficiary has claimed tokens
    uint256 public expectedAmount = 193991920 * (10**18);   //should hold 193,991,920 x 10^18 (43.59% of total supply)
    uint256 public amountLocked;
    uint256 public amountClaimed;
    uint256 public releaseTime;     //investor claim starting time
    uint256 public claimEndTime;    //investor claim expiration time
    uint256 public fUseAmount;  //amount of tokens for future use
    address fUseBeneficiary;    //address of future use tokens beneficiary
    uint256 fUseReleaseTime;    //release time of locked future use tokens
    
    /** Modifiers **/
    modifier isLocked() {
        require(state == ContractState.Locked);
        _;
    }
    
    modifier isClaiming() {
        require(state == ContractState.Claiming);
        _;
    }
    
    modifier isFinalized() {
        require(state == ContractState.Finalized);
        _;
    }
    
    /** Constructor **/
    function YUPTimelock(
        uint256 _releaseTime,
        uint256 _amountLocked,
        address _fUseBeneficiary,
        uint256 _fUseReleaseTime
    ) public {
        require(_releaseTime > now);
        
        releaseTime = _releaseTime;
        amountLocked = _amountLocked;
        fUseAmount = 84550000 * 10**18;     //84,550,000 tokens (with 18 decimals)
        claimEndTime = now + 60*60*24*275;  //9 months (in seconds) from time of lock
        fUseBeneficiary = _fUseBeneficiary;
        fUseReleaseTime = _fUseReleaseTime;
        
        if (amountLocked != expectedAmount)
            revert();
    }
    
    /** Allows the owner to set the token contract address **/
    function setTokenAddr(StandardToken tokAddr) public onlyOwner {
        require(token == address(0x0)); //initialize only once
        
        token = tokAddr;
        
        state = ContractState.Locked; //switch contract to locked state
        IsLocked(now);
    }
    
    /** Retrieves individual investor token balance **/
    function getUserBalance(address _owner) public view returns (uint256) {
        if (claimed[_owner] == false && allocations[_owner] > 0)
            return allocations[_owner];
        else
            return 0;
    }
    
    /** Allows owner to initiate the claiming phase **/
    function startClaim() public isLocked onlyOwner {
        state = ContractState.Claiming;
        IsClaiming(now);
    }
    
    /** Allows owner to finalize contract (only after investor claimEnd time) **/
    function finalize() public isClaiming onlyOwner {
        require(now >= claimEndTime);
        
        state = ContractState.Finalized;
        IsFinalized(now);
    }
    
    /** Allows the owner to claim all unclaimed investor tokens **/
    function ownerClaim() public isFinalized onlyOwner {
        uint256 remaining = token.balanceOf(this);
        amountClaimed = amountClaimed.add(remaining);
        amountLocked = amountLocked.sub(remaining);
        
        token.safeTransfer(owner, remaining);
        Claimed(owner, remaining);
    }
    
    /** Facilitates the assignment of investor addresses and amounts (only before claiming phase starts) **/
    function loadBalances(uint256[] data) public isLocked onlyOwner {
        require(token != address(0x0));  //Fail if token is not set
        
        for (uint256 i = 0; i < data.length; i++) {
            address addr = address(data[i] & (D160 - 1));
            uint256 amount = data[i] / D160;
            
            allocations[addr] = amount;
            claimed[addr] = false;
        }
    }
    
    /** Allows owner to claim future use tokens in favor of fUseBeneficiary account **/
    function claimFutureUse() public onlyOwner {
        require(now >= fUseReleaseTime);
        
        amountClaimed = amountClaimed.add(fUseAmount);
        amountLocked = amountLocked.sub(fUseAmount);
        
        token.safeTransfer(fUseBeneficiary, fUseAmount);
        ClaimedFutureUse(fUseBeneficiary, fUseAmount);
    }
    
    /** Allows presale investors to claim tokens **/
    function claim() external isClaiming {
        require(token != address(0x0)); //Fail if token is not set
        require(now >= releaseTime);
        require(allocations[msg.sender] > 0);
        
        uint256 amount = allocations[msg.sender];
        allocations[msg.sender] = 0;
        claimed[msg.sender] = true;
        amountClaimed = amountClaimed.add(amount);
        amountLocked = amountLocked.sub(amount);
        
        token.safeTransfer(msg.sender, amount);
        Claimed(msg.sender, amount);
    }
}
function YUPTimelock(
    uint256 _releaseTime,
    uint256 _amountLocked,
    address _fUseBeneficiary,
    uint256 _fUseReleaseTime
) public {
    require(_releaseTime > now);
        
    releaseTime = _releaseTime;
    amountLocked = _amountLocked;
    fUseAmount = 84550000 * 10**18;     //84,550,000 tokens (with 18 decimals)
    claimEndTime = now + 60*60*24*275;  //9 months (in seconds) from time of lock
    fUseBeneficiary = _fUseBeneficiary;
    fUseReleaseTime = _fUseReleaseTime;
        
    if (amountLocked != expectedAmount)
        revert();
}

/** Constructor **/