pragma solidity 0.5.4;


library SafeMath {

    uint256 constant internal MAX_UINT = 2 ** 256 - 1; // max uint256

    /**
     * @dev Multiplies two numbers, reverts on overflow.
     */
    function mul(uint256 _a, uint256 _b) internal pure returns(uint256) {
        if (_a == 0) {
            return 0;
        }
        require(MAX_UINT / _a >= _b);
        return _a * _b;
    }

    /**
     * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
     */
    function div(uint256 _a, uint256 _b) internal pure returns(uint256) {
        require(_b != 0);
        return _a / _b;
    }

    /**
     * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
     */
    function sub(uint256 _a, uint256 _b) internal pure returns(uint256) {
        require(_b <= _a);
        return _a - _b;
    }

    /**
     * @dev Adds two numbers, reverts on overflow.
     */
    function add(uint256 _a, uint256 _b) internal pure returns(uint256) {
        require(MAX_UINT - _a >= _b);
        return _a + _b;
    }

}

interface AbcInterface {
    function decimals() external view returns (uint8);
    function tokenFallback(address _from, uint _value, bytes calldata _data) external;
    function transfer(address _to, uint _value) external returns (bool);
}
/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
    address public owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    constructor () public {
        owner = msg.sender;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0));
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}


contract Pausable is Ownable {
    event Pause();
    event Unpause();

    bool public paused = false;

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     */
    modifier whenNotPaused() {
        require(!paused);
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     */
    modifier whenPaused() {
        require(paused);
        _;
    }

    /**
     * @dev called by the owner to pause, triggers stopped state
     */
    function pause() public onlyOwner whenNotPaused {
        paused = true;
        emit Pause();
    }

    /**
     * @dev called by the owner to unpause, returns to normal state
     */
    function unpause() public onlyOwner whenPaused {
        paused = false;
        emit Unpause();
    }
}


contract StandardToken {
    using SafeMath for uint256;

    mapping(address => uint256) internal balances;

    mapping(address => mapping(address => uint256)) internal allowed;

    uint256 public totalSupply;

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Gets the balance of the specified address.
     * @param _owner The address to query the balance of.
     * @return An uint256 representing the value owned by the passed address.
     */
    function balanceOf(address _owner) public view returns(uint256) {
        return balances[_owner];
    }

    /**
     * @dev Function to check the value of tokens that an owner allowed to a spender.
     * @param _owner address The address which owns the funds.
     * @param _spender address The address which will spend the funds.
     * @return A uint256 specifying the value of tokens still available for the spender.
     */
    function allowance(address _owner, address _spender) public view returns(uint256) {
        return allowed[_owner][_spender];
    }

    /**
     * @dev Transfer token for a specified address
     * @param _to The address to transfer to.
     * @param _value The value to be transferred.
     */
    function transfer(address _to, uint256 _value) public returns(bool) {
        require(_to != address(0));
        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 Approve the passed address to spend the specified value of tokens on behalf of msg.sender.
     * Beware that changing an allowance with this method brings the risk that someone may use both the old
     * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
     * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     * @param _spender The address which will spend the funds.
     * @param _value The value of tokens to be spent.
     */
    function approve(address _spender, uint256 _value) public returns(bool) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    /**
     * @dev Transfer tokens from one address to another
     * @param _from address The address which you want to send tokens from
     * @param _to address The address which you want to transfer to
     * @param _value uint256 the value of tokens to be transferred
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns(bool) {
        require(_to != address(0));
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]);

        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        emit Transfer(_from, _to, _value);
        return true;
    }

    /**
     * @dev Increase the value of tokens that an owner allowed to a spender.
     * approve should be called when allowed[_spender] == 0. To increment
     * allowed value is better to use this function to avoid 2 calls (and wait until
     * the first transaction is mined)
     * From MonolithDAO Token.sol
     * @param _spender The address which will spend the funds.
     * @param _addedValue The value of tokens to increase the allowance by.
     */
    function increaseApproval(address _spender, uint256 _addedValue) public returns(bool) {
        allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    /**
     * @dev Decrease the value of tokens that an owner allowed to a spender.
     * approve should be called when allowed[_spender] == 0. To decrement
     * allowed value is better to use this function to avoid 2 calls (and wait until
     * the first transaction is mined)
     * From MonolithDAO Token.sol
     * @param _spender The address which will spend the funds.
     * @param _subtractedValue The value of tokens to decrease the allowance by.
     */
    function decreaseApproval(address _spender, uint256 _subtractedValue) public returns(bool) {
        uint256 oldValue = allowed[msg.sender][_spender];
        if (_subtractedValue >= oldValue) {
            allowed[msg.sender][_spender] = 0;
        } else {
            allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    function _burn(address account, uint256 value) internal {
        require(account != address(0));
        totalSupply = totalSupply.sub(value);
        balances[account] = balances[account].sub(value);
        emit Transfer(account, address(0), value);
    }

    /**
     * @dev Internal function that burns an value of the token of a given
     * account, deducting from the sender's allowance for said account. Uses the
     * internal burn function.
     * @param account The account whose tokens will be burnt.
     * @param value The value that will be burnt.
     */
    function _burnFrom(address account, uint256 value) internal {
        // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
        // this function needs to emit an event with the updated approval.
        allowed[account][msg.sender] = allowed[account][msg.sender].sub(value);
        _burn(account, value);
    }

}


contract BurnableToken is StandardToken {

    /**
     * @dev Burns a specific value of tokens.
     * @param value The value of token to be burned.
     */
    function burn(uint256 value) public {
        _burn(msg.sender, value);
    }

    /**
     * @dev Burns a specific value of tokens from the target address and decrements allowance
     * @param from address The address which you want to send tokens from
     * @param value uint256 The value of token to be burned
     */
    function burnFrom(address from, uint256 value) public {
        _burnFrom(from, value);
    }
}


/**
 * @title Pausable token
 * @dev ERC20 modified with pausable transfers.
 */
contract PausableToken is StandardToken, Pausable {
    function transfer(address to, uint256 value) public whenNotPaused returns (bool) {
        return super.transfer(to, value);
    }

    function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) {
        return super.transferFrom(from, to, value);
    }

    function approve(address spender, uint256 value) public whenNotPaused returns (bool) {
        return super.approve(spender, value);
    }

    function increaseApproval(address spender, uint256 addedValue) public whenNotPaused returns (bool success) {
        return super.increaseApproval(spender, addedValue);
    }

    function decreaseApproval(address spender, uint256 subtractedValue) public whenNotPaused returns (bool success) {
        return super.decreaseApproval(spender, subtractedValue);
    }
}

contract LockableToken is PausableToken {
	struct LockInfo {
		uint256 amount;
		uint256 releaseTime;
	}

	mapping(address => LockInfo[]) public lockDetail;
	mapping(address => uint256) public transferLocked;

	event LockToken(address indexed benefit, uint256 amount, uint256 releasetime);
	event ReleaseToken(address indexed benefit, uint256 amount);
	
	/**
     * @dev Transfers and locks tokens.
     * @param to The address to transfer to.
     * @param value The value to be transferred.
     * @param lockdays The days of locking tokens.
     */
	function transferAndLock(address to, uint256 value, uint256 lockdays) public whenNotPaused returns (bool) {
		release(msg.sender);
		require(to != address(0) && value != 0 && lockdays != 0);
		uint256 _releaseTime = now.add(lockdays.mul(1 days));
		lockDetail[to].push(LockInfo({amount:value, releaseTime:_releaseTime}));
		balances[msg.sender] = balances[msg.sender].sub(value);
		transferLocked[to] = transferLocked[to].add(value);
		emit Transfer(msg.sender, to, value);
		emit LockToken(to, value, _releaseTime);
		return true;
	}

	/**
     * @dev Rewrites function transfer, release tokens before transferring.
     */
    function transfer(address to, uint256 value) public returns (bool) {
		release(msg.sender);
        return super.transfer(to, value);
    }

	/**
     * @dev Rewrites function transferFrom, release tokens before transferring.
     */
    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        release(from);
        return super.transferFrom(from, to, value);
    }

	/**
     * @dev release tokens.
     */
	function release(address benefit) public whenNotPaused {
		uint256 len = lockDetail[benefit].length;
		if( len == 0) return;
		uint256 totalReleasable = 0;
		for(uint256 i = 0; i < len; i = i.add(1)){
			LockInfo memory tmp = lockDetail[benefit][i];
			if(tmp.releaseTime != 0 && now >= tmp.releaseTime){
				totalReleasable = totalReleasable.add(tmp.amount);
				delete lockDetail[benefit][i];
			}
		}
		if(totalReleasable == 0) return;
		balances[benefit] = balances[benefit].add(totalReleasable);
		transferLocked[benefit] = transferLocked[benefit].sub(totalReleasable);
		if(transferLocked[benefit] == 0)
		delete lockDetail[benefit];
		emit ReleaseToken(benefit, totalReleasable);

	}

	/**
     * @dev Calculates the amount of releasable tokens.
     */
	function releasableTokens(address benefit) public view returns(uint256) {
		uint256 len = lockDetail[benefit].length;
		if( len == 0) return 0;
		uint256 releasable = 0;
		for(uint256 i = 0; i < len; i = i.add(1)){
			LockInfo memory tmp = lockDetail[benefit][i];
			if(tmp.releaseTime != 0 && now >= tmp.releaseTime){
				releasable = releasable.add(tmp.amount);
			}
		}	
		return releasable;	
	}
}

contract Token is LockableToken, BurnableToken {
    string public name; // name of Token
    string public symbol; // symbol of Token
    uint8 public decimals;

    constructor(string memory _name, string memory _symbol, uint8 _decimals) public {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
    }
}

contract IOAEXBDR is Token {
    struct Trx {
        bool executed;
        address from;
        uint256 value;
        address[] signers;
    }

    mapping(address => bool) public isSigner;
    mapping(uint256 => Trx) public exchangeTrx;
    address public AbcInstance;  // address of AbcToken
    uint256 public requestSigners = 2;  // BDR => Abc need signers number
    uint256 public applyCounts = 0;  // Sequence of exchange request
    mapping(address => uint256) public exchangeLock;

    event SetSigner(address indexed signer,bool isSigner);  // emit when add/remove signer
    event ApplyExchangeToken(address indexed from,uint256 value,uint256 trxSeq);  // emit when exchange successful
    event ConfirmTrx(address indexed signer,uint256 indexed trxSeq);  // emit when signer confirmed exchange request
    event CancleConfirmTrx(address indexed signer,uint256 indexed trxSeq);  // emit when signer cancles confirmed exchange request
    event CancleExchangeRequest(address indexed signer,uint256 indexed trxSeq);  // emit when signer/requester cancles exchange request
    event TokenExchange(address indexed from,uint256 value,bool AbcExchangeBDR); // emit when Abc <=> Bdr,true:Abc => BDR,false:BDR => abc
    event Mint(address indexed target,uint256 value);

    modifier onlySigner() {
        require(isSigner[msg.sender]);
        _;
    }
    /**
     * @dev initialize token info
     * @param _name string The name of token
     * @param _symbol string The symbol of token
     * @param _decimals uint8 The decimals of token
     */
    constructor(string memory _name, string memory _symbol, uint8 _decimals) Token(_name,_symbol,_decimals) public {
    }

    /**
     * @dev rewrite transfer function，user can't transfer token to AbcToken's address directly
     */
    function transfer(address _to,uint256 _value) public returns (bool success) {
        require(_to != AbcInstance,"can't transfer to AbcToken address directly");
        return super.transfer(_to,_value);
    }

    /**
     * @dev rewrite transferFrom function，user can't transfer token to AbcToken's address directly
     */
    function transferFrom(address _from, address _to,uint256 _value) public returns (bool success) {
        require(_to != AbcInstance,"can't transfer to AbcToken address directly");
        return super.transferFrom(_from,_to,_value);
    }

    /**
     * @dev rewrite transferAndLock function，user can't transfer token to AbcToken's address directly
     */
    function transferAndLock(address _to, uint256 _value, uint256 _lockdays) public returns (bool success) {
        require(_to != AbcInstance,"can't transfer to AbcToken address directly");
        return super.transferAndLock(_to,_value,_lockdays);
    }   

    /**
     * @dev set AbcToken's address
     */
    function setAbcInstance(address _abc) public onlyOwner {
        require(_abc != address(0));
        AbcInstance = _abc;
    }

    /**
     * @dev add/remove signers
     * @param _signers address[] The array of signers to add/remove
     * @param _addSigner bool true:add signers,false:remove:signers
     */
    function setSigners(address[] memory _signers,bool _addSigner) public onlyOwner {
        for(uint256 i = 0;i< _signers.length;i++){
            require(_signers[i] != address(0));
            isSigner[_signers[i]] = _addSigner;
            emit SetSigner(_signers[i],_addSigner);
        }
    }

    /**
     * @dev set the number of exchange request in order to execute
     * @param _requestSigners uint256 The number of signers
     */
    function setrequestSigners(uint256 _requestSigners) public onlyOwner {
        require(_requestSigners != 0);
        requestSigners = _requestSigners;
    }

    /**
     * @dev check whether the signer confirmed this exchange request
     */
    function isConfirmer(uint256 _trxSeq,address _signer) public view returns (bool) {
        require(exchangeTrx[_trxSeq].from != address(0),"trxSeq not exist");
        for(uint256 i = 0;i < exchangeTrx[_trxSeq].signers.length;i++){
            if(exchangeTrx[_trxSeq].signers[i] == _signer){
                return true;
            }
        }
        return false;
    }

    /**
     * @dev get how many signers that confirmed this exchange request
     */
    function getConfirmersLengthOfTrx(uint256 _trxSeq) public view returns (uint256) {
        return exchangeTrx[_trxSeq].signers.length;
    }

    /**
     * @dev get signers's address that confirmed this exchange request
     * @param _trxSeq uint256 the Sequence of exchange request
     * @param _index uint256 the index of signers
     */
    function getConfirmerOfTrx(uint256 _trxSeq,uint256 _index) public view returns (address) {
        require(_index < getConfirmersLengthOfTrx(_trxSeq),"out of range");
        return exchangeTrx[_trxSeq].signers[_index];
    }

    /**
     * @dev apply BDR exchange Abc
     * @param _value uint256 amount of BDR to exchange
     * @return uint256 the sequence of exchange request
     */
    function applyExchangeToken(uint256 _value) public whenNotPaused returns (uint256) {
        uint256 trxSeq = applyCounts;
        require(exchangeTrx[trxSeq].from == address(0),"trxSeq already exist");
        require(balances[msg.sender] >= _value);
        exchangeTrx[trxSeq].executed = false;
        exchangeTrx[trxSeq].from = msg.sender;
        exchangeTrx[trxSeq].value = _value;
        applyCounts = applyCounts.add(1);
        balances[address(this)] = balances[address(this)].add(_value);
        balances[exchangeTrx[trxSeq].from] = balances[exchangeTrx[trxSeq].from].sub(_value);
        exchangeLock[exchangeTrx[trxSeq].from] = exchangeLock[exchangeTrx[trxSeq].from].add(_value);
        emit ApplyExchangeToken(exchangeTrx[trxSeq].from,exchangeTrx[trxSeq].value,trxSeq);
        emit Transfer(msg.sender,address(this),_value);
        return trxSeq;
    }

    /**
     * @dev signer confirms one exchange request
     * @param _trxSeq uint256 the Sequence of exchange request
     */
    function confirmExchangeTrx(uint256 _trxSeq) public onlySigner {
        require(exchangeTrx[_trxSeq].from != address(0),"_trxSeq not exist");
        require(exchangeTrx[_trxSeq].signers.length < requestSigners,"trx already has enough signers");
        require(exchangeTrx[_trxSeq].executed == false,"trx already executed");
        require(isConfirmer(_trxSeq, msg.sender) == false,"signer already confirmed");
        exchangeTrx[_trxSeq].signers.push(msg.sender);
        emit ConfirmTrx(msg.sender, _trxSeq);
    }

    /**
     * @dev signer cancel confirmed exchange request
     * @param _trxSeq uint256 the Sequence of exchange request
     */
    function cancelConfirm(uint256 _trxSeq) public onlySigner {
        require(exchangeTrx[_trxSeq].from != address(0),"_trxSeq not exist");
        require(isConfirmer(_trxSeq, msg.sender),"Signer didn't confirm");
        require(exchangeTrx[_trxSeq].executed == false,"trx already executed");
        uint256 len = exchangeTrx[_trxSeq].signers.length;
        for(uint256 i = 0;i < len;i++){
            if(exchangeTrx[_trxSeq].signers[i] == msg.sender){
                exchangeTrx[_trxSeq].signers[i] = exchangeTrx[_trxSeq].signers[len.sub(1)] ;
                exchangeTrx[_trxSeq].signers.length --;
                break;
            }
        }
        emit CancleConfirmTrx(msg.sender,_trxSeq);
    }

    /**
     * @dev signer cancel exchange request
     * @param _trxSeq uint256 the Sequence of exchange request
     */
    function cancleExchangeRequest(uint256 _trxSeq) public {
        require(exchangeTrx[_trxSeq].from != address(0),"_trxSeq not exist");
        require(exchangeTrx[_trxSeq].executed == false,"trx already executed");
        require(isSigner[msg.sender] || exchangeTrx[_trxSeq].from == msg.sender);
        balances[address(this)] = balances[address(this)].sub(exchangeTrx[_trxSeq].value);
        balances[exchangeTrx[_trxSeq].from] = balances[exchangeTrx[_trxSeq].from].add(exchangeTrx[_trxSeq].value);
        exchangeLock[exchangeTrx[_trxSeq].from] = exchangeLock[exchangeTrx[_trxSeq].from].sub(exchangeTrx[_trxSeq].value);
        delete exchangeTrx[_trxSeq];
        emit CancleExchangeRequest(msg.sender,_trxSeq);
        emit Transfer(address(this),exchangeTrx[_trxSeq].from,exchangeTrx[_trxSeq].value);
    }

    /**
     * @dev execute exchange request which confirmed by enough signers
     * @param _trxSeq uint256 the Sequence of exchange request
     */
    function executeExchangeTrx(uint256 _trxSeq) public whenNotPaused{
        address from = exchangeTrx[_trxSeq].from;
        uint256 value = exchangeTrx[_trxSeq].value;
        require(from != address(0),"trxSeq not exist");
        require(exchangeTrx[_trxSeq].executed == false,"trxSeq has executed");
        require(exchangeTrx[_trxSeq].signers.length >= requestSigners);
        require(from == msg.sender|| isSigner[msg.sender]);
        require(value <= balances[address(this)]);
        _burn(address(this), value);
        exchangeLock[from] = exchangeLock[from].sub(value);
        exchangeTrx[_trxSeq].executed = true;
        AbcInterface(AbcInstance).tokenFallback(from,value,bytes(""));
        emit TokenExchange(exchangeTrx[_trxSeq].from,exchangeTrx[_trxSeq].value,false);
    }

    /**
     * @dev exchange Abc token to BDR token,only AbcInstance can invoke this function
     */
    function tokenFallback(address _from, uint _value, bytes memory) public {
        require(msg.sender == AbcInstance);
        require(_from != address(0));
        require(_value > 0);
        uint256 exchangeAmount = _value.mul(10**uint256(decimals)).div(10**uint256(AbcInterface(AbcInstance).decimals()));
        _mint(_from, exchangeAmount);
        emit Transfer(address(0x00),_from,exchangeAmount);
        emit TokenExchange(_from,_value,true);
    }

    /**
     * @dev mint BDR token
     */
    function _mint(address target, uint256 value ) internal {
        balances[target] = balances[target].add(value);
        totalSupply = totalSupply.add(value);
        emit Mint(target,value);
    }
}