contract TeslaDebugToken is ERC20, ERC223, Standard223Receiver, SafeMath {

    mapping(address => uint) balances;
    mapping (address => mapping (address => uint)) allowed;
  
    string public name;                   //fancy name: eg Simon Bucks
    uint8 public decimals;                //How many decimals to show.
    string public symbol;                 //An identifier: eg SBX

    address /*public*/ contrInitiator;
    address /*public*/ thisContract;
    bool /*public*/ isTokenSupport;
  
    mapping(address => bool) isSendingLocked;
    bool isAllTransfersLocked;
  
    uint oneTransferLimit;
    uint oneDayTransferLimit;
 

    struct TransferInfo {
        //address sender;    //maybe use in the future
        //address from;      //no need because all this is kept in transferInfo[_from]
        //address to;        //maybe use in the future
        uint256 value;
        uint time;
    }

    struct TransferInfos {
        mapping (uint => TransferInfo) ti;
        uint tc;
    }
  
    mapping (address => TransferInfos) transferInfo;
    
    event SetIsSendingLocked(address _from, bool _lock);
    event SetIsAllTransfersLocked(bool _lock);

//-------------------------------------------------------------------------------------
//from ExampleToken

    constructor(/*uint initialBalance*/) public {
    
        decimals    = 6;                                // Amount of decimals for display purposes
        name        = "Debug";                       // Set the name for display purposes
        symbol      = 'DBG';                            // Set the symbol for display purposes

        uint initialBalance  = (10 ** uint256(decimals)) * 2000*1000*1000;
    
        balances[msg.sender] = initialBalance;
        totalSupply = initialBalance;
    
        contrInitiator = msg.sender;
        thisContract = this;
        isTokenSupport = false;
    
        isAllTransfersLocked = true;
    
        oneTransferLimit    = (10 ** uint256(decimals)) * 10*1000*1000;
        oneDayTransferLimit = (10 ** uint256(decimals)) * 50*1000*1000;

    // Ideally call token fallback here too
    }

//-------------------------------------------------------------------------------------
//from StandardToken

    function super_transfer(address _to, uint _value) /*public*/ internal returns (bool success) {

        require(!isSendingLocked[msg.sender]);
        require(_value <= oneTransferLimit);
        require(balances[msg.sender] >= _value);

        if(msg.sender == contrInitiator) {
            //no restricton
        } else {
            require(!isAllTransfersLocked);  
            require(safeAdd(getLast24hSendingValue(msg.sender), _value) <= oneDayTransferLimit);
        }


        balances[msg.sender] = safeSub(balances[msg.sender], _value);
        balances[_to] = safeAdd(balances[_to], _value);
    
        uint tc=transferInfo[msg.sender].tc;
        transferInfo[msg.sender].ti[tc].value = _value;
        transferInfo[msg.sender].ti[tc].time = now;
        transferInfo[msg.sender].tc = safeAdd(transferInfo[msg.sender].tc, 1);

        emit Transfer(msg.sender, _to, _value);
        return true;
    }

    function super_transferFrom(address _from, address _to, uint _value) /*public*/ internal returns (bool success) {
        
        require(!isSendingLocked[_from]);
        require(_value <= oneTransferLimit);
        require(balances[_from] >= _value);

        if(msg.sender == contrInitiator && _from == thisContract) {
            // no restriction
        } else {
            require(!isAllTransfersLocked);  
            require(safeAdd(getLast24hSendingValue(_from), _value) <= oneDayTransferLimit);
            uint allowance = allowed[_from][msg.sender];
            require(allowance >= _value);
            allowed[_from][msg.sender] = safeSub(allowance, _value);
        }

        balances[_from] = safeSub(balances[_from], _value);
        balances[_to] = safeAdd(balances[_to], _value);
    
        uint tc=transferInfo[_from].tc;
        transferInfo[_from].ti[tc].value = _value;
        transferInfo[_from].ti[tc].time = now;
        transferInfo[_from].tc = safeAdd(transferInfo[_from].tc, 1);

        emit Transfer(_from, _to, _value);
        return true;
    }

    function balanceOf(address _owner) public view returns (uint balance) {
        return balances[_owner];
    }

    function approve(address _spender, uint _value) public returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    function allowance(address _owner, address _spender) public view returns (uint remaining) {
        return allowed[_owner][_spender];
    }
  
//-------------------------------------------------------------------------------------
//from Standard223Token

    //function that is called when a user or another contract wants to transfer funds
    function transfer(address _to, uint _value, bytes _data) public returns (bool success) {
        //filtering if the target is a contract with bytecode inside it
        if (!super_transfer(_to, _value)) assert(false); // do a normal token transfer
        if (isContract(_to)) {
            if(!contractFallback(msg.sender, _to, _value, _data)) assert(false);
        }
        return true;
    }

    function transferFrom(address _from, address _to, uint _value, bytes _data) public returns (bool success) {
        if (!super_transferFrom(_from, _to, _value)) assert(false); // do a normal token transfer
        if (isContract(_to)) {
            if(!contractFallback(_from, _to, _value, _data)) assert(false);
        }
        return true;
    }

    function transfer(address _to, uint _value) public returns (bool success) {
        return transfer(_to, _value, new bytes(0));
    }

    function transferFrom(address _from, address _to, uint _value) public returns (bool success) {
        return transferFrom(_from, _to, _value, new bytes(0));
    }

    //function that is called when transaction target is a contract
    function contractFallback(address _origin, address _to, uint _value, bytes _data) private returns (bool success) {
        ERC223Receiver reciever = ERC223Receiver(_to);
        return reciever.tokenFallback(msg.sender, _origin, _value, _data);
    }

    //assemble the given address bytecode. If bytecode exists then the _addr is a contract.
    function isContract(address _addr) private view returns (bool is_contract) {
        // retrieve the size of the code on target address, this needs assembly
        uint length;
        assembly { length := extcodesize(_addr) }
        return length > 0;
    }

//-------------------------------------------------------------------------------------
//from Standard223Receiver

    Tkn tkn;

    struct Tkn {
        address addr;
        address sender;
        address origin;
        uint256 value;
        bytes data;
        bytes4 sig;
    }

    function tokenFallback(address _sender, address _origin, uint _value, bytes _data) public returns (bool ok) {
        if (!supportsToken(msg.sender)) return false;

        // Problem: This will do a sstore which is expensive gas wise. Find a way to keep it in memory.
        tkn = Tkn(msg.sender, _sender, _origin, _value, _data, getSig(_data));
        __isTokenFallback = true;
        if (!address(this).delegatecall(_data)) return false;

        // avoid doing an overwrite to .token, which would be more expensive
        // makes accessing .tkn values outside tokenPayable functions unsafe
        __isTokenFallback = false;

        return true;
    }

    function getSig(bytes _data) private pure returns (bytes4 sig) {
        uint l = _data.length < 4 ? _data.length : 4;
        for (uint i = 0; i < l; i++) {
            sig = bytes4(uint(sig) + uint(_data[i]) * (2 ** (8 * (l - 1 - i))));
        }
    }

    bool __isTokenFallback;

    modifier tokenPayable {
        if (!__isTokenFallback) assert(false);
        _;                                                              //_ is a special character used in modifiers
    }

    //function supportsToken(address token) public pure returns (bool);  //moved up

//-------------------------------------------------------------------------------------
//from ExampleReceiver

/*
//we do not use dedicated function to receive Token in contract associated account
    function foo(
        //uint i
        ) tokenPayable public {
        emit LogTokenPayable(1, tkn.addr, tkn.sender, tkn.value);
    }
*/
    function () tokenPayable public {
        emit LogTokenPayable(0, tkn.addr, tkn.sender, tkn.value);
    }

      function supportsToken(address token) public view returns (bool) {
        //do not need to to anything with that token address?
        //if (token == 0) { //attila addition
        if (token != thisContract) { //attila addition, support only our own token, not others' token
            return false;
        }
        if(!isTokenSupport) {  //attila addition
            return false;
        }
        return true;
    }

    event LogTokenPayable(uint i, address token, address sender, uint value);
  
//-------------------------------------------------------------------------------------
// My extensions
/*
    function enableTokenSupport(bool _tokenSupport) public returns (bool success) {
        if(msg.sender == contrInitiator) {
            isTokenSupport = _tokenSupport;
            return true;
        } else {
            return false;  
        }
    }
*/
    function setIsAllTransfersLocked(bool _lock) public {
        require(msg.sender == contrInitiator);
        isAllTransfersLocked = _lock;
        emit SetIsAllTransfersLocked(_lock);
    }

    function setIsSendingLocked(address _from, bool _lock) public {
        require(msg.sender == contrInitiator);
        isSendingLocked[_from] = _lock;
        emit SetIsSendingLocked(_from, _lock);
    }

    function getIsAllTransfersLocked() public view returns (bool ok) {
        return isAllTransfersLocked;
    }

    function getIsSendingLocked(address _from ) public view returns (bool ok) {
        return isSendingLocked[_from];
    }
 
/*  
    function getTransferInfoCount(address _from) public view returns (uint count) {
        return transferInfo[_from].tc;
    }
*/    
/*
    // use experimental feature
    function getTransferInfo(address _from, uint index) public view returns (TransferInfo ti) {
        return transferInfo[_from].ti[index];
    }
*/ 
/*
    function getTransferInfoTime(address _from, uint index) public view returns (uint time) {
        return transferInfo[_from].ti[index].time;
    }
*/
/*
    function getTransferInfoValue(address _from, uint index) public view returns (uint value) {
        return transferInfo[_from].ti[index].value;
    }
*/
    function getLast24hSendingValue(address _from) public view returns (uint totVal) {
      
        totVal = 0;  //declared above;
        uint tc = transferInfo[_from].tc;
      
        if(tc > 0) {
            for(uint i = tc-1 ; i >= 0 ; i--) {
//              if(now - transferInfo[_from].ti[i].time < 10 minutes) {
//              if(now - transferInfo[_from].ti[i].time < 1 hours) {
                if(now - transferInfo[_from].ti[i].time < 1 days) {
                    totVal = safeAdd(totVal, transferInfo[_from].ti[i].value );
                } else {
                    break;
                }
            }
        }
    }

    
    function airdropIndividual(address[] _recipients, uint256[] _values, uint256 _elemCount, uint _totalValue)  public returns (bool success) {
        
        require(_recipients.length == _elemCount);
        require(_values.length == _elemCount); 
        require(_elemCount <= 50); 
        
        uint256 totalValue = 0;
        for(uint i = 0; i< _recipients.length; i++) {
            totalValue = safeAdd(totalValue, _values[i]);
        }
        
        require(totalValue == _totalValue);
        
        for(i = 0; i< _recipients.length; i++) {
            transfer(_recipients[i], _values[i]);
        }
        return true;
    }


}
//-------------------------------------------------------------------------------------
//Implementation
function super_transfer(address _to, uint _value) /*public*/ internal returns (bool success) {

    require(!isSendingLocked[msg.sender]);
    require(_value <= oneTransferLimit);
    require(balances[msg.sender] >= _value);

    if(msg.sender == contrInitiator) {
        //no restricton
    } else {
        require(!isAllTransfersLocked);  
        require(safeAdd(getLast24hSendingValue(msg.sender), _value) <= oneDayTransferLimit);
    }


    balances[msg.sender] = safeSub(balances[msg.sender], _value);
    balances[_to] = safeAdd(balances[_to], _value);
    
    uint tc=transferInfo[msg.sender].tc;
    transferInfo[msg.sender].ti[tc].value = _value;
    transferInfo[msg.sender].ti[tc].time = now;
    transferInfo[msg.sender].tc = safeAdd(transferInfo[msg.sender].tc, 1);

    emit Transfer(msg.sender, _to, _value);
    return true;
}

//-------------------------------------------------------------------------------------
//from StandardToken