/*
 * File    : WordSearch.java
 * Created : 09-sep-2001 19:24
 * 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.textGrid;

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.JList;
import javax.swing.JScrollPane;

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

  String[] clues;
  int[] clueItems;

  /** Creates new WordSearch */
  public WordSearch(JClicProject project) {
    super(project);
    boxGridPos = AB;
    abc = new ActiveBagContent[1];
    clues = null;
    clueItems = null;
    invAss = false;
  }

  @Override
  public void initNew() {
    super.initNew();
    clues = new String[0];
    clueItems = new int[0];
    tgc = TextGridContent.initNew(3, 3, 'A');
  }

  protected static final String INVERSE = "inverse";

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

    if (clues == null || tgc == null)
      return null;

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

    e.addContent(tgc.getJDomElement());

    ex = new org.jdom.Element(CLUES);
    for (int i = 0; i < clues.length; i++) {
      ex2 = new org.jdom.Element(CLUE);
      ex2.setAttribute(ID, Integer.toString(clueItems[i]));
      ex2.setText(clues[i]);
      ex.addContent(ex2);
    }
    e.addContent(ex);

    if (abc[0] != null) {
      e.addContent(abc[0].getJDomElement().setAttribute(ID, SECONDARY));
      ex = new org.jdom.Element(LAYOUT);
      ex.setAttribute(POSITION, LAYOUT_NAMES[boxGridPos]);
      e.addContent(ex);

      if (scramble[0]) {
        ex = new org.jdom.Element(SCRAMBLE);
        ex.setAttribute(TIMES, Integer.toString(shuffles));
        ex.setAttribute(SECONDARY, JDomUtility.boolString(scramble[0]));
        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, child2;

    super.setProperties(e, aux);

    if ((child = e.getChild(TextGridContent.ELEMENT_NAME)) == null)
      throw new IllegalArgumentException("WordSearch without TextGridContent!");

    tgc = TextGridContent.getTextGridContent(child);

    if ((child = e.getChild(CLUES)) == null)
      throw new IllegalArgumentException("WordSearch without clues!");

    java.util.List<?> cluesList = child.getChildren(CLUE);
    int numClues = cluesList.size();
    clues = new String[numClues];
    clueItems = new int[numClues];
    for (int i = 0; i < numClues; i++) {
      child2 = ((org.jdom.Element) cluesList.get(i));
      clueItems[i] = JDomUtility.getIntAttr(child2, ID, i);
      clues[i] = child2.getText();
    }

    child = e.getChild(ActiveBagContent.ELEMENT_NAME);
    if (child != null) {
      if (!SECONDARY.equals(child.getAttributeValue(ID)))
        throw new IllegalArgumentException("WordSearch expects only \"secondary\" BagContent!");
      abc[0] = ActiveBagContent.getActiveBagContent(child, project.mediaBag);

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

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

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

  @Override
  public void setProperties(edu.xtec.jclic.clic3.Clic3Activity c3a) throws Exception {
    super.setProperties(c3a);
    boxGridPos = c3a.graPos;
    tgc = new TextGridContent();
    tgc.nch = c3a.nctxh;
    tgc.ncw = c3a.nctxw;
    tgc.w = c3a.txtCW;
    tgc.h = c3a.txtCH;
    tgc.border = c3a.delim[1];
    tgc.text = c3a.graTxt;
    clues = c3a.tags[0];
    clueItems = new int[clues.length];
    for (int i = 0; i < clues.length; i++)
      clueItems[i] = i;
    tgc.bb = c3a.getBoxBase(0);

    // read second grid
    if (c3a.bar[0]) {
      // clic3 problem
      c3a.txtCW = c3a.txtCW2;
      c3a.txtCH = c3a.txtCH2;
      // ---
      abc[0] = c3a.createActiveBagContent(0);
      abc[0].setBoxBase(c3a.getBoxBase(1));
    } else
      abc[0] = null;
  }

  public int getMinNumActions() {
    return clues.length;
  }

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

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

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

  /**
   * Getter for property clues.
   *
   * @return Value of property clues.
   */
  public String[] getClues() {
    return clues;
  }

  /**
   * Setter for property clues.
   *
   * @param cl      New value of property "clues"
   * @param clItems New value of property "clueItems"
   */
  public void setClues(String[] cl, int[] clItems) {
    clues = cl;
    if (clues == null)
      clues = new String[0];
    clueItems = clItems;
    if (clueItems == null || clueItems.length != clues.length) {
      clueItems = new int[clues.length];
      for (int i = 0; i < clueItems.length; i++)
        clueItems[i] = i;
    }
  }

  class Panel extends Activity.Panel {
    TextGrid grid;
    ActiveBoxBag bgAlt;
    boolean[] resolvedClues;

    protected Panel(PlayStation ps) {
      super(ps);
      bc = new BoxConnector(this);
      resolvedClues = new boolean[clues.length];
      grid = null;
      bgAlt = null;
    }

    public void clear() {
      if (grid != null) {
        grid.end();
        grid = null;
      }
      if (bgAlt != null) {
        bgAlt.end();
        bgAlt = null;
      }
    }

    @Override
    public void buildVisualComponents() throws Exception {

      if (firstRun)
        super.buildVisualComponents();

      clear();

      if (acp != null && abc != null)
        acp.generateContent(new ActiveBagContentKit(0, 0, abc, false), ps);

      if (tgc != null) {
        grid = TextGrid.createEmptyGrid(null, this, margin, margin, tgc, false);

        if (abc[0] != null)
          bgAlt = ActiveBoxGrid.createEmptyGrid(null, this, margin, margin, abc[0]);

        grid.setVisible(true);
        invalidate();
      }
    }

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

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

      setAndPlayMsg(MAIN, EventSounds.START);
      if (grid != null) {
        grid.setChars(tgc.text);
        grid.randomize();
        grid.setAllCellsAttribute(TextGrid.INVERTED, false);

        for (int i = 0; i < clueItems.length; i++)
          resolvedClues[i] = false;

        if (bgAlt != null) {
          bgAlt.setContent(abc[0]);
          if (scramble[0])
            shuffle(new ActiveBoxBag[] { bgAlt }, true, true);
          bgAlt.setVisible(invAss);
        }

        playing = true;
      }
    }

    public int getCurrentScore() {
      int result = 0;
      if (clues != null)
        for (int i = 0; i < clues.length; i++)
          if (resolvedClues[i])
            result++;
      return result;
    }

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

    public Dimension setDimension(Dimension preferredMaxSize) {
      if (grid == null || getSize().equals(preferredMaxSize))
        return preferredMaxSize;
      if (bgAlt != null)
        return BoxBag.layoutDouble(preferredMaxSize, grid, bgAlt, boxGridPos, margin);
      else
        return BoxBag.layoutSingle(preferredMaxSize, grid, margin);
    }

    @Override
    public void processMouse(MouseEvent e) {
      Point pt1, pt2;
      Point p = e.getPoint();
      boolean m = false;

      if (playing)
        switch (e.getID()) {
        case MouseEvent.MOUSE_PRESSED:
          ps.stopMedia(1);
          if (bc.active) {
            bc.end();
            pt1 = grid.getLogicalCoords(bc.origin);
            pt2 = grid.getLogicalCoords(bc.dest);
            if (pt1 != null && pt2 != null) {
              String s = grid.getStringBetween(pt1.x, pt1.y, pt2.x, pt2.y);
              if (s != null && s.length() > 0) {
                boolean ok = false;
                boolean repeated = false;
                int c;
                for (c = 0; c < clues.length; c++)
                  if (s.equals(clues[c])) {
                    ok = true;
                    break;
                  }
                if (ok && !(repeated = resolvedClues[c])) {
                  resolvedClues[c] = true;
                  grid.setAttributeBetween(pt1.x, pt1.y, pt2.x, pt2.y, TextGrid.INVERTED, true);
                  if (bgAlt != null) {
                    int k = clueItems[c];
                    if (k >= 0 && k < bgAlt.getNumCells()) {
                      ActiveBox bx = bgAlt.getActiveBox(clueItems[c]);
                      if (bx != null) {
                        bx.setVisible(!invAss);
                        m = bx.playMedia(ps);
                      }
                    }
                  }
                }
                if (!repeated) {
                  int r = getCurrentScore();
                  ps.reportNewAction(getActivity(), ACTION_SELECT, s, null, ok, r);
                  if (r == clues.length)
                    finishActivity(true);
                  else if (!m)
                    playEvent(ok ? EventSounds.ACTION_OK : EventSounds.ACTION_ERROR);
                } else if (!ok && !m)
                  playEvent(EventSounds.ACTION_ERROR);
              } else
                playEvent(EventSounds.ACTION_ERROR);
            }
          } else {
            if (grid.contains(p)) {
              playEvent(EventSounds.CLICK);
              bc.begin(p);
            }
          }
          break;

        case MouseEvent.MOUSE_MOVED:
        case MouseEvent.MOUSE_DRAGGED:
          if (bc.active) {
            bc.moveTo(p);
          }
          break;
        }
    }

    @Override
    public void showHelp() {
      if (!helpWindowAllowed() || grid == null)
        return;

      HelpActivityComponent hac = null;
      if (showSolution) {
        hac = new HelpActivityComponent(this) {
          JScrollPane scrollPane = null;
          JList<Object> cluesList = null;

          public void render(Graphics2D g2, Rectangle dirtyRegion) {
            // do nothing
          }

          @Override
          public void init() {
            cluesList = new JList<Object>(clues);
            cluesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
            cluesList.addListSelectionListener(this);
            scrollPane = new JScrollPane(cluesList);
            add(scrollPane);
            Dimension size = grid.getBounds().getSize();
            scrollPane.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) grid.x - DEFAULT_MARGIN, (int) grid.y - DEFAULT_MARGIN);
          }

          @Override
          public void doLayout() {
            Rectangle r = getBounds();
            if (scrollPane != null)
              scrollPane.setBounds(DEFAULT_MARGIN, DEFAULT_MARGIN, r.width - 2 * DEFAULT_MARGIN,
                  r.height - 2 * DEFAULT_MARGIN);
          }

          @Override
          public void valueChanged(javax.swing.event.ListSelectionEvent ev) {
            if (bgAlt != null && !ev.getValueIsAdjusting()) {
              int i = cluesList.getSelectedIndex();
              if (i < 0) {
                unmarkBox();
              } else {
                playEvent(EventSounds.CLICK);
                markBox(bgAlt.getActiveBox(clueItems[i]), true);
              }
            }
          }
        };
        hac.init();
      }
      if (ps.showHelp(hac, helpMsg))
        ps.reportNewAction(getActivity(), ACTION_HELP, null, null, false, getCurrentScore());
      if (hac != null)
        hac.end();
    }
  }
}
