import java.util.*;

//Need to write function to output plate to a file that I can read in.

public class Plate {
    private List<List<Integer[]>> wells;
    private Random rand = new Random();
    private int size;
    private double error;
    private Integer[] concentrations;
    private double stdDev;

    public Plate (int size, double error, Integer[] concentrations, double stdDev) {
        this.size = size;
        this.error = error;
        this.concentrations = concentrations;
        this.stdDev = stdDev;
        wells = new ArrayList<>();
    }

    public Plate(List<List<Integer[]>> wells){
        this.wells = wells;
        this.size = wells.size();
    }

    public void fillWells(List<Integer[]> cells) {
        int numSections = concentrations.length;
        int section = 0;
        double m;
        int n;
        //testing
        //System.out.println("Cell size: " + cells.get(0).length);
        while (section < numSections){
            for (int i = 0; i < (size / numSections); i++) {
                List<Integer[]> well = new ArrayList<>();
                for (int j = 0; j < concentrations[section]; j++) {
                    do {
                        m = (rand.nextGaussian() * stdDev) + (cells.size() / 2);
                    } while (m >= cells.size() || m < 0);
                    n = (int) Math.floor(m);
                    Integer[] cellToAdd = cells.get(n).clone();
                    for(int k = 0; k < cellToAdd.length; k++){
                        if(Math.abs(rand.nextDouble()) < error){//error applied to each peptide
                            cellToAdd[k] = -1;
                        }
                    }
                    well.add(cellToAdd);
                }
                wells.add(well);
            }
            section++;
        }
    }

    public void writePlateToFile(String filename) {


    }

    public Integer[] getConcentrations(){
        return concentrations;
    }

    public int getSize(){
        return size;
    }

    public double getStdDev() {
        return stdDev;
    }

    public double getError() {
        return error;
    }

    public List<List<Integer[]>> getWells() {
        return wells;
    }


    //returns a map of counts of all the CDR3s (alphas and betas) in all wells
    public Map<Integer, Integer>assayWellsCDR3(){
        return this.assayWellsCDR3(0, size);
    }
    //returns a map of counts of all the CDR3 alphas in all wells
    public Map<Integer, Integer> assayWellsCDR3Alpha() {
        return this.assayWellsCDR3Alpha(0, size);
    }
    //returns a map of counts of all the CDR3 betas in all wells
    public Map<Integer, Integer> assayWellsCDR3Beta() {
        return this.assayWellsCDR3Beta(0, size);
    }
    //returns a map of counts of all CDR1s (alphas and betas) in all wells
    public Map<Integer, Integer> assayWellsCDR1(){
        return this.assayWellsCDR1(0, size);
    }
    //returns a map of counts of all the CDR1 alphas in all wells
    public Map<Integer, Integer> assayWellsCDR1Alpha() {
        return this.assayWellsCDR1Alpha(0, size);
    }
    //returns a map of counts of all the CDR1 betas in all wells
    public Map<Integer, Integer> assayWellsCDR1Beta() {
        return this.assayWellsCDR1Beta(0, size);
    }

    //returns a map of counts of the CDR3s (alphas and betas) in a specific well
    public Map<Integer, Integer>assayWellsCDR3(int n){
        return this.assayWellsCDR3(n, n+1);
    }
    //returns a map of counts of the CDR1s (alphas and betas) in a specific well
    public Map<Integer, Integer> assayWellsCDR1(int n){
        return this.assayWellsCDR1(n, n+1);
    }
    //returns a map of counts of the CDR3 alphas in a specific well
    public Map<Integer, Integer> assayWellsCDR3Alpha(int n) {
        return this.assayWellsCDR3Alpha(n, n+1);
    }
    //returns a map of counts of the CDR3 betas in a specific well
    public Map<Integer, Integer> assayWellsCDR3Beta(int n) {
        return this.assayWellsCDR3Beta(n, n+1);
    }
    //returns a map of counts of the CDR1 alphas in a specific well
    public Map<Integer, Integer> assayWellsCDR1Alpha(int n) {
        return this.assayWellsCDR1Alpha(n, n+1);
    }
    //returns a map of counts of the CDR1 betas in a specific well
    public Map<Integer, Integer> assayWellsCDR1Beta(int n) {
        return this.assayWellsCDR1Beta(n, n+1);
    }


    //returns a map of the counts of the CDR3s (alphas and betas) in a range of wells
    public Map<Integer, Integer>assayWellsCDR3(int start, int end){
        Map<Integer,Integer> assay = new HashMap<>();
        for(int i = start; i < end; i++){
            countCDR3Alphas(assay, wells.get(i));
            countCDR3Betas(assay,wells.get(i));
        }
        return assay;
    }
    //returns a map of the counts of the CDR1s (alphas and betas) in a range of wells
    public Map<Integer, Integer>assayWellsCDR1(int start, int end){
        Map<Integer,Integer> assay = new HashMap<>();
        for(int i = start; i < end; i++){
            countCDR1Alphas(assay, wells.get(i));
            countCDR1Betas(assay,wells.get(i));
        }
        return assay;
    }
    //returns a map of the counts of the CDR3 alphas in a range of wells
    public Map<Integer, Integer> assayWellsCDR3Alpha(int start, int end) {
        Map<Integer, Integer> assay = new HashMap<>();
        for(int i = start; i < end; i++){
            countCDR3Alphas(assay, wells.get(i));
        }
        return assay;
    }
    //returns a map of the counts of the CDR3 betas in a range of wells
    public Map<Integer, Integer> assayWellsCDR3Beta(int start, int end) {
        Map<Integer, Integer> assay = new HashMap<>();
        for(int i = start; i < end; i++){
            countCDR3Betas(assay, wells.get(i));
        }
        return assay;
    }
    //returns a map of the counts of the CDR1 alphas in a range of wells
    public Map<Integer, Integer> assayWellsCDR1Alpha(int start, int end) {
        Map<Integer, Integer> assay = new HashMap<>();
        for(int i = start; i < end; i++){
            countCDR1Alphas(assay, wells.get(i));
        }
        return assay;
    }
    //returns a map of the counts of the CDR1 betas in a range of wells
    public Map<Integer, Integer> assayWellsCDR1Beta(int start, int end) {
        Map<Integer, Integer> assay = new HashMap<>();
        for(int i = start; i < end; i++){
            countCDR1Betas(assay, wells.get(i));
        }
        return assay;
    }


    //given a map, counts distinct CDR3 alphas in a well
    private void countCDR3Alphas(Map<Integer, Integer> wellMap, List<Integer[]> well){
        for(Integer[] cell : well) {
            if(cell[0] != -1){
                //keys are alphas, value is how many of them have been assayed
                wellMap.merge(cell[0], 1, (oldValue, newValue) -> oldValue + newValue);
            }
        }
    }
    //given a map, counts distinct CDR3 betas in a well
    private void countCDR3Betas(Map<Integer, Integer> wellMap, List<Integer[]> well){
        for(Integer[] cell : well) {
            if(cell[1] != -1){
                wellMap.merge(cell[1], 1, (oldValue, newValue) -> oldValue + newValue);
            }
        }
    }
    //given a map, counts distinct CDR1 alphas in a well
    private void countCDR1Alphas(Map<Integer, Integer> wellMap, List<Integer[]> well){
        for(Integer[] cell: well){
            if(cell[2] != -1){
                wellMap.merge(cell[2], 1, (oldValue, newValue) -> oldValue + newValue);
            }
        }
    }
    //given a map, counts distinct CDR1 betas in a well
    private void countCDR1Betas(Map<Integer, Integer> wellMap, List<Integer[]> well){
        for(Integer[] cell: well){
            if(cell[3] != -1){
                wellMap.merge(cell[3], 1, (oldValue, newValue) -> oldValue + newValue);
            }
        }
    }

}
