/*
 * 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 performance;

import java.awt.BorderLayout;
import java.io.File;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;

import com.ilog.tools.bench.BenchProgram;

// JTGO import statements
import ilog.cpl.IlpEquipment;
import ilog.cpl.datasource.IlpDefaultDataSource;
import ilog.cpl.graphic.IlpPoint;
import ilog.cpl.graphic.IlpPosition;
import ilog.cpl.graphic.views.IlpShelfItemPosition;
import ilog.cpl.model.IlpObject;
import ilog.tgo.IltSystem;
import ilog.tgo.datasource.IltDefaultDataSource;
import ilog.tgo.model.IltAlarm;
import ilog.tgo.model.IltCard;
import ilog.tgo.model.IltOSIObjectState;
import ilog.tgo.model.IltObject;
import ilog.tgo.model.IltObjectState;
import ilog.tgo.model.IltShelf;
import ilog.views.IlvPoint;
import ilog.views.util.IlvProductUtil;
import shared.ResourceUtils;

/**
 * JTGO equipment performance sample.
 */
public class Main extends BenchProgram {

  // The width and height of the frame
  static final int sampleWidth = 750;
  static final int sampleHeight = 500;

  // The number of objects to create in the data source
  int numberOfShelves = 10;
  int numberOfCards = 100;

  // Data source input file
  String dataSourceInputFileName;
  boolean loadDataSourceFromFile = false;

  // Operation configuration parameters
  int numberOfScrolledRows = 50;

  // Components
  JFrame frame;
  IlpEquipment equipmentComponent;
  IlpDefaultDataSource dataSource;

  /**
   * Application entry point.
   */
  public static void main(String[] args) {
    // This sample uses JViews TGO features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews TGO Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_TGO_Deployment);

    Main sample = new Main();
    sample.handleArgs(args);
    sample.runBench();
    System.exit(0);
  }

  /**
   * Constructs a <code>Main</code>.
   */
  public Main() {
    super("JViews TGO Equipment Component Benchmarks");
  }

  // ---------------------------------------------------------------------------
  // Implementation of BenchProgram
  //

  /**
   * Handle bench specific command line arguments.
   *
   * @param args
   *          The command line arguments
   */
  public void handleArgs(String[] args) {
    for (int i = 0; i < args.length; i++) {
      if (args[i].startsWith("-shelves:")) {
        try {
          numberOfShelves = Integer.parseInt(args[i].substring("-shelves:".length()));
        } catch (NumberFormatException e) {
          usage("Missing number of shelves");
        }
      } else if (args[i].startsWith("-cards:")) {
        try {
          numberOfCards = Integer.parseInt(args[i].substring("-cards:".length()));
        } catch (NumberFormatException e) {
          usage("Missing number of cards");
        }
      } else if (args[i].startsWith("-input:")) {
        try {
          dataSourceInputFileName = args[i].substring("-input:".length());
          loadDataSourceFromFile = true;
        } catch (NumberFormatException e) {
          usage("Missing input file URL");
        }
      }
    }
  }

  /**
   * Implementation of the bench program.
   */
  Override
  public void benchIt() {
    recordBench();

    // Create the Swing container
    recordOperation("create the Swing container", new Runnable() {
      Override
      public void run() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      }
    });

    // Initialize JTGO
    recordOperation("initialize JTGO", new Runnable() {
      Override
      public void run() {
        try {
          IltSystem.Init("deploy.xml");
        } catch (Exception x) {
          x.printStackTrace();
        }
      }
    });

    final URL cssconfig = IltSystem.GetDefaultContext().getURLAccessService().getFileLocation("equipment.css");

    // Create the equipment component
    recordOperation("create the equipment component", new Runnable() {
      Override
      public void run() {
        equipmentComponent = new IlpEquipment(cssconfig);
      }
    });

    // Update the ResourceUtils to obtain the sample name
    ResourceUtils.initResources(equipmentComponent.getContext().getLocale(), "SampleMessages",
        "Perforce JViews TGO Sample");

    // Set the sample name
    frame.setTitle(
        MessageFormat.format(ResourceUtils.getString("sample.title"), new Object[] { "Perforce JViews TGO" }));

    // Fill the data source
    recordOperationAndAWT("fill the data source", new Runnable() {
      Override
      public void run() {
        dataSource = fillDataSource();
      }
    });

    // Connect the data source to the component
    recordOperationAndAWT("connect the data source to the component", new Runnable() {
      Override
      public void run() {
        equipmentComponent.setDataSource(dataSource);
      }
    });

    // Make the frame visible
    recordOperation("make the frame visible", new Runnable() {
      Override
      public void run() {
        frame.getContentPane().add(equipmentComponent, BorderLayout.CENTER);
        frame.setSize(550, 300);
        frame.setVisible(true);
      }
    });

    // Add an alarm to all objects
    recordOperationAndAWT("add an alarm to all objects", new Runnable() {
      Override
      public void run() {
        addAlarmToAllObjects(dataSource);
      }
    });

    // Update position of all shelves
    recordOperationAndAWT("update position of all shelves", new Runnable() {
      Override
      public void run() {
        updatePositionOfAllShelves(dataSource);
      }
    });

    // Select all objects
    recordOperationAndAWT("select all objects", new Runnable() {
      Override
      public void run() {
        equipmentComponent.getView().selectAll();
      }
    });

    // Clear the selection
    recordOperationAndAWT("clear the selection", new Runnable() {
      Override
      public void run() {
        equipmentComponent.getView().clearSelection();
      }
    });

    // Scroll vertically
    recordOperationAndAWT("scroll", new Runnable() {
      Override
      public void run() {
        final JScrollBar scrollBar = equipmentComponent.getView().getVerticalScrollBar();
        for (int i = 0; i < numberOfScrolledRows; ++i) {
          SwingUtilities.invokeLater(new Runnable() {
            Override
            public void run() {
              scrollBar.setValue(scrollBar.getValue() + scrollBar.getUnitIncrement(1));
            }
          });
        }
      }
    });

    // Zoom in
    recordOperation("zoom in", new Runnable() {
      Override
      public void run() {
        equipmentComponent.getView().getManagerView().zoom(new IlvPoint(50, 50), 2, 2, true);
      }
    });

    // Zoom out a first time
    recordOperation("zoom out (1st time)", new Runnable() {
      Override
      public void run() {
        equipmentComponent.getView().getManagerView().zoom(new IlvPoint(50, 50), .5, .5, true);
      }
    });

    // Zoom out a second time
    recordOperation("zoom out (2nd time)", new Runnable() {
      Override
      public void run() {
        equipmentComponent.getView().getManagerView().zoom(new IlvPoint(50, 50), .5, .5, true);
      }
    });

    // Fit to content
    recordOperation("fit to content", new Runnable() {
      Override
      public void run() {
        equipmentComponent.getView().fitToContents();
      }
    });

    // Clear the data source
    recordOperationAndAWT("clear the data source", new Runnable() {
      Override
      public void run() {
        dataSource.clear();
      }
    });
  }

  /**
   * Records the bench parameters.
   */
  protected void recordBench() {
    if (loadDataSourceFromFile) {

      int extensionIndex = dataSourceInputFileName.lastIndexOf(".");
      int pathSeparatorIndex = dataSourceInputFileName.lastIndexOf(File.separator);
      String shortFileName = dataSourceInputFileName.substring(pathSeparatorIndex >= 0 ? pathSeparatorIndex + 1 : 0,
          extensionIndex >= 0 ? extensionIndex : dataSourceInputFileName.length());

      recordBench("F" + shortFileName, dataSourceInputFileName);
      addConfigParameter("Input data file", dataSourceInputFileName);

    } else {

      recordBench("S" + numberOfShelves + "-C" + numberOfCards,
          "" + numberOfShelves + " shelves " + numberOfCards + " cards");
      addConfigParameter("Shelves", Integer.toString(numberOfShelves));
      addConfigParameter("Cards", Integer.toString(numberOfCards));
    }
  }

  // ---------------------------------------------------------------------------
  //
  //

  /**
   * Create and fill a data source. The data source is either filled from a
   * input file or a sample content is automatically generated.
   */
  protected IlpDefaultDataSource fillDataSource() {
    // Create a datasource
    IltDefaultDataSource dataSource = new IltDefaultDataSource();

    if (loadDataSourceFromFile) {
      try {
        // Read an XML file into the datasource
        dataSource.parse(dataSourceInputFileName);
      } catch (Exception x) {
        x.printStackTrace();
      }
    } else {
      createData(dataSource);
    }
    return dataSource;
  }

  /**
   * Create sample data.
   *
   * @param dataSource
   */
  protected void createData(IlpDefaultDataSource dataSource) {
    int objectCount = numberOfShelves * numberOfCards + numberOfShelves;

    List<IlpObject> objects = new ArrayList<IlpObject>(objectCount);

    for (int i = 0; i < numberOfShelves; ++i) {
      String shelfName = "" + i;
      IltShelf shelf = new IltShelf(numberOfCards, 15, 30, 0);
      shelf.setName(shelfName);
      shelf.setPosition(new IlpPoint(0, i * 100));
      objects.add(shelf);

      List<Object> cardsIds = new ArrayList<Object>(numberOfCards);

      for (int j = 0; j < numberOfCards; ++j) {
        IltCard card = new IltCard((IltObjectState) null);
        card.setPosition(new IlpShelfItemPosition(j, 0, 1, 1));
        objects.add(card);
        cardsIds.add(card.getIdentifier());
      }
      dataSource.setChildren(shelf.getIdentifier(), cardsIds);
    }
    dataSource.addObjects(objects);
  }

  /**
   * Add an alarm to all objects in the data source.
   */
  protected void addAlarmToAllObjects(IlpDefaultDataSource dataSource) {
    Collection<IlpObject> objects = dataSource.getObjects();
    Iterator<IlpObject> it = objects.iterator();

    dataSource.startBatch();

    while (it.hasNext()) {
      IltObject object = (IltObject) it.next();
      IltOSIObjectState ostate = new IltOSIObjectState();
      IltAlarm.State alarms = (IltAlarm.State) ostate.getAlarmState();
      alarms.addNewAlarm(IltAlarm.Severity.Major);
      object.setObjectState(ostate);
    }

    dataSource.endBatch();
  }

  /**
   * Update the position of all shelves in the data source.
   */
  protected void updatePositionOfAllShelves(IlpDefaultDataSource dataSource) {
    Collection<IlpObject> objects = dataSource.getObjects();
    Iterator<IlpObject> it = objects.iterator();

    dataSource.startBatch();

    while (it.hasNext()) {
      IltObject object = (IltObject) it.next();
      IlpPosition position = object.getPosition();

      // Shelves have an IlpRect position
      if (position instanceof IlpPoint) {
        // Use a new instance to defeat optimization in the equipment
        IlpPoint oldPosition = (IlpPoint) position;
        IlpPoint newPosition = new IlpPoint(oldPosition.x + 10, oldPosition.y + 10);
        object.setPosition(newPosition);
      }
    }

    dataSource.endBatch();
  }

  // ---------------------------------------------------------------------------
  //
  //

  private void usage(String message) {
    usage(message, 1);
  }

  private void usage(String message, int exitCode) {
    String className = this.getClass().getName();
    if (message != null) {
      System.out.println(className + ": " + message);
    }
    System.out.println("usage: " + className + " [-options]");
    System.out.println();
    System.out.println("Where options include:");
    System.out.println("    -shelves:<n>      number of shelves (default: 10)");
    System.out.println("    -cards:<n>        number of cards per shelf (default: 100)");
    System.out.println("    -input:<filename> data source input data file. Overrides -shelves and -cards options");
    System.exit(exitCode);
  }
}