contract NewDecentBetToken is ERC20, SafeMath {

    // Token information
    bool public isDecentBetToken;

    string public constant name = "Decent.Bet Token";

    string public constant symbol = "DBET";

    uint256 public constant decimals = 18;  // decimal places

    uint256 public constant housePercentOfTotal = 10;

    uint256 public constant vaultPercentOfTotal = 18;

    uint256 public constant bountyPercentOfTotal = 2;

    uint256 public constant crowdfundPercentOfTotal = 70;

    // flag to determine if address is for a real contract or not
    bool public isNewToken = false;

    // Token information
    mapping (address => uint256) balances;

    mapping (address => mapping (address => uint256)) allowed;

    // Upgrade information
    NewUpgradeAgent public upgradeAgent;

    NextUpgradeAgent public nextUpgradeAgent;

    bool public finalizedNextUpgrade = false;

    address public nextUpgradeMaster;

    event Upgrade(address indexed _from, address indexed _to, uint256 _value);

    event UpgradeFinalized(address sender, address nextUpgradeAgent);

    event UpgradeAgentSet(address agent);

    uint256 public totalUpgraded;

    // Old Token Information
    OldToken public oldToken;

    address public decentBetMultisig;

    uint256 public oldTokenTotalSupply;

    NewDecentBetVault public timeVault;

    function NewDecentBetToken(address _upgradeAgent,
    address _oldToken, address _nextUpgradeMaster) public {

        isNewToken = true;

        isDecentBetToken = true;

        if (_upgradeAgent == 0x0) revert();
        upgradeAgent = NewUpgradeAgent(_upgradeAgent);

        if (_nextUpgradeMaster == 0x0) revert();
        nextUpgradeMaster = _nextUpgradeMaster;

        oldToken = OldToken(_oldToken);
        if (!oldToken.isDecentBetToken()) revert();
        oldTokenTotalSupply = oldToken.totalSupply();

        decentBetMultisig = oldToken.decentBetMultisig();
        if (!MultiSigWallet(decentBetMultisig).isMultiSigWallet()) revert();

        timeVault = new NewDecentBetVault(decentBetMultisig);
        if (!timeVault.isDecentBetVault()) revert();

        // Founder's supply : 18% of total goes to vault, time locked for 1 year
        uint256 vaultTokens = safeDiv(safeMul(oldTokenTotalSupply, vaultPercentOfTotal),
        crowdfundPercentOfTotal);
        balances[timeVault] = safeAdd(balances[timeVault], vaultTokens);
        Transfer(0, timeVault, vaultTokens);

        // House: 10% of total goes to Decent.bet for initial house setup
        uint256 houseTokens = safeDiv(safeMul(oldTokenTotalSupply, housePercentOfTotal),
        crowdfundPercentOfTotal);
        balances[decentBetMultisig] = safeAdd(balances[decentBetMultisig], houseTokens);
        Transfer(0, decentBetMultisig, houseTokens);

        // Bounties: 2% of total goes to Decent bet for bounties
        uint256 bountyTokens = safeDiv(safeMul(oldTokenTotalSupply, bountyPercentOfTotal),
        crowdfundPercentOfTotal);
        balances[decentBetMultisig] = safeAdd(balances[decentBetMultisig], bountyTokens);
        Transfer(0, decentBetMultisig, bountyTokens);

        totalSupply = safeAdd(safeAdd(vaultTokens, houseTokens), bountyTokens);
    }

    // Upgrade-related methods
    function createToken(address _target, uint256 _amount) public {
        if (msg.sender != address(upgradeAgent)) revert();
        if (_amount == 0) revert();

        balances[_target] = safeAdd(balances[_target], _amount);
        totalSupply = safeAdd(totalSupply, _amount);
        Transfer(_target, _target, _amount);
    }

    // ERC20 interface: transfer _value new tokens from msg.sender to _to
    function transfer(address _to, uint256 _value) returns (bool success) {
        if (_to == 0x0) revert();
        if (_to == address(upgradeAgent)) revert();
        if (_to == address(this)) revert();
        //if (_to == address(UpgradeAgent(upgradeAgent).oldToken())) revert();
        if (balances[msg.sender] >= _value && _value > 0) {
            balances[msg.sender] = safeSub(balances[msg.sender], _value);
            balances[_to] = safeAdd(balances[_to], _value);
            Transfer(msg.sender, _to, _value);
            return true;
        }
        else {return false;}
    }

    // ERC20 interface: transfer _value new tokens from _from to _to
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        if (_to == 0x0) revert();
        if (_to == address(upgradeAgent)) revert();
        if (_to == address(this)) revert();
        //if (_to == address(UpgradeAgent(upgradeAgent).oldToken())) revert();
        if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value) {
            balances[_to] = safeAdd(balances[_to], _value);
            balances[_from] = safeSub(balances[_from], _value);
            allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender], _value);
            Transfer(_from, _to, _value);
            return true;
        }
        else {return false;}
    }

    // ERC20 interface: delegate transfer rights of up to _value new tokens from
    // msg.sender to _spender
    function approve(address _spender, uint256 _value) returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }

    // ERC20 interface: returns the amount of new tokens belonging to _owner
    // that _spender can spend via transferFrom
    function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
        return allowed[_owner][_spender];
    }

    // ERC20 interface: returns the wmount of new tokens belonging to _owner
    function balanceOf(address _owner) constant returns (uint256 balance) {
        return balances[_owner];
    }

    // Token upgrade functionality

    /// @notice Upgrade tokens to the new token contract.
    /// @param value The number of tokens to upgrade
    function upgrade(uint256 value) external {
        if (nextUpgradeAgent.owner() == 0x0) revert();
        // need a real upgradeAgent address
        if (finalizedNextUpgrade) revert();
        // cannot upgrade if finalized

        // Validate input value.
        if (value == 0) revert();
        if (value > balances[msg.sender]) revert();

        // update the balances here first before calling out (reentrancy)
        balances[msg.sender] = safeSub(balances[msg.sender], value);
        totalSupply = safeSub(totalSupply, value);
        totalUpgraded = safeAdd(totalUpgraded, value);
        nextUpgradeAgent.upgradeFrom(msg.sender, value);
        Upgrade(msg.sender, nextUpgradeAgent, value);
    }

    /// @notice Set address of next upgrade target contract and enable upgrade
    /// process.
    /// @param agent The address of the UpgradeAgent contract
    function setNextUpgradeAgent(address agent) external {
        if (agent == 0x0) revert();
        // don't set agent to nothing
        if (msg.sender != nextUpgradeMaster) revert();
        // Only a master can designate the next agent
        nextUpgradeAgent = NextUpgradeAgent(agent);
        if (!nextUpgradeAgent.isUpgradeAgent()) revert();
        nextUpgradeAgent.setOriginalSupply();
        UpgradeAgentSet(nextUpgradeAgent);
    }

    /// @notice Set address of next upgrade master and enable upgrade
    /// process.
    /// @param master The address that will manage upgrades, not the upgradeAgent contract address
    function setNextUpgradeMaster(address master) external {
        if (master == 0x0) revert();
        if (msg.sender != nextUpgradeMaster) revert();
        // Only a master can designate the next master
        nextUpgradeMaster = master;
    }

    /// @notice finalize the upgrade
    /// @dev Required state: Success
    function finalizeNextUpgrade() external {
        if (nextUpgradeAgent.owner() == 0x0) revert();
        // we need a valid upgrade agent
        if (msg.sender != nextUpgradeMaster) revert();
        // only upgradeMaster can finalize
        if (finalizedNextUpgrade) revert();
        // can't finalize twice

        finalizedNextUpgrade = true;
        // prevent future upgrades

        nextUpgradeAgent.finalizeUpgrade();
        // call finalize upgrade on new contract
        UpgradeFinalized(msg.sender, nextUpgradeAgent);
    }

    /// @dev Fallback function throws to avoid accidentally losing money
    function() {revert();}
}
function balanceOf(address _owner) constant returns (uint256 balance) {
    return balances[_owner];
}

// ERC20 interface: returns the wmount of new tokens belonging to _owner