package qora.account;
import java.math.BigDecimal;
import java.util.Arrays;
import controller.Controller;
import qora.BlockGenerator;
import qora.block.Block;
import qora.transaction.Transaction;
import database.DBSet;
public class Account {
public static final int ADDRESS_LENGTH = 25;
protected String address;
private byte[] lastBlockSignature;
private BigDecimal generatingBalance;
protected Account()
{
this.generatingBalance = BigDecimal.ZERO.setScale(8);
}
public Account(String address)
{
this.address = address;
}
public String getAddress()
{
return address;
}
//BALANCE
public BigDecimal getUnconfirmedBalance()
{
return this.getUnconfirmedBalance(DBSet.getInstance());
}
public BigDecimal getUnconfirmedBalance(DBSet db)
{
return Controller.getInstance().getUnconfirmedBalance(this.getAddress());
}
public BigDecimal getConfirmedBalance()
{
return this.getConfirmedBalance(DBSet.getInstance());
}
public BigDecimal getConfirmedBalance(DBSet db)
{
return db.getBalanceMap().get(getAddress());
}
public BigDecimal getConfirmedBalance(long key)
{
return this.getConfirmedBalance(key, DBSet.getInstance());
}
public BigDecimal getConfirmedBalance(long key, DBSet db)
{
return db.getBalanceMap().get(getAddress(), key);
}
public void setConfirmedBalance(BigDecimal amount)
{
this.setConfirmedBalance(amount, DBSet.getInstance());
}
public void setConfirmedBalance(BigDecimal amount, DBSet db)
{
//UPDATE BALANCE IN DB
db.getBalanceMap().set(getAddress(), amount);
}
public void setConfirmedBalance(long key, BigDecimal amount)
{
this.setConfirmedBalance(key, amount, DBSet.getInstance());
}
public void setConfirmedBalance(long key, BigDecimal amount, DBSet db)
{
//UPDATE BALANCE IN DB
db.getBalanceMap().set(getAddress(), key, amount);
}
public BigDecimal getBalance(int confirmations)
{
return this.getBalance(confirmations, DBSet.getInstance());
}
public BigDecimal getBalance(int confirmations, DBSet db)
{
//CHECK IF UNCONFIRMED BALANCE
if(confirmations <= 0)
{
return this.getUnconfirmedBalance(db);
}
//IF 1 CONFIRMATION
if(confirmations == 1)
{
return this.getConfirmedBalance(db);
}
//GO TO PARENT BLOCK 10
BigDecimal balance = this.getConfirmedBalance(db);
Block block = db.getBlockMap().getLastBlock();
for(int i=1; i<confirmations && block != null && block instanceof Block; i++)
{
for(Transaction transaction: block.getTransactions())
{
if(transaction.isInvolved(this))
{
balance = balance.subtract(transaction.getAmount(this));
}
}
block = block.getParent(db);
}
//RETURN
return balance;
}
private void updateGeneratingBalance(DBSet db)
{
//CHECK IF WE NEED TO RECALCULATE
if(this.lastBlockSignature == null)
{
this.lastBlockSignature = db.getBlockMap().getLastBlockSignature();
calculateGeneratingBalance(db);
}
else
{
//CHECK IF WE NEED TO RECALCULATE
if(!Arrays.equals(this.lastBlockSignature, db.getBlockMap().getLastBlockSignature()))
{
this.lastBlockSignature = db.getBlockMap().getLastBlockSignature();
calculateGeneratingBalance(db);
}
}
}
public void calculateGeneratingBalance(DBSet db)
{
//CONFIRMED BALANCE + ALL NEGATIVE AMOUNTS IN LAST 9 BLOCKS
BigDecimal balance = this.getConfirmedBalance(db);
Block block = db.getBlockMap().getLastBlock();
for(int i=1; i<BlockGenerator.RETARGET && block != null && block.getHeight(db) > 1; i++)
{
for(Transaction transaction: block.getTransactions())
{
if(transaction.isInvolved(this))
{
if(transaction.getAmount(this).compareTo(BigDecimal.ZERO) == 1)
{
balance = balance.subtract(transaction.getAmount(this));
}
}
}
block = block.getParent(db);
}
//DO NOT GO BELOW 0
if(balance.compareTo(BigDecimal.ZERO) == -1)
{
balance = BigDecimal.ZERO.setScale(8);
}
this.generatingBalance = balance;
}
public BigDecimal getGeneratingBalance()
{
return this.getGeneratingBalance(DBSet.getInstance());
}
public BigDecimal getGeneratingBalance(DBSet db)
{
//UPDATE
updateGeneratingBalance(db);
//RETURN
return this.generatingBalance;
}
//REFERENCE
public byte[] getLastReference()
{
return this.getLastReference(DBSet.getInstance());
}
public byte[] getLastReference(DBSet db)
{
return db.getReferenceMap().get(this);
}
public void setLastReference(byte[] reference)
{
this.setLastReference(reference, DBSet.getInstance());
}
public void setLastReference(byte[] reference, DBSet db)
{
db.getReferenceMap().set(this, reference);
}
public void removeReference()
{
this.removeReference(DBSet.getInstance());
}
public void removeReference(DBSet db)
{
db.getReferenceMap().delete(this);
}
//TOSTRING
@Override
public String toString()
{
return this.getBalance(0).toPlainString() + " - " + this.getAddress();
}
public String toString(long key)
{
return this.getConfirmedBalance(key).toPlainString() + " - " + this.getAddress();
}
//EQUALS
@Override
public boolean equals(Object b)
{
if(b instanceof Account)
{
return this.getAddress().equals(((Account) b).getAddress());
}
return false;
}
}