package inventory;

/**
 * Created on 09.04.2022 15:19:33
 *
 * @author Alexander Mikhailovich Kovshov
 */
public class InventoryModel21 {

    public static void main(String[] args) {

        int n = 3;                    //число промежутков времени (месяцев)
        int max = 3;                  //наибольшее значение для остатка на складе
        double[] inprice =  {3, 6, 8};//цены закупочные за каждый месяц              
        double[] outprice = {3, 7, 9};//цены отпускные за каждый месяц          
        double[] store =    {1, 1, 1};//стоимость хранения единицы товара за каждый месяц
        int[]    need =     {2, 2, 2};//потребность за каждый месяц

        double[] profit = new double[max + 1];          //Наибольшая прибыль за все последующие месяцы в зависимости от остатка на конец текущего месяца.
        double[] profitTemp = new double[max + 1];      //Прибыль за текущий месяц и все последующие за ним месяцы в зависимости от остатка на начало месяца.
        int[][] restFromProfit = new int[n][max + 1];   //Наилучший остаток на конец каждого месяца в зависимости от остатка на начало каждого месяца.
        int[][] saleFromProfit = new int[n][max + 1];   //Наилучший объём продаж в каждом месяце в зависимости от остатка на начало каждого месяца.

        //Вычисление возможной прибыли за конечный (n-й) месяц в зависимости от остатка на складе.
        //r --- возможный остаток на складе от предыдущего месяца
        for (int r = 0; r <= max; r++) {

            profit[r] = Integer.MIN_VALUE;
            double profitFromSale ;
            for (int sale = 0; sale <= need[n - 1]; sale++) {
                int add_r = sale - r; //Приобретение за конечный (n-й) месяц.
                if (add_r < 0) {
                    profitFromSale = Integer.MIN_VALUE;

                    continue;
                }
                profitFromSale 
                        = outprice[n - 1] * sale
                        - inprice[n - 1] * add_r
                        - store[n - 1] * ((double) r / 2);
                if (profitFromSale > profit[r]) {
                    profit[r] = profitFromSale;
                    saleFromProfit[n - 1][r] = sale;

                }

            }
        }

        for (int m = n - 1; m > 0; m--) {
            //Вычисление возможной прибыли за m-й месяц в зависимости от остатка на складе.
            //r  --- возможный остаток на складе на начало месяца. 
            //rr --- возможный остаток на складе на конец месяца.
            for (int r = 0; r <= max; r++) {
                profitTemp[r] = Integer.MIN_VALUE;
                for (int rr = 0; rr <= max; rr++) {

                    for (int sale = 0; sale <= need[m - 1]; sale++) {
                    //Вычисление прибыли за m-й и последующие месяцы в зависимости от остатка на конец месяца
                    //и объёма продаж в этом месяце.
                        //Приобретение за m-й месяц.
                        int add_r = sale + rr - r;
                        if (add_r < 0) {
                            continue;   //Отрицательное приобретение невозможно.
                        } else {
                            //Прибыль за этот и последующие месяцы в зависимости от остатков 
                            //на начало и конец этого месяца (r и rr).
                            double H
                                    = outprice[m - 1] * sale
                                    - inprice[m - 1] * add_r
                                    - store[m - 1] * ((double) (r + rr) / 2) + profit[rr];
                            //Если прибыль для данного остатка rr на конец месяца оказалась самой большой
                            //среди прибылей для других остатков на конец месяца при данном остатке r на
                            //начало месяца, то эта пока наибольшая прибыль сохраняется.
                            if (H > profitTemp[r]) {
                                profitTemp[r] = H;              //Сохраняется пока наилучшая прибыль для данного входящего остатка r.
                                restFromProfit[m - 1][r] = rr;  //Сохраняется и пока наилучший остаток rr.
                                saleFromProfit[m - 1][r] = sale;//Сохраняется и пока наилучшая продажа sale.

                            }


                        }
                    }
                }
            }
            //Значение наилучшей прибыли теперь будет включать и прибыль за текущий месяц m.
            System.out.print("profit");
            for (int i = 0; i < profit.length; i++) {
                profit[i] = profitTemp[i];

            }

        }

        System.out.println("=========== План закупок ===============");
        for (int m = 0, r = 0; m < n; m++) {

            System.out.println((m + 1)
                    + ";  Спрос: " + need[m]
                    + ";  Продажи: " + saleFromProfit[m][r]
                    + ";  Остаток на начало: " + r
                    + ";  Остаток на конец: " + restFromProfit[m][r]
                    + ";  Закупки: " + (saleFromProfit[m][r] - r + restFromProfit[m][r])
            );
            r = restFromProfit[m][r];
        }
        System.out.println("Прибыль " + profit[0]);

    }
}