/*
 * File    : BoxBase.java
 * Created : 12-dec-2000 11:28
 * 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.boxes;

import edu.xtec.jclic.Constants;
import edu.xtec.jclic.misc.Gradient;
import edu.xtec.util.Domable;
import edu.xtec.util.FontCheck;
import edu.xtec.util.JDomUtility;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;

/**
 * This class contains all the main visual attributes needed to draw
 * {@link edu.xtec.jclic.boxes.AbstractBox} objects: background and foreground
 * color and gradient, colors for special states (inactive, alternative,
 * disabled...), mrgins, fonts, strokes for borders, etc. Objects derived from
 * <CODE>AbstractBox</CODE> can have inheritance: boxes that act as "containers"
 * of other boxes (like {@link edu.xtec.jclic.boxes.BoxBag}). Most of the
 * attributes of <CODE>BoxBase</CODE> can be <I>null</I>, meaning that the value
 * of the ancestor, or a default value if the box has no ancestors, must be
 * taken.
 *
 * @author Francesc Busquets (fbusquets@xtec.cat)
 * @version 13.08.28
 */
public class BoxBase extends Object implements Cloneable, Domable {

  private Font originalFont;
  public Color backColor;
  public Gradient bgGradient;
  public Color textColor;
  public Color shadowColor;
  public Color borderColor;
  public Color inactiveColor;
  public Color alternativeColor;
  public boolean shadow;
  public boolean transparent;
  public int textMargin;
  protected BasicStroke borderStroke, markerStroke;

  private Font font;
  private float dynFontSize;
  private static final float REDUCE_FONT_STEP = 1;
  public static final float MIN_FONT_SIZE = 8;
  private static int resetAllFontsCounter = 0;
  public static boolean flagFontReduced = false;
  private int resetFontCounter;
  public static final Stroke DEFAULT_STROKE = new BasicStroke(1);
  public static final Color DEFAULT_BACK_COLOR = Color.lightGray;
  public static final Color DEFAULT_TEXT_COLOR = Color.black;
  public static final Color DEFAULT_SHADOW_COLOR = Color.gray;
  public static final Color DEFAULT_INACTIVE_COLOR = Color.gray;
  public static final Color DEFAULT_ALTERNATIVE_COLOR = Color.gray;
  public static final Color DEFAULT_BORDER_COLOR = Color.black;
  public static final float DEFAULT_BORDER_STROKE_WIDTH = 0.75f;
  public static final BasicStroke DEFAULT_BORDER_STROKE = new BasicStroke(DEFAULT_BORDER_STROKE_WIDTH);
  public static final float DEFAULT_MARKER_STROKE_WIDTH = 2.75f;
  public static final BasicStroke DEFAULT_MARKER_STROKE = new BasicStroke(DEFAULT_MARKER_STROKE_WIDTH);
  // default font will be build on first call to getDefaultFont();
  private static Font DEFAULT_FONT = null;

  public static BoxBase DEFAULT_BOX_BASE = new BoxBase();

  /** Creates new BoxBase */
  public BoxBase() {
    setFont(getDefaultFont());
    dynFontSize = font.getSize2D();
    backColor = DEFAULT_BACK_COLOR;
    bgGradient = null;
    textColor = DEFAULT_TEXT_COLOR;
    shadowColor = DEFAULT_SHADOW_COLOR;
    inactiveColor = DEFAULT_INACTIVE_COLOR;
    alternativeColor = DEFAULT_ALTERNATIVE_COLOR;
    borderColor = DEFAULT_BORDER_COLOR;
    shadow = false;
    transparent = false;
    textMargin = Constants.AC_MARGIN;
    resetFontCounter = resetAllFontsCounter;
    borderStroke = DEFAULT_BORDER_STROKE;
    markerStroke = DEFAULT_MARKER_STROKE;
  }

  @Override
  public Object clone() {
    BoxBase result = null;
    try {
      result = (BoxBase) super.clone();
      if (bgGradient != null)
        result.bgGradient = (Gradient) bgGradient.clone();
      result.setFont(originalFont);
    } catch (Exception ex) {
      System.err.println("Unexpected error cloning BoxBase!");
    }
    return result;
  }

  @Override
  public boolean equals(Object o) {
    if (o == null)
      return false;

    boolean result = (o == this);
    if (!result && o instanceof BoxBase) {
      BoxBase bb = (BoxBase) o;
      result = originalFont.equals(bb.originalFont) && backColor.equals(bb.backColor)
          && ((bgGradient == null && bb.bgGradient == null) || (bgGradient != null && bgGradient.equals(bb.bgGradient)))
          && textColor.equals(bb.textColor) && shadowColor.equals(bb.shadowColor) && borderColor.equals(bb.borderColor)
          && inactiveColor.equals(bb.inactiveColor) && alternativeColor.equals(bb.alternativeColor)
          && shadow == bb.shadow && transparent == bb.transparent && textMargin == bb.textMargin
          && borderStroke.equals(bb.borderStroke) && markerStroke.equals(bb.markerStroke);
    }
    return result;
  }

  public static Font getDefaultFont() {
    if (DEFAULT_FONT == null)
      DEFAULT_FONT = FontCheck.getValidFont("Arial", Font.PLAIN, 17);
    return DEFAULT_FONT;
  }

  public static final String ELEMENT_NAME = "style";
  public static final String COLOR = "color", FOREGROUND = "foreground", BACKGROUND = "background", SHADOW = "shadow",
      INACTIVE = "inactive", ALTERNATIVE = "alternative", BORDER = "border", TRANSPARENT = "transparent",
      MARGIN = "margin", BORDER_STROKE = "borderStroke", MARKER_STROKE = "markerStroke";

  public org.jdom.Element getJDomElement() {
    org.jdom.Element e = new org.jdom.Element(ELEMENT_NAME);

    if (!getDefaultFont().equals(originalFont))
      e.addContent(JDomUtility.fontToElement(originalFont));

    org.jdom.Element ce = new org.jdom.Element(COLOR);
    if (!textColor.equals(DEFAULT_TEXT_COLOR))
      ce.setAttribute(FOREGROUND, JDomUtility.colorToString(textColor));
    if (!backColor.equals(DEFAULT_BACK_COLOR))
      ce.setAttribute(BACKGROUND, JDomUtility.colorToString(backColor));
    if (!shadowColor.equals(DEFAULT_SHADOW_COLOR))
      ce.setAttribute(SHADOW, JDomUtility.colorToString(shadowColor));
    if (!inactiveColor.equals(DEFAULT_INACTIVE_COLOR))
      ce.setAttribute(INACTIVE, JDomUtility.colorToString(inactiveColor));
    if (!alternativeColor.equals(DEFAULT_ALTERNATIVE_COLOR))
      ce.setAttribute(ALTERNATIVE, JDomUtility.colorToString(alternativeColor));
    if (!borderColor.equals(DEFAULT_BORDER_COLOR))
      ce.setAttribute(BORDER, JDomUtility.colorToString(borderColor));
    if (!ce.getAttributes().isEmpty())
      e.addContent(ce);

    if (bgGradient != null)
      e.addContent(bgGradient.getJDomElement());

    if (shadow)
      e.setAttribute(SHADOW, JDomUtility.boolString(shadow));
    if (transparent)
      e.setAttribute(TRANSPARENT, JDomUtility.boolString(transparent));
    if (textMargin != Constants.AC_MARGIN)
      e.setAttribute(MARGIN, Integer.toString(textMargin));
    if (borderStroke.getLineWidth() != DEFAULT_BORDER_STROKE_WIDTH)
      e.setAttribute(BORDER_STROKE, Float.toString(borderStroke.getLineWidth()));
    if (markerStroke.getLineWidth() != DEFAULT_MARKER_STROKE_WIDTH)
      e.setAttribute(MARKER_STROKE, Float.toString(markerStroke.getLineWidth()));

    return e;
  }

  public static BoxBase getBoxBase(org.jdom.Element e) throws Exception {
    BoxBase bb = new BoxBase();
    bb.setProperties(e, null);
    return bb;
  }

  public void setProperties(org.jdom.Element e, Object aux) throws Exception {

    JDomUtility.checkName(e, ELEMENT_NAME);
    org.jdom.Element child;
    String s;

    if ((child = e.getChild(JDomUtility.FONT)) != null) {
      setFont(JDomUtility.elementToFont(child));
    }
    if ((child = e.getChild(COLOR)) != null) {
      textColor = JDomUtility.getColorAttr(child, FOREGROUND, textColor);
      backColor = JDomUtility.getColorAttr(child, BACKGROUND, backColor);
      shadowColor = JDomUtility.getColorAttr(child, SHADOW, shadowColor);
      inactiveColor = JDomUtility.getColorAttr(child, INACTIVE, inactiveColor);
      alternativeColor = JDomUtility.getColorAttr(child, ALTERNATIVE, alternativeColor);
      borderColor = JDomUtility.getColorAttr(child, BORDER, borderColor);
    }
    shadow = JDomUtility.getBoolAttr(e, SHADOW, shadow);
    transparent = JDomUtility.getBoolAttr(e, TRANSPARENT, transparent);
    textMargin = JDomUtility.getIntAttr(e, MARGIN, textMargin);
    if ((s = e.getAttributeValue(BORDER_STROKE)) != null)
      setBorderWidth(Float.parseFloat(s));
    if ((s = e.getAttributeValue(MARKER_STROKE)) != null)
      setMarkerWidth(Float.parseFloat(s));

    if ((child = e.getChild(Gradient.ELEMENT_NAME)) != null)
      bgGradient = Gradient.getGradient(child);
  }

  public Stroke getBorder() {
    return borderStroke;
  }

  public float getBorderWidth() {
    return borderStroke.getLineWidth();
  }

  public void setBorderWidth(float w) {
    borderStroke = new BasicStroke(w);
  }

  public Stroke getMarker() {
    return markerStroke;
  }

  public float getMarkerWidth() {
    return markerStroke.getLineWidth();
  }

  public void setMarkerWidth(float w) {
    markerStroke = new BasicStroke(w);
  }

  public static void resetAllFonts() {
    resetAllFontsCounter++;
  }

  public void setFont(Font newFont) {
    if (newFont != null) {
      font = newFont;
      dynFontSize = font.getSize();
      originalFont = font.deriveFont(new AffineTransform());
    }
  }

  public void resetFont() {
    resetFontCounter = resetAllFontsCounter - 1;
  }

  public Font getFont() {
    if (resetFontCounter < resetAllFontsCounter) {
      resetFontCounter = resetAllFontsCounter;
      font = originalFont.deriveFont(new AffineTransform());
      dynFontSize = font.getSize();
    }
    return font;
  }

  public Font getOriginalFont() {
    return originalFont;
  }

  public float getDynFontSize() {
    return dynFontSize;
  }

  public boolean reduceFont() {
    if (dynFontSize <= MIN_FONT_SIZE)
      return false;
    flagFontReduced = true;
    dynFontSize -= REDUCE_FONT_STEP;
    font = font.deriveFont(dynFontSize);
    return true;
  }
}
