contract TokenERC20 is RunningContractManager {
    using SafeMath for uint256;

    // Public variables of the token
    string public name;
    string public symbol;
    uint8 public decimals = 18;    // 18 decimals is the strongly suggested default, avoid changing it
    uint256 public totalSupply;

    /* This creates an array with all balances */
    mapping (address => uint256) internal balances;
    mapping (address => mapping (address => uint256)) internal allowed;
    //mapping (address => bool) public frozenAccount; // SmartDec Recommendations
    mapping (address => uint256) public frozenExpired;

    //bool private initialized = false;
    bool private initialized; // SmartDec Recommendations

    /**
     * This is area for some variables to add.
     * Please add variables from the end of pre-declared variables
     * if you would have added some variables and re-deployed the contract,
     * tokenPerEth would get garbage value. so please reset tokenPerEth variable
     *
     * uint256 something..;
     */


    // This generates a public event on the blockchain that will notify clients
    event Transfer(address indexed from, address indexed to, uint256 value);
    event LastBalance(address indexed account, uint256 value);

    // This notifies clients about the allowance of balance
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    // This notifies clients about the amount burnt
    // event Burn(address indexed from, uint256 value); // callisto recommendation

    // This notifies clients about the freezing address
    //event FrozenFunds(address target, bool frozen); // callisto recommendation
    event FrozenFunds(address target, uint256 expirationDate); // SmartDec Recommendations

    /**
     * Initialize Token Function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */

    function initToken(
        string memory _tokenName,
        string memory _tokenSymbol,
        uint256 _initialSupply,
        address _marketSaleManager,
        address _serviceOperationManager,
        address _dividendManager,
        address _incentiveManager,
        address _reserveFundManager
    ) internal onlyOwner committeeApproved {
        require( initialized == false );
        require(_initialSupply > 0 && _initialSupply <= 2**uint256(184)); // [2019.03.05] Fixed for Mythril Vulerablity SWC ID:101 => _initialSupply <= 2^184 <= (2^256 / 10^18)

        name = _tokenName;                                       // Set the name for display purposes
        symbol = _tokenSymbol;                                   // Set the symbol for display purposes
        //totalSupply = convertToDecimalUnits(_initialSupply);     // Update total supply with the decimal amount

        /*balances[msg.sender] = totalSupply;                     // Give the creator all initial tokens
        emit Transfer(address(this), address(0), totalSupply);
        emit LastBalance(address(this), 0);
        emit LastBalance(msg.sender, totalSupply);*/

        // SmartDec Recommendations
        uint256 tempSupply = convertToDecimalUnits(_initialSupply);

        uint256 dividendBalance = tempSupply.div(10);               // dividendBalance = 10%
        uint256 reserveFundBalance = dividendBalance;               // reserveFundBalance = 10%
        uint256 marketSaleBalance = tempSupply.div(5);              // marketSaleBalance = 20%
        uint256 serviceOperationBalance = marketSaleBalance.mul(2); // serviceOperationBalance = 40%
        uint256 incentiveBalance = marketSaleBalance;               // incentiveBalance = 20%

        balances[_marketSaleManager] = marketSaleBalance;
        balances[_serviceOperationManager] = serviceOperationBalance;
        balances[_dividendManager] = dividendBalance;
        balances[_incentiveManager] = incentiveBalance;
        balances[_reserveFundManager] = reserveFundBalance;

        totalSupply = tempSupply;

        emit Transfer(address(0), _marketSaleManager, marketSaleBalance);
        emit Transfer(address(0), _serviceOperationManager, serviceOperationBalance);
        emit Transfer(address(0), _dividendManager, dividendBalance);
        emit Transfer(address(0), _incentiveManager, incentiveBalance);
        emit Transfer(address(0), _reserveFundManager, reserveFundBalance);

        emit LastBalance(address(this), 0);
        emit LastBalance(_marketSaleManager, marketSaleBalance);
        emit LastBalance(_serviceOperationManager, serviceOperationBalance);
        emit LastBalance(_dividendManager, dividendBalance);
        emit LastBalance(_incentiveManager, incentiveBalance);
        emit LastBalance(_reserveFundManager, reserveFundBalance);

        assert( tempSupply ==
          marketSaleBalance.add(serviceOperationBalance).
                            add(dividendBalance).
                            add(incentiveBalance).
                            add(reserveFundBalance)
        );


        initialized = true;
    }


    /**
     * Convert tokens units to token decimal units
     *
     * @param _value Tokens units without decimal units
     */
    function convertToDecimalUnits(uint256 _value) internal view returns (uint256 value) {
        value = _value.mul(10 ** uint256(decimals));
        return value;
    }

    /**
     * Get tokens balance
     *
     * @notice Query tokens balance of the _account
     *
     * @param _account Account address to query tokens balance
     */
    function balanceOf(address _account) public view returns (uint256 balance) {
        balance = balances[_account];
        return balance;
    }

    /**
     * Get allowed tokens balance
     *
     * @notice Query tokens balance allowed to _spender
     *
     * @param _owner Owner address to query tokens balance
     * @param _spender The address allowed tokens balance
     */
    function allowance(address _owner, address _spender) external view returns (uint256 remaining) {
        remaining = allowed[_owner][_spender];
        return remaining;
    }

    /**
     * Internal transfer, only can be called by this contract
     */
    function _transfer(address _from, address _to, uint256 _value) internal {
        require(_to != address(0x0));                                            // Prevent transfer to 0x0 address.
        require(balances[_from] >= _value);                             // Check if the sender has enough
        if(frozenExpired[_from] != 0 ){                                 // Check if sender is frozen
            require(block.timestamp > frozenExpired[_from]);
            _unfreezeAccount(_from);
        }
        if(frozenExpired[_to] != 0 ){                                   // Check if recipient is frozen
            require(block.timestamp > frozenExpired[_to]);
            _unfreezeAccount(_to);
        }

        uint256 previousBalances = balances[_from].add(balances[_to]);  // Save this for an assertion in the future

        balances[_from] = balances[_from].sub(_value);                  // Subtract from the sender
        balances[_to] = balances[_to].add(_value);                      // Add the same to the recipient
        emit Transfer(_from, _to, _value);
        emit LastBalance(_from, balances[_from]);
        emit LastBalance(_to, balances[_to]);

        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balances[_from] + balances[_to] == previousBalances);
    }

    /**
     * Transfer tokens
     *
     * @notice Send `_value` tokens to `_to` from your account
     *
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transfer(address _to, uint256 _value) public noReentrancy returns (bool success) {
        _transfer(msg.sender, _to, _value);
        success = true;
        return success;
    }


    /**
     * Transfer tokens from other address
     *
     * Send `_value` tokens to `_to` on behalf of `_from`
     *
     * @param _from The address of the sender
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transferFrom(address _from, address _to, uint256 _value) public noReentrancy returns (bool success) {
        require(_value <= allowed[_from][msg.sender]);     // Check allowance
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        _transfer(_from, _to, _value);
        success = true;
        return success;
    }

    /**
     * Internal approve, only can be called by this contract
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function _approve(address _spender, uint256 _value) internal returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        success = true;
        return success;
    }

    /**
     * Set allowance for other address
     *
     * Allows `_spender` to spend no more than `_value` tokens on your behalf
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public noReentrancy returns (bool success) {
        success = _approve(_spender, _value);
        return success;
    }


    /**
     * @dev Increase the amount 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 amount 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 amount 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 amount 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;
    }




    /// @notice `freeze? Prevent` `target` from sending & receiving tokens
    /// @param target Address to be frozen
    function freezeAccount(address target, uint256 freezeExpiration) onlyOwner committeeApproved public {
        frozenExpired[target] = freezeExpiration;
        //emit FrozenFunds(target, true);
        emit FrozenFunds(target, freezeExpiration); // SmartDec Recommendations
    }

    /// @notice  `freeze? Allow` `target` from sending & receiving tokens
    /// @notice if expiration date was over, when this is called with transfer transaction, auto-unfreeze is occurred without committeeApproved
    ///         the reason why it's separated from wrapper function.
    /// @param target Address to be unfrozen
    function _unfreezeAccount(address target) internal returns (bool success) {
        frozenExpired[target] = 0;
        //emit FrozenFunds(target, false);
        emit FrozenFunds(target, 0); // SmartDec Recommendations
        success = true;
        return success;
    }

    /// @notice _unfreezeAccount wrapper function.
    /// @param target Address to be unfrozen
    function unfreezeAccount(address target) onlyOwner committeeApproved public returns(bool success) {
        success = _unfreezeAccount(target);
        return success;
    }

}
/**
 * NemoLab ERC20 Token
 * Written by Shin HyunJae
 * version 12
 */
function initToken(
        string memory _tokenName,
        string memory _tokenSymbol,
        uint256 _initialSupply,
        address _marketSaleManager,
        address _serviceOperationManager,
        address _dividendManager,
        address _incentiveManager,
        address _reserveFundManager
    ) internal onlyOwner committeeApproved {
        require( initialized == false );
        require(_initialSupply > 0 && _initialSupply <= 2**uint256(184)); // [2019.03.05] Fixed for Mythril Vulerablity SWC ID:101 => _initialSupply <= 2^184 <= (2^256 / 10^18)

        name = _tokenName;                                       // Set the name for display purposes
        symbol = _tokenSymbol;                                   // Set the symbol for display purposes
        //totalSupply = convertToDecimalUnits(_initialSupply);     // Update total supply with the decimal amount

        /*balances[msg.sender] = totalSupply;                     // Give the creator all initial tokens
        emit Transfer(address(this), address(0), totalSupply);
        emit LastBalance(address(this), 0);
        emit LastBalance(msg.sender, totalSupply);*/

        // SmartDec Recommendations
        uint256 tempSupply = convertToDecimalUnits(_initialSupply);

        uint256 dividendBalance = tempSupply.div(10);               // dividendBalance = 10%
        uint256 reserveFundBalance = dividendBalance;               // reserveFundBalance = 10%
        uint256 marketSaleBalance = tempSupply.div(5);              // marketSaleBalance = 20%
        uint256 serviceOperationBalance = marketSaleBalance.mul(2); // serviceOperationBalance = 40%
        uint256 incentiveBalance = marketSaleBalance;               // incentiveBalance = 20%

        balances[_marketSaleManager] = marketSaleBalance;
        balances[_serviceOperationManager] = serviceOperationBalance;
        balances[_dividendManager] = dividendBalance;
        balances[_incentiveManager] = incentiveBalance;
        balances[_reserveFundManager] = reserveFundBalance;

        totalSupply = tempSupply;

        emit Transfer(address(0), _marketSaleManager, marketSaleBalance);
        emit Transfer(address(0), _serviceOperationManager, serviceOperationBalance);
        emit Transfer(address(0), _dividendManager, dividendBalance);
        emit Transfer(address(0), _incentiveManager, incentiveBalance);
        emit Transfer(address(0), _reserveFundManager, reserveFundBalance);

        emit LastBalance(address(this), 0);
        emit LastBalance(_marketSaleManager, marketSaleBalance);
        emit LastBalance(_serviceOperationManager, serviceOperationBalance);
        emit LastBalance(_dividendManager, dividendBalance);
        emit LastBalance(_incentiveManager, incentiveBalance);
        emit LastBalance(_reserveFundManager, reserveFundBalance);

        assert( tempSupply ==
          marketSaleBalance.add(serviceOperationBalance).
                            add(dividendBalance).
                            add(incentiveBalance).
                            add(reserveFundBalance)
        );


        initialized = true;
    }
/**
 * Initialize Token Function
 *
 * Initializes contract with initial supply tokens to the creator of the contract
 */