/**
  *
  * Furniture Moving problem in JaCoP.
  *
  * Problem from Marriott & Stuckey: 'Programming with constraints', page  112f
  *
  * Feature: testing cumulative.
  *
  * Other models of this problem:
  *   Choco (version 1): http://www.hakank.org/constraints/FurnitureMoving.java 
  *   MiniZinc: http://www.hakank.org/minizinc/furniture_moving.mzn
  *
  *
  * JaCoP Model by Hakan Kjellerstrand (hakank@bonetmail.com)
  * Also see http://www.hakank.org/JaCoP/
  *
  */

import JaCoP.constraints.*;
import JaCoP.core.*;
import JaCoP.search.*;

import java.util.*;

public class FurnitureMoving {

    public static void main(String args[]) {

        // boolean generateAll = true;
        boolean generateAll = false;

        Store store = new Store();

        IntVar numPersons = new IntVar(store, "numPersons", 2, 5); // will be minimized
        IntVar maxTime    = new IntVar(store, "maxTime", 60,60);

        // Start times
        IntVar Sp = new IntVar(store, "Sp", 0, 60); // Piano
        IntVar Sc = new IntVar(store, "Sc", 0, 60); // Chair 
        IntVar Sb = new IntVar(store, "Sb", 0, 60); // Bed
        IntVar St = new IntVar(store, "St", 0, 60); // Table
        IntVar sumStartTimes = new IntVar(store, "SumStartTimes", 0, 1000);

        IntVar[] starts = {Sp,Sc,Sb,St};
        store.impose(new Sum(starts, sumStartTimes));

        IntVar[] durations     = new IntVar[4];
        IntVar[] resources     = new IntVar[4];
        IntVar[] endTimes      = new IntVar[4];
        int durationsInts[] = {30,10,15,15}; // duration of task
        int resourcesInts[] = {3,1,3,2};     // resources: num persons required for each task
        for (int i = 0; i < durationsInts.length; i++) {
            // converts to IntVar
            durations[i] = new IntVar(store, "dur_"+i, durationsInts[i], durationsInts[i]);
            // converts to IntVar
            resources[i] = new IntVar(store, "res_"+i, resourcesInts[i], resourcesInts[i]);

            // all tasks must be finished in 60 minutes
            endTimes[i]  = new IntVar(store, "end_"+i, 0, 120);
            store.impose(new XplusYeqZ(starts[i], durations[i], endTimes[i]));
            store.impose(new XlteqY(endTimes[i], maxTime));
        }

        store.impose(new Cumulative(starts, durations, resources, numPersons));

        if (generateAll) {
            // generate all optimal solutions
            store.impose(new XeqC(numPersons, 3));
        }

        // HakankUtil.toXML(store, -1, ".", "FurnitureMoving.xml");

        ArrayList<IntVar> allVars = new ArrayList<IntVar>();
        for(IntVar s: starts) 
            allVars.add(s);

        for(IntVar e: endTimes) 
            allVars.add(e);

        allVars.add(numPersons);


        //
        // Search
        //
        SelectChoicePoint select = new SimpleSelect (
                                                     allVars.toArray(new IntVar[1]),
                                                     new SmallestDomain(),
                                                     new IndomainMin ());
        Search label = new DepthFirstSearch ();
        label.getSolutionListener().searchAll(true);
        label.getSolutionListener().recordSolutions(true);

        boolean result;
        if (generateAll) {
            // Generate all optimal solutions. 
            // Note: Gives null pointer exception when searchAll(true)
            result = label.labeling(store, select); 
        } else {
            // minimize over numPersons
            result = label.labeling(store, select, numPersons); 
        }


        Var[] variables = label.getSolutionListener().getVariables();
        for(int i = 0; i < variables.length; i++) {
            System.out.println("Variable " + i + " " + variables[i]);
        }

        if(result) {
            int numSolutions = label.getSolutionListener().solutionsNo();
            label.printAllSolutions();

            System.out.println("\nNumber of persons needed: " + numPersons.value());
            System.out.println(
                               "Piano: " + starts[0].value() + ".." + endTimes[0].value() + "\n" +
                               "Chair: " + starts[1].value() + ".." + endTimes[1].value() + "\n" +
                               "Bed  : " + starts[2].value() + ".." + endTimes[2].value() + "\n" + 
                               "Table: " + starts[3].value() + ".." + endTimes[3].value()
                               );

                               
            
        } // end if result

    } // end main

} // end class FurnitureMoving
