/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import ilog.views.gantt.model.IlvDefaultGanttModel;
import ilog.views.gantt.*;
import ilog.views.util.time.IlvCalendarUtil;

import java.util.Calendar;
import java.util.Date;

public class SimpleProject extends IlvDefaultGanttModel {

  protected IlvActivityFactory activityFactory;
  protected IlvResourceFactory resourceFactory;
  protected IlvConstraintFactory constraintFactory;
  protected IlvReservationFactory reservationFactory;
  protected Calendar calendar;

  /**
   * Creates a new data model using the specified data factories.
   */
  public SimpleProject(IlvActivityFactory activityFactory,
                       IlvResourceFactory resourceFactory,
                       IlvConstraintFactory constraintFactory,
                       IlvReservationFactory reservationFactory) {
    this.activityFactory = activityFactory;
    this.resourceFactory = resourceFactory;
    this.constraintFactory = constraintFactory;
    this.reservationFactory = reservationFactory;
    init();
  }

  /**
   * Creates a new data model using the data factories of the specified chart.
   */
  public SimpleProject(IlvHierarchyChart chart) {
    this(chart.getActivityFactory(),
         chart.getResourceFactory(),
         chart.getConstraintFactory(),
         chart.getReservationFactory());
  }

  /**
   * Requests the activity factory to create an activity with the specified
   * parameters.
   *
   * @param id       The activity ID.
   * @param name     The activity name.
   * @param start    The start time of the activity.
   * @param duration The duration of the activity.
   * @return The new activity.
   */
  protected IlvActivity createActivity(String id, String name, Date start,
                                       IlvDuration duration) {
    // The activity factory will create an IlvActivity implementation of the
    // desired class, but only its time interval will be initialized. All of
    // the other activity properties will have default values that we must
    // explicitly set.
    Date end = IlvTimeUtil.add(start, duration);
    IlvActivity activity =
        activityFactory.createActivity(new IlvTimeInterval(start, end));
    activity.setID(id);
    activity.setName(name);
    return activity;
  }

  /**
   * Requests the activity factory to create an activity with the specified
   * parameters.
   *
   * @param id    The activity ID.
   * @param name  The activity name.
   * @param start The start time of the activity.
   * @param end   The end time of the activity.
   * @return The new activity.
   */
  protected IlvActivity createActivity(String id, String name,
                                       Date start, Date end) {
    return createActivity(id, name, start, IlvTimeUtil.subtract(end, start));
  }

  /**
   * Requests the activity factory to create an activity with the specified
   * parameters.
   *
   * @param id   The activity ID.
   * @param name The activity name.
   * @return The new activity.
   */
  protected IlvActivity createActivity(String id, String name) {
    return createActivity(id, name, new Date(), IlvDuration.ONE_DAY);
  }

  /**
   * Requests the resource factory to create a resource with the specified
   * parameters.
   *
   * @param id   The resource ID.
   * @param name The resource name.
   * @return The new resource.
   */
  protected IlvResource createResource(String id, String name) {
    // The resource factory will create an IlvResource implementation of the
    // desired class, but all of its properties will be set to default values.
    // Therefore, we explicitly set the resource ID and name property.
    IlvResource resource = resourceFactory.createResource();
    resource.setID(id);
    resource.setName(name);
    return resource;
  }

  /**
   * Requests the constraint factory to create a constraint between the
   * specified activities.
   *
   * @param from The <em>from</em> activity.
   * @param to   The <em>to</em> activity.
   * @param type The constraint type.
   * @return The new constraint.
   */
  protected IlvConstraint createConstraint(IlvActivity from, IlvActivity to,
                                           IlvConstraintType type) {
    IlvConstraint constraint =
        constraintFactory.createConstraint(from, to, type);
    return constraint;
  }

  /**
   * Requests the reservation factory to create a reservation between the
   * specified resource and activity.
   *
   * @param resource The resource.
   * @param activity The activity.
   * @return The new reservation.
   */
  protected IlvReservation createReservation(IlvResource resource,
                                             IlvActivity activity) {
    IlvReservation reservation =
        reservationFactory.createReservation(resource, activity);
    return reservation;
  }

  /**
   * Populates the data model with the project schedule.
   */
  protected void init() {
    calendar = Calendar.getInstance();
    IlvCalendarUtil.dayFloor(calendar);
    Date now = calendar.getTime();

    // Create the example activities.
    IlvActivity aRoot;
    IlvActivity aGatherReqts;
    IlvActivity aTalkToCusts;
    IlvActivity aCustomrList;
    IlvActivity aContactCust;
    IlvActivity aWriteUpReqs;
    IlvActivity a_ReviewReqs;
    IlvActivity aMarktngSpec;
    IlvActivity aDraftSpec_1;
    IlvActivity aDraftSpec_2;
    IlvActivity a_EngMktSpec;
    IlvActivity aPrfOfConcpt;
    IlvActivity aRoughDesign;
    IlvActivity a__CADLayout;
    IlvActivity a__Detailing;
    IlvActivity aPrototypFab;
    IlvActivity aOrderMaterl;
    IlvActivity a__Machining;
    IlvActivity aBurninTestg;
    IlvActivity aPrepareDemo;
    IlvActivity aDesignDevel;
    IlvActivity aDevelPhase1;
    IlvActivity aDevelPhase2;
    IlvActivity aDevelPhase3;
    IlvActivity a__DocAndPkg;
    IlvActivity a_UserManual;
    IlvActivity aInstallScpt;
    IlvActivity aWebSiteUpdt;
    // Sets the startDate to be 2 months before now
    Date startDate = IlvTimeUtil.subtract(now,
                                          IlvDuration.ONE_WEEK.multiply(8));

    aRoot = createActivity("A-Root", "Project Summary");
    setRootActivity(aRoot);
    aGatherReqts = createActivity("A-1", "Gather Requirements");
    addActivity(aGatherReqts, aRoot);
    aTalkToCusts = createActivity("A-1.1", "Talk to customers");
    addActivity(aTalkToCusts, aGatherReqts);
    aCustomrList =
        createActivity("A-1.1.1", "Compile customer list",
                       startDate,
                       add(startDate, Calendar.DAY_OF_YEAR, 2)); // 2 days
    addActivity(aCustomrList, aTalkToCusts);
    startDate = aCustomrList.getEndTime();
    aContactCust =
        createActivity("A-1.1.2", "Contact customers",
                       startDate,
                       add(startDate, Calendar.DAY_OF_YEAR, 8)); // 8 days
    addActivity(aContactCust, aTalkToCusts);
    startDate = aTalkToCusts.getEndTime();
    aWriteUpReqs =
        createActivity("A-1.2", "Write up requirements",
                       startDate,
                       add(startDate, Calendar.DAY_OF_YEAR, 3)); // 3 days
    addActivity(aWriteUpReqs, aGatherReqts);
    startDate = add(aWriteUpReqs.getEndTime(),
                    Calendar.DAY_OF_YEAR, -1); // -1 day
    a_ReviewReqs =
        createActivity("A-1.3", "Review requirements",
                       startDate,
                       add(startDate, Calendar.DAY_OF_YEAR, 5)); // 5 days
    addActivity(a_ReviewReqs, aGatherReqts);
    aMarktngSpec = createActivity("A-2", "Marketing Specification");
    addActivity(aMarktngSpec, aRoot);
    startDate = aGatherReqts.getEndTime();
    aDraftSpec_1 =
        createActivity("A-2.1", "First Draft Specification",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 1)); // 1 week
    addActivity(aDraftSpec_1, aMarktngSpec);
    startDate = aDraftSpec_1.getEndTime();
    aDraftSpec_2 =
        createActivity("A-2.2", "Second Draft Specification",
                       startDate,
                       add(startDate, Calendar.DAY_OF_YEAR, 5)); // 5 days
    addActivity(aDraftSpec_2, aMarktngSpec);
    startDate = aMarktngSpec.getEndTime();
    a_EngMktSpec =
        createActivity("A-3", "Engineering Review",
                       startDate,
                       add(startDate, Calendar.DAY_OF_YEAR, 10)); // 10 days
    addActivity(a_EngMktSpec, aRoot);
    aPrfOfConcpt = createActivity("A-4", "Proof of Concept");
    addActivity(aPrfOfConcpt, aRoot);
    aRoughDesign = createActivity("A-4.1", "Rough Design");
    addActivity(aRoughDesign, aPrfOfConcpt);
    startDate = a_EngMktSpec.getEndTime();
    a__CADLayout =
        createActivity("A-4.1.1", "CAD Layout",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 2)); // 2 weeks
    addActivity(a__CADLayout, aRoughDesign);
    startDate = add(a__CADLayout.getEndTime(),
                    Calendar.DAY_OF_YEAR, -3); // -3 days
    a__Detailing =
        createActivity("A-4.1.2", "Detailing",
                       startDate,
                       add(startDate, Calendar.DAY_OF_YEAR, 10)); // 10 days
    addActivity(a__Detailing, aRoughDesign);
    aPrototypFab = createActivity("A-4.2", "Fabricate Prototype");
    addActivity(aPrototypFab, aPrfOfConcpt);
    startDate = add(a__Detailing.getStartTime(),
                    Calendar.DAY_OF_YEAR, 5); // 5 days
    aOrderMaterl =
        createActivity("A-4.2.1", "Order Materials",
                       startDate,
                       add(startDate, Calendar.DAY_OF_YEAR, 8)); // 8 days
    addActivity(aOrderMaterl, aPrototypFab);
    startDate = aOrderMaterl.getEndTime();
    a__Machining =
        createActivity("A-4.2.2", "Machining",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 2)); // 2 weeks
    addActivity(a__Machining, aPrototypFab);
    startDate = a__Machining.getEndTime();
    aBurninTestg =
        createActivity("A-4.3", "Burn-in Testing",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 1)); // 1 week
    addActivity(aBurninTestg, aPrfOfConcpt);
    startDate = aBurninTestg.getEndTime();
    aPrepareDemo =
        createActivity("A-4.4", "Prepare Demo",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 1)); // 1 week
    addActivity(aPrepareDemo, aPrfOfConcpt);
    aDesignDevel = createActivity("A-5", "Design and Development");
    addActivity(aDesignDevel, aRoot);
    startDate = aPrfOfConcpt.getEndTime();
    aDevelPhase1 =
        createActivity("A-5.1", "Phase I Development",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 8)); // 8 weeks
    addActivity(aDevelPhase1, aDesignDevel);
    startDate = aDevelPhase1.getEndTime();
    aDevelPhase2 =
        createActivity("A-5.2", "Phase II Development",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 6)); // 6 weeks
    addActivity(aDevelPhase2, aDesignDevel);
    startDate = aDevelPhase2.getEndTime();
    aDevelPhase3 =
        createActivity("A-5.3", "Phase III Development",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 5)); // 5 weeks
    addActivity(aDevelPhase3, aDesignDevel);
    a__DocAndPkg = createActivity("A-6", "Packaging");
    addActivity(a__DocAndPkg, aRoot);
    a_UserManual = createActivity("A-6.1", "User Manual",
                                  aDevelPhase2.getStartTime(),
                                  aDevelPhase3.getEndTime());
    addActivity(a_UserManual, a__DocAndPkg);
    startDate = add(aDevelPhase3.getEndTime(), Calendar.WEEK_OF_YEAR, -2);
    aInstallScpt =
        createActivity("A-6.2", "Installation Procedures",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 4)); // 4 weeks
    addActivity(aInstallScpt, a__DocAndPkg);
    startDate = aDevelPhase3.getEndTime();
    aWebSiteUpdt =
        createActivity("A-6.3", "Update WebSite",
                       startDate,
                       add(startDate, Calendar.WEEK_OF_YEAR, 2)); // 2 weeks
    addActivity(aWebSiteUpdt, a__DocAndPkg);

    // Create the schedule constraints.
    addConstraint(createConstraint(aGatherReqts, aMarktngSpec,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aMarktngSpec, a_EngMktSpec,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(a_EngMktSpec, aPrfOfConcpt,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aPrfOfConcpt, aDesignDevel,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aCustomrList, aContactCust,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aTalkToCusts, aWriteUpReqs,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aWriteUpReqs, a_ReviewReqs,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aDraftSpec_1, aDraftSpec_2,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(a__CADLayout, a__Detailing,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(a__Detailing, aOrderMaterl,
                                   IlvConstraintType.START_START));
    addConstraint(createConstraint(aOrderMaterl, a__Machining,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aDevelPhase1, aDevelPhase2,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aDevelPhase2, aDevelPhase3,
                                   IlvConstraintType.END_START));
    addConstraint(createConstraint(aDevelPhase2, a_UserManual,
                                   IlvConstraintType.START_START));

    // Create the schedule resources.
    IlvResource rRoot;
    IlvResource rMkting;
    IlvResource rBMcDnd;
    IlvResource rStvKnl;
    IlvResource rMkSmth;
    IlvResource rLDupnt;
    IlvResource rRchDev;
    IlvResource rLnDane;
    IlvResource rJmHook;
    IlvResource rSWashn;
    IlvResource rGHoppr;
    IlvResource rTMonhn;
    IlvResource rDocmtn;
    IlvResource rSyLadd;
    IlvResource rBRobtn;

    rRoot = createResource("JCOM", "JCompany Employees");
    setRootResource(rRoot);
    rMkting = createResource("MKT", "Marketing");
    addResource(rMkting, rRoot);
    rBMcDnd = createResource("BM", "Bill McDonald");
    addResource(rBMcDnd, rMkting);
    rStvKnl = createResource("SK", "Steve Knoll");
    addResource(rStvKnl, rMkting);
    rMkSmth = createResource("MS", "Michael Smith");
    addResource(rMkSmth, rMkting);
    rLDupnt = createResource("LP", "Luc Dupont");
    addResource(rLDupnt, rMkting);
    rRchDev = createResource("RND", "Research and Development");
    addResource(rRchDev, rRoot);
    rLnDane = createResource("LD", "Linus Dane");
    addResource(rLnDane, rRchDev);
    rJmHook = createResource("JH", "James Hook");
    addResource(rJmHook, rRchDev);
    rSWashn = createResource("SW", "Scott Washington");
    addResource(rSWashn, rRchDev);
    rGHoppr = createResource("GH", "Gill Hopper");
    addResource(rGHoppr, rRchDev);
    rTMonhn = createResource("TM", "Thomas Monahan");
    addResource(rTMonhn, rRchDev);
    rDocmtn = createResource("DOC", "Documentation");
    addResource(rDocmtn, rRoot);
    rSyLadd = createResource("SL", "Sandy Ladd");
    addResource(rSyLadd, rDocmtn);
    rBRobtn = createResource("BR", "Bob Robertson");
    addResource(rBRobtn, rDocmtn);

    // Create the schedule reservations.
    addReservation(createReservation(rBMcDnd, aTalkToCusts));
    addReservation(createReservation(rStvKnl, aCustomrList));
    addReservation(createReservation(rMkSmth, aCustomrList));
    addReservation(createReservation(rLDupnt, aCustomrList));
    addReservation(createReservation(rStvKnl, aContactCust));
    addReservation(createReservation(rLDupnt, aContactCust));
    addReservation(createReservation(rMkSmth, aWriteUpReqs));
    addReservation(createReservation(rBMcDnd, a_ReviewReqs));
    addReservation(createReservation(rMkSmth, a_ReviewReqs));
    addReservation(createReservation(rLnDane, a_ReviewReqs));

    addReservation(createReservation(rMkSmth, aDraftSpec_1));
    addReservation(createReservation(rLDupnt, aDraftSpec_1));
    addReservation(createReservation(rLDupnt, aDraftSpec_2));

    addReservation(createReservation(rBMcDnd, a_EngMktSpec));
    addReservation(createReservation(rJmHook, a_EngMktSpec));
    addReservation(createReservation(rGHoppr, a_EngMktSpec));

    addReservation(createReservation(rLnDane, aPrfOfConcpt));
    addReservation(createReservation(rSWashn, aRoughDesign));
    addReservation(createReservation(rTMonhn, a__CADLayout));
    addReservation(createReservation(rTMonhn, a__Detailing));
    addReservation(createReservation(rGHoppr, a__Detailing));
    addReservation(createReservation(rJmHook, aPrototypFab));
    addReservation(createReservation(rTMonhn, aOrderMaterl));
    addReservation(createReservation(rGHoppr, a__Machining));
    addReservation(createReservation(rSWashn, aBurninTestg));
    addReservation(createReservation(rTMonhn, aBurninTestg));
    addReservation(createReservation(rGHoppr, aPrepareDemo));
    addReservation(createReservation(rTMonhn, aPrepareDemo));
    addReservation(createReservation(rLDupnt, aPrepareDemo));

    addReservation(createReservation(rJmHook, aDesignDevel));
    addReservation(createReservation(rLnDane, aDevelPhase1));
    addReservation(createReservation(rLnDane, aDevelPhase2));
    addReservation(createReservation(rSWashn, aDevelPhase2));
    addReservation(createReservation(rSWashn, aDevelPhase3));
    addReservation(createReservation(rTMonhn, aDevelPhase3));

    addReservation(createReservation(rSyLadd, a_UserManual));
    addReservation(createReservation(rBRobtn, a_UserManual));
    addReservation(createReservation(rBRobtn, aInstallScpt));
    addReservation(createReservation(rLnDane, aInstallScpt));
    addReservation(createReservation(rSyLadd, aWebSiteUpdt));
    addReservation(createReservation(rGHoppr, aWebSiteUpdt));
    addReservation(createReservation(rMkSmth, aWebSiteUpdt));
  }


  private Date add(Date date, int field, int amount) {
    calendar.setTime(date);
    calendar.add(field, amount);
    return calendar.getTime();
  }

}