/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

package demo.accessible;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicBag;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvLinkImage;
import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.IlvRect;
import ilog.views.sdm.renderer.IlvSubGraphRenderer.SubGrapher;
import ilog.views.servlet.IlvHitmapDefinition;
import ilog.views.servlet.IlvNavigationInfoGenerator;
import ilog.views.util.hitmap.IlvHitmapConstants;
import ilog.views.util.hitmap.IlvNavigationInfo;

/**
 * 
 * @author Loki
 *
 */
public class CustomizedNavigationInfoGenerator extends IlvNavigationInfoGenerator {

  private List<Integer> emptyFrom = new ArrayList<Integer>();

  private List<Integer> emptyTo = new ArrayList<Integer>();

  private int selectedIndex = 0;

  public CustomizedNavigationInfoGenerator(IlvHitmapDefinition hitmapDefinition) {
    super(hitmapDefinition);
  }

  Override
  protected IlvNavigationInfo[] prepareNavigationInfos() {
    Vector<IlvGraphic> v = this.hitmapDefinition.getGraphics();
    int size = v.size();
    IlvNavigationInfo[] relations = new IlvNavigationInfo[size];
    // Fix the issue JV-6190
    // Initialize navigation info object.
    for (int i = 0; i < size; i++) {
      relations[i] = new IlvNavigationInfo(0, 0, 0, 0);
    }

    IlvManagerView managerView = this.hitmapDefinition.getManagerView();
    IlvManager manager = managerView.getManager();
    for (int i = 0; i < size; i++) {
      if (manager.isSelected(v.elementAt(i))) {
        selectedIndex = i + 1;
        break;
      }
    }

    // IlvGraphicEnumeration enumeration = manager.getObjects();
    emptyFrom.clear();
    emptyTo.clear();
    recur(v, manager, relations);

    for (int i = 0; i < emptyTo.size(); i++) {
      int in = emptyTo.get(i).intValue();
      if (relations[in].leftTarget + relations[in].rightTarget == 0) {
        if (i + 1 < emptyTo.size())
          relations[in].rightTarget = emptyTo.get(i + 1).intValue() + 1;
        else {
          IlvGraphicBag inGb = v.get(in).getGraphicBag();
          for (int j = 0; j < emptyFrom.size(); j++) {
            int f = emptyFrom.get(j).intValue();
            IlvGraphicBag gb = v.get(f).getGraphicBag();
            if (gb.equals(inGb)) {
              relations[in].downTarget = f + 1;
              relations[f].upTarget = in + 1;
              break;
            }
          }
        }
      }
    }

    return relations;
  }

  Override
  public String buildInformation(String infoType) {
    relations = prepareNavigationInfos();
    Vector<IlvGraphic> graphics = this.hitmapDefinition.getGraphics();
    StringBuilder builder = new StringBuilder(infoType);
    builder.append(IlvHitmapConstants.COLON);
    builder.append("[" + selectedIndex + ",");

    for (int i = 0; i < graphics.size(); i++) {
      IlvGraphic graphic = graphics.elementAt(i);
      String graphicHitmapInfo = getRegionInfo(graphic, infoType).trim();
      if (graphicHitmapInfo != null && graphicHitmapInfo.length() > 0) {
        if (graphicHitmapInfo.startsWith("["))
          builder.append(graphicHitmapInfo);
        else
          builder.append("\"" + graphicHitmapInfo + "\"");
      }
      if (i < graphics.size() - 1)
        builder.append(",\n");
    }
    builder.append("],\n");
    return builder.toString();
  }

  private void recur(Vector<IlvGraphic> v, IlvManager manager, IlvNavigationInfo[] relations) {
    int i = -1;
    IlvGraphicEnumeration enumeration = manager.getObjects();
    while (enumeration.hasMoreElements()) {
      IlvGraphic g = enumeration.nextElement();
      if (v.contains(g)) {
        if (g instanceof IlvLinkImage) {
          i = v.indexOf(g);
          IlvLinkImage link = (IlvLinkImage) g;
          IlvGraphic from = link.getFrom();
          IlvGraphic to = link.getTo();
          int fromIndex = v.indexOf(from);
          int toIndex = v.indexOf(to);
          relations[i].upTarget = fromIndex + 1;
          relations[i].downTarget = toIndex + 1;

          recurFrom(relations, i, fromIndex);
          recurTo(relations, i, toIndex);
        } else if (g instanceof SubGrapher) {
          int j = v.indexOf(g);
          if (j < 0) {
            continue;
          }
          SubGrapher sub = (SubGrapher) g;
          recur(v, sub, relations);

          IlvGraphicEnumeration objects = sub.getObjects();

          if (objects.hasMoreElements()) {
            IlvGraphic leftest = objects.nextElement();
            while (leftest.getGraphicBag().equals(sub) && objects.hasMoreElements()) {
              IlvGraphic nextGraphic = objects.nextElement();
              if (v.contains(nextGraphic)) {
                IlvRect r = leftest.boundingBox();
                IlvRect n = nextGraphic.boundingBox();
                BigDecimal rx = new BigDecimal(r.getX());
                BigDecimal nx = new BigDecimal(n.getX());
                if (rx.compareTo(nx) == 1) {
                  leftest = nextGraphic;
                }
                int indexOf = v.indexOf(nextGraphic);
                if (relations[indexOf].downTarget == 0) {
                  // jump from the subgraph by click right arrow key
                  // for the last node of the subgraph.
                  relations[indexOf].rightTarget = j + 1;
                }
              }
            }
            int leftmostIndex = v.indexOf(leftest);
            if (leftmostIndex > -1) {
              // jump into the subgraph with left key
              relations[j].leftTarget = leftmostIndex + 1;
              // use right key to jump out of the subgraph always no matter
              // current focus is on a node or subgraph
              relations[leftmostIndex].rightTarget = j + 1;
            }
          }
          i = j;
        }
      }
    }
  }

  private void recurTo(IlvNavigationInfo[] relations, int i, int toIndex) {
    if (toIndex != -1) {
      if (relations[toIndex].upTarget == 0) {
        relations[toIndex].upTarget = i + 1;
      } else {
        relations[relations[toIndex].upTarget - 1].leftTarget = i + 1;
        relations[i].rightTarget = relations[toIndex].upTarget;
      }
    } else {
      emptyTo.add(i);
    }
  }

  private void recurFrom(IlvNavigationInfo[] relations, int i, int fromIndex) {
    if (fromIndex != -1) {
      if (relations[fromIndex].downTarget == 0) {
        relations[fromIndex].downTarget = i + 1;
      } else {
        relations[i].rightTarget = relations[fromIndex].downTarget;
        relations[relations[fromIndex].downTarget - 1].leftTarget = i + 1;
      }
    } else {
      emptyFrom.add(i);
    }
  }
}