contract ERC1155 is IERC1155, ERC165 , Allowable{
    using SafeMath for uint256;
    using Address for address;
    
    /***********************************|
    |        Variables and Events       |
    |__________________________________*/
    
    // Contract name
      string public name;
      // Contract symbol
      string public symbol;
    
    // onReceive function signatures
    bytes4 constant internal ERC1155_RECEIVED_VALUE = 0xf23a6e61;
    bytes4 constant internal ERC1155_BATCH_RECEIVED_VALUE = 0xbc197c81;
    
    // Objects balances
    mapping (address => mapping(uint256 => uint256)) internal balances;
    
    // Operator Functions
    mapping (address => mapping(address => bool)) internal operators;
    
    //  A record of each accounts delegate
    mapping (address => mapping (uint256 => address)) internal _delegates;
    
    /// @notice A checkpoint for marking number of votes from a given block
    struct Checkpoint {
        uint32 fromBlock;
        uint256 votes;
    }
    
    /// @notice A record of votes checkpoints for each account, by index
    mapping (address => mapping (uint32 => mapping(uint256=> Checkpoint))) public checkpoints;
    
    /// @notice The number of checkpoints for each account
    mapping (address => mapping (uint256 => uint32)) public numCheckpoints;
    
    /// @notice The EIP-712 typehash for the contract's domain
    bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
    
    /// @notice The EIP-712 typehash for the delegation struct used by the contract
    bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
    
    /// @notice A record of states for signing / validating signatures
    mapping (address => uint) public nonces;
    
      /// @notice An event thats emitted when an account changes its delegate
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
    
    /// @notice An event thats emitted when a delegate account's vote balance changes
    event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
    
    
    
    
    /***********************************|
    |     Public Transfer Functions     |
    |__________________________________*/
    
    /**
    * @notice Transfers amount amount of an _id from the _from address to the _to address specified
    * @param _from    Source address
    * @param _to      Target address
    * @param _id      ID of the token type
    * @param _amount  Transfered amount
    * @param _data    Additional data with no specified format, sent in call to `_to`
    */
    function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount, bytes memory _data)
        public active() whenActive(_from,_to,_id) override 
    {
        require((msg.sender == _from) || isApprovedForAll(_from, msg.sender), "ERC1155#safeTransferFrom: INVALID_OPERATOR");
        require(_to != address(0),"ERC1155#safeTransferFrom: INVALID_RECIPIENT");
        // require(_amount <= balances[_from][_id]) is not necessary since checked with safemath operations
        
        _safeTransferFrom(_from, _to, _id, _amount);
        _callonERC1155Received(_from, _to, _id, _amount, gasleft(), _data);
    
    }
    
    /**
    
    /**
    * @notice Send multiple types of Tokens from the _from address to the _to address (with safety call)
    * @param _from     Source addresses
    * @param _to       Target addresses
    * @param _ids      IDs of each token type
    * @param _amounts  Transfer amounts per token type
    * @param _data     Additional data with no specified format, sent in call to `_to`
    */
    function safeBatchTransferFrom(address _from, address _to, uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data)
        public override active() 
    {
        // Requirements
        require((msg.sender == _from) || isApprovedForAll(_from, msg.sender), "ERC1155#safeBatchTransferFrom: INVALID_OPERATOR");
        require(_to != address(0), "ERC1155#safeBatchTransferFrom: INVALID_RECIPIENT");
        
        _safeBatchTransferFrom(_from, _to, _ids, _amounts);
        _callonERC1155BatchReceived(_from, _to, _ids, _amounts, gasleft(), _data);
    }
    
    
    /***********************************|
    |    Internal Transfer Functions    |
    |__________________________________*/
    
    /**
    * @notice Transfers amount amount of an _id from the _from address to the _to address specified
    * @param _from    Source address
    * @param _to      Target address
    * @param _id      ID of the token type
    * @param _amount  Transfered amount
    */
    function _safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount)
        internal
    {
        // Update balances
        balances[_from][_id] = balances[_from][_id].sub(_amount); // Subtract amount
        balances[_to][_id] = balances[_to][_id].add(_amount);     // Add amount
        
        _moveDelegates(_delegates[_from][_id], _delegates[_to][_id], _amount,_id);
        
        // Emit event
        emit TransferSingle(msg.sender, _from, _to, _id, _amount);
    }
    
    /**
    * @notice Verifies if receiver is contract and if so, calls (_to).onERC1155Received(...)
    */
    function _callonERC1155Received(address _from, address _to, uint256 _id, uint256 _amount, uint256 _gasLimit, bytes memory _data)
        internal
    {
        // Check if recipient is contract
        if (_to.isContract()) {
          bytes4 retval = IERC1155TokenReceiver(_to).onERC1155Received{gas: _gasLimit}(msg.sender, _from, _id, _amount, _data);
          require(retval == ERC1155_RECEIVED_VALUE, "ERC1155#_callonERC1155Received: INVALID_ON_RECEIVE_MESSAGE");
        }
    }
    
    /**
    * @notice Send multiple types of Tokens from the _from address to the _to address (with safety call)
    * @param _from     Source addresses
    * @param _to       Target addresses
    * @param _ids      IDs of each token type
    * @param _amounts  Transfer amounts per token type
    */
    function _safeBatchTransferFrom(address _from, address _to, uint256[] memory _ids, uint256[] memory _amounts)
        internal
    {
        require(_ids.length == _amounts.length, "ERC1155#_safeBatchTransferFrom: INVALID_ARRAYS_LENGTH");
        
        // Number of transfer to execute
        uint256 nTransfer = _ids.length;
        
        // Executing all transfers
        for (uint256 i = 0; i < nTransfer; i++) {
            require (!permissions[_from][_ids[i]] && !permissions[_to][_ids[i]],"blocked user");          
          // Update storage balance of previous bin
          balances[_from][_ids[i]] = balances[_from][_ids[i]].sub(_amounts[i]);
          balances[_to][_ids[i]] = balances[_to][_ids[i]].add(_amounts[i]);
        }
        
        // Emit event
        emit TransferBatch(msg.sender, _from, _to, _ids, _amounts);
    }
    
    /**
    * @notice Verifies if receiver is contract and if so, calls (_to).onERC1155BatchReceived(...)
    */
    function _callonERC1155BatchReceived(address _from, address _to, uint256[] memory _ids, uint256[] memory _amounts, uint256 _gasLimit, bytes memory _data)
        internal
    {
        // Pass data if recipient is contract
        if (_to.isContract()) {
          bytes4 retval = IERC1155TokenReceiver(_to).onERC1155BatchReceived{gas: _gasLimit}(msg.sender, _from, _ids, _amounts, _data);
          require(retval == ERC1155_BATCH_RECEIVED_VALUE, "ERC1155#_callonERC1155BatchReceived: INVALID_ON_RECEIVE_MESSAGE");
        }
    }
    
    
    /***********************************|
    |         Operator Functions        |
    |__________________________________*/
    
    /**
    * @notice Enable or disable approval for a third party ("operator") to manage all of caller's tokens
    * @param _operator  Address to add to the set of authorized operators
    * @param _approved  True if the operator is approved, false to revoke approval
    */
    function setApprovalForAll(address _operator, bool _approved)
        external  override
    {
        // Update operator status
        operators[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }
    
    
    
    /**
    * @notice Queries the approval status of an operator for a given owner
    * @param _owner     The owner of the Tokens
    * @param _operator  Address of authorized operator
    * @return isOperator True if the operator is approved, false if not
    */
    function isApprovedForAll(address _owner, address _operator)
        public override view returns (bool isOperator)
    {
        return operators[_owner][_operator];
    }
    
    
    /***********************************|
    |         Balance Functions         |
    |__________________________________*/
    
    /**
    * @notice Get the balance of an account's Tokens
    * @param _owner  The address of the token holder
    * @param _id     ID of the Token
    * @return The _owner's balance of the Token type requested
    */
    function balanceOf(address _owner, uint256 _id)
        public override view returns (uint256)
    {
        return balances[_owner][_id];
    }
    
    /**
    * @notice Get the balance of multiple account/token pairs
    * @param _owners The addresses of the token holders
    * @param _ids    ID of the Tokens
    * @return        The _owner's balance of the Token types requested (i.e. balance for each (owner, id) pair)
    */
    function balanceOfBatch(address[] memory _owners, uint256[] memory _ids)
        public override view returns (uint256[] memory)
    {
        require(_owners.length == _ids.length, "ERC1155#balanceOfBatch: INVALID_ARRAY_LENGTH");
        
        // Variables
        uint256[] memory batchBalances = new uint256[](_owners.length);
        
        // Iterate over each owner and token ID
        for (uint256 i = 0; i < _owners.length; i++) {
          batchBalances[i] = balances[_owners[i]][_ids[i]];
        }
        
        return batchBalances;
    }
    
    /**
    * @notice Manages the delagates
    * @param delegator  The addresses of delegator
    * @param delegatee  The addresses of delegator
    * @param tokenId    Token type
    */ 
    function _delegate(address delegator, address delegatee, uint256 tokenId)
        internal
    {
        address currentDelegate = _delegates[delegator][tokenId];
        uint256 delegatorBalance = balanceOf(delegator,tokenId); // balance of underlying (not scaled);
        _delegates[delegator][tokenId] = delegatee;
    
        emit DelegateChanged(delegator, currentDelegate, delegatee);
    
        _moveDelegates(currentDelegate, delegatee, delegatorBalance,tokenId);
    }
    
    /**
    * @notice Move the delegate control to delegatee
    * @param srcRep  The addresses of delegator
    * @param dstRep  The addresses of delegator
    * @param amount  Delegator balance
    * @param tokenId Token type
    */ 
    function _moveDelegates(address srcRep, address dstRep, uint256 amount, uint256 tokenId) 
        internal 
    {
        if (srcRep != dstRep && amount > 0) {
            if (srcRep != address(0)) {
                // decrease old representative
                uint32 srcRepNum = numCheckpoints[srcRep][tokenId];
                uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1][tokenId].votes : 0;
                uint256 srcRepNew = srcRepOld.sub(amount);
                _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew,tokenId);
            }
    
            if (dstRep != address(0)) {
                // increase new representative
                uint32 dstRepNum = numCheckpoints[dstRep][tokenId];
                uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1][tokenId].votes : 0;
                uint256 dstRepNew = dstRepOld.add(amount);
                _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew,tokenId);
            }
        }
    }
    
    /**
    * @notice Internal call to update the votes to delegatee
    */ 
    function _writeCheckpoint(
        address delegatee,
        uint32 nCheckpoints,
        uint256 oldVotes,
        uint256 newVotes,
        uint256 tokenID
    )
        internal
    {
        uint32 blockNumber = safe32(block.number, "_writeCheckpoint: block number exceeds 32 bits");
    
        if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1][tokenID].fromBlock == blockNumber) {
            checkpoints[delegatee][nCheckpoints - 1][tokenID].votes = newVotes;
        } else {
            checkpoints[delegatee][nCheckpoints][tokenID] = Checkpoint(blockNumber, newVotes);
            numCheckpoints[delegatee][tokenID] = nCheckpoints + 1;
        }
    
        emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
    }
    
    function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
        require(n < 2**32, errorMessage);
        return uint32(n);
    }
    
    function getChainId() internal pure returns (uint) {
        uint256 chainId;
        assembly { chainId := chainid() }
        return chainId;
    }
    
    /**
     * @notice Delegate votes from `msg.sender` to `delegatee`
     * @param delegator The address to get delegatee for
     */
    function delegates(address delegator, uint256 tokenID)
        external
        view
        returns (address)
    {
        return _delegates[delegator][tokenID];
    }
    
    /**
    * @notice Delegate votes from `msg.sender` to `delegatee`
    * @param delegatee The address to delegate votes to
    */
    function delegate(address delegatee, uint256 tokenID) external  {
        return _delegate(msg.sender, delegatee, tokenID);
    }
    
    /**
     * @notice Delegates votes from signatory to `delegatee`
     * @param delegatee The address to delegate votes to
     * @param nonce The contract state required to match the signature
     * @param expiry The time at which to expire the signature
     * @param v The recovery byte of the signature
     * @param r Half of the ECDSA signature pair
     * @param s Half of the ECDSA signature pair
     */
    function delegateBySig(
        address delegatee,
        uint nonce,
        uint expiry,
        uint8 v,
        bytes32 r,
        bytes32 s,
        uint tokenID
    )
        external
        
    {
        bytes32 domainSeparator = keccak256(
            abi.encode(
                DOMAIN_TYPEHASH,
                keccak256(bytes(name)),
                getChainId(),
                address(this)
            )
        );
    
        bytes32 structHash = keccak256(
            abi.encode(
                DELEGATION_TYPEHASH,
                delegatee,
                nonce,
                expiry
            )
        );
    
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                domainSeparator,
                structHash
            )
        );
    
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), "delegateBySig: invalid signature");
        require(nonce == nonces[signatory]++, "delegateBySig: invalid nonce");
        require(now <= expiry, "delegateBySig: signature expired");
        return _delegate(signatory, delegatee, tokenID);
    }
    
    /**
     * @notice Gets the current votes balance for `account`
     * @param account The address to get votes balance
     * @return The number of current votes for `account`
     */
    function getCurrentVotes(address account, uint256 tokenID)
        external
        view
        returns (uint256)
    {
        uint32 nCheckpoints = numCheckpoints[account][tokenID];
        return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1][tokenID].votes : 0;
    }
    
    /**
     * @notice Determine the prior number of votes for an account as of a block number
     * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
     * @param account The address of the account to check
     * @param blockNumber The block number to get the vote balance at
     * @return The number of votes the account had as of the given block
     */
    function getPriorVotes(address account, uint blockNumber,uint256 tokenId)
        external
        view
        returns (uint256)
    {
        require(blockNumber < block.number, "getPriorVotes: not yet determined");
    
        uint32 nCheckpoints = numCheckpoints[account][tokenId];
        if (nCheckpoints == 0) {
            return 0;
        }
    
        // First check most recent balance
        if (checkpoints[account][nCheckpoints - 1][tokenId].fromBlock <= blockNumber) {
            return checkpoints[account][nCheckpoints - 1][tokenId].votes;
        }
    
        // Next check implicit zero balance
        if (checkpoints[account][0][tokenId].fromBlock > blockNumber) {
            return 0;
        }
    
        uint32 lower = 0;
        uint32 upper = nCheckpoints - 1;
        while (upper > lower) {
            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            Checkpoint memory cp = checkpoints[account][center][tokenId];
            if (cp.fromBlock == blockNumber) {
                return cp.votes;
            } else if (cp.fromBlock < blockNumber) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return checkpoints[account][lower][tokenId].votes;
    }
    
    
    
    /***********************************|
    |          ERC165 Functions         |
    |__________________________________*/
    
    /**
    * @notice Query if a contract implements an interface
    * @param _interfaceID  The interface identifier, as specified in ERC-165
    * @return `true` if the contract implements `_interfaceID` and
    */
    function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) {
        if (_interfaceID == type(IERC1155).interfaceId) {
          return true;
        }
        return super.supportsInterface(_interfaceID);
    }
}
/**
 * @dev Implementation of Multi-Token Standard contract
 */
function balanceOfBatch(address[] memory _owners, uint256[] memory _ids)
    public override view returns (uint256[] memory)
{
    require(_owners.length == _ids.length, "ERC1155#balanceOfBatch: INVALID_ARRAY_LENGTH");
        
    // Variables
    uint256[] memory batchBalances = new uint256[](_owners.length);
        
    // Iterate over each owner and token ID
    for (uint256 i = 0; i < _owners.length; i++) {
      batchBalances[i] = balances[_owners[i]][_ids[i]];
    }
        
    return batchBalances;
}

/**
 * @notice Get the balance of multiple account/token pairs
 * @param _owners The addresses of the token holders
 * @param _ids    ID of the Tokens
 * @return        The _owner's balance of the Token types requested (i.e. balance for each (owner, id) pair)
 */