contract Leasing is Ownable {
    
    using SafeMath for uint256;
    
    address XR = address(0); // testnet;
    SingleToken token;
    
    struct Stakes {
        uint256 stakingCurrency; // 0 for ETH 1 for XR
        uint256 stakingAmount;
        bytes coordinates;
    }
    
    struct Tenant {
        uint256 ids;
        Stakes[] stakes;
    }
    
    uint256 public tokenRate = 0;
    address public companyWallet = 0x553654Ad7808625B36F6AB29DdB41140300E024F;
    
    mapping (address => Tenant) public tenants;
    
    
    event Deposit(address indexed user, uint256 indexed amount, string indexed currency, uint256 timestamp);
    event Withdraw(address indexed user, uint256 indexed amount, string indexed currency, uint256 timestamp);
    
    constructor (address _xr) public {
        XR = _xr;
    }
    
    function () payable external {
        require(1 == 0);
        
    }
    

    /**
     * 0 - pre-ICO stage; Assuming 1 ETH = 150$; 1 ETH = 1500XR
     * 1 - ICO stage; Assuming 1 ETH = 150$; 1 ETH = 1000XR
     * 2 - post-ICO stage; Using price from exchange
    */
    function projectStage (uint256 _stage) public onlyOwner returns (bool) {
        if (_stage == 0) 
            tokenRate = 1500;
        if (_stage == 1)
            tokenRate = 1000;
        if (_stage == 2)
            tokenRate = 0;
    }
    

    /**
     * @dev Back-end will call that function to set Price from exchange
     * @param _rate the 1 ETH = _rate XR 
    */
    function oracleSetPrice (uint256 _rate) public onlyOwner returns (bool) {
        tokenRate = _rate;
        return true;
    }
    
    
    function stakeEth (bytes memory _coordinates) payable public returns (bool) {
        require(msg.value != 0);
        require(tokenRate != 0, "XR is on exchange, need to get price");
        
        uint256 fee = msg.value * 10 / 110;
        address(0x553654Ad7808625B36F6AB29DdB41140300E024F).transfer(fee);
        uint256 afterFee = msg.value - fee;
        
        Stakes memory stake = Stakes(0, afterFee, _coordinates);
        tenants[msg.sender].stakes.push(stake);
        
        tenants[msg.sender].ids = tenants[msg.sender].ids.add(1);
        
        emit Deposit(msg.sender, afterFee, "ETH", block.timestamp);
        return true;
    }
    
    
    function returnEth (uint256 _id) public returns (bool) {
        require(_id != 0, "always invalid id");
        require(tenants[msg.sender].ids != 0, "nothing to return");
        require(tenants[msg.sender].ids >= _id, "no staking data with such id");
        require(tenants[msg.sender].stakes[_id-1].stakingCurrency == 0, 'use returnXR');
        require(tokenRate != 0, "XR is on exchange, need to get price");
        
        uint256 indexify = _id-1;
        uint256 ethToReturn = tenants[msg.sender].stakes[indexify].stakingAmount;
        
        removeStakeById(indexify);

        ethToReturn = ethToReturn * 9 / 10;
        uint256 tokenAmountToReturn = ethToReturn * tokenRate / 10E9;
        
        require(SingleToken(XR).transferFrom(companyWallet, msg.sender, tokenAmountToReturn), "can not transfer tokens");
    
        emit Withdraw(msg.sender, tokenAmountToReturn, "ETH", block.timestamp);
        return true;
    }
    
    
    function returnTokens (uint256 _id) public returns (bool){
        require(_id != 0, "always invalid id");
        require(tenants[msg.sender].ids != 0, "nothing to return");
        require(tenants[msg.sender].ids >= _id, "no staking data with such id");
        require(tenants[msg.sender].stakes[_id-1].stakingCurrency == 1, 'use returnETH');

        uint256 indexify = _id-1;
        uint256 tokensToReturn = tenants[msg.sender].stakes[indexify].stakingAmount;
    
        SingleToken _instance = SingleToken(XR);
        
        removeStakeById(indexify);
        
        _instance.transfer(msg.sender, tokensToReturn);
        
        emit Withdraw(msg.sender, tokensToReturn, "XR", block.timestamp);
        return true;
    }
    
   
    function stakeTokens (uint256 amount, bytes memory _coordinates) public returns (bool) {
        require(amount != 0, "staking can not be 0");
        
        Stakes memory stake = Stakes(1, amount, _coordinates);
        tenants[msg.sender].stakes.push(stake);
        
        tenants[msg.sender].ids = tenants[msg.sender].ids.add(1);
        
        require(SingleToken(XR).transferFrom(msg.sender, address(this), amount), "can not transfer tokens");
        
        emit Deposit(msg.sender, amount, "XR", block.timestamp);
        return true;
    }
    
    
    function removeStakeById (uint256 _id) internal returns (bool) {
        for (uint256 i = _id; i < tenants[msg.sender].stakes.length-1; i++) {
            tenants[msg.sender].stakes[i] = tenants[msg.sender].stakes[i+1];
        }
        tenants[msg.sender].stakes.length--;
        tenants[msg.sender].ids = tenants[msg.sender].ids.sub(1);
        
        return true;
    }
    
    
    function getStakeById (uint256 _id) public view returns (string memory, uint256, bytes memory) {
        require(_id != 0, "always invalid id");
        require(tenants[msg.sender].ids != 0, "no staking data");
        require(tenants[msg.sender].ids >= _id, "no staking data with such id");
        
        uint256 indexify = _id-1;
        string memory currency;
        if (tenants[msg.sender].stakes[indexify].stakingCurrency == 0)
            currency = "ETH";
        else 
            currency = "XR";
        
        return (currency, tenants[msg.sender].stakes[indexify].stakingAmount, tenants[msg.sender].stakes[indexify].coordinates);
    }
    
    
    function getStakingStructLength () public view returns (uint256) {
        return tenants[msg.sender].stakes.length;
    }
}
function oracleSetPrice (uint256 _rate) public onlyOwner returns (bool) {
    tokenRate = _rate;
    return true;
}

/**
 * @dev Back-end will call that function to set Price from exchange
 * @param _rate the 1 ETH = _rate XR 
 */