/*
 * $Id: GamePieceImage.java 4812 2009-01-12 22:06:21Z uckelman $
 *
 * Copyright (c) 2005 by Rodney Kinney, Brent Easton
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License (LGPL) as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, copies are available
 * at http://www.opensource.org.
 */

package VASSAL.build.module.gamepieceimage;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import VASSAL.build.AbstractConfigurable;
import VASSAL.build.AutoConfigurable;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.configure.Configurer;
import VASSAL.configure.ConfigurerFactory;
import VASSAL.configure.VisibilityCondition;
import VASSAL.tools.ArchiveWriter;
import VASSAL.tools.ErrorDialog;
import VASSAL.tools.UniqueIdManager;
import VASSAL.tools.imageop.Op;
import VASSAL.tools.imageop.SourceOp;

/**
 * 
 */
public class GamePieceImage extends AbstractConfigurable implements Visualizable, Cloneable, UniqueIdManager.Identifyable {

  protected static final String NAME = "name"; //$NON-NLS-1$
  protected static final String PROPS = "props"; //$NON-NLS-1$

  public static final String PART_SIZE = "Size"; //$NON-NLS-1$
  public static final String PART_SYMBOL1 = "Symbol1"; //$NON-NLS-1$
  public static final String PART_SYMBOL2 = "Symbol2"; //$NON-NLS-1$

  public static final String BG_COLOR = "bgColor"; //$NON-NLS-1$
  public static final String BORDER_COLOR = "borderColor"; //$NON-NLS-1$

  protected List<ItemInstance> instances = new ArrayList<ItemInstance>();
  protected InstanceConfigurer defnConfig = null;
  protected GamePieceLayout layout;
  protected ColorSwatch bgColor = ColorSwatch.getWhite();
  protected ColorSwatch borderColor = ColorSwatch.getBlack();
  protected String id;

  protected static UniqueIdManager idMgr = new UniqueIdManager("GamePieceImage"); //$NON-NLS-1$
  protected String nameInUse;
  protected Image visImage = null;
 
  protected SourceOp srcOp;

  public GamePieceImage() {
    super();
    setConfigureName(""); //$NON-NLS-1$
  }

  public GamePieceImage(String s) {
    instances = InstanceConfigurer.StringToProperties(s, this);
  }

  public GamePieceImage(GamePieceLayout l) {
    this();
    setConfigureName(l.getConfigureName());
    layout = l;
  }

  public GamePieceImage(GamePieceImage defn) {
    this();
    this.setConfigureName(defn.getConfigureName());
    this.layout = defn.getLayout();
    this.bgColor = defn.getBgColor();
    this.borderColor = defn.getBorderColor();
    this.instances.addAll(defn.getInstances());
  }

  /*
   * The Generic trait needs a deep copy of the Image Definition
   */
  public Object clone() {
    return new GamePieceImage(this);
  }

  public List<ItemInstance> getInstances() {
    return instances;
  }

  public String[] getAttributeDescriptions() {
    return new String[] {
      "Name:  ",
      "Background Color:  ",
      "Border Color:  ",
      "" //$NON-NLS-1$
    };
  }

  public Class<?>[] getAttributeTypes() {
    return new Class<?>[] {
      String.class,
      BgColorSwatchConfig.class,
      BorderColorSwatchConfig.class,
      DefnConfig.class
    };
  }

  public static class BgColorSwatchConfig implements ConfigurerFactory {
    public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
      return new ColorSwatchConfigurer(key, name, ((GamePieceImage) c).getBgColor());
    }
  }

  public static class BorderColorSwatchConfig implements ConfigurerFactory {
    public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
      return new ColorSwatchConfigurer(key, name, ((GamePieceImage) c).getBorderColor());
    }
  }

  public static class DefnConfig implements ConfigurerFactory {
    static GamePieceImage id;

    public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
      id = (GamePieceImage) c;
      id.defnConfig = new InstanceConfigurer(key, name, id);
      id.rebuildInstances();
      return id.defnConfig;
    }

    public static void refresh() {
      if (id.defnConfig != null) {
        id.defnConfig.repack();
      }
    }
  }

  public String[] getAttributeNames() {
    return new String[] {NAME, BG_COLOR, BORDER_COLOR, PROPS};
  }

  public ColorSwatch getBgColor() {
    return bgColor;
  }

  public ColorSwatch getBorderColor() {
    return borderColor;
  }

  public void setAttribute(String key, Object value) {
    if (NAME.equals(key)) {
      final String newName = (String) value;
      final String oldName = getConfigureName();
      if (newName != oldName && oldName.length() > 0) {
        final ArchiveWriter w = GameModule.getGameModule().getArchiveWriter();
        w.removeImage(oldName);
        w.addImage(newName, getEncodedImage((BufferedImage) visImage));
      }
      setConfigureName(newName);
    }
    else if (BG_COLOR.equals(key)) {
      if (value instanceof String) {
        value = new ColorSwatch((String) value);
      }
      bgColor = (ColorSwatch) value;
      if (defnConfig != null) {
        defnConfig.visualizer.rebuild();
      }
    }
    else if (BORDER_COLOR.equals(key)) {
      if (value instanceof String) {
        value = new ColorSwatch((String) value);
      }
      borderColor = (ColorSwatch) value;
      if (defnConfig != null) {
        defnConfig.visualizer.rebuild();
      }
    }
    else if (PROPS.equals(key)) {
      if (value instanceof String) {
        value = InstanceConfigurer.StringToProperties((String) value, this);
      }
      if (instances instanceof List) {
        instances = (List<ItemInstance>) value;
      }
      if (defnConfig != null) {
        rebuildInstances();
        defnConfig.visualizer.rebuild();
        defnConfig.repack();
      }
    }
    if (defnConfig != null) {
      rebuildVisualizerImage();
    }
  }

  public String getAttributeValueString(String key) {
    if (NAME.equals(key)) {
      return getConfigureName();
    }
    else if (BG_COLOR.equals(key)) {
      return bgColor.encode();
    }
    else if (BORDER_COLOR.equals(key)) {
      return borderColor.encode();
    }
    else if (PROPS.equals(key)) {
      return InstanceConfigurer.PropertiesToString(instances);
    }
    else
      return null;
  }

  public VisibilityCondition getAttributeVisibility(String name) {
    if (BORDER_COLOR.equals(name)) {
      return borderCond;
    }
    else {
      return super.getAttributeVisibility(name);
    }
  }

  private VisibilityCondition borderCond = new VisibilityCondition() {
    public boolean shouldBeVisible() {
      if (getLayout() == null) {
        return false;
      }
      else {
        return getLayout().isColoredBorder();
      }
    }
  };

  public void removeFrom(Buildable parent) {
    idMgr.remove(this);
  }

  public HelpFile getHelpFile() {
    return HelpFile.getReferenceManualPage("GamePieceImage.htm"); //$NON-NLS-1$
  }

  public Class<?>[] getAllowableConfigureComponents() {
    return new Class[0];
  }

  public void addTo(Buildable parent) {
    layout = (GamePieceLayout) parent;
    idMgr.add(this);
    validator = idMgr;
    setAllAttributesUntranslatable();
    rebuildInstances();
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public static String getConfigureTypeName() {
    return "Game Piece Image";
  }

  public void refreshConfig() {
    rebuildVisualizerImage();
  }

  public GamePieceLayout getLayout() {
    return layout;
  }

  public int getVisualizerHeight() {
    return getLayout().getVisualizerHeight();
  }

  public int getVisualizerWidth() {
    return getLayout().getVisualizerWidth();
  }

  public Image getVisualizerImage() {
    if (visImage == null) {
      rebuildVisualizerImage();
    }
    return visImage;
  }

  // Build the new image and add to the archive
  public void rebuildVisualizerImage() {
    if (layout != null) {
      visImage = layout.buildImage(this);

      final ArchiveWriter w = GameModule.getGameModule().getArchiveWriter();
      if (w != null) {
        if (getConfigureName() != null && getConfigureName().length() > 0) {
          w.addImage(getConfigureName(),
                     getEncodedImage((BufferedImage) visImage));
          SourceOp op = Op.load(getConfigureName());
          op.update();
        }       
      }
    }
  }

  public byte[] getEncodedImage(BufferedImage bufferedImage) {
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    try {
      ImageIO.write(bufferedImage,"png", out); //$NON-NLS-1$
    }
    catch (IOException e) {
      ErrorDialog.bug(e);
// FIXME: why byte[1] instead of byte[0]?
      return new byte[1];
    }
    return out.toByteArray();
  }

  public ItemInstance getInstance(String name) {
    for (ItemInstance instance : instances) {
      if (name.equals(instance.getName())) {
        return instance;
      }
    }
    return null;
  }

  public TextItemInstance getTextInstance(String name) {
    for (ItemInstance instance : instances) {
      if (instance instanceof TextItemInstance) {
        if (name.equals(instance.getName())) {
          return (TextItemInstance) instance;
        }
      }
    }
    return null;
  }

  public TextBoxItemInstance getTextBoxInstance(String name) {
    for (ItemInstance instance : instances) {
      if (instance instanceof TextBoxItemInstance) {
        if (name.equals(instance.getName())) {
          return (TextBoxItemInstance) instance;
        }
      }
    }
    return null;
  }

  public SymbolItemInstance getSymbolInstance(String name) {
    for (ItemInstance instance : instances) {
      if (instance instanceof SymbolItemInstance) {
        if (name.equals(instance.getName())) {
          return (SymbolItemInstance) instance;
        }
      }
    }
    return null;
  }

  public ShapeItemInstance getShapeInstance(String name) {
    for (ItemInstance instance : instances) {
      if (instance instanceof ShapeItemInstance) {
        if (name.equals(instance.getName())) {
          return (ShapeItemInstance) instance;
        }
      }
    }
    return null;
  }

  public ImageItemInstance getImageInstance(String name) {
    for (ItemInstance instance : instances) {
      if (instance instanceof ImageItemInstance) {
        if (name.equals(instance.getName())) {
          return (ImageItemInstance) instance;
        }
      }
    }
    return null;
  }

  /*
   * Reconcile our current elements with the elements in the owning scheme.
   */
  protected void rebuildInstances() {
    final ArrayList<ItemInstance> newInstances = new ArrayList<ItemInstance>();

    for (ItemInstance prop : instances) {
      final Item item = layout.getItem(prop.getName());
      if (item != null && item.getType().equals(prop.getType())) {
        prop.setLocation(item.getLocation());
        newInstances.add(prop);
      }
    }

    if (layout != null) {
      for (Item item : layout.getItems()) {
        final String name = item.getConfigureName();
        final String type = item.getType();
        final String location = item.getLocation();

        boolean found = false;
        for (ItemInstance prop : instances) {
          found = name.equals(prop.getName());
          if (found) break;
        }

        if (!found) {
          final ItemInstance instance =
            ItemInstance.newDefaultInstance(name, type, location);
          instance.addTo(this);
          newInstances.add(instance);
        }
      }
    }

    instances = newInstances;
    if (defnConfig != null) {
      defnConfig.setValue(instances);
    }
    rebuildVisualizerImage();
  }
}
