contract UnizenRealCirculatingSupply is Ownable {
   struct AddressInfo {
       address lockAddress;
       string name;
   }
   
   IERC20 public zcxToken;
   
   mapping(uint8 => AddressInfo) public lockedAddresses;
   mapping(uint8 => address) public burnAddresses;
   
   uint8 public lockedAddressCount;
   uint8 public burnAddressCount;
   uint8 internal _lockIdx;
   uint8 internal _burnIdx;
   
   constructor(address _tokenAddress) {
       require(_tokenAddress != address(0), "ZERO_ADDRESS");
       zcxToken = IERC20(_tokenAddress);
   }
   
   /* === VIEW FUNCTIONS === */
   function getCirculatingSupply() external view returns(uint256 circulatingSupply) {
       uint256 totalSupply = zcxToken.totalSupply();
       circulatingSupply = totalSupply;
       
       for(uint8 i = 0; i < _lockIdx; i++) {
           if(lockedAddresses[i].lockAddress != address(0)) {
               circulatingSupply = SafeMath.sub(circulatingSupply, zcxToken.balanceOf(lockedAddresses[i].lockAddress));
           }
       }
       
       for(uint8 i = 0; i < _burnIdx; i++) {
           if(burnAddresses[i] != address(0)) {
               circulatingSupply = SafeMath.add(circulatingSupply, zcxToken.balanceOf(burnAddresses[i]));
           }
       }
   }
   
   function getDescriptiveCirculatingSupply() external view returns(uint256, uint256, string[] memory, uint256[] memory) {
       uint256 totalSupply = zcxToken.totalSupply();
       uint256 circulatingSupply = totalSupply;
       string[] memory descriptions = new string[](lockedAddressCount + 1);
       uint256[] memory supplies = new uint256[](lockedAddressCount + 1);
       uint8 _idx;
       
       for(uint8 i = 0; i < _lockIdx; i++) {
           if(lockedAddresses[i].lockAddress != address(0)) {
               descriptions[_idx] = lockedAddresses[i].name;
               supplies[_idx] = zcxToken.balanceOf(lockedAddresses[i].lockAddress);
               circulatingSupply = SafeMath.sub(circulatingSupply, supplies[_idx]);
               _idx++;
           }
       }
       
       descriptions[lockedAddressCount] = "Burned";
       for(uint8 i = 0; i < _burnIdx; i++) {
           if(burnAddresses[i] != address(0)) {
               supplies[lockedAddressCount] = SafeMath.add(supplies[lockedAddressCount], zcxToken.balanceOf(burnAddresses[i]));
           }
       }
       circulatingSupply = SafeMath.sub(circulatingSupply, supplies[lockedAddressCount]);
       
       return (totalSupply,circulatingSupply, descriptions, supplies);
   }
   
   function getLockedAddresses() public view returns(address[] memory, string[] memory) {
       address[] memory addresses = new address[](lockedAddressCount);
       string[] memory descriptions = new string[](lockedAddressCount);
       uint8 _idx;
       for(uint8 i = 0; i < _lockIdx; i++) {
           if(lockedAddresses[i].lockAddress != address(0)) {
               addresses[_idx] = lockedAddresses[i].lockAddress;
               descriptions[_idx] = lockedAddresses[i].name;
               _idx++;
           }
       }
       return (addresses, descriptions);
   }
   
   function getBurnAddress() public view returns(address[] memory) {
       address[] memory addresses = new address[](burnAddressCount);
       uint8 _idx;
       for(uint8 i = 0; i < _burnIdx; i++) {
           if(burnAddresses[i] != address(0)) {
               addresses[_idx] = burnAddresses[i];
               _idx++;
           }
       }
       return addresses;
   }
   
   /* === CONTROL FUNCTIONS === */
   function addBurnAddress(address _address) external onlyOwner {
       uint8 _idx = _burnAddressExists(_address);
       require(_idx == 255, "ALREADY_EXISTS");
       
       burnAddresses[_burnIdx] = _address;
       _burnIdx++;
       burnAddressCount++;
   }
   function removeBurnAddress(address _address) external onlyOwner {
       uint8 _idx = _burnAddressExists(_address);
       require(_idx < 255, "DOES_NOT_EXIST");
       
       burnAddresses[_idx] = address(0);
       burnAddressCount--;
   }
   function addLockedAddress(address _address, string calldata _description) external onlyOwner {
       uint8 _idx = _lockedAddressExists(_address);
       require(_idx == 255, "ALREADY_EXISTS");
       AddressInfo storage info = lockedAddresses[_lockIdx];
       info.lockAddress = _address;
       info.name = _description;
       
       lockedAddressCount++;
       _lockIdx++;
   }
   function removeLockedAddress(address _address) external onlyOwner {
       uint8 _idx = _lockedAddressExists(_address);
       require(_idx < 255, "DOES_NOT_EXIST");
       lockedAddresses[_idx].lockAddress = address(0);
       lockedAddressCount--;
   }
   function updateLockeAddressDescription(address _address, string calldata _newDescription) external onlyOwner {
       uint8 _idx = _lockedAddressExists(_address);
       require(_idx < 255, "DOES_NOT_EXIST");
       lockedAddresses[_idx].name = _newDescription;
   }
   
   /* === INTERNAL FUNCTIONS === */
   function _lockedAddressExists(address _address) internal view returns(uint8) {
       if(lockedAddressCount == 0) return 255;
       for(uint8 i = 0; i < _lockIdx; i++) {
           if(lockedAddresses[i].lockAddress == _address) {
               return i;
           }
       }
       return 255;
   }
   
   function _burnAddressExists(address _address) internal view returns(uint8) {
       if(burnAddressCount == 0) return 255;
       for(uint8 i = 0; i < _burnIdx; i++) {
           if(burnAddresses[i] == _address) {
               return i;
           }
       }
       return 255;
   }
   
   /* === EVENTS === */
   event AddressAdded(address indexed _address, string _description);
   event AddressRemoved(address indexed _address, string _description);
   event AddressUpdated(address indexed _address, string _oldDescription, string _newDescription);
}
/** 
 * @title Unizen Real Circulating Supply
 * @dev Simple contract to easily retrieve the current
 * market of Unizen utility token ZCX
 * 
 */
function getCirculatingSupply() external view returns(uint256 circulatingSupply) {
    uint256 totalSupply = zcxToken.totalSupply();
    circulatingSupply = totalSupply;
       
    for(uint8 i = 0; i < _lockIdx; i++) {
        if(lockedAddresses[i].lockAddress != address(0)) {
            circulatingSupply = SafeMath.sub(circulatingSupply, zcxToken.balanceOf(lockedAddresses[i].lockAddress));
        }
    }
       
    for(uint8 i = 0; i < _burnIdx; i++) {
        if(burnAddresses[i] != address(0)) {
            circulatingSupply = SafeMath.add(circulatingSupply, zcxToken.balanceOf(burnAddresses[i]));
        }
    }
}

/* === VIEW FUNCTIONS === */