# TEN SKRYPT MA BYĆ URUCHAMIANY W JUPYTER NOTEBOOK (UŻYŁEM VS CODE)

# %%
import pandas as pd
import numpy as np
import pulp as plp


# %%
# Importowanie danych z pliku Excela
warehouse_supply_df = pd.read_excel(r'c:\Users\User\Desktop\Extending Power BI\kod\Extending-Power-BI-with-Python-and-R-main\Chapter10\RetailData.xlsx',
                                    sheet_name='Warehouse Supply', engine='openpyxl')
warehouse_supply = warehouse_supply_df['product_qty'].to_numpy()

country_demands_df = pd.read_excel(r'c:\Users\User\Desktop\Extending Power BI\kod\Extending-Power-BI-with-Python-and-R-main\Chapter10\RetailData.xlsx',
                                   sheet_name='Country Demand', engine='openpyxl')
country_demands = country_demands_df['product_qty'].to_numpy()

cost_matrix_df = pd.read_excel(r'c:\Users\User\Desktop\Extending Power BI\kod\Extending-Power-BI-with-Python-and-R-main\Chapter10\RetailData.xlsx',
                               sheet_name='Shipping Cost', engine='openpyxl')

n_warehouses = cost_matrix_df.nunique()['warehouse_name']
n_countries = cost_matrix_df.nunique()['country_name']

cost_matrix = cost_matrix_df['shipping_cost'].to_numpy().reshape(n_warehouses,n_countries)

# %%
# Tworzenie obiektu problemu LP
model = plp.LpProblem("supply-demand-minimize-costs-problem", plp.LpMinimize)

# Nazwy zmiennych decyzyjnych
var_indexes = [str(i)+str(j) for i in range(1, n_warehouses+1) for j in range(1, n_countries+1)]
print("Indeksy zmiennych:", var_indexes)

# %%
# Definiowanie zmiennych decyzyjnych
decision_vars = plp.LpVariable.matrix(
    name="x",            # nazwa zmiennej
    indexs=var_indexes,  # indeksy zmiennych
    cat="Integer",       # Zmienne decyzyjne mogą przyjmować tylko wartości całkowite (default='Continuous')
    lowBound=0 )         # wartości nie mogą być ujemne

# Zmień kształt macierzy, aby mieć takie same rozmiary macierzy kosztów
shipping_mtx = np.array(decision_vars).reshape(n_warehouses,n_countries)

print("Macierz liczby sztuk wysyłanych produktów:")
print(shipping_mtx)
# %%
# Funkcja celu zapisana w całości
objective_func = plp.lpSum(cost_matrix * shipping_mtx)
print(objective_func)

# %%
# Dodawanie funkcji celu do obiektu modelu
model += objective_func
print(model)

# %%
# Wyświetlenie i dodanie ograniczeń zaopatrzenia magazynowego do obiektu modelu
for i in range(n_warehouses):
    print(plp.lpSum(shipping_mtx[i][j] for j in range(n_countries)) <= warehouse_supply[i])
    model += plp.lpSum(shipping_mtx[i][j] for j in range(n_countries)) <= warehouse_supply[i], "Ograniczenia podaży magazynów " + str(i)
    
# %%
# Wyświetlenie i dodanie do obiektu modelu ograniczeń zamówień krajów
for j in range(n_countries):
    print(plp.lpSum(shipping_mtx[i][j] for i in range(n_warehouses)) >= country_demands[j])
    model += plp.lpSum(shipping_mtx[i][j] for i in range(n_warehouses)) >= country_demands[j] , "Ograniczenia zamówień krajów " + str(j)
    
# %%
# Dokładnie sprawdź ostateczny model.
print(model)

# %%
# Możesz również zapisać definicję modelu w pliku LP
model.writeLP('supply-demand-minimize-costs-problem.lp')

# %%
# Rozwiąż problem programowania liniowego
model.solve()

status = plp.LpStatus[model.status]
print(status)  # Jeśli wyjście ma wartość 'Optimal', to znaleziono optymalne rozwiązanie problemu.
               # Jeśli ma wartość 'Infeasible', to ograniczenia uniemożliwiają rozwiązanie problemu.

# %%
# Wyświetla minimalny całkowity koszt, jaki możesz wydać
print("Całkowity koszt:", model.objective.value())

# %%
# Znaleziono wartości zmiennych decyzyjnych
decision_var_results = np.empty(shape=(n_warehouses * n_countries))
z = 0
for v in model.variables():
    try:
        decision_var_results[z] = v.value()
        z += 1
    except:
        print("Błąd: nie można znaleźć rozwiązania")

# %%
# Sprawdź w jasny sposób ilości produktów wysyłanych z magazynów do krajów
# 
decision_var_results = decision_var_results.reshape(n_warehouses,n_countries)

col_idxs = ['Włochy','Francja','Niemcy','Japonia','Chiny','USA']
row_idxs = ['Magazyn ITA','Magazyn DEU','Magazyn JPN','Magazyn USA']
dv_res_df = pd.DataFrame(decision_var_results, columns=col_idxs, index=row_idxs)
dv_res_df

# %%
# Uzyskaj dla każdego magazynu całkowite ilości wysłanych towarów
warehouse_shipped_qty = np.zeros(shape=(n_warehouses))
z = 0
for i in range(n_warehouses):
    warehouse_shipped_qty[z] = plp.lpSum(shipping_mtx[i][j].value() for j in range(n_countries)).value()
    z += 1
    
# %%
w_shipped_df = pd.DataFrame(warehouse_shipped_qty, columns=['qty'], index=row_idxs)
w_shipped_df
# %%
