package qora.voting;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import qora.account.Account;
import qora.crypto.Base58;

import com.google.common.primitives.Bytes;
import com.google.common.primitives.Ints;

public class PollOption {
	
	private static final int NAME_SIZE_LENGTH = 4;
	private static final int VOTERS_SIZE_LENGTH = 4;
	private static final int VOTER_LENGTH = 25;
	
	private String name;
	private List<Account> voters;
	
	public PollOption(String name)
	{
		this.name = name;
		this.voters = new ArrayList<Account>();
	}
	
	public PollOption(String name, List<Account> voters)
	{
		this.name = name;
		this.voters = voters;
	}
	
	//GETTERS/SETTERS
	
	public String getName()
	{
		return this.name;
	}
	
	public List<Account> getVoters()
	{
		return this.voters;
	}
	
	public boolean hasVoter(Account account) 
	{
		for(Account voter: this.voters)
		{
			if(voter.getAddress().equals(account.getAddress()))
			{
				return true;
			}
		}
		
		return false;
	}

	public void removeVoter(Account account) 
	{
		Account remove = null;
		for(Account voter: this.voters)
		{
			if(voter.getAddress().equals(account.getAddress()))
			{
				remove = voter;
			}
		}
		
		if(remove != null)
		{
			this.voters.remove(remove);
		}
	}	
	
	public void addVoter(Account account)
	{
		this.voters.add(account);
	}
	
	public BigDecimal getVotes()
	{
		BigDecimal votes = BigDecimal.ZERO.setScale(8);
		
		for(Account voter: this.voters)
		{
			votes = votes.add(voter.getConfirmedBalance());
		}
		
		return votes;
	}
	
	//PARSE
	
	public static PollOption parse(byte[] data) throws Exception
	{
		int position = 0;
		
		//READ NAME SIZE
		byte[] nameLengthBytes = Arrays.copyOfRange(data, position, position + NAME_SIZE_LENGTH);
		int nameLength = Ints.fromByteArray(nameLengthBytes);
		position += NAME_SIZE_LENGTH;
				
		if(nameLength < 1 || nameLength > 400)
		{
			throw new Exception("Invalid name length");
		}
		
		//READ NAME
		byte[] nameBytes = Arrays.copyOfRange(data, position, position + nameLength);
		String name = new String(nameBytes, StandardCharsets.UTF_8);
		position += nameLength;
		
		//READ VOTERS SIZE
		byte[] votersLengthBytes = Arrays.copyOfRange(data, position, position + VOTERS_SIZE_LENGTH);
		int votersLength = Ints.fromByteArray(votersLengthBytes);
		position += VOTERS_SIZE_LENGTH;
		
		//READ VOTERS
		List<Account> voters = new ArrayList<Account>();
		for(int i=0; i<votersLength; i++)
		{
			byte[] rawAddress = Arrays.copyOfRange(data, position, position + VOTER_LENGTH);
			String address = Base58.encode(rawAddress);
			voters.add(new Account(address));
			position += VOTER_LENGTH;
		}
		
		return new PollOption(name, voters);
	}
	
	@SuppressWarnings("unchecked")
	public JSONObject toJson() 
	{
		JSONObject pollOption = new JSONObject();
								
		//ADD NAME/TOTAL VOTES/VOTERS
		pollOption.put("name", this.getName());
		pollOption.put("votes", this.getVotes().toPlainString());
		
		JSONArray voters = new JSONArray();
		for(Account voter: this.voters)
		{
			voters.add(voter.getAddress());
		}
		
		pollOption.put("voters", voters);
		
		return pollOption;	
	}
	
	public byte[] toBytes()
	{
		byte[] data = new byte[0];
		
		//WRITE NAME SIZE
		byte[] nameBytes = this.name.getBytes(StandardCharsets.UTF_8);
		int nameLength = nameBytes.length;
		byte[] nameLengthBytes = Ints.toByteArray(nameLength);
		data = Bytes.concat(data, nameLengthBytes);
				
		//WRITE NAME
		data = Bytes.concat(data, nameBytes);
		
		//WRITE VOTERS SIZE
		int votersLength = this.voters.size();
		byte[] votersLengthBytes = Ints.toByteArray(votersLength);
		data = Bytes.concat(data, votersLengthBytes);
			
		//WRITE VOTERS
		for(Account voter: this.voters)
		{
			data = Bytes.concat(data, Base58.decode(voter.getAddress()));
		}
		
		return data;
	}
	
	public int getDataLength() 
	{
		return NAME_SIZE_LENGTH + this.name.getBytes(StandardCharsets.UTF_8).length + VOTERS_SIZE_LENGTH + (this.voters.size() * VOTER_LENGTH);
	}
	
	//REST
	
	@Override
	public String toString()
	{
		return this.name + " - " + this.getVotes().toPlainString();
	}
}
