contract LiquidToken is Ownable, AbstractERC20 {
    
    using SafeMath for uint256;

    string public name;
    string public symbol;
    uint8 public decimals;
    
    address public teamWallet;
    address public advisorsWallet;
    address public founderWallet;
    address public bountyWallet;
    
    mapping (address => uint256) public balances;
    /// The transfer allowances
    mapping (address => mapping (address => uint256)) public allowed;
    
    mapping(address => bool) public isTeamOrAdvisorsOrFounder;

    event Burn(address indexed burner, uint256 value);
    
    constructor() public {
    
        name = "Liquid";
        symbol = "LIQUID";
        decimals = 18;
        totalSupply = 58e6 * 10**18;    // 58 million tokens
        owner = msg.sender;
        balances[owner] = totalSupply;
        emit Transfer(0x0, owner, totalSupply);
    }

    /**
    * @dev Check balance of given account address
    * @param owner The address account whose balance you want to know
    * @return balance of the account
    */
    function balanceOf(address owner) public view returns (uint256){
        return balances[owner];
    }

    /**
    * @dev transfer token for a specified address (written due to backward compatibility)
    * @param to address to which token is transferred
    * @param value amount of tokens to transfer
    * return bool true=> transfer is succesful
    */
    function transfer(address to, uint256 value) public returns (bool) {

        require(to != address(0x0));
        require(value <= balances[msg.sender]);
        balances[msg.sender] = balances[msg.sender].sub(value);
        balances[to] = balances[to].add(value);
        emit Transfer(msg.sender, to, value);
        return true;
    }

    /**
    * @dev Transfer tokens from one address to another
    * @param from address from which token is transferred 
    * @param to address to which token is transferred
    * @param value amount of tokens to transfer
    * @return bool true=> transfer is succesful
    */
    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        require(to != address(0x0));
        require(value <= balances[from]);
        require(value <= allowed[from][msg.sender]);
        balances[from] = balances[from].sub(value);
        allowed[from][msg.sender] = allowed[from][msg.sender].sub(value); 
        balances[to] = balances[to].add(value);
        emit Transfer(from, to, value);
        return true;
    }

    /**
    * @dev Approve function will delegate spender to spent tokens on msg.sender behalf
    * @param spender ddress which is delegated
    * @param value tokens amount which are delegated
    * @return bool true=> approve is succesful
    */
    function approve(address spender, uint256 value) public returns (bool) {
        allowed[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;
    }

    /**
    * @dev it will check amount of token delegated to spender by owner
    * @param owner the address which allows someone to spend fund on his behalf
    * @param spender address which is delegated
    * @return return uint256 amount of tokens left with delegator
    */
    function allowance(address owner, address spender) public view returns (uint256) {
        return allowed[owner][spender];
    }

    /**
    * @dev increment the spender delegated tokens
    * @param spender address which is delegated
    * @param valueToAdd tokens amount to increment
    * @return bool true=> operation is succesful
    */
    function increaseApproval(address spender, uint valueToAdd) public returns (bool) {
        allowed[msg.sender][spender] = allowed[msg.sender][spender].add(valueToAdd);
        emit Approval(msg.sender, spender, allowed[msg.sender][spender]);
        return true;
    }

    /**
    * @dev deccrement the spender delegated tokens
    * @param spender address which is delegated
    * @param valueToSubstract tokens amount to decrement
    * @return bool true=> operation is succesful
    */
    function decreaseApproval(address spender, uint valueToSubstract) public returns (bool) {
        uint oldValue = allowed[msg.sender][spender];
        if (valueToSubstract > oldValue) {
          allowed[msg.sender][spender] = 0;
        } else {
          allowed[msg.sender][spender] = oldValue.sub(valueToSubstract);
        }
        emit Approval(msg.sender, spender, allowed[msg.sender][spender]);
        return true;
    }

    /**
    * @dev Burns a specific amount of tokens.
    * @param _value The amount of token to be burned.
    *
    */
    function burn(address _who, uint256 _value) public onlyOwner {
        // no need to require value <= totalSupply, since that would imply the
        // sender's balance is greater than the totalSupply, which *should* be an assertion failure
        balances[_who] = balances[_who].sub(_value);
        totalSupply = totalSupply.sub(_value);
        emit Burn(_who, _value);
        emit Transfer(_who, address(0), _value);
    }
    
        /**
    * @dev set team wallet, funds of team will be allocated to a account controlled by admin/founder
    * @param _teamWallet address of bounty wallet.
    *
    */
    function setTeamWallet (address _teamWallet) public onlyOwner returns (bool) {
        require(_teamWallet   !=  address(0x0));
        if(teamWallet ==  address(0x0)){  
            teamWallet    =   _teamWallet;
            balances[teamWallet]  =   4e6 * 10**18;
            balances[owner] = balances[owner].sub(balances[teamWallet]);
        }else{
            address oldTeamWallet =   teamWallet;
            teamWallet    =   _teamWallet;
            balances[teamWallet]  =   balances[oldTeamWallet];
        }
        return true;
    }

    /**
    * @dev set AdvisorsWallet, funds of team will be allocated to a account controlled by admin/founder
    * @param _advisorsWallet address of Advisors wallet.
    *
    */
    function setAdvisorsWallet (address _advisorsWallet) public onlyOwner returns (bool) {
        require(_advisorsWallet   !=  address(0x0));
        if(advisorsWallet ==  address(0x0)){  
            advisorsWallet    =   _advisorsWallet;
            balances[advisorsWallet]  =   2e6 * 10**18;
            balances[owner] = balances[owner].sub(balances[teamWallet]);
        }else{
            address oldAdvisorsWallet =   advisorsWallet;
            advisorsWallet    =   _advisorsWallet;
            balances[advisorsWallet]  =   balances[oldAdvisorsWallet];
        }
        return true;
    }

    /**
    * @dev set Founders wallet, funds of team will be allocated to a account controlled by admin/founder
    * @param _founderWallet address of Founder wallet.
    *
    */
    function setFoundersWallet (address _founderWallet) public onlyOwner returns (bool) {
        require(_founderWallet   !=  address(0x0));
        if(founderWallet ==  address(0x0)){  
            founderWallet    =   _founderWallet;
            balances[founderWallet]  =  8e6 * 10**18;
            balances[owner] = balances[owner].sub(balances[founderWallet]);
        }else{
            address oldFounderWallet =   founderWallet;
            founderWallet    =   _founderWallet;
            balances[founderWallet]  =   balances[oldFounderWallet];
        }
        return true;
    }
    /**
    * @dev set bounty wallet
    * @param _bountyWallet address of bounty wallet.
    *
    */
    function setBountyWallet (address _bountyWallet) public onlyOwner returns (bool) {
        require(_bountyWallet   !=  address(0x0));
        if(bountyWallet ==  address(0x0)){  
            bountyWallet    =   _bountyWallet;
            balances[bountyWallet]  =   4e6 * 10**18;
            balances[owner] = balances[owner].sub(balances[bountyWallet]);
        }else{
            address oldBountyWallet =   bountyWallet;
            bountyWallet    =   _bountyWallet;
            balances[bountyWallet]  =   balances[oldBountyWallet];
        }
        return true;
    }

    /**
    * @dev Function to Airdrop tokens from bounty wallet to contributors as long as there are enough balance.
    * @param dests destination address of bounty beneficiary.
    * @param values tokesn for beneficiary.
    * @return number of address airdropped and True if the operation was successful.
    */
    function airdrop(address[] dests, uint256[] values) public onlyOwner returns (uint256, bool) {
        require(dests.length == values.length);
        uint8 i = 0;
        while (i < dests.length && balances[bountyWallet] >= values[i]) {
            balances[bountyWallet]  =   balances[bountyWallet].sub(values[i]);
            balances[dests[i]]  =   balances[dests[i]].add(values[i]);
            i += 1;
        }
        return (i, true);
    }

    /**
    * @dev Function to transferTokensToTeams tokens from Advisor wallet to contributors as long as there are enough balance.
    * @param teamMember destination address of team beneficiary.
    * @param values tokens for beneficiary.
    * @return True if the operation was successful.
    */
    function transferTokensToTeams(address teamMember, uint256 values) public onlyOwner returns (bool) {
        require(teamMember != address(0));
        require (values != 0);
        balances[teamWallet]  =   balances[teamWallet].sub(values);
        balances[teamMember]  =   balances[teamMember].add(values);
        isTeamOrAdvisorsOrFounder[teamMember] = true;
        return true;
    }
     
    /**
    * @dev Function to transferTokensToFounders tokens from Advisor wallet to contributors as long as there are enough balance.
    * @param founder destination address of team beneficiary.
    * @param values tokens for beneficiary.
    * @return True if the operation was successful.
    */
    function transferTokensToFounders(address founder, uint256 values) public onlyOwner returns (bool) {
        require(founder != address(0));
        require (values != 0);
        balances[founderWallet]  =   balances[founderWallet].sub(values);
        balances[founder]  =   balances[founder].add(values);
        isTeamOrAdvisorsOrFounder[founder] = true;
        return true;
    }

    /**
    * @dev Function to transferTokensToAdvisors tokens from Advisor wallet to contributors as long as there are enough balance.
    * @param advisor destination address of team beneficiary.
    * @param values tokens for beneficiary.
    * @return True if the operation was successful.
    */
    function transferTokensToAdvisors(address advisor, uint256 values) public onlyOwner returns (bool) {
        require(advisor != address(0));
        require (values != 0);
        balances[advisorsWallet]  =   balances[advisorsWallet].sub(values);
        balances[advisor]  =   balances[advisor].add(values);
        isTeamOrAdvisorsOrFounder[advisor] = true;
        return true;
    }

}
function setBountyWallet (address _bountyWallet) public onlyOwner returns (bool) {
    require(_bountyWallet   !=  address(0x0));
    if(bountyWallet ==  address(0x0)){  
        bountyWallet    =   _bountyWallet;
        balances[bountyWallet]  =   4e6 * 10**18;
        balances[owner] = balances[owner].sub(balances[bountyWallet]);
    }else{
        address oldBountyWallet =   bountyWallet;
        bountyWallet    =   _bountyWallet;
        balances[bountyWallet]  =   balances[oldBountyWallet];
    }
    return true;
}

/**
 * @dev set bounty wallet
 * @param _bountyWallet address of bounty wallet.
 *
 */