/*
 * 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 pl.lodz.p.zsk.ssbd2015.ssbd07.mopr.endpoints;

import java.rmi.RemoteException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.SessionContext;
import javax.ejb.SessionSynchronization;
import javax.ejb.Stateful;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.interceptor.Interceptors;
import pl.lodz.p.zsk.ssbd2015.ssbd07.encje.Konto;
import pl.lodz.p.zsk.ssbd2015.ssbd07.encje.Pracownik;
import pl.lodz.p.zsk.ssbd2015.ssbd07.encje.Pracownikarchiwum;
import pl.lodz.p.zsk.ssbd2015.ssbd07.encje.Pracownikzlecenie;
import pl.lodz.p.zsk.ssbd2015.ssbd07.exceptions.AbstractException;
import pl.lodz.p.zsk.ssbd2015.ssbd07.interceptory.LoggerInterceptor;
import pl.lodz.p.zsk.ssbd2015.ssbd07.mopr.fasady.*;
import pl.lodz.p.zsk.ssbd2015.ssbd07.utils.TimeUtils;

/**
 *
 * @author Marek Pawłowski
 */
@Stateful
@Interceptors(LoggerInterceptor.class)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class MOPREndpoint implements MOPREndpointLocal, SessionSynchronization {

    @EJB(name = "MoprPracownikarchiwumFacade")
    private PracownikarchiwumFacadeLocal pracownikarchiwumFacade;
    @EJB(name = "MoprPracownikzlecenieFacade")
    private PracownikzlecenieFacadeLocal pracownikzlecenieFacade;
    @EJB(name = "MoprKontoFacade")
    private KontoFacadeLocal kontoFacade;
    @EJB(name = "MoprPoziomDostepuFacade")
    private PoziomDostepuFacadeLocal poziomDostepuFacade;
    @EJB(name = "MoprPracownikFacade")
    private PracownikFacadeLocal pracownikFacade;

    @Resource
    private SessionContext sessionContext;

    private Pracownikzlecenie zleceniestan;

    private Pracownik pracownikEdytuj;

    /**
     * Pobranie zleceń przypisanych pracownikowi
     *
     * @param pracownik Pracownik, dla którego szukamy zleceń
     * @return Lista zleceń
     */
    @Override
    @RolesAllowed("pobierzZlecenia")
    public List<Pracownikzlecenie> pobierzZlecenia(Pracownik pracownik) {
        return pracownikzlecenieFacade.findPracownikStatus(pracownik);
    }

    /**
     * Pobranie aktualnie zalogowanego użytkownika
     *
     * @return Aktualnie zalogowany pracownik
     */
    @Override
    @RolesAllowed("znajdzPracownika")
    public Pracownik znajdzPracownika() {
        String login = sessionContext.getCallerPrincipal().getName();
        Konto konto = kontoFacade.findByLogin(login);
        Long id = poziomDostepuFacade.findByKonto(konto);
        return pracownikFacade.find(id);
    }

    /**
     * Pobieranie zlecenia do potwierdzenia
     *
     * @param zlecenie pobierane zlecenie
     * @return
     */
    @Override
    @RolesAllowed("pobierzZlecenieDoEdycji")
    public Pracownikzlecenie pobierzZlecenieDoEdycji(Pracownikzlecenie zlecenie) {
        zleceniestan = pracownikzlecenieFacade.find(zlecenie.getId());
        return zleceniestan;
    }

    /**
     * Zapisanie zlecenia po potwierdzeniu
     *
     * @param zlecenie
     * @throws AbstractException Wyjątek stojący na szczycie hierarchii wyjątków
     * aplikacyjnych
     */
    @Override
    @RolesAllowed("zapiszZleceniePoEdycji")
    public void zapiszZleceniePoEdycji(Pracownikzlecenie zlecenie) throws AbstractException {
        this.agregujGodziny(zleceniestan.getCzasTrwania());
        pracownikzlecenieFacade.edit(zleceniestan);
        zleceniestan = null;
    }

    /**
     * Metoda zerująca w podanej dacie godziny, minuty oraz sekundy.
     *
     * @return data z wyzerowaną cześcią dotyczącą godziny
     * @param data
     * @author Marek Pawłowski
     */
    @Override
    @RolesAllowed("zerujGodziny")
    public Date zerujGodziny(Date data) {

        Calendar cal = Calendar.getInstance();
        cal.setTime(data);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        return cal.getTime();

    }

    /**
     * Metoda sprawdzająca czy w danym tygodniu był już nowy wpis do
     * Pracownikarchiwum
     *
     * @return true - w danym tygodniu nie było jeszcze wpisu, false - wpis już
     * był
     * @param ostatnia ostatnia data wpisana do kolekcji
     * @param dzisiejsza aktualna data
     * @author Marek Pawłowski
     */
    @Override
    @RolesAllowed("czyNastepnyTydzien")
    public boolean czyNastepnyTydzien(Date ostatnia, Date dzisiejsza) {

        SimpleDateFormat dayOfWeek = new SimpleDateFormat("u");
        SimpleDateFormat dayOfMonth = new SimpleDateFormat("d");

        if (dzisiejsza.after(ostatnia)) {

            if (Integer.parseInt(dayOfMonth.format(dzisiejsza)) - Integer.parseInt(dayOfMonth.format(ostatnia)) <= 7) {

                if (Integer.parseInt(dayOfWeek.format(dzisiejsza)) <= Integer.parseInt(dayOfWeek.format(ostatnia))) {
                    return true;
                }
            } else {
                return true;
            }
        }

        return false;
    }

    /**
     * Metoda sumująca przepracowane przez dany tydzień godziny.
     *
     * @param przepracowanyCzas
     * @throws AbstractException
     * @author Marek Pawłowski
     */
    @Override
    @RolesAllowed("agregujGodziny")
    public void agregujGodziny(int przepracowanyCzas) throws AbstractException {

        this.pracownikEdytuj = this.znajdzPracownika();
        List<Pracownikarchiwum> listaEdytuj = (List<Pracownikarchiwum>) this.pracownikEdytuj.getPracownikarchiwumCollection();

        Date dataObecna = new Date();

        if (listaEdytuj.size() == 0) {

            Pracownikarchiwum temp = new Pracownikarchiwum();
            temp.setIdPracownika(this.pracownikEdytuj);
            temp.setIloscGodzin(0);
            temp.setTydzienMiesiacRok(dataObecna);
            listaEdytuj.add(temp);
        }

        int najpozniejszyWpis = 0;
        Pracownikarchiwum archiwumTemp = listaEdytuj.get(0);

        for (int i = 0; i < listaEdytuj.size(); i++) {
            if (archiwumTemp.getTydzienMiesiacRok().before(listaEdytuj.get(i).getTydzienMiesiacRok())) {
                najpozniejszyWpis = i;
            }
        }

        dataObecna = this.zerujGodziny(dataObecna);
        Date dataZBazy = listaEdytuj.get(najpozniejszyWpis).getTydzienMiesiacRok();

        if (this.czyNastepnyTydzien(dataZBazy, dataObecna) == true) {

            Pracownikarchiwum temp = new Pracownikarchiwum();
            temp.setIdPracownika(this.pracownikEdytuj);
            temp.setIloscGodzin(0);
            temp.setTydzienMiesiacRok(dataObecna);
            listaEdytuj.add(temp);

            najpozniejszyWpis = listaEdytuj.indexOf(temp);
        }

        int liczbaGodzinWBazie = listaEdytuj.get(najpozniejszyWpis).getIloscGodzin();
        listaEdytuj.get(najpozniejszyWpis).setIloscGodzin(liczbaGodzinWBazie + przepracowanyCzas);

        this.pracownikEdytuj.setPracownikarchiwumCollection(listaEdytuj);

        pracownikFacade.edit(this.pracownikEdytuj);
    }

    /**
     * Metoda zwracająca listę obietków "Pracownikarchiwum" należących do danego
     * pracownika
     *
     * @return lista zawierająca obiekty "Pracownikarchiwum"
     * @author Marek Pawłowski
     */
    @Override
    @RolesAllowed("getPracownikarchiwumList")
    public List<Pracownikarchiwum> getPracownikarchiwumList() {
        return (List<Pracownikarchiwum>) this.znajdzPracownika().getPracownikarchiwumCollection();
    }

    /**
     * Metoda zwracająca listę obietków "Pracownikarchiwum" należących do
     * pracownika o podanym peselu
     *
     * @param pesel
     * @return lista zawierająca obiekty "Pracownikarchiwum"
     * @throws AbstractException
     * @author Marek Pawłowski
     */
    @Override
    @RolesAllowed("getPracownikByPesel")
    public Pracownik getPracownikByPesel(String pesel) throws AbstractException {
        return pracownikFacade.findByPesel(pesel);
    }

    @Resource
    SessionContext sctx;
    private String transationId;
    protected static final Logger LOGGER = Logger.getGlobal();

    /**
     * Implementacja interfejsu SessionSynchronization
     *
     * @author Szymon Kaminski
     * @throws java.rmi.RemoteException
     */
    @Override
    public void afterBegin() throws EJBException, RemoteException {
        transationId = Long.toString(System.currentTimeMillis())
                + ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
        LOGGER.log(Level.INFO, TimeUtils.getTime() + "transakcja TX={0} rozpoczeta w {1}" + "tozsamosc {2}", new Object[]{transationId, this.getClass().getName(), sctx.getCallerPrincipal().getName()});
    }

    /**
     * Implementacja interfejsu SessionSynchronization
     *
     * @author Szymon Kaminski
     * @throws java.rmi.RemoteException
     */
    @Override
    public void beforeCompletion() throws EJBException, RemoteException {
        LOGGER.log(Level.INFO, TimeUtils.getTime() + "transakcja TX={0} przed zatwierdzeniem w {1}" + "tozsamosc {2}", new Object[]{transationId, this.getClass().getName(), sctx.getCallerPrincipal().getName()});
    }

    /**
     * Implementacja interfejsu SessionSynchronization
     *
     * @param committed
     * @author Szymon Kaminski
     * @throws java.rmi.RemoteException
     */
    @Override
    public void afterCompletion(boolean committed) throws EJBException, RemoteException {
        LOGGER.log(Level.INFO, TimeUtils.getTime() + "transakcja TX={0} zakonczona w {1}" + "przez {3}, tozsamosc {2}", new Object[]{transationId, this.getClass().getName(), sctx.getCallerPrincipal().getName(), committed ? "ZATWIERDZENIE" : "ODWOLANIE"});
    }

}
