contract NIFTYSouq1155 is ERC1155, Ownable, ERC1155Burnable{
    event Approve(
        address indexed owner,
        uint256 indexed token_id,
        bool approved
    );
    event OrderPlace(
        address indexed from,
        uint256 indexed tokenId,
        uint256 indexed value
    );
    event CancelOrder(address indexed from, uint256 indexed tokenId);
    event ChangePrice(address indexed from, uint256 indexed tokenId, uint256 indexed value);

    using SafeMath for uint256;

    struct Order {
        uint256 tokenId;
        uint256 price;
    }

    mapping(address => mapping(uint256 => Order)) public order_place;
    mapping(uint256 => mapping(address => bool)) public checkOrder;
    mapping(uint256 => uint256) public totalQuantity;

    
    mapping(uint256 => uint256) public _royal;
    mapping(string => address) private tokentype;

    uint256 private serviceValue;
    string private _currentBaseURI;
    uint256 public _tid;

    constructor(
        uint256 id,
        uint256 _serviceValue,
        string memory tokenName,
        address tokenAddress,
        string memory _name,
        string memory _symbol
    ) ERC1155("",_name,_symbol) {
         _tid = id;
        serviceValue = _serviceValue;
        addTokenType(tokenName,tokenAddress);
    }

    function getServiceFee() public view returns (uint256) {
        return serviceValue;
    }
    function serviceFunction(uint256 _serviceValue) public onlyOwner{
        serviceValue = _serviceValue;
    }
    function addID(uint256 value) public returns (uint256) {
        _tid = _tid + value;
        return _tid;
    }

    function getTokenAddress(string memory _type)
        public
        view
        returns (address)
    {
        return tokentype[_type];
    }
    function addTokenType(string memory _type, address tokenAddress)
        public
        onlyOwner
    {
        tokentype[_type] = tokenAddress;
    }

    function setApproval(address operator, bool approved)
        public
        returns (uint256)
    {
        setApprovalForAll(operator, approved);
        uint256 id_ = addID(1).add(block.timestamp);
        emit Approve(msg.sender, id_, approved);
        return id_;
    }
    function mint(uint256 id, uint256 value, uint256 royal, uint256 nooftoken, string memory ipfsname,
        string memory ipfsimage,
         string memory ipfsmetadata) public {
        _mint(msg.sender, id, nooftoken, "");
        token_id[id] = Metadata(ipfsname, ipfsimage, ipfsmetadata);
        _creator[id]=msg.sender;
       _royal[id]=royal.mul(1e18);
       if(value != 0){
            orderPlace(id, value);
        }
    }

    function mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts
    ) public {
        _mintBatch(to, ids, amounts, "");
    }

    function saleWithToken(
        address payable from,
        uint256 tokenId,
        uint256 amount,
        uint256 nooftoken,
        string memory bidtoken
        ) public{
            require(amount == order_place[from][tokenId].price.mul(nooftoken) , "Insufficent Balance");
            _saleToken(from, tokenId, amount, bidtoken);
            _setApprovalForAll(from, msg.sender,true);
            safeTransferFrom(from, msg.sender, tokenId, nooftoken, "");
            
        }
    // ETH TRANSFER PURCHASE
    function saleToken(
        address payable from,
        uint256 tokenId,
        uint256 amount,
        uint256 nooftoken
    ) public payable {
        require(amount == order_place[from][tokenId].price.mul(nooftoken) , "Invalid Balance");
        _saleToken(from, tokenId, amount, "BNB");
        _setApprovalForAll(from, msg.sender,true);
        safeTransferFrom(from, msg.sender, tokenId, nooftoken, "");
    }

    function _saleToken(
        address payable from,
        uint256 tokenId,
        uint256 amount,
        string memory bidtoken
    ) internal {
        uint256 val = pERCent(amount, serviceValue).add(amount);
        if(keccak256(abi.encodePacked((bidtoken))) == keccak256(abi.encodePacked(("BNB")))){   
            require( msg.value == val, "Insufficient Balance");
            address payable create = payable(_creator[tokenId]);
            (uint256 _adminfee, uint256 roy, uint256 netamount) = calc(amount, _royal[tokenId], serviceValue);
            require( msg.value == _adminfee.add(roy.add(netamount)), "Insufficient Balance");
            address payable admin = payable(owner());
            admin.transfer(_adminfee);
            create.transfer(roy);
            from.transfer(netamount);
        }
        else{
            ERC20 t = ERC20(tokentype[bidtoken]);
            uint256 approveValue = t.allowance(msg.sender, address(this));
            require( approveValue >= val, "Insufficient Balance");
            (uint256 _adminfee, uint256 roy, uint256 netamount) = calc(amount, _royal[tokenId], serviceValue);
            require( approveValue >= _adminfee.add(roy.add(netamount)), "Insufficient Balance");
            t.transferFrom(msg.sender, owner(), _adminfee);
            t.transferFrom(msg.sender,_creator[tokenId],roy);
            t.transferFrom(msg.sender,from,netamount);
        }
    }

    function pERCent(uint256 value1, uint256 value2)
        internal
        pure
        returns (uint256)
    {
        uint256 result = value1.mul(value2).div(1e20);
        return (result);
    }

    function calc(
        uint256 amount,
        uint256 royal,
        uint256 _serviceValue
    )
        internal
        pure
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        uint256 fee = pERCent(amount, _serviceValue);
        uint256 roy = pERCent(amount, royal);
        uint256 netamount = amount.sub(fee.add(roy));
        fee = fee.add(fee);
        return (fee, roy, netamount);
    }
    function acceptBId(string memory bidtoken,address bidaddr, uint256 amount, uint256 tokenId, uint256 NOFToken) public{
        _acceptBId(bidtoken, bidaddr, amount, tokenId, owner());
        safeTransferFrom(msg.sender,bidaddr, tokenId, NOFToken, ""); 
            if(checkOrder[tokenId][msg.sender] == true){
                if(_balances[tokenId][msg.sender] == 0){   
                    delete order_place[msg.sender][tokenId];
                    checkOrder[tokenId][msg.sender] = false;
                }
            }
    }
    function _acceptBId(string memory tokenAss,address from, uint256 amount, uint256 tokenId, address admin) internal{
        uint256 val = pERCent(amount, serviceValue).add(amount);
        ERC20 t = ERC20(tokentype[tokenAss]);
        uint256 approveValue = t.allowance(from, address(this));
        require( approveValue >= val, "Insufficient Balance");
        require(_balances[tokenId][msg.sender] > 0, "Is Not a Owner");
        (uint256 _adminfee, uint256 roy, uint256 netamount) = calc(amount, _royal[tokenId], serviceValue);
        require( approveValue >= _adminfee.add(roy.add(netamount)), "Insufficient Balance");
        t.transferFrom(from, admin, _adminfee);
        t.transferFrom(from,_creator[tokenId],roy);
        t.transferFrom(from,msg.sender,netamount);
    }
    function orderPlace(uint256 tokenId, uint256 _price) public{
        require( _balances[tokenId][msg.sender] > 0, "Is Not a Owner");
        Order memory order;
        order.tokenId = tokenId;
        order.price = _price;
        order_place[msg.sender][tokenId] = order;
        checkOrder[tokenId][msg.sender] = true;
        emit OrderPlace(msg.sender, tokenId, _price);
    }
    function cancelOrder(uint256 tokenId) public{
        require(_balances[tokenId][msg.sender] > 0, "Is Not a Owner");
        delete order_place[msg.sender][tokenId];
        checkOrder[tokenId][msg.sender] = false;
        emit CancelOrder(msg.sender, tokenId);
    }
    function changePrice(uint256 value, uint256 tokenId) public{
        require( _balances[tokenId][msg.sender] > 0, "Is Not a Owner");
        require( value < order_place[msg.sender][tokenId].price);
        order_place[msg.sender][tokenId].price = value;
        emit ChangePrice(msg.sender, tokenId, value);
    }
    function burnToken(address from, uint256 tokenId, uint256 NOFToken ) public{
        require( (_balances[tokenId][from] >= NOFToken && from == msg.sender) || msg.sender == owner(), "Your Not a Token Owner or insufficient Token Balance");
        require( _balances[tokenId][from] >= NOFToken, "Your Not a Token Owner or insufficient Token Balance");
            burn(from, tokenId, NOFToken);
            if(_balances[tokenId][from] == NOFToken){
                if(checkOrder[tokenId][from]==true){
                    delete order_place[from][tokenId];
                    checkOrder[tokenId][from] = false;
                }
            }
    }
}
function saleToken(
        address payable from,
        uint256 tokenId,
        uint256 amount,
        uint256 nooftoken
    ) public payable {
        require(amount == order_place[from][tokenId].price.mul(nooftoken) , "Invalid Balance");
        _saleToken(from, tokenId, amount, "BNB");
        _setApprovalForAll(from, msg.sender,true);
        safeTransferFrom(from, msg.sender, tokenId, nooftoken, "");
    }
// ETH TRANSFER PURCHASE