contract AntiscamToken is Ownable, Inject, IERC20 {
    using DistributedStorage for bytes32;
    using SafeMath for uint256;

    // Distribution
    event Choosen(address indexed _addr, uint256 _value);

    // Org
    event SetName(string _prev, string _new);
    event SetExtraGas(uint256 _prev, uint256 _new);
    event Setmound(address _prev, address _new);
    event WhitelistFrom(address _addr, bool _whitelisted);
    event WhitelistTo(address _addr, bool _whitelisted);

    uint256 public totalSupply;
    

    bytes32 private constant BALANCE_KEY = keccak256("balance");

    // Mechanism
    uint256 public constant FEE = 50;

    // Token
    string public name = "AntiscamToken (AST)";
    string public constant symbol = "AST";
    uint8 public constant decimals = 18;

    // fee whitelist
    mapping(address => bool) public whitelistFrom;
    mapping(address => bool) public whitelistTo;

    // mound
    Mound public mound;

    // internal
    uint256 public extraGas;
    bool inited;

    function init(
        address _to,
        uint256 _amount
    ) external {
        // Init limited to one
        assert(!inited);
        inited = true;

        assert(totalSupply == 0);
        assert(address(mound) == address(0));

        // Create mound
        mound = new Mound();
        emit Setmound(address(0), address(mound));

        extraGas = 15;
        emit SetExtraGas(0, extraGas);
        emit Transfer(address(0), _to, _amount);
        _setBalance(_to, _amount);
        totalSupply = _amount;
    }

    // Get Functions

    function _toKey(address a) internal pure returns (bytes32) {
        return bytes32(uint256(a));
    }

    function _balanceOf(address _addr) internal view returns (uint256) {
        return uint256(_toKey(_addr).read(BALANCE_KEY));
    }

    function _allowance(address _addr, address _spender) internal view returns (uint256) {
        return uint256(_toKey(_addr).read(keccak256(abi.encodePacked("allowance", _spender))));
    }

    function _nonce(address _addr, uint256 _cat) internal view returns (uint256) {
        return uint256(_toKey(_addr).read(keccak256(abi.encodePacked("nonce", _cat))));
    }

    // Set Functions

    function _setAllowance(address _addr, address _spender, uint256 _value) internal {
        _toKey(_addr).write(keccak256(abi.encodePacked("allowance", _spender)), bytes32(_value));
    }

    function _setNonce(address _addr, uint256 _cat, uint256 _value) internal {
        _toKey(_addr).write(keccak256(abi.encodePacked("nonce", _cat)), bytes32(_value));
    }

    function _setBalance(address _addr, uint256 _balance) internal {
        _toKey(_addr).write(BALANCE_KEY, bytes32(_balance));
        mound.update(_addr, _balance);
    }

    // Distribution Functions

    function _isWhitelisted(address _from, address _to) internal view returns (bool) {
        return whitelistFrom[_from]||whitelistTo[_to];
    }

    function _random(address _s1, uint256 _s2, uint256 _s3, uint256 _max) internal pure returns (uint256) {
        uint256 rand = uint256(keccak256(abi.encodePacked(_s1, _s2, _s3)));
        return rand % (_max + 1);
    }

    function _pickChoosen(address _from, uint256 _value) internal returns (address choosen) {
        uint256 magnitude = Math.orderOfMagnitude(_value);
        uint256 nonce = _nonce(_from, magnitude);
        _setNonce(_from, magnitude, nonce + 1);
        // choose from mound
        choosen = mound.addressAt(_random(_from, nonce, magnitude, mound.size() - 1));
    }

    function _transferFrom(address _operator, address _from, address _to, uint256 _value, bool _payFee) internal {
        if (_value == 0) {
            emit Transfer(_from, _to, 0);
            return;
        }

        uint256 balanceFrom = _balanceOf(_from);
        require(balanceFrom >= _value, "balance not enough");

        if (_from != _operator) {
            uint256 allowanceFrom = _allowance(_from, _operator);
            if (allowanceFrom != uint(-1)) {
                require(allowanceFrom >= _value, "allowance not enough");
                _setAllowance(_from, _operator, allowanceFrom.sub(_value));
            }
        }

        uint256 receive = _value;
        uint256 burn = 0;
        uint256 distribute = 0;

        _setBalance(_from, balanceFrom.sub(_value));

        // Fees Calculation
        if (_payFee || !_isWhitelisted(_from, _to)) {
            // SAME for BURN and DISTRIBUTION
            burn = _value.divRound(FEE);
            distribute = _value == 1 ? 0 : burn;

            receive = receive.sub(burn.add(distribute));

            // Burn 
            totalSupply = totalSupply.sub(burn);
            emit Transfer(_from, address(0), burn);
            

            // Distribute to choosen add
            address choosen = _pickChoosen(_from, _value);
            // Tokens to choosen
            _setBalance(choosen, _balanceOf(choosen).add(distribute));
            emit Choosen(choosen, distribute);
            emit Transfer(_from, choosen, distribute);
        }
function _toKey(address a) internal pure returns (bytes32) {
        return bytes32(uint256(a));
    }
// Get Functions