contract Dvision_1155dvi is ERC1155, Ownable 
{    
    using SafeMath for uint256;
    
    constructor(address _erc20, address payable _fee_addr, string memory _uri) 
    ERC1155("DVI 1155") Ownable() public 
    {
       currencyToken = IERC20(_erc20);
       fee_addr = _fee_addr;
       _setURI(_uri);
    }
    
    uint private _currentTokenId = 0;
    uint private _currentOrderId = 0;
    uint private _currentSaleAmount = 0;
    
    mapping(uint => bool) public _itemExists;
    mapping(address => mapping(uint => Item)) public _items;
    //mapping(uint => uint256) public _itemSupply;
    mapping(uint => Order) public _itemForSale;
    

    //token
    struct Item 
    { 
        uint256 supply;
        uint256 amount;
        uint256 orderId;
    }
    
    struct Order
    {
        address seller;
        uint256 tokenId;
        uint256 price;
        uint256 currency;
        uint256 sell_amount;
        bool forSale;
    }
    
    
    event ItemSelled(uint256 _tokenId);
    event mint_event(uint256 _tokenId);
    
    address payable fee_addr;
    uint256 fee_percent = 10;
    uint256 fee_on_mint = 0;
    
    
    IERC20 currencyToken;
    
    function Set_20Token(address _new) onlyOwner public 
    {
        currencyToken = IERC20(_new);
    }
    
    function Show_20Token() onlyOwner public view returns(address)
    {
        return address(currencyToken);
    }
    
    function Set_URI (string memory _new_uri) public onlyOwner {
       _setURI(_new_uri);
    }
    
    function Mint(uint256 _amount) public payable returns(uint256)
    {
        require(_amount > 0, "ERC1155 : Amount of token to create must be bigger than 0");
        require(msg.value >= fee_on_mint, "fee is not enough");
        
        Item memory _item = Item({
            supply : _amount,
            amount : _amount,
            orderId : 0
        });
        
        uint _id = _getNextTokenId();
        _mint(msg.sender, _id, _amount, "");
        
        _incrementTokenId();
        
        _items[msg.sender][_id] = _item;
        _itemExists[_id] = true;
        
        //_itemSupply[_id] = _amount;
        
        emit mint_event(_id);
        return (_id);
    }
    
    function MintTo(address _to, uint256 _amount) public onlyOwner returns(uint256)
    {
        require(_amount > 0, "ERC1155 : Amount of token to create must be bigger than 0");
        
        Item memory _item = Item({
            supply : _amount,
            amount : _amount,
            orderId : 0
        });
        
        uint _id = _getNextTokenId();
        _mint(_to, _id, _amount, "");
        
        _incrementTokenId();
        
        _items[_to][_id] = _item;
        _itemExists[_id] = true;

        //_itemSupply[_id] = _amount;
        
        emit mint_event(_id);
        return (_id);
    }
    
    
    function _getNextTokenId() private view returns (uint256) 
    {
        return _currentTokenId.add(1);
    }
    
    function _incrementTokenId() private 
    {
        _currentTokenId++;
    }
    
    function Sell_Item(address _marketaddress, uint256 _tokenId, uint256 _price, uint256 _currency, uint256 _sell_amount) public 
    {
        require(balanceOf(msg.sender, _tokenId) >= _sell_amount);
        
        if(_items[msg.sender][_tokenId].orderId > 0)
        {
            if(_itemForSale[_items[msg.sender][_tokenId].orderId].forSale == false) //selled many times
            {
                uint _orderId = _getNextOrderId();
                _items[msg.sender][_tokenId].orderId = _orderId;
                
                _itemForSale[_orderId].seller = msg.sender;
                _itemForSale[_orderId].tokenId = _tokenId;
                _itemForSale[_orderId].sell_amount = _sell_amount;
                _itemForSale[_orderId].price = _price;
                _itemForSale[_orderId].currency = _currency;
                _itemForSale[_orderId].forSale = true;
                
                setApprovalForAll(_marketaddress, true);
                
                _currentSaleAmount++;
                _incrementOrderId();
                
                emit ItemSelled(_tokenId);
            }
            else //renewed selling data
            {
                _itemForSale[_items[msg.sender][_tokenId].orderId].seller = msg.sender;
                _itemForSale[_items[msg.sender][_tokenId].orderId].tokenId = _tokenId;
                _itemForSale[_items[msg.sender][_tokenId].orderId].sell_amount = _sell_amount;
                _itemForSale[_items[msg.sender][_tokenId].orderId].price = _price;
                _itemForSale[_items[msg.sender][_tokenId].orderId].currency = _currency;
                _itemForSale[_items[msg.sender][_tokenId].orderId].forSale = true;
                
                setApprovalForAll(_marketaddress, true);
                
                emit ItemSelled(_tokenId);
            }
        }
        else // selled first time
        {
            uint _orderId = _getNextOrderId();
            _items[msg.sender][_tokenId].orderId = _orderId;
            
            _itemForSale[_orderId].seller = msg.sender;
            _itemForSale[_orderId].tokenId = _tokenId;
            _itemForSale[_orderId].sell_amount = _sell_amount;
            _itemForSale[_orderId].price = _price;
            _itemForSale[_orderId].currency = _currency;
            _itemForSale[_orderId].forSale = true;
            
            setApprovalForAll(_marketaddress, true);
            
            _currentSaleAmount++;
            _incrementOrderId();
            
            emit ItemSelled(_tokenId);
        }        
    }
    
    //_from : seller, _to : buyer
    function Transaction_Item(address _from, address _to, uint256 _tokenId, uint256 _amount) external payable 
    { 
        address payable owner = address(uint160(_from));
        require(owner != msg.sender, "Owner can't buy their tokens.");
        require(owner != address(0), "Owner must not be zero address.");
        
        //Item storage _item = _items[_from][_tokenId];
        require(msg.value >= _itemForSale[_items[_from][_tokenId].orderId].price * _amount, "Not Enough Pays.");
        require(_itemForSale[_items[_from][_tokenId].orderId].sell_amount > 0, "Not For Sale.");
        require(_itemForSale[_items[_from][_tokenId].orderId].sell_amount >= _amount, "Not Enough Tokens.");
        require(_itemForSale[_items[_from][_tokenId].orderId].currency == 0, "This token can buy on ethereum.");
        
        //approve(_to, _order.itemId);
        owner.transfer(_itemForSale[_items[_from][_tokenId].orderId].price * _amount * (1 - fee_percent / 100));
        fee_addr.transfer(_itemForSale[_items[_from][_tokenId].orderId].price * _amount * (fee_percent / 100));
        
        safeTransferFrom(owner, _to, _tokenId, _amount, "");
        
        _itemForSale[_items[_from][_tokenId].orderId].sell_amount -= _amount;
        _items[_from][_tokenId].amount -= _amount;
        
        _items[_to][_tokenId].amount += _amount;
        
       
        if(_items[_from][_tokenId].amount == 0)
        {
            _itemForSale[_items[_from][_tokenId].orderId].forSale = false;
            
            if(_currentSaleAmount > 0)
            {
                _currentSaleAmount--;
            }
        }
    }
    
    //_from : seller, _to : buyer
    function Transaction_Item_WithToken(address _from, address _to, uint256 _tokenId, uint256 _amount, uint256 _price) external 
    {
        address owner = address(uint160(_from));
        require(owner != msg.sender, "Owner can't buy their tokens.");
        require(owner != address(0), "Owner must not be zero address.");
    
        //Item storage _item = _items[_from][_tokenId];
        require(_price >= _itemForSale[_items[_from][_tokenId].orderId].price * _amount, "Not Enough Pays.");
        require(_itemForSale[_items[_from][_tokenId].orderId].sell_amount > 0, "Not For Sale.");
        require(_itemForSale[_items[_from][_tokenId].orderId].sell_amount >= _amount, "Not Enough Tokens");
        require(_itemForSale[_items[_from][_tokenId].orderId].currency == 1, "This token can buy on DVI token.");
        
        //approve(_to, _order.itemId);
        bool result = currencyToken.transferFrom(_to, owner, _price);
        require(result, "transfer token failed");
        
        safeTransferFrom(owner, _to, _tokenId, _amount, "");
        
        _itemForSale[_items[_from][_tokenId].orderId].sell_amount -= _amount;
        _items[_from][_tokenId].amount -= _amount;
        
        _items[_to][_tokenId].amount += _amount;
        
        if(_items[_from][_tokenId].amount == 0)
        {
            _itemForSale[_items[_from][_tokenId].orderId].forSale = false;
            
            if(_currentSaleAmount > 0)
            {
                _currentSaleAmount--;
            }
        }
    }
    
    function _getNextOrderId() private view returns (uint256) 
    {
        return _currentOrderId.add(1);
    }
    
    function _incrementOrderId() private 
    {
        _currentOrderId++;
    }
    
    
    function Get_Orders(address _seller, uint _tokenId) 
    public 
    view 
    returns(uint256 _id, uint256 _price, uint256 _currency, uint256 _amount, address _owner)
    {
        Order storage _order = _itemForSale[_items[_seller][_tokenId].orderId];
        
        _id = _tokenId;
        _price = _order.price;
        _currency = _order.currency;
        _amount = _order.sell_amount;
        _owner = _order.seller;
    }
    
    function TokensOfOwner(address _owner) public view returns(uint[] memory)
    {
        uint256 _balance = 0;
        
        uint256 _maxTokenId = _currentTokenId;
        uint256 _tokenId = 0;
        
        
        for(_tokenId = 1; _tokenId < _maxTokenId + 1; _tokenId++)
        {
            if(balanceOf(_owner, _tokenId) > 0)
            {
                _balance++;
            }
        }
        
        if(_balance == 0)
        {
            return new uint256[](0);
        }
        else
        {
            uint[] memory _result = new uint[](_balance);
            uint256 _idx = 0;
            
            for(_tokenId = 1; _tokenId < _maxTokenId + 1; _tokenId++)
            {
                if(balanceOf(_owner, _tokenId) > 0)
                {
                    _result[_idx] = _tokenId;
                    _idx++;
                }
            }
            
            return _result;
        }
    }
    
    function Get_SellingItems() 
    public
    view
    returns(uint[] memory, address[] memory, uint[] memory, uint[] memory, uint[] memory)
    {
        uint[] memory _result_Id = new uint[](_currentSaleAmount);
        address[] memory _result_Addr = new address[](_currentSaleAmount);
        uint[] memory _result_Price = new uint[](_currentSaleAmount); 
        uint[] memory _result_Currency = new uint[](_currentSaleAmount);
        uint[] memory _result_SellAmount = new uint[](_currentSaleAmount);

        uint256 _maxOrderId = _currentOrderId;
        uint256 _idx = 0;
        
        uint256 _orderId = 0;
        for(_orderId = 1; _orderId < _maxOrderId + 1; _orderId++)
        {
            if(_itemForSale[_orderId].forSale == true)
            {
                _result_Id[_idx] = _orderId;
                _result_Addr[_idx] = _itemForSale[_orderId].seller;
                _result_Price[_idx] = _itemForSale[_orderId].price;
                _result_Currency[_idx] = _itemForSale[_orderId].currency;
                _result_SellAmount[_idx] = _itemForSale[_orderId].sell_amount;
                _idx++;
            }
        }   
            
        return (_result_Id, _result_Addr, _result_Price, _result_Currency, _result_SellAmount);
    }
    
    //// About Fee
    function Change_Fee_Addr(address payable _addr) onlyOwner public returns(address)
    {
        fee_addr = _addr;
        
        return(fee_addr);
    }
    
    function Show_Fee_Addr() onlyOwner public view returns(address)
    {
        return(fee_addr);
    }
    
    function Change_Fee_Percent(uint256 _percent) onlyOwner public returns(uint256)
    {
        fee_percent = _percent;
        
        return(fee_percent);
    }
    
    function Show_Fee_Percent() public view returns(uint256){
        return(fee_percent);
    }
    
    function Change_Fee_On_Mint(uint256 _new) onlyOwner public returns(uint256)
    {
        fee_on_mint = _new;
        
        return(fee_on_mint);
    }
    
    function Show_Fee_On_Mint() public view returns(uint256)
    {
        return(fee_on_mint);
    }
    
    
    
    
    function Set_Base_URI(string memory _new) onlyOwner public returns(string memory)
    {
        _setURI(_new);
        return _new;
    }
    
}
function Transaction_Item_WithToken(address _from, address _to, uint256 _tokenId, uint256 _amount, uint256 _price) external 
{
    address owner = address(uint160(_from));
    require(owner != msg.sender, "Owner can't buy their tokens.");
    require(owner != address(0), "Owner must not be zero address.");
    
    //Item storage _item = _items[_from][_tokenId];
    require(_price >= _itemForSale[_items[_from][_tokenId].orderId].price * _amount, "Not Enough Pays.");
    require(_itemForSale[_items[_from][_tokenId].orderId].sell_amount > 0, "Not For Sale.");
    require(_itemForSale[_items[_from][_tokenId].orderId].sell_amount >= _amount, "Not Enough Tokens");
    require(_itemForSale[_items[_from][_tokenId].orderId].currency == 1, "This token can buy on DVI token.");
        
    //approve(_to, _order.itemId);
    bool result = currencyToken.transferFrom(_to, owner, _price);
    require(result, "transfer token failed");
        
    safeTransferFrom(owner, _to, _tokenId, _amount, "");
        
    _itemForSale[_items[_from][_tokenId].orderId].sell_amount -= _amount;
    _items[_from][_tokenId].amount -= _amount;
        
    _items[_to][_tokenId].amount += _amount;
        
    if(_items[_from][_tokenId].amount == 0)
    {
        _itemForSale[_items[_from][_tokenId].orderId].forSale = false;
            
        if(_currentSaleAmount > 0)
        {
            _currentSaleAmount--;
        }
    }
}

//_from : seller, _to : buyer