/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <https://www.compadre.org/osp/>
 */

package org.opensourcephysics.display2d;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Shape;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import org.opensourcephysics.display.DisplayRes;
import org.opensourcephysics.display.DrawingPanel;
import org.opensourcephysics.display.InteractivePanel;
import org.opensourcephysics.display.Measurable;
import org.opensourcephysics.display.axes.XAxis;
import org.opensourcephysics.display.axes.XYAxis;

/**
 *  A TriangularByteLattice is an array where each array element can assume one
 *  of 256 values. Values can be set between -128 and 127. Because byte values
 *  larger than 127 overflow to negative, values can also be set between 0 and
 *  255.
 *
 * @author     Joshua Gould
 * @author     Wolfgang Christian
 * @created    May 13, 2003
 * @version    1.0
 */
public class TriangularByteLattice implements Measurable {
  int nrow, ncol;
  byte[][] data;
  double xmin, ymin, xmax, ymax;
  double xminLattice, yminLattice, xmaxLattice, ymaxLattice;
  final static double SQRT3_OVER2 = Math.sqrt(3)/2.0;
  boolean visible = true;
  Color[] colors = new Color[256];
  final static int radius = 3;
  final static int diameter = radius*2;
  JFrame legendFrame;

  /**
   *  Constructs a byte lattice with the given size. Cell values are -128 to 127.
   *
   * @param  _row  the number of rows
   * @param  _col  the number of columns
   */
  public TriangularByteLattice(int _row, int _col) {
    nrow = _row;
    ncol = _col;
    createDefaultColors();
    data = new byte[nrow][ncol];
    // col in x direction, row in y direction
    xminLattice=xmin = 0;
    xmaxLattice =xmax = ncol-0.5;
    ymin = (nrow-1)*SQRT3_OVER2;
    if(ymin==0) {
      ymin = SQRT3_OVER2; // FIXME
    }
    yminLattice = ymin;
    ymaxLattice=ymax = 0; // zero is on top
  }

  public void resizeLattice(int _row, int _col) {
    nrow = _row;
    ncol = _col;
    data = new byte[nrow][ncol];
    // col in x direction, row in y direction
    xminLattice=xmin = 0;
    xmaxLattice =xmax = ncol-0.5;
    ymin = (nrow-1)*SQRT3_OVER2;
    if(ymin==0) {
      ymin = SQRT3_OVER2; // FIXME
    }
    yminLattice = ymin;
    ymaxLattice=ymax = 0; // zero is on top
  }
  
  public void setVisible (boolean _vis) {
    this.visible = _vis;  
  }
  
  public void setMinMax(double xmin, double xmax, double ymin, double ymax) {
    this.xmin = xmin;
    this.xmax = xmax;
    this.ymin = ymin;
    this.ymax = ymax;
  }

  /**
   *  Draws the lattice.
   *
   * @param  panel
   * @param  g
   */
  public void draw(DrawingPanel panel, Graphics g) {
    if(!visible) {
      return;
    }
    Shape oldClip = g.getClip();
    g.setClip(null);
    double xScale=(xmax-xmin)/(xmaxLattice-xminLattice);
    double yScale=-(ymax-ymin)/(ymaxLattice-yminLattice);
    for(int yi = 0; yi<nrow; yi++) {
      for(int xi = 0; xi<ncol; xi++) {
        byte val = data[yi][xi];
        g.setColor(colors[val&0xFF]);
        // each row is sqrt(3)/2 offset previous row
        if((yi%2)==1) {
          double x=(xi+0.5)*xScale+xmin;
          double y=yi*SQRT3_OVER2*yScale+ymin;
          g.fillOval(panel.xToPix(x)-radius, panel.yToPix(y)-radius, diameter, diameter); // shift to right by 0.5
        } else {
          double x=xi*xScale+xmin;
          double y=yi*SQRT3_OVER2*yScale+ymin;
          g.fillOval(panel.xToPix(x)-radius, panel.yToPix(y)-radius, diameter, diameter);
        }
      }
    }
    g.setClip(oldClip);
  }

  /**
   *  Sets a block of cells to new values.
   *
   * @param  row_offset
   * @param  col_offset
   * @param  val
   */
  public void setBlock(int row_offset, int col_offset, byte val[][]) {
    if((row_offset<0)||(row_offset+val.length>nrow)) {
      throw new IllegalArgumentException("Row index out of range in binary lattice setBlock."); //$NON-NLS-1$
    }
    if((col_offset<0)||(col_offset+val[0].length>ncol)) {
      throw new IllegalArgumentException("Column index out of range in binary lattice setBlock."); //$NON-NLS-1$
    }
    for(int rindex = row_offset, nr = val.length+row_offset; rindex<nr; rindex++) {
      for(int cindex = col_offset, nc = val[0].length+col_offset; cindex<nc; cindex++) {
        data[rindex][cindex] = val[rindex-row_offset][cindex-col_offset];
      }
    }
  }
  
  /**
   *  Sets a block of cells to new values.
   *
   * @param  row_offset
   * @param  col_offset
   * @param  val
   */
  public void setBlock(int row_offset, int col_offset, int val[][]) {
    if((row_offset<0)||(row_offset+val.length>nrow)) {
      throw new IllegalArgumentException("Row index out of range in binary lattice setBlock."); //$NON-NLS-1$
    }
    if((col_offset<0)||(col_offset+val[0].length>ncol)) {
      throw new IllegalArgumentException("Column index out of range in binary lattice setBlock."); //$NON-NLS-1$
    }
    for(int rindex = row_offset, nr = val.length+row_offset; rindex<nr; rindex++) {
      for(int cindex = col_offset, nc = val[0].length+col_offset; cindex<nc; cindex++) {
        data[rindex][cindex] = (byte) val[rindex-row_offset][cindex-col_offset];
      }
    }
  }

  /**
   *  Sets a column of cells to new values.
   *
   * @param  row_offset
   * @param  col
   * @param  val
   */
  public void setCol(int row_offset, int col, byte val[]) {
    if((row_offset<0)||(row_offset+val.length>nrow)) {
      throw new IllegalArgumentException("Row index out of range in binary lattice setBlock."); //$NON-NLS-1$
    }
    if((col<0)||(col>=ncol)) {
      throw new IllegalArgumentException("Column index out of range in binary lattice setBlock."); //$NON-NLS-1$
    }
    for(int rindex = row_offset, nr = val.length+row_offset; rindex<nr; rindex++) {
      data[rindex][col] = val[rindex-row_offset];
    }
  }

  /**
   *  Sets a row of cells to new values.
   *
   * @param  row
   * @param  col_offset
   * @param  val
   */
  public void setRow(int row, int col_offset, byte val[]) {
    if((row<0)||(row>=nrow)) {
      throw new IllegalArgumentException("Row index out of range in binary lattice setBlock."); //$NON-NLS-1$
    }
    if((col_offset<0)||(col_offset+val.length>ncol)) {
      throw new IllegalArgumentException("Column index out of range in binary lattice setBlock."); //$NON-NLS-1$
    }
    for(int cindex = col_offset, nc = val.length+col_offset; cindex<nc; cindex++) {
      data[row][cindex] = val[cindex-col_offset];
    }
  }

  /**
   *  Sets a lattice cell to a new value.
   *
   * @param  row
   * @param  col
   * @param  val
   */
  public void setCell(int row, int col, byte val) {
    data[row][col] = val;
    // note that increasing x corresponds to an increaing column index
  }

  /**
   *  Gets a lattice cell value.
   *
   * @param  row
   * @param  col
   * @return      the cell value.
   */
  public int getCell(int row, int col) {
    return data[row][col];
  }

  /**  Ranomizes the lattice values. */
  public void randomize() {
    Random random = new Random();
    for(int rindex = 0, nr = data.length; rindex<nr; rindex++) {
      for(int cindex = 0, nc = data[0].length; cindex<nc; cindex++) {
        data[rindex][cindex] = (byte) random.nextInt(256);
      }
    }
  }

  /**  Shows the color associated with each value. */
  public void showLegend() {
    InteractivePanel dp = new InteractivePanel();
    dp.setPreferredSize(new java.awt.Dimension(300, 66));
    dp.setPreferredGutters(0, 0, 0, 35);
    dp.setClipAtGutter(false);
    if((legendFrame==null)||!legendFrame.isDisplayable()) {
      legendFrame = new JFrame(DisplayRes.getString("GUIUtils.Legend")); //$NON-NLS-1$
    }
    legendFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    legendFrame.setResizable(false);
    legendFrame.setContentPane(dp);
    TriangularByteLattice lattice = new TriangularByteLattice(1, 256);
    lattice.setMinMax(-128, 127, 0, 1);
    byte[][] data = new byte[1][256];
    for(int i = 0; i<256; i++) {
      data[0][i] = (byte) i;
    }
    lattice.setBlock(0, 0, data);
    dp.addDrawable(lattice);
    XAxis xaxis = new XAxis(""); //$NON-NLS-1$
    xaxis.setLocationType(XYAxis.DRAW_AT_LOCATION);
    xaxis.setLocation(-0.5);
    xaxis.setEnabled(true);
    dp.addDrawable(xaxis);
    legendFrame.pack();
    legendFrame.setVisible(true);
  }

  /**
   *  Sets the color palette.
   *
   * @param  colors
   */
  public void setColorPalette(Color[] colors) {
    for(int i = 0, n = colors.length; i<n; i++) {
      this.colors[i] = colors[i];
    }
    for(int i = colors.length; i<256; i++) {
      this.colors[i] = Color.black;
    }
  }

  /**
   *  Sets the color for a single index.
   *
   * @param  i
   * @param  color
   */
  public void setIndexedColor(int i, Color color) {
    // i = i % colors.length;
    i = (i+256)%colors.length;
    colors[i] = color;
  }

  public boolean isMeasured() {
    return true;
  }

  public double getXMin() {
    return xmin;
  }

  public double getYMin() {
    return ymin;
  }

  public double getXMax() {
    return xmax;
  }

  public double getYMax() {
    return ymax;
  }

  public void createDefaultColors() {
    for(int i = 0; i<256; i++) {
      Color c = Color.getHSBColor((-0.07f+0.80f*i/255f)%1, 1, 1);
      colors[i] = c;
    }
  }

}

/*
 * Open Source Physics software is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License,
 * or(at your option) any later version.

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2019  The Open Source Physics project
 *                     https://www.compadre.org/osp
 */