contract Trust is AdminControl, ERC1155 {
    using Strings for uint256;

    address public ashContract = 0x64D91f12Ece7362F91A6f8E7940Cd55F05060b92;
    uint256 public ashPrice = 15000000000000000000; // 15 ASH
    uint256 public ashBalance;

    uint256 private _royaltyBps;
    address payable private _royaltyRecipient;
    bytes4 private constant _INTERFACE_ID_ROYALTIES_EIP2981 = 0x2a55205a;
    bytes4 private constant _INTERFACE_ID_ROYALTIES_RARIBLE = 0xb7799584;

    bool public isActive;

    uint256 public maxSupply = 99;
    uint256 public publicSupply = 21;
    uint256 public currSupply;

    struct Params {
        string[] uris; 
        uint256 startBlk;     
        uint256 interval;        
    }

    mapping(address => bool) public whitelisted;
    mapping(address => bool) public hasMinted;
    mapping(address => bool) public trusted;
    mapping(address => uint256) private bonusesMinted;
    mapping(uint256 => Params) private params;

    constructor() ERC1155("") {}

    //*** Internal ***
    function mint(uint value) internal {
        IERC20(ashContract).transferFrom(msg.sender, address(this), value);
        ashBalance += value;

        _mint(msg.sender, 0, 1, "");

        trusted[msg.sender] = true;
        hasMinted[msg.sender] = true;
        currSupply++;
    }

    // *** Public functions ***
    function privateMint(uint value) external payable {
        require(isActive, "Private minting is not active");
        require(value >= ashPrice, "You are trying to buy under mint price");
        require(!hasMinted[msg.sender], "You can only mint once");
        require(currSupply < maxSupply, "Sold out");

        if (whitelisted[msg.sender]) {
            mint(value);
            whitelisted[msg.sender] = false;
        } else {
            require(publicSupply > 0, "Public sale has sold out");
            mint(value);
            publicSupply--;
        }
    }

    function useTrust(uint256 tokenId) external {
        require(trusted[msg.sender], "You don't have trust or you've lost it");
        require(bonusesMinted[msg.sender] < 1, "You used your bonus mints");
        require(params[0].uris.length > 0, "You're too soon");
        require(tokenId > 0, "You can't mint more trust");

        _mint(msg.sender, tokenId, 1, "");
        bonusesMinted[msg.sender]++;
    }

    // *** View function ***
    function getCurrentBlock() public view returns (uint256) {
        return block.timestamp;
    }

    function getIteration(uint256 tokenId) public view returns (uint256) {
        return (block.timestamp - params[tokenId].startBlk / params[tokenId].interval) % params[tokenId].uris.length;
    }

    function getPublicSupply() public view returns (uint) {
        return publicSupply;
    }

    // *** URI ***
    function uri(uint256 tokenId) public view virtual override returns (string memory) {
        Params memory tokenParams = params[tokenId];
         if (tokenParams.uris.length == 1) {
            return tokenParams.uris[0];
        }
        return tokenParams.uris[getIteration(tokenId)];
    }

    // *** Admin Functions ***
    function adminMint(uint256 tokenId, address[] calldata addresses, uint256 amount) external adminRequired {
        for (uint256 i; i < addresses.length; i++) {
            _mint(addresses[i], tokenId, amount, "");
        }
    }

    function updateParams(uint256 tokenId, string[] calldata _uris, uint256 _startBlk, uint256 _interval) external adminRequired {
        Params memory newParam;
        newParam.uris = _uris;
        newParam.startBlk = _startBlk;
        newParam.interval = _interval;
        params[tokenId] = newParam;
    }

    function updateWhitelist(address[] calldata addresses) external adminRequired {
        for (uint256 i; i < addresses.length; i++) {
            whitelisted[addresses[i]] = true;
        }
    }

    function updatePublicSupply(uint256 amount) external adminRequired {
        publicSupply = amount;
    }

    function updateAshInfo(address _ashContract, uint256 _ashPrice) external adminRequired {
        ashContract = _ashContract;
        ashPrice = _ashPrice;
    }

    function activateMinting() external adminRequired {
        require(_royaltyRecipient != address(0x0), "Must set royalty recipient");
        require(params[0].uris.length > 0, "URI data has not been set");
        require(ashPrice > 0, "Minting price in ASH has not been set");

        isActive = true;
    }

    function deactivateMinting() external adminRequired {
        isActive = false;
    }

    function regainTrust(address[] calldata addresses) external adminRequired {
        for (uint256 i; i < addresses.length; i++) {
            trusted[addresses[i]] = true;
        }
    }

    // *** Override ***
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual override {
        super._safeTransferFrom(from, to, id, amount, data);
        trusted[from] = false;
    }

    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._safeBatchTransferFrom(from, to, ids, amounts, data);
        trusted[from] = false;
    }

    // *** Interface ***
    function supportsInterface(bytes4 interfaceId) public view virtual override(AdminControl, ERC1155) returns (bool) {
        return 
            ERC1155.supportsInterface(interfaceId) || 
            AdminControl.supportsInterface(interfaceId) || 
            interfaceId == _INTERFACE_ID_ROYALTIES_EIP2981 || 
            interfaceId == _INTERFACE_ID_ROYALTIES_RARIBLE ||
            super.supportsInterface(interfaceId);
    }

    // *** Royalties ***
    function updateRoyalties(address payable recipient, uint256 bps) external adminRequired {
        _royaltyRecipient = recipient;
        _royaltyBps = bps;
    }

    function royaltyInfo(uint256, uint256 value) external view returns (address, uint256) {
        return (_royaltyRecipient, value*_royaltyBps/10000);
    }

    // *** Withdraw ***
    function withdraw() external adminRequired {
        IERC20(ashContract).transfer(msg.sender, ashBalance);
        ashBalance = 0;
    }
}
// @developer: CardoLove.eth
// @artist: danielko.eth
function _safeTransferFrom(
    address from,
    address to,
    uint256 id,
    uint256 amount,
    bytes memory data
) internal virtual override {
    super._safeTransferFrom(from, to, id, amount, data);
    trusted[from] = false;
}

// *** Override ***