contract SponseeTokenModel is StandardToken {

    string public name;
    string public symbol;
    uint8 public decimals = 0;
    uint public totalSupply = 0;
    uint public cap = 100000000;                    // maximum cap = 1 000 000 $ = 100 000 000 tokens
    uint public minimumSupport = 500;               // minimum support is 5$(500 cents)
    uint public etherRatioForInvestor = 10;         // etherRatio (10%) to send ether to investor
    address public sponseeAddress;
    bool public isPayableEnabled = true;
    RBInformationStore public rbInformationStore;
    Rate public rate;

    event LogReceivedEther(address indexed from, address indexed to, uint etherValue, string tokenName);
    event LogBuy(address indexed from, address indexed to, uint indexed value, uint paymentId);
    event LogRollbackTransfer(address indexed from, address indexed to, uint value);
    event LogExchange(address indexed from, address indexed token, uint value);
    event LogIncreaseCap(uint value);
    event LogDecreaseCap(uint value);
    event LogSetRBInformationStoreAddress(address indexed to);
    event LogSetName(string name);
    event LogSetSymbol(string symbol);
    event LogMint(address indexed to, uint value);
    event LogChangeSponseeAddress(address indexed to);
    event LogChangeIsPayableEnabled(bool flag);

    modifier onlyAccountAddressForSponsee() {
        require(rbInformationStore.accountAddressForSponsee() == msg.sender);
        _;
    }

    modifier onlyMultiSig() {
        require(rbInformationStore.multiSigAddress() == msg.sender);
        _;
    }

    // constructor
    function SponseeTokenModel(
        string _name,
        string _symbol,
        address _rbInformationStoreAddress,
        address _rateAddress,
        address _sponsee
    ) {
        name = _name;
        symbol = _symbol;
        rbInformationStore = RBInformationStore(_rbInformationStoreAddress);
        rate = Rate(_rateAddress);
        sponseeAddress = _sponsee;
    }

    /**
    @notice Receive ether from any EOA accounts. Amount of ether received in this function is distributed to 3 parts.
    One is a profitContainerAddress which is address of containerWallet to dividend to investor of Boost token.
    Another is an ownerAddress which is address of owner of REALBOOST site.
    The other is an sponseeAddress which is address of owner of this contract.
    Then, return token of this contract to msg.sender related to the amount of ether that msg.sender sent and rate (US cent) of ehter stored in Rate contract.
    */
    function() payable {

        // check condition
        require(isPayableEnabled && rbInformationStore.isPayableEnabledForAll());

        // check validation
        if (msg.value <= 0) { revert(); }

        // calculate support amount in US
        uint supportedAmount = msg.value.mul(rate.ETH_USD_rate()).div(10**18);

        // if support is less than minimum => return money to supporter
        if (supportedAmount < minimumSupport) { revert(); }

        // calculate the ratio of Ether for distribution
        uint etherRatioForOwner = rbInformationStore.etherRatioForOwner();
        uint etherRatioForSponsee = uint(100).sub(etherRatioForOwner).sub(etherRatioForInvestor);

        /* divide Ether */
        // calculate
        uint etherForOwner = msg.value.mul(etherRatioForOwner).div(100);
        uint etherForInvestor = msg.value.mul(etherRatioForInvestor).div(100);
        uint etherForSponsee = msg.value.mul(etherRatioForSponsee).div(100);

        // get address
        address profitContainerAddress = rbInformationStore.profitContainerAddress();
        address companyWalletAddress = rbInformationStore.companyWalletAddress();

        // send Ether
        if (!profitContainerAddress.send(etherForInvestor)) { revert(); }
        if (!companyWalletAddress.send(etherForOwner)) { revert(); }
        if (!sponseeAddress.send(etherForSponsee)) { revert(); }

        // token amount is transfered to sender
        // 1 token = 1 cent, 1 usd = 100 cents
        uint tokenAmount = msg.value.mul(rate.ETH_USD_rate()).div(10**18);

        // add tokens
        balances[msg.sender] = balances[msg.sender].add(tokenAmount);

        // increase total supply
        totalSupply = totalSupply.add(tokenAmount);

        // check cap
        if (totalSupply > cap) { revert(); }

        // send Event
        LogExchange(msg.sender, this, tokenAmount);
        LogReceivedEther(msg.sender, this, msg.value, name);
        Transfer(address(0x0), msg.sender, tokenAmount);
    }

    /**
    @notice Change rbInformationStoreAddress.
    @param _address The address of new rbInformationStore
    */
    function setRBInformationStoreAddress(address _address) onlyMultiSig {

        rbInformationStore = RBInformationStore(_address);

        // send Event
        LogSetRBInformationStoreAddress(_address);
    }

    /**
    @notice Change name.
    @param _name The new name of token
    */
    function setName(string _name) onlyAccountAddressForSponsee {

        name = _name;

        // send Event
        LogSetName(_name);
    }

    /**
    @notice Change symbol.
    @param _symbol The new symbol of token
    */
    function setSymbol(string _symbol) onlyAccountAddressForSponsee {

        symbol = _symbol;

        // send Event
        LogSetSymbol(_symbol);
    }

    /**
    @notice Mint new token amount.
    @param _address The address that new token amount is added
    @param _value The new amount of token
    */
    function mint(address _address, uint _value) onlyAccountAddressForSponsee {

        // add tokens
        balances[_address] = balances[_address].add(_value);

        // increase total supply
        totalSupply = totalSupply.add(_value);

        // check cap
        if (totalSupply > cap) { revert(); }

        // send Event
        LogMint(_address, _value);
        Transfer(address(0x0), _address, _value);
    }

    /**
    @notice Increase cap.
    @param _value The amount of token that should be increased
    */
    function increaseCap(uint _value) onlyAccountAddressForSponsee {

        // change cap here
        cap = cap.add(_value);

        // send Event
        LogIncreaseCap(_value);
    }

    /**
    @notice Decrease cap.
    @param _value The amount of token that should be decreased
    */
    function decreaseCap(uint _value) onlyAccountAddressForSponsee {

        // check whether cap is lower than totalSupply or not
        if (totalSupply > cap.sub(_value)) { revert(); }

        // change cap here
        cap = cap.sub(_value);

        // send Event
        LogDecreaseCap(_value);
    }

    /**
    @notice Rollback transfer.
    @param _from The EOA address for rollback transfer
    @param _to The EOA address for rollback transfer
    @param _value The number of token for rollback transfer
    */
    function rollbackTransfer(address _from, address _to, uint _value) onlyPayloadSize(3 * 32) onlyMultiSig {

        balances[_to] = balances[_to].sub(_value);
        balances[_from] = balances[_from].add(_value);

        // send Event
        LogRollbackTransfer(_from, _to, _value);
        Transfer(_from, _to, _value);
    }

    /**
    @notice Transfer from msg.sender for downloading of content.
    @param _to The EOA address for buy content
    @param _value The number of token for buy content
    @param _paymentId The id of content which msg.sender want to buy
    */
    function buy(address _to, uint _value, uint _paymentId) {

        transfer(_to, _value);

        // send Event
        LogBuy(msg.sender, _to, _value, _paymentId);
    }

    /**
    @notice This method will change old sponsee address with a new one.
    @param _newAddress new address is set
    */
    function changeSponseeAddress(address _newAddress) onlyAccountAddressForSponsee {

        sponseeAddress = _newAddress;

        // send Event
        LogChangeSponseeAddress(_newAddress);
    }

    /**
    @notice This method will change isPayableEnabled flag.
    */
    function changeIsPayableEnabled() onlyMultiSig {

        isPayableEnabled = !isPayableEnabled;

        // send Event
        LogChangeIsPayableEnabled(isPayableEnabled);
    }
}
/**
 @title SponseeTokenModel
 */
function decreaseCap(uint _value) onlyAccountAddressForSponsee {

    // check whether cap is lower than totalSupply or not
    if (totalSupply > cap.sub(_value)) { revert(); }

    // change cap here
    cap = cap.sub(_value);

    // send Event
    LogDecreaseCap(_value);
}

/**
 @notice Decrease cap.
 @param _value The amount of token that should be decreased
 */