/*
 * File    : ActivitySequence.java
 * Created : 19-dec-2000 16:35
 * By      : fbusquets
 *
 * JClic - Authoring and playing system for educational activities
 *
 * Copyright (C) 2000 - 2018 Francesc Busquets & Departament
 * d'Educacio de la Generalitat de Catalunya
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details (see the LICENSE file).
 */

package edu.xtec.jclic.bags;

import edu.xtec.jclic.edit.Editable;
import edu.xtec.jclic.edit.Editor;
import edu.xtec.jclic.fileSystem.FileSystem;
import edu.xtec.jclic.project.JClicProject;
import edu.xtec.jclic.report.Reporter;
import edu.xtec.jclic.report.SequenceReg;
import edu.xtec.util.Domable;
import edu.xtec.util.JDomUtility;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * This class stores the definition of the sequence of activities related to a
 * specific {@link edu.xtec.jclic.project.JClicProject}. The sequence are formed
 * by an ordered list of objects of type
 * {@link edu.xtec.jclic.bags.ActivitySequenceElement}, internally stored in a
 * {@link java.util.ArrayList}. It stores also a transient pointer to a current
 * element, and provides several methods useful to deal with sequences.
 *
 * @author Francesc Busquets (fbusquets@xtec.cat)
 * @version 13.09.10
 */
public class ActivitySequence extends Object implements Editable, Domable {

  private int currentAct;
  private List<ActivitySequenceElement> elements;
  protected JClicProject project;

  /** Creates new ActivitySequence */
  public ActivitySequence(JClicProject project) {
    this.project = project;
    elements = new ArrayList<ActivitySequenceElement>(20);
    currentAct = -1;
  }

  public Editor getEditor(Editor parent) {
    return Editor.createEditor(getClass().getName() + "Editor", this, parent);
  }

  public JClicProject getProject() {
    return project;
  }

  public void add(ActivitySequenceElement ase) {
    elements.add(ase);
    if (elements.size() == 1) {
      currentAct = 0;
      if (ase.getTag() == null)
        ase.setTag("start");
    }
  }

  public void insertElementAt(ActivitySequenceElement ase, int index) {
    elements.add(index, ase);
  }

  public void remove(ActivitySequenceElement ase) {
    if (elements.contains(ase)) {
      if (elements.indexOf(ase) == currentAct)
        currentAct = -1;
      elements.remove(ase);
    }
  }

  public int getSize() {
    return elements.size();
  }

  public static final String ELEMENT_NAME = "sequence";

  public org.jdom.Element getJDomElement() {
    org.jdom.Element e = new org.jdom.Element(ELEMENT_NAME);
    for (int i = 0; i < elements.size(); i++) {
      e.addContent(getElement(i, false).getJDomElement());
    }
    return e;
  }

  public void setProperties(org.jdom.Element e, Object aux) throws Exception {
    JDomUtility.checkName(e, ELEMENT_NAME);
    Iterator it = e.getChildren(ActivitySequenceElement.ELEMENT_NAME).iterator();
    while (it.hasNext()) {
      add(ActivitySequenceElement.getActivitySequenceElement((org.jdom.Element) it.next()));
    }
  }

  public void clear() {
    currentAct = -1;
    elements.clear();
  }

  public int getElementIndex(ActivitySequenceElement ase) {
    return ase == null ? -1 : elements.indexOf(ase);
  }

  public boolean checkAllElements() throws Exception {
    boolean result = true;
    for (int i = 0; i < elements.size(); i++)
      if (project.activityBag.getElement(getElement(i, false).getActivityName()) == null)
        result = false;
    return result;
  }

  public ActivitySequenceElement getElement(int n, boolean updateCurrentAct) {
    ActivitySequenceElement result;
    try {
      result = elements.get(n);
    } catch (Exception ex) {
      result = null;
    }
    if (result != null && updateCurrentAct)
      currentAct = n;
    return result;
  }

  public ActivitySequenceElement getElementByTag(String tag, boolean updateCurrentAct) {
    int i;
    int k = elements.size();
    if (k < 1 || tag == null)
      return null;

    String normalizedTag = FileSystem.stdFn(tag);
    ActivitySequenceElement ase = null;
    for (i = 0; i < k; i++) {
      ase = getElement(i, false);
      if (ase != null && ase.getTag() != null && ase.getTag().equals(normalizedTag))
        break;
    }

    if (i == k)
      ase = null;
    else if (updateCurrentAct)
      currentAct = i;

    return ase;
  }

  public ActivitySequenceElement[] getElements() {
    return elements.toArray(new ActivitySequenceElement[elements.size()]);
  }

  public boolean hasNextAct(boolean hasReturn) {
    boolean result = false;
    ActivitySequenceElement ase = getCurrentAct();
    if (ase != null) {
      if (ase.fwdJump == null)
        result = true;
      else
        switch (ase.fwdJump.action) {
        case JumpInfo.STOP:
          break;
        case JumpInfo.RETURN:
          result = (hasReturn);
          break;
        default:
          result = true;
        }
    }
    return result;
  }

  public boolean hasPrevAct(boolean hasReturn) {
    boolean result = false;
    ActivitySequenceElement ase = getCurrentAct();
    if (ase != null) {
      if (ase.backJump == null)
        result = true;
      else
        switch (ase.backJump.action) {
        case JumpInfo.STOP:
          break;
        case JumpInfo.RETURN:
          result = hasReturn;
          break;
        default:
          result = true;
        }
    }
    return result;
  }

  public int getNavButtonsFlag() {
    int flag = ActivitySequenceElement.NAV_NONE;
    ActivitySequenceElement ase = getCurrentAct();
    if (ase != null)
      flag = ase.navButtons;
    return flag;
  }

  public JumpInfo getJump(boolean back, Reporter rep) {
    ActivitySequenceElement ase;
    JumpInfo result = null;
    if ((ase = getCurrentAct()) != null) {
      ActivitySequenceJump asj = (back ? ase.backJump : ase.fwdJump);
      if (asj == null) {
        int i = currentAct + (back ? -1 : 1);
        if (i >= elements.size() || i < 0)
          i = 0;
        result = new JumpInfo(JumpInfo.JUMP, i);
      } else {
        int rating = -1;
        int time = -1;
        if (rep != null) {
          SequenceReg.Info info = rep.getCurrentSequenceInfo();
          if (info != null) {
            rating = (int) (info.tScore);
            time = (int) (info.tTime / 1000);
          }
        }
        result = asj.resolveJump(rating, time);
      }
    }
    return result;
  }

  public ActivitySequenceElement getCurrentAct() {
    return getElement(currentAct, false);
  }

  public int getCurrentActNum() {
    return currentAct;
  }

  public String getSequenceForElement(int e) {
    String s = null;
    if (e >= 0 && e < elements.size())
      for (int i = e; i >= 0; i--)
        if ((s = getElement(i, false).getTag()) != null)
          break;
    return s;
  }

  public ActivitySequenceElement getElementByActivityName(String activityName) {
    ActivitySequenceElement result = null;
    if (activityName != null) {
      for (int i = 0; i < elements.size(); i++) {
        ActivitySequenceElement ase = getElement(i, false);
        if (ase.getActivityName().equalsIgnoreCase(activityName)) {
          result = ase;
          break;
        }
      }
    }
    return result;
  }

  public boolean checkCurrentActivity(String name) {
    ActivitySequenceElement ase = getCurrentAct();
    if (ase == null || !ase.getActivityName().equalsIgnoreCase(name)) {
      for (int i = 0; i < elements.size(); i++) {
        if (getElement(i, false).getActivityName().equalsIgnoreCase(name)) {
          currentAct = i;
          return false;
        }
      }
      ase = new ActivitySequenceElement(name, 0, ActivitySequenceElement.NAV_BOTH);
      ase.fwdJump = new ActivitySequenceJump(JumpInfo.STOP);
      ase.backJump = new ActivitySequenceJump(JumpInfo.STOP);
      elements.add(ase);
      currentAct = elements.size() - 1;
      return false;
    }
    return true;
  }

  public void listReferences(String type, Map<String, String> map) {
    for (int i = 0; i < elements.size(); i++)
      getElement(i, false).listReferences(type, map);
  }
}
