contract RewardsMinter is Claimable, NoOwner {
    using SafeMath for uint256;

    struct MintProposal {
        address beneficiary;    //Who will receive tokens
        uint256 amount;         //How much tokens will be minted
        mapping(address => bool) signers;   //Who already signed
        uint256 weight;         //Current proposal weight
        bool minted;            //If tokens where already minted for this proposal
    }

    RewardsToken public token;
    mapping(address => uint256) public signers;     //Mapping of proposal signer to his weight
    uint256 public requiredWeight;                  //Weight required for proposal to be confirmed

    MintProposal[] public mintProposals;

    event SignerWeightChange(address indexed signer, uint256 oldWeight, uint256 newWeight);
    event RequiredWeightChange(uint256 oldWeight, uint256 newWeight);
    event MintProposalCreated(uint256 proposalId, address indexed beneficiary, uint256 amount);
    event MintProposalApproved(uint256 proposalId, address indexed signer);
    event MintProposalCompleted(uint256 proposalId, address indexed beneficiary, uint256 amount);

    modifier onlySigner(){
        require(signers[msg.sender] > 0 );
        _;
    }

    constructor(address _token, uint256 _requiredWeight, uint256 _ownerWeight) public {
        if(_token == 0x0){
            token = new RewardsToken();
            token.setWhitelisted(address(this), true);
            token.setWhitelisted(msg.sender, true);
            token.pause();
        }else{
            token = RewardsToken(_token);
        }

        requiredWeight = _requiredWeight;         //Requires at least one signer for proposal
        signers[owner] = _ownerWeight;    //makes owner also the signer
        emit SignerWeightChange(owner, 0, _ownerWeight);
    }

    function mintProposalCount() view public returns(uint256){
        return mintProposals.length;
    }

    /**
    * @notice Add/Remove/Change signer
    */
    function setSignerWeight(address signer, uint256 weight) onlyOwner external {
        emit SignerWeightChange(signer, signers[signer], weight);
        signers[signer] = weight;
    }
    function setRequiredWeight(uint256 weight) onlyOwner external {
        requiredWeight = weight;
    }

    /**
    * @notice Create new proposal and vote for it
    */
    function createProposal(address _beneficiary, uint256 _amount) onlySigner external returns(uint256){
        uint256 idx = mintProposals.length++;
        mintProposals[idx].beneficiary = _beneficiary;
        mintProposals[idx].amount = _amount;
        mintProposals[idx].minted = false;
        mintProposals[idx].signers[msg.sender] = true;
        mintProposals[idx].weight = signers[msg.sender];
        emit MintProposalCreated(idx, _beneficiary, _amount);
        emit MintProposalApproved(idx, msg.sender);
        mintIfWeightEnough(idx);
        return idx;
    }

    /**
    * @notice Create new proposal and vote for it
    */
    function approveProposal(uint256 idx, address _beneficiary, uint256 _amount) onlySigner external {
        require(mintProposals[idx].beneficiary == _beneficiary);
        require(mintProposals[idx].amount == _amount);
        require(mintProposals[idx].signers[msg.sender] == false);
        mintProposals[idx].signers[msg.sender] = true;
        mintProposals[idx].weight = mintProposals[idx].weight.add(signers[msg.sender]);
        emit MintProposalApproved(idx, msg.sender);
        mintIfWeightEnough(idx);
    }

    /**
    * @dev Check current proposal weight and mint if ready
    */
    function mintIfWeightEnough(uint256 idx) internal {
        if(mintProposals[idx].weight >= requiredWeight && !mintProposals[idx].minted){
            mint(mintProposals[idx].beneficiary, mintProposals[idx].amount);
            mintProposals[idx].minted = true;
            emit MintProposalCompleted(idx, mintProposals[idx].beneficiary, mintProposals[idx].amount);
        }
    }

    /**
    * @dev Function to mint tokens
    * @param _to The address that will receive the minted tokens.
    * @param _amount The amount of tokens to mint.
    * @return A boolean that indicates if the operation was successful.
    */
    function mint(address _to, uint256 _amount) internal returns (bool){
        return token.mint(_to, _amount);
    }


    //Token management
    function tokenPause() onlyOwner public {
        token.pause();
    }
    function tokenUnpause() onlyOwner public {
        token.unpause();
    }
    function tokenSetWhitelisted(address who, bool allowTransfers) onlyOwner public {
        token.setWhitelisted(who, allowTransfers);
    }
    function tokenRevoke(address _from, uint256 _value) onlyOwner public {
        token.revoke(_from, _value);
    }
    function tokenFinishMinting() onlyOwner public {
        token.finishMinting();
    }
}
function mint(address _to, uint256 _amount) internal returns (bool){
    return token.mint(_to, _amount);
}

/**
 * @dev Function to mint tokens
 * @param _to The address that will receive the minted tokens.
 * @param _amount The amount of tokens to mint.
 * @return A boolean that indicates if the operation was successful.
 */