contract FactbarDeed is ERC721Deed, Pausable, PullPayment, ReentrancyGuard {

  using SafeMath for uint256;

  /* Events */
  // When a deed is created by the contract owner.
  event Creation(uint256 indexed id, bytes32 indexed name, address factTeam);

  // When a deed is appropriated, the ownership of the deed is transferred to the new owner.
  // The old owner is reimbursed, and he gets the new price minus the transfer fee.
  event Appropriation(uint256 indexed id, address indexed oldOwner, 
  address indexed newOwner, uint256 oldPrice, uint256 newPrice,
  uint256 transferFeeAmount, uint256 excess,  uint256 oldOwnerPaymentAmount );

  // Payments to the deed's fee address via PullPayment are also supported by this contract.
  event Payment(uint256 indexed id, address indexed sender, address 
  indexed factTeam, uint256 amount);

  // Factbars, like facts, cannot be destroyed. So we have removed 
  // all the deletion and desctruction features

  // The data structure of the Factbar deed
  
  struct Factbar {
    bytes32 name;
    address factTeam;
    uint256 price;
    uint256 created;
  }

  // Mapping from _deedId to Factbar
  mapping (uint256 => Factbar) private deeds;

  // Mapping from deed name to boolean indicating if the name is already taken
  mapping (bytes32 => bool) private deedNameExists;

  // Needed to make all deeds discoverable. The length of this array also serves as our deed ID.
  uint256[] private deedIds;

  // These are the admins who have the power to create deeds.
  mapping (address => bool) private admins;

  /* Variables in control of owner */

  // The contract owner can change the initial price of deeds at Creation.
  uint256 private creationPrice = 0.0005 ether; 

  // The contract owner can change the base URL, in case it becomes necessary. It is needed for Metadata.
  string public url = "https://fact-bar.org/facts/";

  // ERC-165 Metadata
  bytes4 internal constant INTERFACE_SIGNATURE_ERC165 = // 0x01ffc9a7
      bytes4(keccak256('supportsInterface(bytes4)'));

  bytes4 internal constant INTERFACE_SIGNATURE_ERC721 = // 0xda671b9b
      bytes4(keccak256('ownerOf(uint256)')) ^
      bytes4(keccak256('countOfDeeds()')) ^
      bytes4(keccak256('countOfDeedsByOwner(address)')) ^
      bytes4(keccak256('deedOfOwnerByIndex(address,uint256)')) ^
      bytes4(keccak256('approve(address,uint256)')) ^
      bytes4(keccak256('takeOwnership(uint256)'));

  bytes4 internal constant INTERFACE_SIGNATURE_ERC721Metadata = // 0x2a786f11
      bytes4(keccak256('name()')) ^
      bytes4(keccak256('symbol()')) ^
      bytes4(keccak256('deedUri(uint256)'));


  function FactbarDeed() public {}

  // payable removed from fallback function following audit
  function() public {}

  modifier onlyExistingNames(uint256 _deedId) {
    require(deedNameExists[deeds[_deedId].name]);
    _;
  }

  modifier noExistingNames(bytes32 _name) {
    require(!deedNameExists[_name]);
    _;
  }
  
  modifier onlyAdmins() {
    require(admins[msg.sender]);
    _;
  }


   /* ERC721Metadata */

  function name()
  external pure returns (string) {
    return "Factbar";
  }

  function symbol()
  external pure returns (string) {
    return "FTBR";
  }

  function supportsInterface(bytes4 _interfaceID)
  external pure returns (bool) {
    return (
      _interfaceID == INTERFACE_SIGNATURE_ERC165
      || _interfaceID == INTERFACE_SIGNATURE_ERC721
      || _interfaceID == INTERFACE_SIGNATURE_ERC721Metadata
    );
  }

  function deedUri(uint256 _deedId)
  external view onlyExistingNames(_deedId) returns (string _uri) {
    _uri = _strConcat(url, _bytes32ToString(deeds[_deedId].name));
  }

  function deedName(uint256 _deedId)
  external view onlyExistingNames(_deedId) returns (string _name) {
    _name = _bytes32ToString(deeds[_deedId].name);
  }


  // get pending payments to address, generated from appropriations
  function getPendingPaymentAmount(address _account)
  external view returns (uint256 _balance) {
     uint256 payment = payments[_account];
    _balance = payment;
  }

  // get Ids of all deeds  
  function getDeedIds()
  external view returns (uint256[]) {
    return deedIds;
  }
 
  /// Logic for pricing of deeds
  function nextPriceOf (uint256 _deedId) public view returns (uint256 _nextPrice) {
    return calculateNextPrice(priceOf(_deedId));
  }

  uint256 private increaseLimit1 = 0.02 ether;
  uint256 private increaseLimit2 = 0.5 ether;
  uint256 private increaseLimit3 = 2.0 ether;
  uint256 private increaseLimit4 = 5.0 ether;

  function calculateNextPrice (uint256 _price) public view returns (uint256 _nextPrice) {
    if (_price < increaseLimit1) {
      return _price.mul(200).div(100);
    } else if (_price < increaseLimit2) {
      return _price.mul(135).div(100);
    } else if (_price < increaseLimit3) {
      return _price.mul(125).div(100);
    } else if (_price < increaseLimit4) {
      return _price.mul(117).div(100);
    } else {
      return _price.mul(115).div(100);
    }
  }

  function calculateTransferFee (uint256 _price) public view returns (uint256 _devCut) {
    if (_price < increaseLimit1) {
      return _price.mul(5).div(100); // 5%
    } else if (_price < increaseLimit2) {
      return _price.mul(4).div(100); // 4%
    } else if (_price < increaseLimit3) {
      return _price.mul(3).div(100); // 3%
    } else if (_price < increaseLimit4) {
      return _price.mul(3).div(100); // 3%
    } else {
      return _price.mul(3).div(100); // 3%
    }
  }


  // Forces the transfer of the deed to a new owner, 
  // if a higher price was paid. This functionality can be paused by the owner.
  function appropriate(uint256 _deedId)
  external whenNotPaused nonReentrant payable {

    // Get current price of deed
    uint256 price = priceOf(_deedId);

     // The current owner is forbidden to appropriate himself.
    address oldOwner = this.ownerOf(_deedId);
    address newOwner = msg.sender;
    require(oldOwner != newOwner);
    
    // price must be more than zero
    require(priceOf(_deedId) > 0); 
    
    // offered price must be more than or equal to the current price
    require(msg.value >= price); 

    /// Any over-payment by the buyer will be sent back to him/her
    uint256 excess = msg.value.sub(price);

    // Clear any outstanding approvals and transfer the deed.*/
    clearApprovalAndTransfer(oldOwner, newOwner, _deedId);
    uint256 nextPrice = nextPriceOf(_deedId);
    deeds[_deedId].price = nextPrice;
    
    // transfer fee is calculated
    uint256 transferFee = calculateTransferFee(price);

    /// previous owner gets entire new payment minus the transfer fee
    uint256 oldOwnerPayment = price.sub(transferFee);

    /// using Pullpayment for safety
    asyncSend(factTeamOf(_deedId), transferFee);
    asyncSend(oldOwner, oldOwnerPayment);

    if (excess > 0) {
       asyncSend(newOwner, excess);
    }

    emit Appropriation(_deedId, oldOwner, newOwner, price, nextPrice,
    transferFee, excess, oldOwnerPayment);
  }

  // these events can be turned on to make up for Solidity's horrifying logging situation
  // event logUint(address add, string text, uint256 value);
  // event simpleLogUint(string text, uint256 value);

  // Send a PullPayment.
  function pay(uint256 _deedId)
  external nonReentrant payable {
    address factTeam = factTeamOf(_deedId);
    asyncSend(factTeam, msg.value);
    emit Payment(_deedId, msg.sender, factTeam, msg.value);
  }

  // The owner can only withdraw what has not been assigned to the transfer fee address as PullPayments.
  function withdraw()
  external nonReentrant {
    withdrawPayments();
    if (msg.sender == owner) {
      // The contract's balance MUST stay backing the outstanding withdrawals.
      //  Only the surplus not needed for any backing can be withdrawn by the owner.
      uint256 surplus = address(this).balance.sub(totalPayments);
      if (surplus > 0) {
        owner.transfer(surplus);
      }
    }
  }

  /* Owner Functions */

  // The contract owner creates deeds. Newly created deeds are
  // initialised with a name and a transfer fee address
  // only Admins can create deeds
  function create(bytes32 _name, address _factTeam)
  public onlyAdmins noExistingNames(_name) {
    deedNameExists[_name] = true;
    uint256 deedId = deedIds.length;
    deedIds.push(deedId);
    super._mint(owner, deedId);
    deeds[deedId] = Factbar({
      name: _name,
      factTeam: _factTeam,
      price: creationPrice,
      created: now
      // deleted: 0
    });
    emit Creation(deedId, _name, owner);
  }

  // the owner can add and remove admins as per his/her whim

  function addAdmin(address _admin)  
  public onlyOwner{
    admins[_admin] = true;
  }

  function removeAdmin (address _admin)  
  public onlyOwner{
    delete admins[_admin];
  }

  // the owner can set the creation price 

  function setCreationPrice(uint256 _price)
  public onlyOwner {
    creationPrice = _price;
  }

  function setUrl(string _url)
  public onlyOwner {
    url = _url;
  }

  /* Other publicly available functions */

  // Returns the last paid price for this deed.
  function priceOf(uint256 _deedId)
  public view returns (uint256 _price) {
    _price = deeds[_deedId].price;
  }

  // Returns the current transfer fee address
  function factTeamOf(uint256 _deedId)
  public view returns (address _factTeam) {
    _factTeam = deeds[_deedId].factTeam;
  }


  /* Private helper functions */        

  function _bytes32ToString(bytes32 _bytes32)
  private pure returns (string) {
    bytes memory bytesString = new bytes(32);
    uint charCount = 0;
    for (uint j = 0; j < 32; j++) {
      byte char = byte(bytes32(uint(_bytes32) * 2 ** (8 * j)));
      if (char != 0) {
        bytesString[charCount] = char;
        charCount++;
      }
    }
    bytes memory bytesStringTrimmed = new bytes(charCount);
    for (j = 0; j < charCount; j++) {
      bytesStringTrimmed[j] = bytesString[j];
    }

    return string(bytesStringTrimmed);
  }

  function _strConcat(string _a, string _b)
  private pure returns (string) {
    bytes memory _ba = bytes(_a);
    bytes memory _bb = bytes(_b);
    string memory ab = new string(_ba.length + _bb.length);
    bytes memory bab = bytes(ab);
    uint k = 0;
    for (uint i = 0; i < _ba.length; i++) bab[k++] = _ba[i];
    for (i = 0; i < _bb.length; i++) bab[k++] = _bb[i];
    return string(bab);
  }

}
function nextPriceOf (uint256 _deedId) public view returns (uint256 _nextPrice) {
  return calculateNextPrice(priceOf(_deedId));
}

/// Logic for pricing of deeds