package com.yworks.yfiles.server.graphml.experimental;

import org.freehep.graphics2d.VectorGraphics;
import org.freehep.graphicsio.swf.SWFGraphics2D;
import org.freehep.util.io.Base64OutputStream;
import org.w3c.dom.Node;
import y.base.DataMap;
import y.base.DataProvider;
import y.io.graphml.NamespaceConstants;
import y.io.graphml.graph2d.AbstractNodeRealizerSerializer;
import y.io.graphml.graph2d.GraphicsSerializationToolkit;
import y.io.graphml.input.GraphMLParseContext;
import y.io.graphml.output.GraphMLWriteContext;
import y.io.graphml.output.GraphMLWriteException;
import y.io.graphml.output.XmlWriter;
import y.view.NodeLabel;
import y.view.NodeRealizer;

import java.awt.Dimension;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * A NodeRealizer serializer implementation that exports SWF bytecode for the graphics of a given
 * NodeRealizer subclass.
 */
public class SWFNodeSerializer extends AbstractNodeRealizerSerializer {

  private final Class realizerClass;

  /**
   * A DataProvider key that can be used to associate a node to object cache with
   * the graph that will be used by this instance to cache the generated SWF snippets.
   */
  public static final String NODE_2_SWF_CACHE_DPKEY = "SWFNodeSerializer#NODE_2_SWF_CACHE_DPKEY";

  /**
   * Creates an instance for the given realizerClass.
   * @param realizerClass The NodeRealizer subclass that is to be serialized by this serializer
   */
  public SWFNodeSerializer( Class realizerClass) {
    this.realizerClass = realizerClass;
  }

  /**
   * Not supported.
   */
  public void parse( NodeRealizer nodeRealizer, Node node, GraphMLParseContext graphMLParseContext ) {
    throw new UnsupportedOperationException( "Not implemented" );
  }

  /**
   * Does nothing.
   */
  public void writeAttributes( NodeRealizer nodeRealizer, XmlWriter xmlWriter, GraphMLWriteContext graphMLWriteContext ) {
  }

  /**
   *  Writes the realizer.
   */
  public void write( NodeRealizer nodeRealizer, XmlWriter xmlWriter, GraphMLWriteContext graphMLWriteContext ) throws
      GraphMLWriteException {

    String swfData;

    DataProvider dataProvider = nodeRealizer.getNode().getGraph().getDataProvider(NODE_2_SWF_CACHE_DPKEY);

    if (dataProvider instanceof DataMap){
      DataMap node2SwfCache = (DataMap) dataProvider;
      swfData = ( String ) node2SwfCache.get( nodeRealizer.getNode() );

      if ( null == swfData ) {
        swfData = encodeSWF( realizer2SWF( nodeRealizer ) );
        node2SwfCache.set( nodeRealizer.getNode(), swfData );
      }
    } else {
      swfData = encodeSWF( realizer2SWF( nodeRealizer ) );
    }

    xmlWriter.writeStartElement( "SWFData", NamespaceConstants.YFILES_JAVA_NS );
    xmlWriter.writeText( swfData );
    xmlWriter.writeEndElement();

    GraphicsSerializationToolkit.writeNodeLayout(xmlWriter, nodeRealizer);

    for ( int i = 0; i < nodeRealizer.labelCount(); i++ ) {
      writeNodeLabel(nodeRealizer.getLabel(i), xmlWriter, graphMLWriteContext);
    }

  }

  /**
   *  Returns the name of the XML element tag which denotes this type of realizer.
   */
  public String getName() {
    return "SWFNode";
  }

  /**
   * Returns the target namespace of the toplevel element
   */
  public String getNamespaceURI() {
    return NamespaceConstants.YFILES_JAVA_NS;
  }

  /**
   * Returns the namespace prefix of the toplevel element
   */
  public String getNamespacePrefix() {
    return null;
  }

  /**
   * Returns the class of which the realizer is an instance of.
   */
  public Class getRealizerClass() {
    return realizerClass;
  }

  private String encodeSWF( byte[] swfData ) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Base64OutputStream base64OS = new Base64OutputStream( baos );
    try {
      base64OS.write( swfData );
    } catch ( IOException e ) {
      e.printStackTrace(); //TODO handle
    }
    return baos.toString();
  }

  private byte[] realizer2SWF( NodeRealizer realizer ) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    VectorGraphics g = new SWFGraphics2D( baos,
            new Dimension( ( int ) realizer.getWidth(), ( int ) realizer.getHeight() ) );

    g.startExport();

    // backup location
    double tx = realizer.getX();
    double ty = realizer.getY();
    realizer.setLocation( 0, 0 );

    // backup label visibility
    boolean[] labelState = new boolean[realizer.labelCount()];
    for ( int i = 0; i < realizer.labelCount(); i++ ) {
      NodeLabel label = realizer.getLabel( i );
      labelState[i] = label.isVisible();
      label.setVisible( false );
    }

    realizer.paint( g );

    // restore label visibility
    for ( int i = 0; i < labelState.length; i++ ) {
      realizer.getLabel( i ).setVisible( labelState[i] );
    }

    // restore location
    realizer.setLocation( tx, ty );

    g.endExport();

    return baos.toByteArray();
  }
}
