package com.yoghurt.crypto.transactions.client.widget;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map.Entry;

import com.google.gwt.core.client.GWT;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
import com.googlecode.gwt.crypto.bouncycastle.util.encoders.Hex;
import com.yoghurt.crypto.transactions.client.resources.R;
import com.yoghurt.crypto.transactions.client.util.misc.Color;

public abstract class HexViewer<T> extends ContextFieldSet<Entry<T, byte[]>> {
  interface HexViewerUiBinder extends UiBinder<Widget, HexViewer<?>> {}

  private static final HexViewerUiBinder UI_BINDER = GWT.create(HexViewerUiBinder.class);

  public interface CustomStyle extends CssResource {
    String byteSet();

    String byteField();
  }

  @UiField CustomStyle style;

  private FlowPanel byteSetContainer;

  protected ArrayList<ContextField<Entry<T, byte[]>>> activeFields;

  public HexViewer() {
    this(null);
  }

  public HexViewer(final FieldContextFactory<Entry<T, byte[]>> contextFactory) {
    super(contextFactory);

    UI_BINDER.createAndBindUi(this);
  }

  /**
   * Set the container for this hex viewer.
   */
  public void setValue(final Collection<Entry<T, byte[]>> container) {
    boolean quickAdd = false;
    int counter = 0;
    for (final Entry<T, byte[]> entry : container) {
      final byte[] bytesForValue = getBytesForValue(entry);

      final Color typeColor = getFieldColor(entry);

      // Change the value for each field
      for (final byte bite : bytesForValue) {
        final String hex = getHexFromByte(bite);

        if(quickAdd || counter >= fields.size()) {
          ContextField<Entry<T, byte[]>> addField = addField(entry, typeColor, hex);
          addField.addStyleName(style.byteField());
          quickAdd = true;
        } else {
          final ContextField<Entry<T, byte[]>> contextField = fields.get(counter);
          contextField.setValue(entry);
          contextField.setColor(typeColor);
          contextField.setContent(hex);
        }

        counter++;
      }
    }

    // Reverse remove the remainder
    for(int i = fields.size() - 1;i >= counter; i--) {
      removeField(i);
    }
  }

  public void addFields(final Entry<T, byte[]> value) {
    final Color typeColor = getFieldColor(value);

    for (final byte bite : getBytesForValue(value)) {
      final String hex = getHexFromByte(bite);

      addField(value, typeColor, hex);
    }
  }

  protected String getHexFromByte(final byte bite) {
    return new String(Hex.encode(new byte[] { bite })).toUpperCase();
  }

  @Override
  protected void displayContextForValue(final Entry<T, byte[]> value) {
    clearActiveFields();

    //    activeFields = findValueFields(value);

    if (activeFields == null) {
      return;
    }

    for (final ContextField<Entry<T, byte[]>> w : activeFields) {
      w.setActive(true);
    }
  }

  @Override
  protected void clearActivity() {
    super.clearActivity();

    clearActiveFields();
  }

  private void clearActiveFields() {
    if (activeFields != null) {
      for (final ContextField<Entry<T, byte[]>> field : activeFields) {
        field.setActive(false);
      }
    }
  }

  protected byte[] getBytesForValue(final Entry<T, byte[]> entry) {
    return entry.getValue();
  }

  @Override
  protected ContextField<Entry<T, byte[]>> addField(final ContextField<Entry<T, byte[]>> field) {
    if (byteSetContainer == null) {
      byteSetContainer = new FlowPanel();
      byteSetContainer.setStyleName(style.byteSet());
      byteSetContainer.addStyleName(R.css().flex());
      add(byteSetContainer);
    }

    byteSetContainer.add(field);

    if (byteSetContainer.getWidgetCount() == 8) {
      byteSetContainer = null;
    }

    return field;
  }

  @Override
  public void removeField(final int index) {
    final ContextField<Entry<T, byte[]>> contextField = fields.get(index);

    final FlowPanel byteSetContainer = (FlowPanel) contextField.getParent();

    contextField.removeFromParent();

    if(byteSetContainer.getWidgetCount() == 0) {
      byteSetContainer.removeFromParent();
    } else {
      this.byteSetContainer = byteSetContainer;
    }

    fields.remove(index);
  }

  @Override
  public void clear() {
    super.clear();
    byteSetContainer = null;
    clearActiveFields();
  }
}