ReservationRuleService.java

/*
 * Copyright (c) 2002-2022, City of Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.plugins.appointment.service;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;

import fr.paris.lutece.plugins.appointment.business.planning.TimeSlot;
import fr.paris.lutece.plugins.appointment.business.planning.TimeSlotHome;
import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
import fr.paris.lutece.plugins.appointment.business.planning.WorkingDay;
import fr.paris.lutece.plugins.appointment.business.planning.WorkingDayHome;
import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
import fr.paris.lutece.plugins.appointment.business.rule.ReservationRuleHome;
import fr.paris.lutece.plugins.appointment.service.listeners.WeekDefinitionManagerListener;
import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.util.ReferenceList;
import fr.paris.lutece.util.sql.TransactionManager;

/**
 * Service class for the reservation rule
 * 
 * @author Laurent Payen
 *
 */
public final class ReservationRuleService
{
    private static final String CONST_COPY_OF_WEEK = "Copy ";

    /**
     * Private constructor - this class does not need to be instantiated
     */
    private ReservationRuleService( )
    {
    }

    /**
     * Create in database a reservation rule object from an appointmentForm DTO
     * 
     * @param appointmentForm
     *            the appointmentForm DTO
     * @param nIdForm
     *            the form Id
     * @param dateOfApply
     *            the date of the reservation rule
     * @return the Reservation Rule object created
     */
    public static ReservationRule createReservationRule( AppointmentFormDTO appointmentForm, int nIdForm )
    {
        ReservationRule reservationRule = new ReservationRule( );
        fillInReservationRule( reservationRule, appointmentForm, nIdForm );
        ReservationRuleHome.create( reservationRule );
        return reservationRule;
    }

    /**
     * Create in database a reservation rule object from an appointmentForm DTO
     * 
     * @param appointmentForm
     *            the appointmentForm DTO
     * 
     * @return the Reservation Rule id
     */
    public static int createTypicalWeek( AppointmentFormDTO appointmentForm )
    {

        int nIdForm = appointmentForm.getIdForm( );
        ReservationRule reservationRule = createReservationRule( appointmentForm, nIdForm );
        int nMaxCapacity = reservationRule.getMaxCapacityPerSlot( );
        LocalTime startingTime = LocalTime.parse( appointmentForm.getTimeStart( ) );
        LocalTime endingTime = LocalTime.parse( appointmentForm.getTimeEnd( ) );
        int nDuration = appointmentForm.getDurationAppointments( );
        for ( DayOfWeek dayOfWeek : WorkingDayService.getOpenDays( appointmentForm ) )
        {
            WorkingDayService.generateWorkingDayAndListTimeSlot( reservationRule.getIdReservationRule( ), dayOfWeek, startingTime, endingTime, nDuration,
                    nMaxCapacity );
        }
        return reservationRule.getIdReservationRule( );
    }

    /**
     * Make a copy of typical week, with all its values
     * 
     * @param nIdReservationRule
     *            the reservationRule Id to copy
     * @return the id of the reservation rule created
     */

    public static int copyReservationRule( int nIdReservationRule )
    {
        ReservationRule reservationRule = findReservationRuleById( nIdReservationRule );
        if ( reservationRule != null )
        {

            reservationRule.setName( CONST_COPY_OF_WEEK + reservationRule.getName( ) );
            TransactionManager.beginTransaction( AppointmentPlugin.getPlugin( ) );
            try
            {
                ReservationRuleHome.create( reservationRule );
                for ( WorkingDay workingDay : reservationRule.getListWorkingDay( ) )
                {
                    workingDay.setIdReservationRule( reservationRule.getIdReservationRule( ) );
                    WorkingDayHome.create( workingDay );
                    for ( TimeSlot timeSlotTemp : workingDay.getListTimeSlot( ) )
                    {

                        timeSlotTemp.setIdWorkingDay( workingDay.getIdWorkingDay( ) );
                        TimeSlotHome.create( timeSlotTemp );
                    }
                }
                TransactionManager.commitTransaction( AppointmentPlugin.getPlugin( ) );
                return reservationRule.getIdReservationRule( );
            }
            catch( RuntimeException e )
            {
                TransactionManager.rollBack( AppointmentPlugin.getPlugin( ) );
                AppLogService.error( "Error copy typical week" + e.getMessage( ), e );
                return 0;
            }
        }

        return 0;
    }

    /**
     * Update a form with the new values of an appointmentForm DTO Advanced Parameters (with a date of application) --> new Typical Week
     * 
     * @param appointmentForm
     *            the appointmentForm DTO
     */
    public static void updateAdvancedParameters( AppointmentFormDTO appointmentForm )
    {
        int nIdForm = appointmentForm.getIdForm( );
        ReservationRule reservationRule = updateReservationRule( appointmentForm, nIdForm );
        int nMaxCapacity = reservationRule.getMaxCapacityPerSlot( );
        List<WorkingDay> listWorkingDay = WorkingDayService.findListWorkingDayByWeekDefinitionRule( reservationRule.getIdReservationRule( ) );

        if ( CollectionUtils.isNotEmpty( listWorkingDay ) )
        {
            for ( WorkingDay workingDay : listWorkingDay )
            {
                TimeSlotHome.deleteByIdWorkingDay( workingDay.getIdWorkingDay( ) );
            }
            WorkingDayHome.deleteByIdReservationRule( reservationRule.getIdReservationRule( ) );
        }
        LocalTime startingHour = LocalTime.parse( appointmentForm.getTimeStart( ) );
        LocalTime endingHour = LocalTime.parse( appointmentForm.getTimeEnd( ) );
        int nDuration = appointmentForm.getDurationAppointments( );
        for ( DayOfWeek dayOfWeek : WorkingDayService.getOpenDays( appointmentForm ) )
        {
            WorkingDayService.generateWorkingDayAndListTimeSlot( reservationRule.getIdReservationRule( ), dayOfWeek, startingHour, endingHour, nDuration,
                    nMaxCapacity );
        }
        List<WeekDefinition> listWeek = WeekDefinitionService.findByReservationRule( appointmentForm.getIdReservationRule( ) );
        if ( CollectionUtils.isNotEmpty( listWeek ) )
        {

            WeekDefinitionManagerListener.notifyListenersListWeekDefinitionChanged( appointmentForm.getIdForm( ), listWeek );
        }
    }

    /**
     * Delete a reservation rule by its id
     * 
     * @param reservationRule
     *            the reservation rule to delete
     */
    public static void removeReservationRule( int nIdReservationRule )
    {
        ReservationRule rule = findReservationRuleById( nIdReservationRule );
        if ( rule != null )
        {
            for ( WorkingDay day : rule.getListWorkingDay( ) )
            {

                TimeSlotHome.deleteByIdWorkingDay( day.getIdWorkingDay( ) );
                WorkingDayHome.delete( day.getIdWorkingDay( ) );

            }
            ReservationRuleHome.delete( rule.getIdReservationRule( ) );
        }
    }

    /**
     * save a reservation rule
     * 
     * @param reservationRule
     *            the reservation rule to save
     */
    public static void saveReservationRule( ReservationRule reservationRule )
    {
        ReservationRuleHome.create( reservationRule );
    }

    /**
     * Update in database a reservation rule with the values of an appointmentForm DTO
     * 
     * @param appointmentForm
     *            the appointmentForm DTO
     * @param nIdForm
     *            the form Id
     * @return the reservation rule object updated
     */
    public static ReservationRule updateReservationRule( AppointmentFormDTO appointmentForm, int nIdForm )
    {
        ReservationRule reservationRule = ReservationRuleService.findReservationRuleById( appointmentForm.getIdReservationRule( ) );
        if ( reservationRule == null )
        {
            reservationRule = createReservationRule( appointmentForm, nIdForm );
        }
        else
        {
            fillInReservationRule( reservationRule, appointmentForm, nIdForm );
            ReservationRuleHome.update( reservationRule );
        }
        return reservationRule;
    }

    /**
     * Fill the reservation rule object with the corresponding values of an appointmentForm DTO
     * 
     * @param reservationRule
     *            the reservation rule object to fill in
     * @param appointmentForm
     *            the appointmentForm DTO
     * @param nIdForm
     *            the form Id
     */
    public static void fillInReservationRule( ReservationRule reservationRule, AppointmentFormDTO appointmentForm, int nIdForm )
    {
        reservationRule.setMaxCapacityPerSlot( appointmentForm.getMaxCapacityPerSlot( ) );
        reservationRule.setMaxPeoplePerAppointment( appointmentForm.getMaxPeoplePerAppointment( ) );
        reservationRule.setName( appointmentForm.getName( ) );
        reservationRule.setDescriptionRule( appointmentForm.getDescriptionRule( ) );
        reservationRule.setDurationAppointments( appointmentForm.getDurationAppointments( ) );
        reservationRule.setColor( appointmentForm.getColor( ) );
        reservationRule.setIdForm( nIdForm );
    }

    /**
     * Find in database a reservation rule of a form closest to a date
     * 
     * @param nIdForm
     *            the form Id
     * @param dateOfApply
     *            the date
     * @return the reservation rule to apply at this date
     */
    public static ReservationRule findReservationRuleByIdFormAndClosestToDateOfApply( int nIdForm, LocalDate dateOfApply )
    {
        ReservationRule reservationRule = ReservationRuleHome.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, dateOfApply );
        if ( reservationRule != null )
        {

            reservationRule.setListWorkingDay( WorkingDayService.findListWorkingDayByWeekDefinitionRule( reservationRule.getIdReservationRule( ) ) );
        }
        return reservationRule;
    }

    /**
     * Find the reservation rule of a form on a specific date
     * 
     * @param nIdForm
     *            the form Id
     * @param dateOfApply
     *            the date of the reservation rule
     * @return the reservation rule object
     */
    public static ReservationRule findReservationRuleByIdFormAndDateOfApply( int nIdForm, LocalDate dateOfApply )
    {

        ReservationRule reservationRule = ReservationRuleHome.findByIdFormAndDateOfApply( nIdForm, dateOfApply );
        if ( reservationRule != null )
        {

            reservationRule.setListWorkingDay( WorkingDayService.findListWorkingDayByWeekDefinitionRule( reservationRule.getIdReservationRule( ) ) );
        }
        return reservationRule;
    }

    /**
     * Find a reservation rule with its primary key
     * 
     * @param nIdReservationRule
     *            the reservation rule Id
     * @return the Reservation Rule Object
     */
    public static ReservationRule findReservationRuleById( int nIdReservationRule )
    {
        ReservationRule reservationRule = ReservationRuleHome.findByPrimaryKey( nIdReservationRule );
        if ( reservationRule != null )
        {

            reservationRule.setListWorkingDay( WorkingDayService.findListWorkingDayByWeekDefinitionRule( reservationRule.getIdReservationRule( ) ) );
        }
        return reservationRule;
    }

    /**
     * Build a reference list of all reservation rules of a form
     * 
     * @param nIdForm
     *            the form Id
     * @return the reference list (id reservation rule / date of apply of the reservation rule)
     */
    public static ReferenceList findAllReservationRule( int nIdForm )
    {
        ReferenceList listRule = new ReferenceList( );
        List<ReservationRule> listReservationRule = ReservationRuleHome.findByIdForm( nIdForm );
        for ( ReservationRule reservationRule : listReservationRule )
        {
            listRule.addItem( reservationRule.getIdReservationRule( ), reservationRule.getName( ) );
        }
        return listRule;
    }

    /**
     * Find all the reservation rule of a form
     * 
     * @param nIdForm
     *            the form Id
     * @return an HashMap with the date of apply in key and the reservation rule in value
     */
    public static Map<WeekDefinition, ReservationRule> findAllReservationRule( int nIdForm, Collection<WeekDefinition> listWeekDefinition )
    {

        Map<WeekDefinition, ReservationRule> mapReservationRule = new HashMap<>( );
        List<ReservationRule> listReservationRule = ReservationRuleHome.findByIdForm( nIdForm );
        for ( WeekDefinition weekDefinition : listWeekDefinition )
        {
            ReservationRule reservationRule = listReservationRule.stream( ).filter( p -> p.getIdReservationRule( ) == weekDefinition.getIdReservationRule( ) )
                    .findAny( ).orElse( null );
            if ( reservationRule != null )
            {
                reservationRule.setListWorkingDay( WorkingDayService.findListWorkingDayByWeekDefinitionRule( reservationRule.getIdReservationRule( ) ) );
                mapReservationRule.put( weekDefinition, reservationRule );
            }

        }
        return mapReservationRule;
    }

    /**
     * Returns a list of the reservation rules of a form
     * 
     * @param nIdForm
     *            the form id
     * @return a list of reservation rules of the form
     */
    public static List<ReservationRule> findListReservationRule( int nIdForm )
    {

        List<ReservationRule> listReservationRule = ReservationRuleHome.findByIdForm( nIdForm );
        for ( ReservationRule reservationRule : listReservationRule )
        {
            reservationRule.setListWorkingDay( WorkingDayService.findListWorkingDayByWeekDefinitionRule( reservationRule.getIdReservationRule( ) ) );
        }
        return listReservationRule;
    }

    /**
     * Returns a list of the reservation rules of a form
     * 
     * @param nIdForm
     *            the form id
     * @param listWeekDefinition
     *            the week definition list
     * @return a list of reservation rules of the form
     */
    public static List<ReservationRule> findListReservationRule( int nIdForm, Collection<WeekDefinition> listWeekDefinition )
    {

        List<ReservationRule> listReservationRule = new ArrayList<>( );
        for ( ReservationRule reservationRule : ReservationRuleHome.findByIdForm( nIdForm ) )
        {
            if ( listWeekDefinition.stream( ).anyMatch( p -> p.getIdReservationRule( ) == reservationRule.getIdReservationRule( ) ) )
            {

                reservationRule.setListWorkingDay( WorkingDayService.findListWorkingDayByWeekDefinitionRule( reservationRule.getIdReservationRule( ) ) );
                listReservationRule.add( reservationRule );
            }
        }
        return listReservationRule;
    }

}