/*
 * File    : Complete.java
 * Created : 24-oct-2001 17:01
 * 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.activities.text;

import edu.xtec.jclic.*;
import edu.xtec.jclic.media.EventSounds;
import edu.xtec.jclic.project.JClicProject;
import edu.xtec.util.Actions;
import edu.xtec.util.StrUtils;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

/**
 * @author Francesc Busquets (fbusquets@xtec.cat)
 * @version 13.09.16
 */
public class Complete extends TextActivityBase {

  Evaluator ev;

  /** Creates new Complete */
  public Complete(JClicProject project) {
    super(project);
    ev = new ComplexEvaluator(project);
    hasCheckButton = true;
  }

  @Override
  public org.jdom.Element getJDomElement() {
    org.jdom.Element e = super.getJDomElement();
    e.addContent(ev.getJDomElement());
    return e;
  }

  @Override
  public void setProperties(org.jdom.Element e, Object aux) throws Exception {
    super.setProperties(e, aux);
    ev = Evaluator.getEvaluator(e.getChild(Evaluator.ELEMENT_NAME), project);
  }

  @Override
  public void setProperties(edu.xtec.jclic.clic3.Clic3Activity c3a) throws Exception {
    super.setProperties(c3a);
    ((ComplexEvaluator) ev).setProperties(c3a);
    hasCheckButton = true;
  }

  @Override
  public boolean needsKeyboard() {
    return true;
  }

  @Override
  public Activity.Panel getActivityPanel(PlayStation ps) {
    return new Panel(ps);
  }

  class Panel extends TextActivityBase.Panel {

    TextActivityDocument playDoc = null;
    boolean locked;
    int nActions;

    protected Panel(PlayStation ps) {
      super(ps);
      locked = true;
      nActions = 0;
    }

    @Override
    protected void initDocument() throws Exception {
      nActions = 0;
      if (tad != null) {
        playing = false;
        playDoc = new TextActivityDocument(styleContext);
        tad.cloneDoc(playDoc, true, false, false);
        pane.setStyledDocument(playDoc);
        playDoc.attachTo(pane, Complete.Panel.this);
        pane.setEnabled(true);
        if (playDoc.tmb.size() > 0) {
          pane.setEditable(true);
          pane.requestFocus();
          pane.getCaret().setVisible(true);
          locked = false;
        } else {
          locked = true;
          pane.setEditable(false);
          pane.getCaret().setVisible(false);
        }
      }
    }

    @Override
    protected TextActivityPane buildPane() {
      CompletePane cp = new CompletePane();
      cp.setActions();
      return cp;
    }

    class CompletePane extends TextActivityPane {
      protected CompletePane() {
        super(Complete.Panel.this);
      }

      @Override
      public boolean processMouse(MouseEvent e) {
        if (super.processMouse(e) && e.getID() == MouseEvent.MOUSE_PRESSED && playing && !locked)
          return true;

        return false;
      }

      protected void invalidateSelection() {
        int offset = getCaret().getDot();
        if (getCaret().getMark() != offset)
          getCaret().setDot(offset);
      }

      @Override
      protected void fireCaretUpdate(CaretEvent e) {
        invalidateSelection();
        super.fireCaretUpdate(e);
      }

      @Override
      public void replaceSelection(String content) {
        invalidateSelection();
        if (content != null && content.length() > 0) {
          char ch = content.charAt(0);
          int offset = getCaret().getDot();
          if (ch >= 0x20 && ch != 0x7F) {
            try {
              playDoc.insertString(offset, new String(new char[] { ch }), playDoc.getTargetAttributeSet());
            } catch (BadLocationException ex) {
              System.err.println("Text activity error:\n" + ex);
            }
          }
        }
      }

      @Override
      public void cut() {
        invalidateSelection();
      }

      @Override
      public void paste() {
        invalidateSelection();
      }

      boolean readyForActions() {
        return playing && !locked && isEditable() && isEnabled();
      }

      // Actions
      Action kitDeletePrevCharAction = null;
      AbstractAction deletePrevCharAction = new AbstractAction(DefaultEditorKit.deletePrevCharAction) {
        public void actionPerformed(ActionEvent e) {
          if (readyForActions() && kitDeletePrevCharAction != null) {
            invalidateSelection();
            int offset = getCaret().getDot() - 1;
            if (offset > 0 && playDoc.checkBooleanAttribute(offset, TextActivityDocument.TARGET) == true) {
              kitDeletePrevCharAction.actionPerformed(e);
            }
          }
        }
      };

      Action kitDeleteNextCharAction = null;
      AbstractAction deleteNextCharAction = new AbstractAction(DefaultEditorKit.deleteNextCharAction) {
        public void actionPerformed(ActionEvent e) {
          if (readyForActions() && kitDeleteNextCharAction != null) {
            invalidateSelection();
            int offset = getCaret().getDot();
            if (offset >= 0 && offset < playDoc.getLength()
                && playDoc.checkBooleanAttribute(offset, TextActivityDocument.TARGET) == true) {
              kitDeleteNextCharAction.actionPerformed(e);
            }
          }
        }
      };

      protected void setActions() {
        kitDeleteNextCharAction = getActionMap().get(DefaultEditorKit.deleteNextCharAction);
        kitDeletePrevCharAction = getActionMap().get(DefaultEditorKit.deletePrevCharAction);
        java.util.Map<String, Object[]> actionKeys = Actions.getActionKeys(this);
        ActionMap am = new ActionMap();
        am.setParent(getActionMap());
        setActionMap(am);
        Actions.mapAction(this, actionKeys, deletePrevCharAction);
        Actions.mapAction(this, actionKeys, deleteNextCharAction);
      }
    }

    @Override
    protected void doCheck(boolean fromButton) {
      if (playDoc == null || locked)
        return;

      String match;
      String current;
      try {
        match = StrUtils.trimEnding(tad.getText(0, tad.getLength()));
        current = StrUtils.trimEnding(playDoc.getText(0, playDoc.getLength()));
      } catch (BadLocationException e) {
        System.err.println("Error: unable to retrieve text:\n" + e);
        return;
      }

      byte[] result = ev.evalText(current, match);
      int score = 0;
      if (result != null) {
        int l = result.length;
        int i = 0;
        while (i < l) {
          while (i < l && !playDoc.checkBooleanAttribute(i, TextActivityDocument.TARGET))
            i++;
          if (i < l) {
            nActions++;
            boolean actionOk = true;
            int j = i;
            while (i < l && playDoc.checkBooleanAttribute(i, TextActivityDocument.TARGET))
              i++;
            playDoc.setCharacterAttributes(j, i - j, styleContext.getStyle(TextActivityDocument.TARGET), true);
            for (int k = j; k < i; k++) {
              if (result[k] != Evaluator.FLAG_OK) {
                actionOk = false;
                playDoc.setCharacterAttributes(k, 1, styleContext.getStyle(TextActivityDocument.TARGET_ERROR), false);
              }
            }
            if (actionOk)
              score++;
          }
        }
      }

      ps.setCounterValue(SCORE_COUNTER, score);
      ps.setCounterValue(ACTIONS_COUNTER, nActions);

      if (Evaluator.isOk(result))
        finishActivity(true);
      else if (fromButton)
        playEvent(EventSounds.FINISHED_ERROR);
    }

    @Override
    public void finishActivity(boolean result) {
      pane.setEditable(false);
      pane.setEnabled(false);
      super.finishActivity(result);
    }
  }
}
