/*
 * File    : WrittenAnswer.java
 * Created : 09-may-2001 11:15
 * 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.automation.ActiveBagContentKit;
import edu.xtec.jclic.boxes.*;
import edu.xtec.jclic.media.EventSounds;
import edu.xtec.jclic.project.JClicProject;
import edu.xtec.util.JDomUtility;
import java.awt.*;
import java.awt.event.MouseEvent;
import javax.swing.JTextField;

/**
 * @author Francesc Busquets (fbusquets@xtec.cat)
 * @version 13.08.28
 */
public class WrittenAnswer extends Activity implements ActiveBagContentKit.Compatible {

  int nonAssignedCells;
  boolean useIdAss;

  /** Creates new WrittenAnswer */
  public WrittenAnswer(JClicProject project) {
    super(project);
    boxGridPos = AB;
    abc = new ActiveBagContent[3];
    scramble[0] = false;

    nonAssignedCells = 0;
    invAss = false;
    useIdAss = true;
  }

  @Override
  public void initNew() {
    super.initNew();
    abc[0] = ActiveBagContent.initNew(3, 2, 'A');
    abc[1] = ActiveBagContent.initNew(3, 2, 'A');
  }

  protected static final String ANSWERS = "answers", INVERSE = "inverse";

  @Override
  public org.jdom.Element getJDomElement() {
    org.jdom.Element ex;

    if (abc[0] == null || abc[1] == null)
      return null;

    org.jdom.Element e = super.getJDomElement();

    e.addContent(abc[0].getJDomElement().setAttribute(ID, PRIMARY));
    e.addContent(abc[1].getJDomElement().setAttribute(ID, ANSWERS));
    if (abc[2] != null)
      e.addContent(abc[2].getJDomElement().setAttribute(ID, SOLVED_PRIMARY));

    ex = new org.jdom.Element(SCRAMBLE);
    {
      ex.setAttribute(TIMES, Integer.toString(shuffles));
      ex.setAttribute(PRIMARY, JDomUtility.boolString(scramble[0]));
      e.addContent(ex);
    }

    ex = new org.jdom.Element(LAYOUT);
    ex.setAttribute(POSITION, LAYOUT_NAMES[boxGridPos]);
    e.addContent(ex);

    if (invAss)
      e.setAttribute(INVERSE, JDomUtility.boolString(invAss));

    return e;
  }

  @Override
  public void setProperties(org.jdom.Element e, Object aux) throws Exception {
    org.jdom.Element child;
    super.setProperties(e, aux);
    ActiveBagContent bag;
    abc[2] = null;
    java.util.Iterator itr = e.getChildren(ActiveBagContent.ELEMENT_NAME).iterator();
    while (itr.hasNext()) {
      child = ((org.jdom.Element) itr.next());
      bag = ActiveBagContent.getActiveBagContent(child, project.mediaBag);
      String id = JDomUtility.getStringAttr(child, ID, PRIMARY, false);
      if (PRIMARY.equals(id))
        abc[0] = bag;
      else if (ANSWERS.equals(id))
        abc[1] = bag;
      else if (SOLVED_PRIMARY.equals(id))
        abc[2] = bag;
    }
    if (abc[0] == null || abc[1] == null)
      throw new IllegalArgumentException("WrittenAnswer without content!");

    if ((child = e.getChild(SCRAMBLE)) != null) {
      shuffles = JDomUtility.getIntAttr(child, TIMES, shuffles);
      scramble[0] = JDomUtility.getBoolAttr(child, PRIMARY, scramble[0]);
    }

    if ((child = e.getChild(LAYOUT)) != null)
      boxGridPos = JDomUtility.getStrIndexAttr(child, POSITION, LAYOUT_NAMES, boxGridPos);

    invAss = JDomUtility.getBoolAttr(e, INVERSE, invAss);

    abc[0].avoidAllIdsNull(abc[1].getNumCells());
  }

  @Override
  public void setProperties(edu.xtec.jclic.clic3.Clic3Activity c3a) throws Exception {
    super.setProperties(c3a);
    // Clic3 problem
    c3a.nctxw = c3a.ncw;
    c3a.nctxh = c3a.nch;
    boxGridPos = c3a.graPos;
    for (int i = 0; i < 2; i++) {
      abc[i] = c3a.createActiveBagContent(i);
      abc[i].setBoxBase(c3a.getBoxBase(i));
    }
    scramble[0] = c3a.bar[0];
    abc[2] = c3a.sol ? c3a.createActiveBagContent(2) : null;

    abc[0].avoidAllIdsNull(abc[1].getNumCells());
  }

  public int getMinNumActions() {
    if (abc[0] == null || abc[1] == null)
      return 0;
    if (invAss)
      return abc[1].getNumCells();
    else
      return abc[0].getNumCells() - nonAssignedCells;
  }

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

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

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

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

  class Panel extends Activity.Panel implements java.awt.event.ActionListener {

    JTextField textField;
    ActiveBoxBag[] bg = new ActiveBoxBag[2];
    int currentCell;
    boolean[] invAssCheck;

    protected Panel(PlayStation ps) {
      super(ps);
      for (int i = 0; i < 2; i++)
        bg[i] = null;
      currentCell = -1;
      textField = null;
      invAssCheck = null;
    }

    public void clear() {
      for (int i = 0; i < 2; i++)
        if (bg[i] != null) {
          bg[i].end();
          bg[i] = null;
        }
      textField = null;
    }

    @Override
    public void buildVisualComponents() throws Exception {

      if (firstRun)
        super.buildVisualComponents();

      ActiveBox bx;
      currentCell = -1;

      clear();

      if (abc[0] != null && abc[1] != null) {

        if (acp != null)
          acp.generateContent(new ActiveBagContentKit(abc[0].nch, abc[0].ncw, abc, true), ps);

        if (invAss) {
          invAssCheck = new boolean[abc[1].getNumCells()];
          for (int i = 0; i < invAssCheck.length; i++)
            invAssCheck[i] = false;
        }

        bg[0] = ActiveBoxGrid.createEmptyGrid(null, this, margin, margin, abc[0]);
        // Clic3 behavior!!!
        double w = abc[1].w;
        if (boxGridPos == AUB || boxGridPos == BUA)
          w = abc[0].getTotalWidth();
        bg[1] = new ActiveBoxGrid(null, this, margin, margin, w, abc[1].h, new edu.xtec.jclic.shapers.Rectangular(1, 1),
            abc[1].bb);

        textField = new JTextField(200);
        textField.setHorizontalAlignment(JTextField.CENTER);
        bx = bg[1].getActiveBox(0);
        bx.setInactive(false);
        bx.hasHostedComponent = true;
        bx.setHostedComponent(textField);
        textField.addActionListener(this);

        bg[0].setContent(abc[0], abc[2]);
        currentCell = 0;

        bg[0].setDefaultIdAss();
        nonAssignedCells = 0;
        for (int i = 0; i < bg[0].getNumCells(); i++) {
          bx = bg[0].getActiveBox(i);
          if (bx.idAss == -1) {
            nonAssignedCells++;
            bx.switchToAlt(ps);
          }
        }

        bg[0].setVisible(true);
        bg[1].setVisible(true);
        invalidate();
      }
    }

    @Override
    public void initActivity() throws Exception {
      super.initActivity();

      if (!firstRun)
        buildVisualComponents();
      else
        firstRun = false;

      setAndPlayMsg(MAIN, EventSounds.START);
      if (bg[0] != null && bg[1] != null) {
        if (scramble[0])
          shuffle(new ActiveBoxBag[] { bg[0] }, true, true);
        if (useOrder)
          currentItem = bg[0].getNextItem(-1);
        playing = true;
        setCurrentCell(0);
      }
    }

    public void render(Graphics2D g2, Rectangle dirtyRegion) {
      for (int i = 0; i < 2; i++) {
        if (bg[i] != null)
          bg[i].update(g2, dirtyRegion, this);
      }
    }

    public Dimension setDimension(Dimension preferredMaxSize) {
      return bg[0] == null || bg[1] == null || getSize().equals(preferredMaxSize) ? preferredMaxSize
          : BoxBag.layoutDouble(preferredMaxSize, bg[0], bg[1], boxGridPos, margin);
    }

    private boolean checkInvAss() {
      if (invAss == false || invAssCheck == null)
        return false;
      for (boolean b : invAssCheck)
        if (!b)
          return false;
      return true;
    }

    private void setCurrentCell(int i) {
      ActiveBox bx;
      boolean m = false;

      if (!playing)
        return;
      if (currentCell != -1) {
        boolean ok = false;
        bx = bg[0].getActiveBoxWithIdLoc(currentCell);
        String src = bx.getDescription();
        bx.setMarked(false);
        int id = bx.idAss;
        String txCheck = (id >= 0 ? abc[1].getActiveBoxContent(id).text : "");
        String txAnswer = textField.getText().trim();
        if (edu.xtec.util.StrUtils.compareMultipleOptions(txAnswer, txCheck, false)) {
          ok = true;
          bx.idAss = -1;

          // 29-mai-2007
          // When in multiple-answer, fill-in textField with
          // the first valid option:
          if (txCheck.indexOf('|') >= 0)
            textField.setText((new java.util.StringTokenizer(txCheck, "|")).nextToken());

          if (abc[2] != null) {
            bx.switchToAlt(ps);
            m = bx.playMedia(ps);
          } else
            bx.clear();
          if (invAss && id >= 0 && id < invAssCheck.length) {
            invAssCheck[id] = true;
          }
          if (useOrder)
            currentItem = bg[0].getNextItem(currentItem);
        }

        int cellsPlaced = bg[0].countCellsWithIdAss(-1);

        if (txAnswer.length() > 0) {
          ps.reportNewAction(getActivity(), ACTION_WRITE, src, txAnswer, ok, cellsPlaced);
        }
        if (ok && (checkInvAss() || cellsPlaced == bg[0].getNumCells())) {
          finishActivity(true);
          textField.setEnabled(false);
          return;
        } else if (!m && txAnswer.length() > 0)
          playEvent(ok ? EventSounds.ACTION_OK : EventSounds.ACTION_ERROR);
      }

      if (useOrder)
        bx = (ActiveBox) bg[0].getBox(currentItem);
      else
        bx = bg[0].getActiveBoxWithIdLoc(i);
      if (bx == null || bx.idAss == -1) {
        for (int j = 0; j < bg[0].getNumCells(); j++) {
          bx = bg[0].getActiveBoxWithIdLoc(j);
          if (bx.idAss != -1)
            break;
        }
        if (bx != null && bx.idAss == -1) {
          // error ?
          finishActivity(false);
          textField.setEnabled(false);
          return;
        }
      }

      // 29 - mai -2007
      // Draw border only if it has more than one cell
      if (bg[0].getNumCells() > 1 && bx != null)
        bx.setMarked(true);
      if (bx != null)
        currentCell = bx.idLoc;
      textField.setText("");
      textField.requestFocus();

      if (bx != null)
        bx.playMedia(ps);
    }

    @Override
    public void requestFocus() {
      if (playing && textField != null)
        textField.requestFocus();
    }

    public void actionPerformed(java.awt.event.ActionEvent e) {
      if (playing && e.getSource() == textField && currentCell != -1)
        setCurrentCell(currentCell);
    }

    @Override
    public void processMouse(MouseEvent e) {
      ActiveBox bx;
      boolean m = false;

      if (playing)
        switch (e.getID()) {
        case MouseEvent.MOUSE_PRESSED:
          ps.stopMedia(1);
          if ((bx = bg[0].findActiveBox(e.getPoint())) != null) {
            if (bx.getContent() != null && bx.getContent().mediaContent == null)
              playEvent(EventSounds.CLICK);
            setCurrentCell(bx.idLoc);
          }
          break;
        }
    }

    @Override
    public void showHelp() {
      if (!helpWindowAllowed() || bg[0] == null)
        return;

      HelpActivityComponent hac = null;
      if (showSolution) {
        hac = new HelpActivityComponent(this) {
          ActiveBoxBag abb = null;
          String currentResponse = "";
          int cellsPlaced = bg[0].countCellsWithIdAss(-1);

          public void render(Graphics2D g2, Rectangle dirtyRegion) {
            if (abb != null)
              abb.update(g2, dirtyRegion, this);
          }

          @Override
          public void init() {
            currentResponse = textField.getText();
            abb = (ActiveBoxBag) bg[0].clone();
            abb.setContainer(this);
            Dimension size = abb.getBounds().getSize();
            abb.setBounds(DEFAULT_MARGIN, DEFAULT_MARGIN, size.width, size.height);
            size.width += 2 * DEFAULT_MARGIN;
            size.height += 2 * DEFAULT_MARGIN;
            setPreferredSize(size);
            setMaximumSize(size);
            setMinimumSize(size);
            Point p = (Point) getClientProperty(HelpActivityComponent.PREFERRED_LOCATION);
            if (p != null)
              p.translate((int) bg[0].x - DEFAULT_MARGIN, (int) bg[0].y - DEFAULT_MARGIN);
          }

          @Override
          public void end() {
            super.end();
            textField.setText(currentResponse);
          }

          @Override
          public void processMouse(MouseEvent e) {
            ActiveBox bx;
            if (abb != null)
              switch (e.getID()) {
              case MouseEvent.MOUSE_PRESSED:
                bx = abb.findActiveBox(e.getPoint());
                if (bx != null) {
                  boolean m = bx.playMedia(ps);
                  String s = abc[1].getActiveBoxContent(bx.idOrder).text;
                  if (s != null)
                    textField.setText(s.replace('|', ' '));
                  ps.reportNewAction(getActivity(), ACTION_HELP, bx.getDescription(), null, false, cellsPlaced);
                  if (!m)
                    playEvent(EventSounds.CLICK);
                }
                break;
              case MouseEvent.MOUSE_RELEASED:
                unmarkBox();
                textField.setText("");
                break;
              }
          }
        };
        hac.init();
      }
      if (ps.showHelp(hac, helpMsg))
        ps.reportNewAction(getActivity(), ACTION_HELP, null, null, false, bg[0].countCellsWithIdAss(-1));

      if (hac != null)
        hac.end();
    }
  }
}
