/*
 * Copyright 2012 s_wolff.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.aegee.runanddine.pathfinder.optimizers;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.aegee.runanddine.pathfinder.OptGroup;
import org.aegee.runanddine.registration.GroupRegistration;
import org.aegee.runanddine.registration.SingleRegistration;
import org.aegee.runanddine.runanddine.RunAndDine;

/**
 * Takes Group- and Single-Registrations and builds groups of 2 persons. Works
 * with the selected kitchens
 *
 * @author s_wolff
 */
public class OptGroupBuilder {

    /**
     * Deletes all OptGroups that are currently in the database and creates new
     * ones. The resulting collection contains OptGroups with a kitchen each. If
     * not every OptGroup can have a kitchen an exception is thrown
     *
     * @param event the event opt groups should be build for
     * @return collection of opt grups
     * @throws
     * org.aegee.runanddine.pathfinder.optimizers.OptGroupBuilder.ToFewKitchenException
     */
    public static Collection<OptGroup> makeOptGroups(RunAndDine event) throws ToFewKitchenException, UnevenNumberOfGroupsException {
        deleteCurrentOptGroups(event);

        List<SingleRegistration> singleRegs = SingleRegistration.OBJECTS.getAllValidatedByRunAndDineId(event.getId());
        List<GroupRegistration> groupRegs = GroupRegistration.OBJECTS.getAllValidatedByRunAndDineId(event.getId());

        if (singleRegs.size() % 2 != 0) {
            throw new UnevenNumberOfGroupsException();
        }

        Collection<OptGroup> optGroups = new LinkedList<OptGroup>();
        // group registrations won't need building - they are groups already
        for (GroupRegistration g : groupRegs) {
            OptGroup og = new OptGroup(g);
            og.setRunAndDineId(event.getId());
            optGroups.add(og);
        }

        // get all single regs with kitchen
        List<SingleRegistration> withKitchen = new LinkedList<SingleRegistration>();
        for (SingleRegistration sr : singleRegs) {
            if (sr.isKitchenAvailable()) {
                withKitchen.add(sr);
            }
        }
        // shuffle
        Collections.shuffle(withKitchen);
        Collections.shuffle(withKitchen);
        Collections.shuffle(withKitchen);
        // select n/2 kitchens where to cook
        List<SingleRegistration> choosen = new LinkedList<SingleRegistration>();
        try {
            for (int i = 0; i < singleRegs.size() / 2; i++) {
                choosen.add(withKitchen.get(i));
            }
        } catch (IndexOutOfBoundsException e) {
            throw new ToFewKitchenException();
        }
        // create list of unchoosen regs
        singleRegs.removeAll(choosen);

        // make OptGroups
        for (int i = 0; i < choosen.size(); i++) {
            OptGroup g = new OptGroup(choosen.get(i), singleRegs.get(i));
            g.setRunAndDineId(event.getId());
            optGroups.add(g);
        }

        return optGroups;
    }

    public static void saveOptGroups(RunAndDine event, Collection<OptGroup> groups) {
        for (OptGroup optGroup : groups) {
            optGroup.setRunAndDineId(event.getId());
            OptGroup.OBJECTS.save(optGroup);
        }
    }

    public static void makeAndSaveOptGroups(RunAndDine event) throws ToFewKitchenException, UnevenNumberOfGroupsException {
        saveOptGroups(event, makeOptGroups(event));
    }

    private static void deleteCurrentOptGroups(RunAndDine event) {
        Collection<OptGroup> currentOptGroups = OptGroup.OBJECTS.getAllByRunAndDineId(event.getId());
        for (OptGroup optGroup : currentOptGroups) {
            OptGroup.OBJECTS.delete(optGroup);
        }
    }

    public static class ToFewKitchenException extends Exception {

        public ToFewKitchenException() {
            super("To few kitchen available. At least half of the SingleRegistrations need a kitchen!");
        }
    }

    public static class UnevenNumberOfGroupsException extends Exception {

        public UnevenNumberOfGroupsException() {
            super("There is a uneven number of OptGroups. Please fix!");
        }
    }
}
