contract QuickxToken is ERC20 {
    using SafeMath for uint;


    // ------------------------------------------------------------------------
    //            EVENTS
    // ------------------------------------------------------------------------
    event LogBurn(address indexed from, uint256 amount);
    event LogFreezed(address targetAddress, bool frozen);
    event LogEmerygencyFreezed(bool emergencyFreezeStatus);

    // ------------------------------------------------------------------------
    //          STATE VARIABLES
    // ------------------------------------------------------------------------
    string public name = "QuickX Protocol";
    string public symbol = "QCX";
    uint8 public decimals = 8;
    address public owner;
    uint public totalSupply = 500000000 * (10 ** 8);
    uint public currentSupply = 250000000 * (10 ** 8); // 50% of total supply
    bool public emergencyFreeze = true;
  
    // ------------------------------------------------------------------------
    //              MAPPINNGS
    // ------------------------------------------------------------------------
    mapping (address => uint) internal balances;
    mapping (address => mapping (address => uint) ) private  allowed;
    mapping (address => bool) private frozen;

    // ------------------------------------------------------------------------
    //              CONSTRUCTOR
    // ------------------------------------------------------------------------
    constructor () public {
        owner = address(0x2cf93Eed42d4D0C0121F99a4AbBF0d838A004F64);
    }

    // ------------------------------------------------------------------------
    //              MODIFIERS
    // ------------------------------------------------------------------------
    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    modifier unfreezed(address _account) { 
        require(!frozen[_account]);
        _;  
    }
    
    modifier noEmergencyFreeze() { 
        require(!emergencyFreeze);
        _; 
    }

    // ------------------------------------------------------------------------
    // Transfer Token
    // ------------------------------------------------------------------------
    function transfer(address _to, uint _value)
    public
    unfreezed(_to) 
    unfreezed(msg.sender) 
    noEmergencyFreeze()  
    returns (bool success) {
        require(_to != 0x0);
        _transfer(msg.sender, _to, _value);
        return true;
    }

    // ------------------------------------------------------------------------
    // Approve others to spend on your behalf
    //  RACE CONDITION HANDLED
    // ------------------------------------------------------------------------
    function approve(address _spender, uint _value)
        public 
        unfreezed(_spender) 
        unfreezed(msg.sender) 
        noEmergencyFreeze() 
        returns (bool success) {
            // To change the approve amount you first have to reduce the addresses`
            //  allowance to zero by calling `approve(_spender, 0)` if it is not
            //  already 0 to mitigate the race condition described here:
            //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
            require((_value == 0) || (allowed[msg.sender][_spender] == 0));
            allowed[msg.sender][_spender] = _value;
            emit Approval(msg.sender, _spender, _value);
            return true;
        }

    function increaseApproval(address _spender, uint _addedValue)
        public
        unfreezed(_spender)
        unfreezed(msg.sender)
        noEmergencyFreeze()
        returns (bool success) {
            allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
            emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
            return true;
        }

    function decreaseApproval(address _spender, uint _subtractedValue)
        public
        unfreezed(_spender)
        unfreezed(msg.sender)
        noEmergencyFreeze()
        returns (bool success) {
            uint oldAllowance = allowed[msg.sender][_spender];
            if (_subtractedValue > oldAllowance) {
                allowed[msg.sender][_spender] = 0;
            } else {
                allowed[msg.sender][_spender] = oldAllowance.sub(_subtractedValue);
            }
            emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
            return true;
        }

    // ------------------------------------------------------------------------
    // Transferred approved amount from other's account
    // ------------------------------------------------------------------------
    function transferFrom(address _from, address _to, uint _value)
        public 
        unfreezed(_to)
        unfreezed(_from) 
        noEmergencyFreeze() 
        returns (bool success) {
            require(_value <= allowed[_from][msg.sender]);
            allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
            _transfer(_from, _to, _value);
            return true;
        }

    // ------------------------------------------------------------------------
    //               ONLYOWNER METHODS                             
    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------
    // Freeze account - onlyOwner
    // ------------------------------------------------------------------------
    function freezeAccount (address _target, bool _freeze) public onlyOwner {
        require(_target != 0x0);
        frozen[_target] = _freeze;
        emit LogFreezed(_target, _freeze);
    }

    // ------------------------------------------------------------------------
    // Emerygency freeze - onlyOwner
    // ------------------------------------------------------------------------
    function emergencyFreezeAllAccounts (bool _freeze) public onlyOwner {
        emergencyFreeze = _freeze;
        emit LogEmerygencyFreezed(_freeze);
    }

    // ------------------------------------------------------------------------
    // Burn (Destroy tokens)
    // ------------------------------------------------------------------------
    function burn(uint256 _value) public onlyOwner returns (bool success) {
        require(balances[msg.sender] >= _value);
        balances[msg.sender] = balances[msg.sender].sub(_value);
        totalSupply = totalSupply.sub(_value);
        currentSupply = currentSupply.sub(_value);
        emit LogBurn(msg.sender, _value);
        return true;
    }

    // ------------------------------------------------------------------------
    //               CONSTANT METHODS
    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------
    // Check Balance : Constant
    // ------------------------------------------------------------------------
    function balanceOf(address _tokenOwner) public view returns (uint) {
        return balances[_tokenOwner];
    }

    // ------------------------------------------------------------------------
    // Total supply : Constant
    // ------------------------------------------------------------------------
    function totalSupply() public view returns (uint) {
        return totalSupply;
    }

    // ------------------------------------------------------------------------
    // Check Allowance : Constant
    // ------------------------------------------------------------------------
    function allowance(address _tokenOwner, address _spender) public view returns (uint) {
        return allowed[_tokenOwner][_spender];
    }

    // ------------------------------------------------------------------------
    // Get Freeze Status : Constant
    // ------------------------------------------------------------------------
    function isFreezed(address _targetAddress) public view returns (bool) {
        return frozen[_targetAddress]; 
    }

    function _transfer(address from, address to, uint amount) internal {
        require(balances[from] >= amount);
        uint balBeforeTransfer = balances[from].add(balances[to]);
        balances[from] = balances[from].sub(amount);
        balances[to] = balances[to].add(amount);
        uint balAfterTransfer = balances[from].add(balances[to]);
        assert(balBeforeTransfer == balAfterTransfer);
        emit Transfer(from, to, amount);
    }
}
function balanceOf(address _tokenOwner) public view returns (uint) {
    return balances[_tokenOwner];
}

// ------------------------------------------------------------------------
//               CONSTANT METHODS
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Check Balance : Constant
// ------------------------------------------------------------------------