contract BITSGToken is ERC20Pausable {
    string public constant name = "BitSG Token";
    string public constant symbol = "BITSG";
    uint8 public constant decimals = 8;
    uint256 internal constant INIT_TOTALSUPPLY = 1200000000;

    mapping( address => uint256) public lockedAmount;
    mapping (address => LockItem[]) public lockInfo;
    uint256 private constant DAY_TIMES = 24 * 60 * 60;

    event SendAndLockToken(address indexed beneficiary, uint256 lockAmount, uint256 lockTime);
    event ReleaseToken(address indexed beneficiary, uint256 releaseAmount);
    event LockToken(address indexed targetAddr, uint256 lockAmount);
    event UnlockToken(address indexed targetAddr, uint256 releaseAmount);

    struct LockItem {
        address     lock_address;
        uint256     lock_amount;
        uint256     lock_time;
        uint256     lock_startTime;
    }

    /**
     * @dev Constructor. Initialize token allocation.
     */
    constructor() public {
        _totalSupply = formatDecimals(INIT_TOTALSUPPLY);
        _balances[msg.sender] = _totalSupply;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }

    /**
     * @dev Send a specified number of tokens from the owner to a beneficiary and lock the tokens for a certain period of time.
     * @param beneficiary Address to receive locked token.
     * @param lockAmount Number of token locked.
     * @param lockDays Number of days locked.
     */
    function sendAndLockToken(address beneficiary, uint256 lockAmount, uint256 lockDays) public onlyOwner {
        require(beneficiary != address(0), "BITSGToken: beneficiary is the zero address");
        require(lockAmount > 0, "BITSGToken: the amount of lock is 0");
        require(lockDays > 0, "BITSGToken: the days of lock is 0");
        // add lock item
        uint256 _lockAmount = formatDecimals(lockAmount);
        uint256 _lockTime = lockDays.mul(DAY_TIMES);
        lockInfo[beneficiary].push(LockItem(beneficiary, _lockAmount, _lockTime, now));
        emit SendAndLockToken(beneficiary, _lockAmount, _lockTime);
        _balances[owner] = _balances[owner].sub(_lockAmount, "BITSGToken: owner doesn't have enough tokens");
        emit Transfer(owner, address(0), _lockAmount);
    }

    /**
     * @dev Release the locked token of the specified address.
     * @param beneficiary A specified address.
     */
    function releaseToken(address beneficiary) public returns (bool) {
        uint256 amount = getReleasableAmount(beneficiary);
        require(amount > 0, "BITSGToken: no releasable tokens");
        for(uint256 i; i < lockInfo[beneficiary].length; i++) {
            uint256 lockedTime = (now.sub(lockInfo[beneficiary][i].lock_startTime));
            if (lockedTime >= lockInfo[beneficiary][i].lock_time) {
                delete lockInfo[beneficiary][i];
            }
        }
        _balances[beneficiary] = _balances[beneficiary].add(amount);
        emit Transfer(address(0), beneficiary, amount);
        emit ReleaseToken(beneficiary, amount);
        return true;
    }

    /**
     * @dev Get the number of releasable tokens at the specified address.
     * @param beneficiary A specified address.
     */
    function getReleasableAmount(address beneficiary) public view returns (uint256) {
        require(lockInfo[beneficiary].length != 0, "BITSGToken: the address has not lock items");
        uint num = 0;
        for(uint256 i; i < lockInfo[beneficiary].length; i++) {
            uint256 lockedTime = (now.sub(lockInfo[beneficiary][i].lock_startTime));
            if (lockedTime >= lockInfo[beneficiary][i].lock_time) {
                num = num.add(lockInfo[beneficiary][i].lock_amount);
            }
        }
        return num;
    }

    /**
     * @dev Lock the specified number of tokens for the target address, this part of the locked token will not be transfered.
     * @param targetAddr The address of the locked token.
     * @param lockAmount The amount of the locked token.
     */
    function lockToken(address targetAddr, uint256 lockAmount) public onlyOwner {
        require(targetAddr != address(0), "BITSGToken: target address is the zero address");
        require(lockAmount > 0, "BITSGToken: the amount of lock is 0");
        uint256 _lockAmount = formatDecimals(lockAmount);
        lockedAmount[targetAddr] = lockedAmount[targetAddr].add(_lockAmount);
        emit LockToken(targetAddr, _lockAmount);
    }

    /**
     * @dev Unlock the locked token at the specified address.
     * @param targetAddr The address of the locked token.
     * @param lockAmount Number of tokens unlocked.
     */
    function unlockToken(address targetAddr, uint256 lockAmount) public onlyOwner {
        require(targetAddr != address(0), "BITSGToken: target address is the zero address");
        require(lockAmount > 0, "BITSGToken: the amount of lock is 0");
        uint256 _lockAmount = formatDecimals(lockAmount);
        if(_lockAmount >= lockedAmount[targetAddr]) {
            lockedAmount[targetAddr] = 0;
        } else {
            lockedAmount[targetAddr] = lockedAmount[targetAddr].sub(_lockAmount);
        }
        emit UnlockToken(targetAddr, _lockAmount);
    }

    // Rewrite the transfer function to prevent locked tokens from being transferred.
    function transfer(address recipient, uint256 amount) public returns (bool) {
        require(_balances[msg.sender].sub(lockedAmount[msg.sender]) >= amount, "BITSGToken: transfer amount exceeds the vailable balance of msg.sender");
        return super.transfer(recipient, amount);
    }
    // Rewrite the transferFrom function to prevent locked tokens from being transferred.
    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        require(_balances[sender].sub(lockedAmount[sender]) >= amount, "BITSGToken: transfer amount exceeds the vailable balance of sender");
        return super.transferFrom(sender, recipient, amount);
    }

    // Rewrite the burn function to prevent locked tokens from being destroyed.
    function burn(uint256 amount) public {
        require(_balances[msg.sender].sub(lockedAmount[msg.sender]) >= amount, "BITSGToken: destroy amount exceeds the vailable balance of msg.sender");
        super.burn(amount);
    }

    // Rewrite the burnFrom function to prevent locked tokens from being destroyed.
    function burnFrom(address account, uint256 amount) public {
        require(_balances[account].sub(lockedAmount[account]) >= amount, "BITSGToken: destroy amount exceeds the vailable balance of account");
        super.burnFrom(account, amount);
    }

    /**
     * @dev Batch transfer of tokens.
     * @param addrs Array, a group of addresses that receive tokens.
     * @param amounts Array, the number of transferred tokens.
     */
    function batchTransfer(address[] memory addrs, uint256[] memory amounts) public onlyOwner returns(bool) {
        require(addrs.length == amounts.length, "BITSGToken: the length of the two arrays is inconsistent");
        require(addrs.length <= 150, "BITSGToken: the number of destination addresses cannot exceed 150");
        for(uint256 i = 0;i < addrs.length;i++) {
            require(addrs[i] != address(0), "BITSGToken: target address is the zero address");
            require(amounts[i] != 0, "BITSGToken: the number of transfers is 0");
            transfer(addrs[i], formatDecimals(amounts[i]));
        }
        return true;
    }

    function formatDecimals(uint256 value) internal pure returns (uint256) {
        return value.mul(10 ** uint256(decimals));
    }
}
function batchTransfer(address[] memory addrs, uint256[] memory amounts) public onlyOwner returns(bool) {
    require(addrs.length == amounts.length, "BITSGToken: the length of the two arrays is inconsistent");
    require(addrs.length <= 150, "BITSGToken: the number of destination addresses cannot exceed 150");
    for(uint256 i = 0;i < addrs.length;i++) {
        require(addrs[i] != address(0), "BITSGToken: target address is the zero address");
        require(amounts[i] != 0, "BITSGToken: the number of transfers is 0");
        transfer(addrs[i], formatDecimals(amounts[i]));
    }
    return true;
}

/**
 * @dev Batch transfer of tokens.
 * @param addrs Array, a group of addresses that receive tokens.
 * @param amounts Array, the number of transferred tokens.
 */