/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package analyticalmethods;

import analyticalmodels.ClosedQueueModel;
import analyticalmodels.StationType;
import distributions.Distribution;

/**
 *
 * @author mattiafolcarelli
 */
public class Convolution {

    double[] visitVector;
    Distribution[] distributions;
    StationType[] stationTypes;
    ClosedQueueModel model;
    int nJobs;
    int nStations;
    double[][] g;
    private double z;

    public Convolution(ClosedQueueModel model, int nJobs) {
        this.nStations = model.getNStations();
        this.visitVector = EquazioniDiBilanciamento.getVisitVector(model.getTransitionMatrix(), nStations - 1);
        this.model = model;
        this.nJobs = nJobs;
        this.visitVector = visitVector.clone();
        this.distributions = model.getServiceTimeDistributions().clone();
        this.stationTypes = model.getStationType().clone();
        //invert(station, nStations - 1);
    }

    /*
    private void invert(int a, int b) {
        double auxv = visitVector[a];
        visitVector[a] = visitVector[b];
        visitVector[b] = auxv;

        Distribution auxd = distributions[a];
        distributions[a] = distributions[b];
        distributions[b] = auxd;

        StationType auxt = stationTypes[a];
        stationTypes[a] = stationTypes[b];
        stationTypes[b] = auxt;
    }
     */
    public void solveG() {
        g = new double[nJobs + 1][nStations + 1];

        for (int i = 0; i < nJobs + 1; i++) {
            for (int j = 0; j < nStations + 1; j++) {
                g[i][j] = 0.0;
            }
        }

        //necessario?
        for (int j = 0; j < nStations + 1; j++) {
            g[0][j] = 1.0;
        }

        for (int s = 1; s < nStations + 1; s++) {
            for (int j = 0; j < nJobs + 1; j++) {
                double sum = 0.0;
                for (int k = 0; k <= j; k++) {
                    sum += f(s, k) * g[j - k][s - 1];
                }
                g[j][s] = sum;
                /*System.out.println("g["+j+"]["+s+"]= "+g[j][s]);
                    System.out.println("g["+j+"-"+k+"]["+s+"-1]= "+g[j - k][s - 1]);
                    System.out.println(j+","+s+" f["+s+"]["+k+"]= "+f(s, k));
                    System.out.println();*/
            }
        }
    }

    /**
     * Attenzione! le stazioni nella tabella g hanno indice 1,2,3...nStation
     * invece di 0,1,2...nStation-1
     *
     * @param i
     * @param k
     * @return
     */
    public double f(int i, int k) {
        if (k == 0) {
            return 1.0;
        } else if (stationTypes[i - 1] == StationType.INFINITE_SERVER) {
            return visitVector[i - 1] * distributions[i - 1].getMean() * f(i, k - 1);
        } else if (stationTypes[i - 1] == StationType.SINGLE_SERVER) {
            return visitVector[i - 1] * distributions[i - 1].getMean() * f(i, k - 1);
        } else {
            throw new IllegalArgumentException("not supported stationType");
        }
    }

    /**
     * Restituisce il valore throughtput della stazione "station"
     *
     * @return
     */
    public double getThroughtput(int i) {
        if (nJobs == 0) {
            return 0.0;
        }
        if (i == nStations - 1) {
            return visitVector[nStations - 1] * (g[nJobs - 1][nStations] / g[nJobs][nStations]);
        } else {
            return (visitVector[i] / visitVector[nStations - 1]) * visitVector[nStations - 1] * (g[nJobs - 1][nStations] / g[nJobs][nStations]);
        }
    }

    public double getUtilization() {
        if (nJobs == 0) {
            return 0.0;
        }
        return 1 - (g[nJobs][nStations - 1] / g[nJobs][nStations]);

    }

    public double getMeanN() {
        double sum = 0;
        for (int k = 1; k <= nJobs; k++) {
            sum += k * getP(nStations, k);
        }
        return sum;
    }

    public double getP(int station, int k) {
        return f(station, k) * (g[nJobs - k][station - 1] / g[nJobs][station]);
    }

    public double getWaitingTime() {
        return getMeanN() / getThroughtput(nStations - 1);
    }

    public double[][] getG() {
        return g;
    }

    /*
    //n = x * Y
    //ri=n/xi-y
    public double getResponseTime() {
        int station = nStations - 1;
        if (nJobs == 0) {
            if (stationTypes[station] == StationType.INFINITE_SERVER) {
                return -model.getServiceTimeDistribution(station).getMean();
            } else {
                return -getWaitingTime();
            }
        } else if (stationTypes[station] == StationType.INFINITE_SERVER) {

            ///////dfsdfdfsdiokji
            return (nJobs / getThroughtput(station)) - model.getServiceTimeDistribution(station).getMean();
        } else {
            return (nJobs / getThroughtput(station)) - getWaitingTime();
        }
    }

    public double getResponseTime(int station) {
        System.out.println("RT" + nJobs + ": " + station + " " + visitVector[station] + " " + getResponseTime() + " = " + visitVector[station] * getResponseTime());

        if (station == nStations - 1) {
            return getResponseTime();
        }
        return visitVector[station] * getResponseTime();
    }*/

    private double getThroughtputnm1(int i) {
        if (nJobs <= 1) {
            return 1.0;
        }
        return (visitVector[i] / visitVector[nStations - 1]) * visitVector[nStations - 1] * (g[nJobs - 2][nStations] / g[nJobs - 1][nStations]);
    }

    public double getResponseTime() {
        if (nJobs == 0) {
            return 0.0;
        } else {
            int station = nStations - 1;
            if (stationTypes[station] == StationType.INFINITE_SERVER) {
                return (nJobs / getThroughtput(station)) - visitVector[nStations - 1] * model.getServiceTimeDistribution(station).getMean();
            } else {
                return (nJobs / getThroughtput(station)) - visitVector[nStations - 1] * model.getServiceTimeDistribution(station).getMean();
            }
        }
    }

    public double getResponseTime(int station) {
        if (nJobs == 0) {
            return 0.0;
        } else if (stationTypes[station] == StationType.INFINITE_SERVER) {
            return ((nJobs / getThroughtput(nStations - 1)) - visitVector[nStations - 1] * model.getServiceTimeDistribution(station).getMean()) * visitVector[station];
        } else {
            return ((nJobs / getThroughtput(nStations - 1)) - visitVector[nStations - 1] * model.getServiceTimeDistribution(station).getMean()) * visitVector[station];
        }
    }

    public double[] getThroughtputs() {
        double[] t = new double[nStations];
        for (int i = 0; i < t.length; i++) {
            t[i] = getThroughtput(i);
        }
        return t;
    }
}
