contract Presale is CappedCrowdsale, Ownable {
    using SafeMath for uint256;

    mapping (address => bool) public whitelist;

    bool public isFinalized = false;
    event Finalized();
    
    address public team = 0x7D72dc07876435d3B2eE498E53A803958bc55b42;
    uint256 public teamShare = 150000000 * (10 ** 18);
    
    address public seed = 0x3669ad54675E94e14196528786645c858b8391F1;
    uint256 public seedShare = 1805067 * (10 ** 18);

    bool public hasAllocated = false;

    address public mediator = 0x0;
    
    function Presale(uint256 _startTime, uint256 _endTime, uint256 _rate, uint256 _cap, address _wallet, address _tokenAddress) 
    Crowdsale(_startTime, _endTime, _rate, _wallet)
    CappedCrowdsale(_cap)
    {
        token = LamdenTau(_tokenAddress);
    }
    
    // Crowdsale overrides
    function createTokenContract() internal returns (MintableToken) {
        return LamdenTau(0x0);
    }

    function validPurchase() internal constant returns (bool) {
        bool withinCap = weiRaised.add(msg.value) <= cap;
        bool valid = super.validPurchase() && withinCap && whitelist[msg.sender];
        return valid;
    }
    // * * *
    
    // Finalizer functions. Redefined from FinalizableCrowdsale to prevent diamond inheritence complexities
    
    function finalize() onlyOwner public {
      require(mediator != 0x0);
      require(!isFinalized);
      require(hasEnded());
      
      finalization();
      Finalized();

      isFinalized = true;
    }
    
    function finalization() internal {
        // set the ownership to the mediator so it can pass it onto the sale contract
        // at the time that the sale contract is deployed
        token.transferOwnership(mediator);
        Mediator m = Mediator(mediator);
        m.acceptToken();
    }
    // * * * 

    // Contract Specific functions
    function assignMediator(address _m) public onlyOwner returns(bool) {
        mediator = _m;
        return true;
    }
    
    function whitelistUser(address _a) public onlyOwner returns(bool){
        whitelist[_a] = true;
        return whitelist[_a];
    }

    function whitelistUsers(address[] users) external onlyOwner {
        for (uint i = 0; i < users.length; i++) {
            whitelist[users[i]] = true;
        }
    }

    function unWhitelistUser(address _a) public onlyOwner returns(bool){
        whitelist[_a] = false;
        return whitelist[_a];
    }

    function unWhitelistUsers(address[] users) external onlyOwner {
        for (uint i = 0; i < users.length; i++) {
            whitelist[users[i]] = false;
        }
    }
    
    function allocateTokens() public onlyOwner returns(bool) {
        require(hasAllocated == false);
        token.mint(team, teamShare);
        token.mint(seed, seedShare);
        hasAllocated = true;
        return hasAllocated;
    }
    
    function acceptToken() public onlyOwner returns(bool) {
        token.acceptOwnership();
        return true;
    }

    function changeEndTime(uint256 _e) public onlyOwner returns(uint256) {
        require(_e > startTime);
        endTime = _e;
        return endTime;
    }
    
    // * * *
}
function assignMediator(address _m) public onlyOwner returns(bool) {
    mediator = _m;
    return true;
}

// * * * 
// Contract Specific functions