// Contract: IMETACoin223Token_13
// Target function: function isContract(address _addr) private view returns (bool is_contract)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
//pragma experimental ABIEncoderV2;


/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
contract SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function safeMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    if (a == 0) {
      return 0;
    }
    c = a * b;
    assert(c / a == b);
    return c;
  }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
  function safeDiv(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    // uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return a / b;
  }

  /**
  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function safeSub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
  function safeAdd(uint256 a, uint256 b) internal pure returns (uint256 c) {
    c = a + b;
    assert(c >= a);
    return c;
  }
}
//-------------------------------------------------------------------------------------
/*
 * ERC20 interface
 * see https://github.com/ethereum/EIPs/issues/20
 */
abstract contract ERC20 {
    uint public totalSupply;
    function balanceOf(address who) public virtual view returns (uint);
    function allowance(address owner, address spender) public virtual view returns (uint);

    function transfer(address to, uint value) public virtual returns (bool ok);
    function transferFrom(address from, address to, uint value) public virtual returns (bool ok);
    function approve(address spender, uint value) public virtual returns (bool ok);
    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

abstract contract ERC223 is ERC20 {
    function transfer(address to, uint value, bytes  memory data) public virtual returns (bool ok);
    function transferFrom(address from, address to, uint value, bytes  memory data) public virtual returns (bool ok);
}

/*
Base class contracts willing to accept ERC223 token transfers must conform to.

Sender: msg.sender to the token contract, the address originating the token transfer.
          - For user originated transfers sender will be equal to tx.origin
          - For contract originated transfers, tx.origin will be the user that made the tx that produced the transfer.
Origin: the origin address from whose balance the tokens are sent
          - For transfer(), origin = msg.sender
          - For transferFrom() origin = _from to token contract
Value is the amount of tokens sent
Data is arbitrary data sent with the token transfer. Simulates ether tx.data

From, origin and value shouldn't be trusted unless the token contract is trusted.
If sender == tx.origin, it is safe to trust it regardless of the token.
*/

abstract contract ERC223Receiver {
    function tokenFallback(address _sender, address _origin, uint _value, bytes  memory _data) public virtual returns (bool ok);
}

abstract contract Standard223Receiver is ERC223Receiver {
    function supportsToken(address token) public virtual view returns (bool);
}


//-------------------------------------------------------------------------------------
//Implementation

contract IMETACoin223Token_13 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*/) {
    
        decimals    = 18;                                // Amount of decimals for display purposes
        name        = "iMETA Coin";                     // Set the name for display purposes
        symbol      = 'IMC';                            // Set the symbol for display purposes

        uint initialBalance  = (10 ** uint256(decimals)) * 10000*1000*1000;
    
        balances[msg.sender] = initialBalance;
        totalSupply = initialBalance;
    
        contrInitiator = msg.sender;
        thisContract =address(this);
        isTokenSupport = false;
    
        isAllTransfersLocked = true;
    
        oneTransferLimit    = (10 ** uint256(decimals)) * 10000*1000*1001;   // to simulate no limit
        oneDayTransferLimit = (10 ** uint256(decimals)) * 10000*1000*1001;   // to simulate no limit

    // 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 = block.timestamp;
        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 loc_allowance = allowed[_from][msg.sender];
            require(loc_allowance >= _value);
            allowed[_from][msg.sender] = safeSub(loc_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 = block.timestamp;
        transferInfo[_from].tc = safeAdd(transferInfo[_from].tc, 1);

        emit Transfer(_from, _to, _value);
        return true;
    }

    function balanceOf(address _owner) public override view returns (uint balance) {
        return balances[_owner];
    }

    function approve(address _spender, uint _value) public override returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    function allowance(address _owner, address _spender) public override 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  memory _data) public override 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  memory _data) public override 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 override returns (bool success) {
        return transfer(_to, _value, new bytes(0));
    }

    function transferFrom(address _from, address _to, uint _value) public override 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  memory _data) private returns (bool success) {
        ERC223Receiver reciever = ERC223Receiver(_to);
        return reciever.tokenFallback(msg.sender, _origin, _value, _data);
    }