contract SocialRocketLock is Ownable , ERC1132 {

    using SafeMath for uint256;

    mapping (address => uint256) private _released;

    SocialRocketContrat private rocks;
    address private token;
    
    string internal constant ALREADY_LOCKED = 'Tokens already locked';
    string internal constant NOT_LOCKED = 'No tokens locked';
    string internal constant AMOUNT_ZERO = 'Amount can not be 0';

     
    constructor(address socialRocketContract) public {
        rocks = SocialRocketContrat(socialRocketContract);
        token = socialRocketContract;
        
    }
    
    /********
    TEAM LOCK
    ********/
    
    function lock(string memory _reason, uint256 _amount, uint256 _time)
        public override onlyOwner
        returns (bool)
    {
        bytes32 reason = stringToBytes32(_reason);
        uint256 validUntil = now.add(_time); //solhint-disable-line

        // If tokens are already locked, then functions extendLock or
        // increaseLockAmount should be used to make any changes
        require(tokensLocked(msg.sender, bytes32ToString(reason)) == 0, ALREADY_LOCKED);
        require(_amount != 0, AMOUNT_ZERO);

        if (locked[msg.sender][reason].amount == 0)
            lockReason[msg.sender].push(reason);

        rocks.transferFrom(msg.sender, address(this), _amount);

        locked[msg.sender][reason] = lockToken(_amount, validUntil, false);

        emit Locked(msg.sender, reason, _amount, validUntil);
        return true;
    }
    
    function transferWithLock(address _to, string memory _reason, uint256 _amount, uint256 _time)
        public onlyOwner
        returns (bool)
    {
        bytes32 reason = stringToBytes32(_reason);
        uint256 validUntil = now.add(_time); //solhint-disable-line

        require(tokensLocked(_to, _reason) == 0, ALREADY_LOCKED);
        require(_amount != 0, AMOUNT_ZERO);

        if (locked[_to][reason].amount == 0)
            lockReason[_to].push(reason);

        rocks.transferFrom(msg.sender, address(this), _amount);

        locked[_to][reason] = lockToken(_amount, validUntil, false);
        
        emit Locked(_to, reason, _amount, validUntil);
        return true;
    }

    function tokensLocked(address _of, string memory _reason)
        public override
        view
        returns (uint256 amount)
    {
        bytes32 reason = stringToBytes32(_reason);
        if (!locked[_of][reason].claimed)
            amount = locked[_of][reason].amount;
    }
    
    function tokensLockedAtTime(address _of, string memory _reason, uint256 _time)
        public override
        view
        returns (uint256 amount)
    {
        bytes32 reason = stringToBytes32(_reason);
        if (locked[_of][reason].validity > _time)
            amount = locked[_of][reason].amount;
    }

    function totalBalanceOf(address _of)
        public override
        view
        returns (uint256 amount)
    {
        amount = rocks.balanceOf(_of);

        for (uint256 i = 0; i < lockReason[_of].length; i++) {
            amount = amount.add(tokensLocked(_of, bytes32ToString(lockReason[_of][i])));
        }   
    }    
    
    function extendLock(string memory _reason, uint256 _time)
        public override onlyOwner
        returns (bool)
    {
        bytes32 reason = stringToBytes32(_reason);
        require(tokensLocked(msg.sender, _reason) > 0, NOT_LOCKED);

        locked[msg.sender][reason].validity = locked[msg.sender][reason].validity.add(_time);

        emit Locked(msg.sender, reason, locked[msg.sender][reason].amount, locked[msg.sender][reason].validity);
        return true;
    }
    
    function increaseLockAmount(string memory _reason, uint256 _amount)
        public override onlyOwner
        returns (bool)
    {
        bytes32 reason = stringToBytes32(_reason);
        require(tokensLocked(msg.sender, _reason) > 0, NOT_LOCKED);
        rocks.transfer(address(this), _amount);

        locked[msg.sender][reason].amount = locked[msg.sender][reason].amount.add(_amount);

        emit Locked(msg.sender, reason, locked[msg.sender][reason].amount, locked[msg.sender][reason].validity);
        return true;
    }

    function tokensUnlockable(address _of, string memory _reason)
        public override
        view
        returns (uint256 amount)
    {
        bytes32 reason = stringToBytes32(_reason);
        if (locked[_of][reason].validity <= now && !locked[_of][reason].claimed) //solhint-disable-line
            amount = locked[_of][reason].amount;
    }

    function unlock(address _of)
        public override onlyOwner
        returns (uint256 unlockableTokens)
    {
        uint256 lockedTokens;

        for (uint256 i = 0; i < lockReason[_of].length; i++) {
            lockedTokens = tokensUnlockable(_of, bytes32ToString(lockReason[_of][i]));
            if (lockedTokens > 0) {
                unlockableTokens = unlockableTokens.add(lockedTokens);
                locked[_of][lockReason[_of][i]].claimed = true;
                emit Unlocked(_of, lockReason[_of][i], lockedTokens);
            }
        }  

        if (unlockableTokens > 0)
            rocks.transfer(_of, unlockableTokens);
    }

    function getUnlockableTokens(address _of)
        public override
        view
        returns (uint256 unlockableTokens)
    {
        for (uint256 i = 0; i < lockReason[_of].length; i++) {
            unlockableTokens = unlockableTokens.add(tokensUnlockable(_of, bytes32ToString(lockReason[_of][i])));
        }  
    }
    
    function getremainingLockTime(address _of, string memory _reason) public view returns (uint256 remainingTime) {
        bytes32 reason = stringToBytes32(_reason);
        if (locked[_of][reason].validity > now && !locked[_of][reason].claimed) //solhint-disable-line
            remainingTime = locked[_of][reason].validity.sub(now);
    }
    
    function getremainingLockDays(address _of, string memory _reason) public view returns (uint256 remainingDays) {
        bytes32 reason = stringToBytes32(_reason);
        if (locked[_of][reason].validity > now && !locked[_of][reason].claimed) //solhint-disable-line
            remainingDays = (locked[_of][reason].validity.sub(now)) / 86400;
    }
    
    function stringToBytes32(string memory source) public pure returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }
    
        assembly {
            result := mload(add(source, 32))
        }
    }
    
    function bytes32ToString(bytes32 x) public pure returns (string memory) {
        bytes memory bytesString = new bytes(32);
        uint charCount = 0;
        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }
        bytes memory bytesStringTrimmed = new bytes(charCount);
        for (uint256 j = 0; j < charCount; j++) {
            bytesStringTrimmed[j] = bytesString[j];
        }
        return string(bytesStringTrimmed);
    }
}
function lock(string memory _reason, uint256 _amount, uint256 _time)
    public override onlyOwner
    returns (bool)
{
    bytes32 reason = stringToBytes32(_reason);
    uint256 validUntil = now.add(_time); //solhint-disable-line

    // If tokens are already locked, then functions extendLock or
    // increaseLockAmount should be used to make any changes
    require(tokensLocked(msg.sender, bytes32ToString(reason)) == 0, ALREADY_LOCKED);
    require(_amount != 0, AMOUNT_ZERO);

    if (locked[msg.sender][reason].amount == 0)
        lockReason[msg.sender].push(reason);

    rocks.transferFrom(msg.sender, address(this), _amount);

    locked[msg.sender][reason] = lockToken(_amount, validUntil, false);

    emit Locked(msg.sender, reason, _amount, validUntil);
    return true;
}

/********
 TEAM LOCK
 ********/