/*
Copyright (C) 2002  Richard Eigenmann.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or any later version. This program 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 program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
The license is in gpl.txt.
See http://www.gnu.org/copyleft/gpl.html for the details.
*/
package ucar.nc2.ui.image;

import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.Enumeration;
import java.util.Vector;

/**
 *   The job of this extended JComponent is to load, scale and display a
 *   picture while
 *   providing mouse control over which area is being shown and at which magnification.
 *   It notifies the registered parent about status changes so that a description object can
 *   be updated. When the image has been rendered and displayed the legend of the image is
 *   send to the parent with the ready status.

 *
 *   The user can zoom in on a picture coordinate by clicking the left mouse button. The middle
 *   button scales the picture so that it fits in the available space and centers it there.
 *   The right mouse button zooms out.

 *
 *   The image is centered on the component to the {@link #focusPoint} in the coordinate space
 *   of the image. This translated using the {@link ScalablePicture#setScaleFactor( double )} to the coordinate
 *   space of the JComponent

 *
 *

 *
 *
 *   The {@link #showInfo} flag controls whether information about the picture is overlayed
 *   on the image.
 */
class PicturePane extends JComponent implements ScalablePictureListener {

  private boolean scaleToFit = true;

	/**
	 *   The currently diplayed ScalablePicture.
	 */
	public ScalablePicture sclPic = new ScalablePicture();

	/**
	 *  Flag that lets the object know if the mouse is in dragging mode.
	 */
	private boolean Dragging = false;

	/**
	 *  Flag that lets this JComponent know if the picture is to be fitted into the available space
	 *  when the threads return the scaled picture.
	 */
	private boolean centerWhenScaled;

	/**
	 *   the point inside the picture that will be put at the middle of the screen.
	 *   the coordinates are in x,y in the coordinate space of the picture.
	 */
	 private Point focusPoint = new Point();

	/**
	 *  used in dragging to find out how much the mouse has moved from the last time
	 */
	private int last_x, last_y;

	/**
	 *  The legend of the picture. Is sent to the listener when the image is ready.
	 */
	private String legend = null;

	/**
	 *   location of the info texts if shown
	 */
	private Point infoPoint = new Point(15, 15);

	/**
	 *   line spacing for the info text that can be superimposed on the picture
	 */
	private int lineSpacing = 12;

	/**
	 *   Font for the info if shown.
	 */
	private Font infoFont =  new Font("Arial", Font.PLAIN, 10);

	/**
	 *  Flag to tell if we want to show info over the picture.
	 */
	private boolean showInfo = false;

	/**
	 *  class to format the scale
	 */
	private DecimalFormat twoDecimalFormatter = new DecimalFormat("###0.00");

	/**
	 *   Constructor
	 **/

	public PicturePane() {

		// register an interest in mouse events
		Listener MouseListener = new Listener();
		addMouseListener( MouseListener );
		addMouseMotionListener( MouseListener );

		// make graphics faster
		this.setDoubleBuffered( false );

		sclPic.addStatusListener( this );

		this.addComponentListener(new ComponentAdapter() {
		  public void componentResized(ComponentEvent event) {
				zoomToFit();
		  }
		});

		//twoDecimalFormatter = new DecimalFormat("###0.00");
		setFont(infoFont);
	}

	/*
	 *  brings up the indicated picture on the display.
	 *  @param pi  The PicutreInfo object that should be displayed
	 *
	public void setPicture( PictureInfo pi ) {
		URL pictureURL;
		String description;
		double rotation = 0;
		try {
			pictureURL = pi.getHighresURL();
			description = pi.getDescription();
			rotation = pi.getRotation();
		} catch ( MalformedURLException x ) {
			Tools.log("PicturePane.changePicture: MarformedURLException trapped on: " + pi.getHighresLocation() + "\nReason: " + x.getMessage());
			return;
		}
		setPicture( pictureURL, description, rotation );
	} */

  /**
   *  brings up the indicated picture on the display.
   *  @param filenameURL  The URL of the picture to display
   *  @param legendParam	The description of the picture
   *  @param rotation  The rotation that should be applied
   */
  public void setPicture( URL filenameURL, String legendParam, double rotation ) {
    legend = legendParam;
    centerWhenScaled = true;
    sclPic.setScaleSize(getSize());

    sclPic.stopLoadingExcept( filenameURL );
    sclPic.loadAndScalePictureInThread( filenameURL, Thread.MAX_PRIORITY, rotation );
  }

  /**
   *  sets the buffered image directly.
   */
  public void setBufferedImage( BufferedImage img, String statusMessage  ) {
    legend = statusMessage;
    centerWhenScaled = true;
    Dimension dim = getSize();
    sclPic.setScaleSize(dim);

    SourcePicture source = new SourcePicture();
    source.setSourceBufferedImage( img, statusMessage );

    sclPic.setSourcePicture( source);
    if (!scaleToFit)
      sclPic.setScaleFactor(1.0);
    sclPic.scalePicture();
    repaint();
  }

	public void setDragging(boolean parameter) {
		Dragging = parameter;
	}

	/////////////////////////
	// Zooming Methods     //
	/////////////////////////

	/**
	 * Multifilies the scale factor so that paint() method scales the
	 * image larger. This method calls
	 * {@link ScalablePicture#createScaledPictureInThread(int)} which in
	 * turn will tell this oject by means of the status update that
	 * the image is ready and should be repainted.
	 */
	public void zoomIn() {
		double OldScaleFactor = sclPic.getScaleFactor();
		double NewScaleFactor = OldScaleFactor * 1.5;

		// If scaling goes from scale down to scale up, set ScaleFactor to exactly 1
		if ((OldScaleFactor < 1) && (NewScaleFactor > 1))
			NewScaleFactor = 1;

		// Check if the picture would get to large and cause the system to "hang"
		if ((sclPic.getOriginalWidth() * sclPic.getScaleFactor() < maximumPictureSize)
		&&  (sclPic.getOriginalHeight() * sclPic.getScaleFactor() < maximumPictureSize) ) {
			sclPic.setScaleFactor(NewScaleFactor);
			sclPic.createScaledPictureInThread( Thread.MAX_PRIORITY );
		}
	}

  private int maximumPictureSize = 5000; // was Settings

	/**
	 *  method that zooms out on the image leaving the center where it is.
	 *  This method calls
	 * {@link ScalablePicture#createScaledPictureInThread(int)} which in
	 * turn will tell this oject by means of the status update that
	 * the image is ready and should be repainted.
	 */
	public void zoomOut() {
		sclPic.setScaleFactor(sclPic.getScaleFactor() / 1.5);
		sclPic.createScaledPictureInThread( Thread.MAX_PRIORITY );
	}

	/**
	 *  this method sets the desired scaled size of the ScalablePicture
	 *  to the size of the JPanel and fires off a createScaledPictureInThread
	 *  request if the ScalablePicture has been loaded or is ready.
	 *
 	 *  @see ScalablePicture#createScaledPictureInThread(int)
	 *
	 */
	public void zoomToFit() {
		//Tools.log("zoomToFit invoked");
		sclPic.setScaleSize( getSize() );
		// prevent useless rescale events when the picture is not ready
		if ( sclPic.getStatusCode() == sclPic.LOADED
		  || sclPic.getStatusCode() == sclPic.READY ) {
			sclPic.createScaledPictureInThread( Thread.MAX_PRIORITY );
		}
	}

	///////////////////////////////////////////////////////////////
	// Scrolling Methods                                         //
	///////////////////////////////////////////////////////////////

	/**
	 * Set image to center of panel by putting the coordinates of the middle of the original image into the
	 * Center to X and Center to Y varaibles by invoking the setCenterLoaction method.
	 * This method
	 * calls repaint() directly since no time consuming image operations need to take place.
	 */
	public void centerImage() {
		if (sclPic.getOriginalImage() != null) {
			setCenterLocation((int) sclPic.getOriginalWidth() / 2, (int) sclPic.getOriginalHeight() / 2);
			repaint();
		}
 }

	/**
	 *  method that moves the image up by 10% of the pixels shown on the screen.
	 * This method
	 * calls repaint() directly since no time consuming image operations need to take place.
	 *

	 *  @see #scrollUp()
	 *  @see #scrollDown()
	 *  @see #scrollLeft()
	 *  @see #scrollRight()
	 *
	 */
	public void scrollUp() {
		// if the bottom edge of the picture is visible, do not scroll
		if (((sclPic.getOriginalHeight() - focusPoint.y) * sclPic.getScaleFactor()) + getSize().height /2 > getSize().height) {
			focusPoint.y = focusPoint.y + (int) (getSize().height * 0.1 / sclPic.getScaleFactor()) ;
			repaint();
		} else  {
			Tools.log ("Scrollup rejected because bottom of picture is already showing.");
		}
	}

	/**
	 *  method that moves the image down by 10% of the pixels shown on the screen.
	 * This method
	 * calls repaint() directly since no time consuming image operations need to take place.
	 *

	 *  @see #scrollUp()
	 *  @see #scrollDown()
	 *  @see #scrollLeft()
	 *  @see #scrollRight()
	 */
	public void scrollDown() {
		if (getSize().height / 2 - focusPoint.y * sclPic.getScaleFactor() < 0) {
			focusPoint.y = focusPoint.y - (int) (getSize().height * 0.1 / sclPic.getScaleFactor()) ;
			repaint();
		} else {
			Tools.log ("Scroll down rejected because top edge is aready visible");
		}
	}

	/**
	 *  method that moves the image left by 10% of the pixels shown on the screen.
	 * This method
	 * calls repaint() directly since no time consuming image operations need to take place.
	 *  works just liks {@link #scrollUp()}.
	 *  @see #scrollUp()
	 *  @see #scrollDown()
	 *  @see #scrollLeft()
	 *  @see #scrollRight()
	 */
	public void scrollLeft() {
		// if the bottom edge of the picture is visible, do not scroll
		if (((sclPic.getOriginalWidth() - focusPoint.x) * sclPic.getScaleFactor()) + getSize().width /2 > getSize().width) {
			focusPoint.x = focusPoint.x + (int) (getSize().width * 0.1 / sclPic.getScaleFactor()) ;
			repaint();
		} else {
			Tools.log("Scrollup rejected because right edge of picture is already showing.");
		}
	}

	/**
	 *  method that moves the image right by 10% of the pixels shown on the screen.
	 * This method
	 * calls repaint() directly since no time consuming image operations need to take place.
	 *  works just liks {@link #scrollDown()}.
	 *  @see #scrollUp()
	 *  @see #scrollDown()
	 *  @see #scrollLeft()
	 *  @see #scrollRight()
	  */
	public void scrollRight() {
		if (getSize().width / 2 - focusPoint.x * sclPic.getScaleFactor() < 0) {
			focusPoint.x = focusPoint.x - (int) (getSize().width * 0.1 / sclPic.getScaleFactor()) ;
			repaint();
		} else {
			Tools.log ("Scroll left rejected because left edge is aready visible");
		}
	}

	/**
	 *  method to set the center of the image to the true coordinates in the picture but doesn't call repaint()
	 **/
	public void setCenterLocation(int Xparameter, int Yparameter) {
		Tools.log("setCenterLocation invoked with "+Xparameter+" x " + Yparameter);
		focusPoint.setLocation(Xparameter, Yparameter);
	}

  public void setScaleToFit(boolean scaleToFit) {
    this.scaleToFit = scaleToFit;
  }

	/**
	 *   we are overriding the default paintComponent method, grabbing the Graphics
	 *   handle and doing our own drawing here. Esentially this method draws a large
	 *   black rectangle. A drawRenderedImage is then painted doing an affine transformation
	 *   on the scaled image to position it so the the desired point is in the middle of the
	 *   Graphics object. The picture is not scaled here because this is a slow operation
	 *   and only needs to be done once, while moving the image is something the user is
	 *   likely to do more often.
	 */
	public void paintComponent(Graphics g) {
		int WindowWidth = getSize().width;
		int WindowHeight = getSize().height;

    Tools.log ("paintComponent called");

		if (Dragging == false) {  //otherwise it's already a move Cursor
			setCursor(new Cursor(Cursor.WAIT_CURSOR));
		}

		if ( sclPic.getScaledPicture() != null ) {
			Graphics2D g2d = (Graphics2D)g;

			int X_Offset = (int) ((double) (WindowWidth / 2) - (focusPoint.x * sclPic.getScaleFactor()));
			int Y_Offset = (int) ((double) (WindowHeight / 2) - (focusPoint.y * sclPic.getScaleFactor()));

			// clear damaged component area
		 	Rectangle clipBounds = g2d.getClipBounds();
			g2d.setColor(Color.black); // getBackground());
			g2d.fillRect(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height);

			g2d.drawRenderedImage(sclPic.getScaledPicture(), AffineTransform.getTranslateInstance( X_Offset, Y_Offset));
			//g2d.drawImage(sclPic.getScaledPicture(), null, 0, 0);

			if (showInfo) {
				g2d.setColor(Color.white);
				g2d.drawString(legend, infoPoint.x, infoPoint.y);
				g2d.drawString("Size: " + Integer.toString(sclPic.getOriginalWidth()) + " x " + Integer.toString(sclPic.getOriginalHeight())
						+ " Offset: " + X_Offset + " x " + Y_Offset
						+ " Mid: " + Integer.toString(focusPoint.x) + " x " + Integer.toString(focusPoint.y)
						+ " Scale: " + twoDecimalFormatter.format(sclPic.getScaleFactor()), infoPoint.x, infoPoint.y + lineSpacing);
				/* g2d.drawString("File: " + sclPic.getFilename()
						, infoPoint.x
						, infoPoint.y + (2 * lineSpacing) );
				g2d.drawString("Loaded in: "
						+ twoDecimalFormatter.format( sclPic.getSourcePicture().loadTime / 1000F )
						+ " Seconds"
						, infoPoint.x
						, infoPoint.y + (3 * lineSpacing) );
				g2d.drawString("Free memory: "
						+ Long.toString( Runtime.getRuntime().freeMemory( )/1024/1024, 0 )
						+ " MB"
						, infoPoint.x
						, infoPoint.y + (4 * lineSpacing) ); */
			}
		} else {
			// paint a black square
			g.setClip(0, 0, WindowWidth, WindowHeight);
			g.setColor(Color.black);
			g.fillRect(0,0,WindowWidth,WindowHeight);
		}

		if (Dragging == false) {  //otherwise a move Cursor and should remain
			setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
		}
	}

	/**
	 *  class that deals with the mouse events. Is built so that the picture can be dragged if
	 *  the mouse button is pressed and the mouse moved. If the left button is clicked the picture is
	 *  zoomed in, middle resets to full screen, right zooms out.
	 */
	class Listener extends MouseInputAdapter {

		/**
		 *   overriden mouseClicket event. Is used to zoom in, zoom out and reset the picture
		 */
		public void mouseClicked(MouseEvent e) {

			// Right Mousebutton zooms out
			if (e.getButton() == 3) {
				centerWhenScaled = false;
				zoomOut();
			}

			// Middle Mousebutton resets
			if (e.getButton() == 2) {
				zoomToFit();
//				centerImage();
				centerWhenScaled = true;
			}

			// Left Mousebutton zooms in on selected spot
			if (e.getButton() == 1) {
				// Convert screen coordinates of the mouse click into true
				// coordinates on the picture:

		      		int WindowWidth = getSize().width;
				int WindowHeight = getSize().height;

				int X_Offset = e.getX() - (int) (WindowWidth / 2);
				int Y_Offset = e.getY() - (int) (WindowHeight / 2);

				setCenterLocation(
					focusPoint.x + (int) (X_Offset / sclPic.getScaleFactor()),
					focusPoint.y + (int) (Y_Offset / sclPic.getScaleFactor())
					);

				centerWhenScaled = false;
				zoomIn ();

	 		}
		}

		/**
		 *   method that is invoked if the mouse is released. If the mouse was dragging the
		 *   image then the dragging flag is turned off and the cursor is reset.
		 */
		public void mouseReleased(MouseEvent e) {
			if (Dragging == true) {
				//Dragging has ended
				Dragging = false;
				setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
				//repaint();
			}
		}

		/**
		 * method that is invoked when the
		 * user drags the mouse with a button pressed. Moves the picture around
		 */
		public void mouseDragged(MouseEvent e) {
			if (Dragging == false) {
				// Switch into dragging mode and record current coordinates
				last_x = e.getX();
				last_y = e.getY();

				Dragging = true;
				setCursor(new Cursor(Cursor.MOVE_CURSOR));

			} else {
				// was already dragging
				int x = e.getX(), y = e.getY();

				focusPoint.setLocation( (int) ((double) focusPoint.x + ((last_x - x) / sclPic.getScaleFactor())),
							(int) ((double) focusPoint.y + ((last_y - y) / sclPic.getScaleFactor())));
				last_x = x; last_y = y;

				Dragging = true;
				repaint();
			}
			centerWhenScaled = false;
		}
	}  //end class Listener

	/**
	 *  display a JDialog with lots of information
	 **/
	public void showInfoPanel() {
		showInfo = ! showInfo;
		repaint();
	}

	/**
	 *  method that gets invoked from the ScalablePicture object to notify of status changes.
	 *  The ScalablePicture goes through several statusses: UNINITIALISED, GARBAGE_COLLECTION,
	 *  LOADING, SCALING, READY, ERROR.

	 *  Each status is passed to the listener upon receipt.

	 *  When the ScalablePicture signals that it is READY the legend of the picture is sent
	 *  to the listener. The method {@link #centerImage} is called and a repaint is requested.
	 */
	public void scalableStatusChange(int pictureStatusCode, String pictureStatusMessage) {
		Tools.log("PicturePane.scalableStatusChange: got a status change: " + pictureStatusMessage);

		if ( pictureStatusCode == ScalablePicture.READY ) {
			Tools.log("PicturePane.scalableStatusChange: a READY status");
			pictureStatusMessage = legend;
			if ( centerWhenScaled ) {
				Tools.log("PicturePane.scalableStatusChange: centering image");
				centerImage();
			}
			Tools.log("PicturePane.scalableStatusChange: forcing Panel repaint");
			repaint();
		}

		Enumeration e = picturePaneListeners.elements();
		while ( e.hasMoreElements() ) {
			( (ScalablePictureListener) e.nextElement() )
				.scalableStatusChange( pictureStatusCode, pictureStatusMessage );
		}
	}

	/**
	 *  pass messages about progress onto the PictureViewer for updating of the progress bar
	 */
	public void sourceLoadProgressNotification( int statusCode, int percentage ) {
		Enumeration e = picturePaneListeners.elements();
		while ( e.hasMoreElements() ) {
			( (ScalablePictureListener) e.nextElement() )
				.sourceLoadProgressNotification( statusCode, percentage );
		}
	}

	/**
	 *  This Vector hold references to objects that would like to
	 *  receive notifications about what is going on with the ScalablePicture
	 *  being displayed in this PicturePane. These objects
	 *  must implement the ScalablePictureListener interface.
	 */
	protected Vector picturePaneListeners = new Vector();

	/**
	 *  method to register the listening object of the status events
	 */
	public void addStatusListener ( ScalablePictureListener listener) {
		picturePaneListeners.add( listener );
	}

	/**
	 *  method to register the listening object of the status events
	 */
	public void removeStatusListener ( ScalablePictureListener listener ) {
		picturePaneListeners.remove( listener );
	}

	/**
	 *  method that returns a handle to the scalable picutre that this component is displaying
	 */
	public ScalablePicture getScalablePicture() {
		return sclPic;
	}

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.iosp.gempak;

import ucar.nc2.constants.CDM;
import ucar.unidata.util.StringUtil2;

/**
 * Class for static GEMPAK utility methods
 */
public final class GempakUtil {

    /** day of month string */
    private static int[] month = new int[] {
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    };

    /** day of month string */
    public static String[] vertCoords = new String[] {
        "NONE", "PRES", "THTA", "HGHT", "SGMA", "DPTH", "HYBL"
    };

    /**
     * This subroutine converts the two integers stored in a grid file
     * into three integers containing the date, time and forecast time.
     * @param  iftime  input time array
     * @param  start offset into the array
     * @return  int[3] with date time and forecast time
     */
    public static int[] TG_FTOI(int[] iftime, int start) {

        int[] intdtf = new int[3];
        // If there is no forecast information, the string is stored as
        // date and time.

        if (iftime[start] < 100000000) {
            intdtf[0] = iftime[start];
            intdtf[1] = iftime[start + 1];
            intdtf[2] = 0;

            //  Otherwise, decode date/time and forecast info from the
            //  two integers.

        } else {

            //  The first word contains MMDDYYHHMM.  This must be turned
            //  into YYMMDD and HHMM.

            intdtf[0] = iftime[start] / 10000;
            intdtf[1] = iftime[start] - intdtf[0] * 10000;
            int mmdd = intdtf[0] / 100;
            int iyyy = intdtf[0] - mmdd * 100;
            intdtf[0] = iyyy * 10000 + mmdd;

            //  The forecast time remains the same.

            intdtf[2] = iftime[start + 1];
        }
        return intdtf;
    }

    /**
     * This subroutine converts an integer time array containing the date,
     * time and forecast time into a GEMPAK grid time.
     *
     * @param intdtf  integer array of date, time and forecast time
     *
     * @return  formatted string
     */
    public static String TG_ITOC(int[] intdtf) {
        String gdattim = "";

        //Check for the blank time which may be found.

        if ((intdtf[0] == 0) && (intdtf[1] == 0) && (intdtf[2] == 0)) {
            return gdattim;
        }

        //  Put the date and time into the character time.

        gdattim = TI_CDTM(intdtf[0], intdtf[1]);

        //  Decode the forecast information if there is any.

        if (intdtf[2] != 0) {
            String[] timeType = TG_CFTM(intdtf[2]);
            String   ftype    = timeType[0];
            String   ftime    = timeType[1];

            //      Combine two parts into string.

            gdattim = gdattim.substring(0, 11) + ftype + ftime;
        }

        return gdattim;
    }

    /**
     * This subroutine converts an integer grid forecast time into
     * the character forecast type and time.  The forecast type
     * is  A (analysis), F (forecast), G (guess) or I (initialize).
     * If the forecast time is less than 100 and the minutes are 00,
     * only hh is returned.
     * @param ifcast   integer forecast time
     * @return type and time.
     */
    public static String[] TG_CFTM(int ifcast) {
        String ftype = "";
        String ftime = "";

        //Check for negative times.
        if (ifcast < 0) {
            return new String[] { ftype, ftime };
        }

        // Get the number representing forecast type and convert to a
        // character.

        int iftype = ifcast / 100000;
        if (iftype == 0) {
            ftype = "A";
        } else if (iftype == 1) {
            ftype = "F";
        } else if (iftype == 2) {
            ftype = "G";
        } else if (iftype == 3) {
            ftype = "I";
        }

        // Convert the time to a character.  Add 100000 so that leading
        // zeros will be encoded.

        int    iftime = ifcast - iftype * 100000;
        int    ietime = iftime + 100000;
        String fff    = ST_INCH(ietime);

        // If the forecast time has minutes, set the character output
        // to all five digits. Otherwise, use only the first three digits,
        // which represent hours.

        if (ietime % 100 == 0) {
            ftime = fff.substring(1, 4);
        } else {
            ftime = fff.substring(1);
        }

        return new String[] { ftype, ftime };
    }

    /**
     * This subroutine converts an integer date (YYMMDD) and time (HHMM)
     *
     * @param idate  integer date
     * @param itime  integer time
     *
     * @return  string date/time
     */
    public static String TI_CDTM(int idate, int itime) {
        String dattim;
        int[]  idtarr = new int[5];

        idtarr[0] = idate / 10000;
        idtarr[1] = (idate - idtarr[0] * 10000) / 100;
        idtarr[2] = idate % 100;
        idtarr[3] = itime / 100;
        idtarr[4] = itime % 100;
        dattim    = TI_ITOC(idtarr);

        return dattim;
    }

    /**
     * This subroutine converts an integer time array into a standard
     * GEMPAK time.  The integers are checked for validity.
     * @param idtarr   Time array (YYYY,MM,DD,HH,MM)
     * @return time as a string
     */
    public static String TI_ITOC(int[] idtarr) {
        String dattim;
        String date, time;

        //   Put array values into variables.

        int iyear  = idtarr[0];
        int imonth = idtarr[1];
        int iday   = idtarr[2];
        int ihour  = idtarr[3];
        int iminut = idtarr[4];

        //  Check for leap year.

        //int ndays = TI_DAYM(iyear, imonth);
        iyear = iyear % 100;

        //  Check that each of these values is valid.

        /*  TODO: Check these
            IF  ( iyear .lt. 0 )  iret = -7
            IF  ( ( imonth .lt. 1 ) .or. ( imonth .gt. 12 ) ) iret = -8
            IF  ( ( iday   .lt. 1 ) .or. ( iday   .gt. ndays ) )
         +                                                    iret = -9
            IF  ( ( ihour  .lt. 0 ) .or. ( ihour  .gt. 24 ) ) iret = -10
            IF  ( ( iminut .lt. 0 ) .or. ( iminut .gt. 60 ) ) iret = -11
            IF  ( iret .ne. 0 )  RETURN
        */

        //  Get the date and time.

        int idate = iyear * 10000 + imonth * 100 + iday;
        int itime = ihour * 100 + iminut;

        //  Convert date and time to character strings.
        //  Fill in blanks with zeroes.
        date   = StringUtil2.padZero(idate, 6);
        time   = StringUtil2.padZero(itime, 4);

        dattim = date + "/" + time;

        return dattim;
    }

    /**
     * This subroutine returns the number of days in the given month.
     * The year must be a full four-digit year.
     *
     * @param iyear  integer year
     * @param imon  integer month
     *
     * @return  number of days
     */
    public static int TI_DAYM(int iyear, int imon) {

        int iday = 0;

        if ((imon > 0) && (imon < 13)) {

            //  Pick the number of days for the given month.

            iday = month[imon - 1];
            if ((imon == 2) && LEAP(iyear)) {
                iday = iday + 1;
            }
        }
        return iday;
    }

    /**
     * Check for leap year
     *
     * @param iyr  year to check
     *
     * @return  true if leap year
     */
    public static boolean LEAP(int iyr) {
        return (iyr % 4 == 0) && ((iyr % 100 != 0) || (iyr % 400 == 0));
    }

    /**
     * Convert a value to a string
     *
     * @param value  value to convert
     *
     * @return  string representation
     */
    public static String ST_INCH(int value) {
        return String.valueOf(value);
    }

    /**
     * Convert the int bits to a string
     *
     * @param value  value to convert
     *
     * @return  string representation
     */
    public static String ST_ITOC(int value) {
        byte[] bval = new byte[4];
        bval[0] = (byte) ((value & 0xff000000) >>> 24);
        bval[1] = (byte) ((value & 0x00ff0000) >>> 16);
        bval[2] = (byte) ((value & 0x0000ff00) >>> 8);
        bval[3] = (byte) ((value & 0x000000ff));
        return new String(bval, CDM.utf8Charset);
    }

    /**
     * Convert the int bits to a string
     *
     * @param values  array of values to convert
     *
     * @return  string representation
     */
    public static String ST_ITOC(int[] values) {
        StringBuilder sb = new StringBuilder();
      for (int value : values) {
        sb.append(ST_ITOC(value));
      }
        return sb.toString();
    }

    /**
     * Test for missing value
     *
     * @param value  value to check
     *
     * @return  true if missing
     */
    public static boolean ERMISS(float value) {
        return Math.abs(value - GempakConstants.RMISSD)
               < GempakConstants.RDIFFD;
    }

    /**
     * This subroutine translates a numeric value for IVCORD into its
     * character value in VCOORD.
     * @param ivcord  integer coordinate value
     * @return  string coordinate name
     */
    public static String LV_CCRD(int ivcord) {
        //Translate known vertical coordinates or look for parameter name.
        String vcoord = "";

        //Check for numeric vertical coordinates.

        if ((ivcord >= 0) && (ivcord < vertCoords.length)) {
            vcoord = vertCoords[ivcord];

        } else if (ivcord > 100) {
            //     Check for character name as vertical coordinate.  Check that
            //     each character is an alphanumeric character.

            vcoord = ST_ITOC(ivcord);
            /*
              Check for bad values

                DO  i = 1, 4
                    v = vcoord (i:i)
                    IF  ( ( ( v .lt. 'A' ) .or. ( v .gt. 'Z' ) ) .and.
     +                    ( ( v .lt. '0' ) .or. ( v .gt. '9' ) ) )  THEN
                        ier = -1
                    END IF
                END DO
            END IF
            */
        }
        return vcoord;
    }

    /**
     * Swap the order of the integer.
     * @param value  array of int values
     * @return swapped value
     */
    public static int swp4(int value) {
        return Integer.reverseBytes(value);
    }

    /**
     * Swap the order of the integers in place.
     * @param values  array of int values
     * @param startIndex  starting index of the array
     * @param number of bytes
     * @return input array with values swapped
     */
    public static int[] swp4(int[] values, int startIndex, int number) {
        for (int i = startIndex; i < startIndex + number; i++) {
            values[i] = Integer.reverseBytes(values[i]);
        }
        return values;
    }

    /**
     * Get a name for the grid packing type
     *
     * @param pktyp   packing type
     *
     * @return  String version of packing type
     */
    public static String getGridPackingName(int pktyp) {
        String packingType = "UNKNOWN";
        switch (pktyp) {

          case GempakConstants.MDGNON :
              packingType = "MDGNON";
              break;

          case GempakConstants.MDGGRB :
              packingType = "MDGGRB";
              break;

          case GempakConstants.MDGNMC :
              packingType = "MDGNMC";
              break;

          case GempakConstants.MDGDIF :
              packingType = "MDGDIF";
              break;

          case GempakConstants.MDGDEC :
              packingType = "MDGDEC";
              break;

          case GempakConstants.MDGRB2 :
              packingType = "MDGRB2";
              break;

          default :
              break;
        }
        return packingType;
    }

    /**
     * Get a name for the data packing type
     *
     * @param typrt   data type
     *
     * @return  String version of data type
     */
    public static String getDataType(int typrt) {
        String dataType = "" + typrt;
        switch (typrt) {

          case GempakConstants.MDREAL :
              dataType = "MDREAL";
              break;

          case GempakConstants.MDINTG :
              dataType = "MDINTG";
              break;

          case GempakConstants.MDCHAR :
              dataType = "MDCHAR";
              break;

          case GempakConstants.MDRPCK :
              dataType = "MDRPCK";
              break;

          case GempakConstants.MDGRID :
              dataType = "MDGRID";
              break;

          default :
              break;
        }
        return dataType;
    }

}


/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2010, OPeNDAP, Inc.
// Copyright (c) 2002,2003 OPeNDAP, Inc.
//
// Author: James Gallagher <jgallagher@opendap.org>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms,
// with or without modification, are permitted provided
// that the following conditions are met:
//
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
//
// - Neither the name of the OPeNDAP nor the names of its contributors may
//   be used to endorse or promote products derived from this software
//   without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/////////////////////////////////////////////////////////////////////////////

package opendap.dap;

import opendap.dap.parsers.ParseException;
import ucar.nc2.constants.CDM;

import java.io.*;
import java.util.Enumeration;
import java.util.zip.DeflaterOutputStream;

/**
 * The DataDDS class extends DDS to add new methods for retrieving data from
 * the server, and printing out the contents of the data.
 *
 * @author jehamby
 * @version $Revision: 21071 $
 * @see DDS
 */
public class DataDDS extends DDS {
    /**
     * The ServerVersion returned from the open OPeNDAP connection.
     */
    private ServerVersion ver;

    /**
     * Construct the DataDDS with the given server version.
     *
     * @param ver the ServerVersion returned from the open OPeNDAP connection.
     */
    public DataDDS(ServerVersion ver) {
        super();
        this.ver = ver;
    }

    public DataDDS(ServerVersion ver, BaseTypeFactory btf) {
        super(btf);
        this.ver = ver;
    }

    /**
     * Returns the ServerVersion given in the constructor.
     *
     * @return the ServerVersion given in the constructor.
     */
    public final ServerVersion getServerVersion() {
        return ver;
    }

    public boolean parse(InputStream stream)
        throws ParseException, DAP2Exception
    {
        try {
            String text = DConnect2.captureDataDDS(stream);
            return parse(text);
        } catch (IOException ioe) {
            throw new ParseException("Cannot read DataDDS",ioe);
        }
    }

    /**
     * Read the data stream from the given InputStream.  In the C++ version,
     * this code was in Connect.
     *
     * @param is       the InputStream to read from
     * @param statusUI the StatusUI object to use, or null
     * @throws EOFException      if EOF is found before the variable is completely
     *                           deserialized.
     * @throws IOException       thrown on any other InputStream exception.
     * @throws DataReadException when invalid data is read, or if the user
     *                           cancels the download.
     * @throws DAP2Exception     if the OPeNDAP server returned an error.
     */
    public void readData(InputStream is, StatusUI statusUI)
            throws IOException, EOFException, DAP2Exception {

      /* ByteArrayOutputStream bout = new ByteArrayOutputStream(50 * 1000);
      copy(is, bout);
      LogStream.dbg.printf(" readData size=%d %n",bout.size());
      LogStream.dbg.logflush();
      ByteArrayInputStream bufferedIS = new ByteArrayInputStream( bout.toByteArray());  */
      //statusUI = new Counter();

        // Buffer the input stream for better performance
        BufferedInputStream bufferedIS = new BufferedInputStream(is);
        // Use a DataInputStream for deserialize
        DataInputStream dataIS = new DataInputStream(bufferedIS);

        for (Enumeration e = getVariables(); e.hasMoreElements();) {
            if (statusUI != null && statusUI.userCancelled()) throw new DataReadException("User cancelled");
            ClientIO bt = (ClientIO) e.nextElement();

          /* if (true) {
            BaseType btt = (BaseType) bt;
            System.out.printf("Deserializing: %s (%s) %n", btt.getEncodedName(), ((BaseType) bt).getTypeName());
          } */
            bt.deserialize(dataIS, ver, statusUI);
        }
        //LogStream.out.printf("Deserializing: total size = %s %n", counter);

        // notify GUI of finished download
        if (statusUI != null)
            statusUI.finished();
    }

  // debug
  static private class Counter implements StatusUI {
    int counter = 0;

    public void incrementByteCount(int bytes) {
      counter += bytes;
    }

    public boolean userCancelled() {
      return false;
    }

    public void finished() {
    }

    @Override
    public String toString() {
      return " "+ counter;
    }
  }

    //debug
    private long copy(InputStream in, OutputStream out) throws IOException {
      long totalBytesRead = 0;
      byte[] buffer = new byte[8000];
      while (true) {
        int bytesRead = in.read(buffer);
        if (bytesRead == -1) break;
        out.write(buffer, 0, bytesRead);
        totalBytesRead += bytesRead;
      }
      return totalBytesRead;
    }

    /**
     * Print the dataset just read.  In the C++ version, this code was in
     * geturl.
     *
     * @param pw the PrintWriter to use.
     */
    public void printVal(PrintWriter pw) {
        for (Enumeration e = getVariables(); e.hasMoreElements();) {
            BaseType bt = (BaseType) e.nextElement();
            bt.printVal(pw, "", true);
            pw.flush();
        }
        pw.println();
    }

    /**
     * Print the dataset using OutputStream.
     *
     * @param os the OutputStream to use.
     */
    public final void printVal(OutputStream os) {
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os,Util.UTF8)));
        printVal(pw);
        pw.flush();
    }

    /**
     * Dump the dataset using externalize methods. This should create
     * a multipart Mime document with the binary representation of the
     * DDS that is currently in memory.
     *
     * @param os       the OutputStream to use.
     * @param compress true if we should compress the output.
     * @param headers  true if we should print HTTP headers.
     * @throws IOException thrown on any OutputStream exception.
     */
    public final void externalize(OutputStream os, boolean compress, boolean headers)
            throws IOException {
        // First, print headers
        if (headers) {
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(os,Util.UTF8));
            pw.println("HTTP/1.0 200 OK");
            pw.println("XDAP: " + ServerVersion.DAP2_PROTOCOL_VERSION);
            pw.println("XDODS-Server: DODS/" + ServerVersion.DAP2_PROTOCOL_VERSION);
            pw.println("Content-type: application/octet-stream");
            pw.println("Content-Description: dods-data");
            if (compress) {
                pw.println("Content-Encoding: deflate");
            }
            pw.println();
            pw.flush();
        }

        // Buffer the output stream for better performance
        OutputStream bufferedOS;
        if (compress) {
            // need a BufferedOutputStream - 3X performance - LOOK: why ??
            bufferedOS = new BufferedOutputStream(new DeflaterOutputStream(os));
        } else {
            bufferedOS = new BufferedOutputStream(os);
        }

        // Redefine PrintWriter here, so the DDS is also compressed if necessary
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(bufferedOS,Util.UTF8));
        print(pw);
        // pw.println("Data:");  // JCARON CHANGED
        pw.flush();
        bufferedOS.write("\nData:\n".getBytes(CDM.utf8Charset)); // JCARON CHANGED
        bufferedOS.flush();

        // Use a DataOutputStream for serialize
        DataOutputStream dataOS = new DataOutputStream(bufferedOS);
        for (Enumeration e = getVariables(); e.hasMoreElements();) {
            ClientIO bt = (ClientIO) e.nextElement();
            bt.externalize(dataOS);
        }
        // Note: for DeflaterOutputStream, flush() is not sufficient to flush
        // all buffered data
        dataOS.close();
    }
}


/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.nc2.iosp.hdf4;

import ucar.nc2.constants.CDM;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.StringTokenizer;

/**
 * @author caron
 * @since Jul 18, 2007
 */
public class TagEnum {
  private static java.util.Map<Short, TagEnum> hash = new java.util.HashMap<>(100);

  public static int SPECIAL_LINKED = 1;    /* Fixed-size Linked blocks */
  public static int SPECIAL_EXT = 2;       /* External */
  public static int SPECIAL_COMP = 3;      /* Compressed */
  public static int SPECIAL_VLINKED = 4;   /* Variable-length linked blocks */
  public static int SPECIAL_CHUNKED = 5;   /* chunked element */
  public static int SPECIAL_BUFFERED = 6;  /* Buffered element */
  public static int SPECIAL_COMPRAS = 7;   /* Compressed Raster element */

  /*
typedef enum
  {
      COMP_CODE_NONE = 0,       /* don't encode at all, just store
      COMP_CODE_RLE,            /* for simple RLE encoding
      COMP_CODE_NBIT,           /* for N-bit encoding
      COMP_CODE_SKPHUFF,        /* for Skipping huffman encoding
      COMP_CODE_DEFLATE,        /* for gzip 'deflate' encoding
      COMP_CODE_SZIP,		        /* for szip encoding
      COMP_CODE_INVALID,        /* invalid last code, for range checking
      COMP_CODE_JPEG            /* _Ugly_ hack to allow JPEG images to be created with GRsetcompress
  }  */
  public static int COMP_CODE_NONE = 0;    // don't encode at all, just store
  public static int COMP_CODE_RLE = 1;     // for simple RLE encoding
  public static int COMP_CODE_NBIT = 2;    // for N-bit encoding
  public static int COMP_CODE_SKPHUFF = 3; // for Skipping huffman encoding
  public static int COMP_CODE_DEFLATE = 4; // for gzip 'deflate' encoding
  public static int COMP_CODE_SZIP = 5;    // for szip encoding

  public final static TagEnum NONE = new TagEnum("NONE", "", (short) 0);
  public final static TagEnum NULL = new TagEnum("NULL", "", (short) 1);
  public final static TagEnum RLE = new TagEnum("RLE", "Run length encoding", (short) 11);
  public final static TagEnum IMC = new TagEnum("IMC", "IMCOMP compression alias", (short) 12);
  public final static TagEnum IMCOMP = new TagEnum("IMCOMP", "IMCOMP compression", (short) 12);
  public final static TagEnum JPEG = new TagEnum("JPEG", "JPEG compression (24-bit data)", (short) 13);
  public final static TagEnum GREYJPEG = new TagEnum("GREYJPEG", "JPEG compression (8-bit data)", (short) 14);
  public final static TagEnum JPEG5 = new TagEnum("JPEG5", "JPEG compression (24-bit data)", (short) 15);
  public final static TagEnum GREYJPEG5 = new TagEnum("GREYJPEG5", "JPEG compression (8-bit data)", (short) 16);

  public final static TagEnum LINKED = new TagEnum("LINKED", "Linked-block special element", (short) 20);
  public final static TagEnum VERSION = new TagEnum("VERSION", "Version", (short) 30);
  public final static TagEnum COMPRESSED = new TagEnum("COMPRESSED", "Compressed special element", (short) 40); // 0x28
  public final static TagEnum VLINKED = new TagEnum("VLINKED", "Variable-len linked-block header", (short) 50);
  public final static TagEnum VLINKED_DATA = new TagEnum("VLINKED_DATA", "Variable-len linked-block data", (short) 51);
  public final static TagEnum CHUNKED = new TagEnum("CHUNKED", "Chunked special element header", (short) 60);
  public final static TagEnum CHUNK = new TagEnum("CHUNK", "Chunk element", (short) 61);  // 0x3d

  public final static TagEnum FID = new TagEnum("FID", "File identifier", (short) 100);
  public final static TagEnum FD = new TagEnum("FD", "File description", (short) 101);
  public final static TagEnum TID = new TagEnum("TID", "Tag identifier", (short) 102);
  public final static TagEnum TD = new TagEnum("TD", "Tag descriptor", (short) 103);
  public final static TagEnum DIL = new TagEnum("DIL", "Data identifier label", (short) 104);
  public final static TagEnum DIA = new TagEnum("DIA", "Data identifier annotation", (short) 105);
  public final static TagEnum NT = new TagEnum("NT", "Number type", (short) 106);
  public final static TagEnum MT = new TagEnum("MT", "Machine type", (short) 107);
  public final static TagEnum FREE = new TagEnum("FREE", "Free space in the file", (short) 108);

  public final static TagEnum ID8 = new TagEnum("ID8", "8-bit Image dimension", (short) 200); // obsolete
  public final static TagEnum IP8 = new TagEnum("IP8", "8-bit Image palette", (short) 201); // obsolete
  public final static TagEnum RI8 = new TagEnum("RI8", "Raster-8 image", (short) 202); // obsolete
  public final static TagEnum CI8 = new TagEnum("CI8", "RLE compressed 8-bit image", (short) 203); // obsolete
  public final static TagEnum II8 = new TagEnum("II8", "IMCOMP compressed 8-bit image", (short) 204); // obsolete

  public final static TagEnum ID = new TagEnum("ID", "Image DimRec", (short) 300);
  public final static TagEnum LUT = new TagEnum("LUT", "Image Palette", (short) 301);
  public final static TagEnum RI = new TagEnum("RI", "Raster Image", (short) 302);
  public final static TagEnum CI = new TagEnum("CI", "Compressed Image", (short) 303);
  public final static TagEnum NRI = new TagEnum("NRI", "New-format Raster Image", (short) 304);
  public final static TagEnum RIG = new TagEnum("RIG", "Raster Image Group", (short) 306);
  public final static TagEnum LD = new TagEnum("LD", "Palette DimRec", (short) 307);
  public final static TagEnum MD = new TagEnum("MD", "Matte DimRec", (short) 308);
  public final static TagEnum MA = new TagEnum("MA", "Matte Data", (short) 309);
  public final static TagEnum CCN = new TagEnum("CCN", "Color correction", (short) 310);
  public final static TagEnum CFM = new TagEnum("CFM", "Color format", (short) 311);
  public final static TagEnum AR = new TagEnum("AR", "Cspect ratio", (short) 312);

  public final static TagEnum DRAW = new TagEnum("DRAW", "Draw these images in sequence", (short) 400);
  public final static TagEnum RUN = new TagEnum("RUN", "Cun this as a program/script", (short) 401);

  public final static TagEnum XYP = new TagEnum("XYP", "X-Y position", (short) 500);
  public final static TagEnum MTO = new TagEnum("MTO", "Machine-type override", (short) 501);

  public final static TagEnum T14 = new TagEnum("T14", "TEK 4014 data", (short) 602);
  public final static TagEnum T105 = new TagEnum("T105", "TEK 4105 data", (short) 603);

  public final static TagEnum SDG = new TagEnum("SDG", "Scientific Data Group", (short) 700);  // obsolete
  public final static TagEnum SDD = new TagEnum("SDD", "Scientific Data DimRec", (short) 701);
  public final static TagEnum SD = new TagEnum("SD", "Scientific Data", (short) 702);
  public final static TagEnum SDS = new TagEnum("SDS", "Scales", (short) 703);
  public final static TagEnum SDL = new TagEnum("SDL", "Labels", (short) 704);
  public final static TagEnum SDU = new TagEnum("SDU", "Units", (short) 705);
  public final static TagEnum SDF = new TagEnum("SDF", "Formats", (short) 706);
  public final static TagEnum SDM = new TagEnum("SDM", "Max/Min", (short) 707);
  public final static TagEnum SDC = new TagEnum("SDC", "Coord sys", (short) 708);
  public final static TagEnum SDT = new TagEnum("SDT", "Transpose", (short) 709); // obsolete
  public final static TagEnum SDLNK = new TagEnum("SDLNK", "Links related to the dataset", (short) 710);
  public final static TagEnum NDG = new TagEnum("NDG", "Numeric Data Group", (short) 720);
  public final static TagEnum CAL = new TagEnum("CAL", "Calibration information", (short) 731);
  public final static TagEnum FV = new TagEnum("FV", "Fill Value information", (short) 732);
  public final static TagEnum BREQ = new TagEnum("BREQ", "Beginning of required tags", (short) 799);
  public final static TagEnum SDRAG = new TagEnum("SDRAG", "List of ragged array line lengths", (short) 781);
  public final static TagEnum EREQ = new TagEnum("EREQ", "Current end of the range", (short) 780);

  public final static TagEnum VG = new TagEnum("VG", "Vgroup", (short) 1965);
  public final static TagEnum VH = new TagEnum("VH", "Vdata Header", (short) 1962);
  public final static TagEnum VS = new TagEnum("VS", "Vdata Storage", (short) 1963);

  private String name, desc;
  private short code;

  private TagEnum(String name, String desc, short code) {
    this.name = name;
    this.desc = desc;
    this.code = code;
    hash.put(code, this);
  }

  public String getDesc() { return desc; }
  public String getName() { return name; }
  public short getCode() { return code; }
  public String toString() { return name+" ("+code+") "+desc; }

  /**
   * Find the Tag that matches the code.
   *
   * @param code find Tag with this code.
   * @return Tag or null if no match.
   */
  public static TagEnum getTag(short code) {
    TagEnum te = hash.get(code);
    if (te == null) te = new TagEnum("UNKNOWN", "UNKNOWN", code);
    return te;
  }

  static public void main( String args[]) throws IOException {
    try (FileInputStream ios = new FileInputStream("C:/dev/hdf4/HDF4.2r1/hdf/src/htags.h")) {
      BufferedReader dataIS = new BufferedReader(new InputStreamReader(ios, CDM.utf8Charset));
      while (true) {
        String line = dataIS.readLine();
        if (line == null) break;
        if (line.startsWith("#define")) {
          StringTokenizer stoker = new StringTokenizer(line, " ()");
          stoker.nextToken(); // skip define
          String name = stoker.nextToken();
          if (!stoker.hasMoreTokens()) continue;
          //System.out.println(line);

          if (name.startsWith("DFTAG_"))
            name = name.substring(6);

          String code = stoker.nextToken();
          if (code.startsWith("u"))
            code = stoker.nextToken();

          int pos = line.indexOf("/*");
          String desc = "";
          if (pos > 0) {
            int pos2 = line.indexOf("*/");
            desc = (pos2 > 0) ? line.substring(pos + 3, pos2) : line.substring(pos + 3);
            desc = desc.trim();
          }

          System.out.println("  public final static Tags " + name + " = new Tags(\"" + name + "\", \"" + desc + "\", (short) " + code + ");");
        }
      }
    }
  }

}


/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2010, OPeNDAP, Inc.
// Copyright (c) 2002,2003 OPeNDAP, Inc.
//
// Author: James Gallagher <jgallagher@opendap.org>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms,
// with or without modification, are permitted provided
// that the following conditions are met:
//
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
//
// - Neither the name of the OPeNDAP nor the names of its contributors may
//   be used to endorse or promote products derived from this software
//   without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/////////////////////////////////////////////////////////////////////////////

package opendap.util;

import java.util.Hashtable;
import java.util.Enumeration;

/**
 * A class for achieving getopts() functionality.
 *

 * Loosely based on perl5's getopt/Std.pm.
 *

 * The object is instantiated with a 'flags' String that is the
 * composed of the set of switches allowed to be passed, and the
 * 'args' String array that has to be parsed opposite the 'flags'
 * string.
 *

 * new Getopts("oif:", args)	-o, -i are boolean flags,
 * -f takes an argument
 *

 * The class processes single-character switches with switch
 * clustering.
 *

 * The list of valid switches is accessible through the 'swList()'
 * method, which returns an Enumeration of the switch names.
 *

 * A local array including the arguments from the 'args' array that
 * was passed as an argument but are the actual command line arguments
 * is generated and is accessible through the 'argList()' method.
 *

 * Options switch content fields can be accessible through the
 * 'OptSwitch' class.
 *

 *
 * @author Arieh Markel  (arieh@selectjobs.com)
 *         thanks to Mark Skipper (mcs@dmu.ac.uk) for bug fix
 * @version 1.1  6/14/98  Updated evaluation following Mark's remarks.
 * @version 1.2  4/10/10  Added some code to provide a gnu style
 *                        getopts.
 * @see OptSwitch
 * @see java.util.Enumeration
 */
/*  $Id: Getopts.java 22593 2010-04-27 20:14:59Z dmh $ */

public class Getopts {

    // The internal storage of options
    //
    Hashtable switchtab;
    String arglist[];

    String progname = null;

    // getSwitch

    /**
     * method to return the OptSwitch object associated with the
     * 'sw' argument.
     *

     *
     * @param sw switch whose class is requested
     *

     */
    public OptSwitch getSwitch(Character sw) {
        return (OptSwitch) switchtab.get(sw);
    }

    /**
     * getOption
     *
     * @param sw Character switch whose option is requested
     */
    public String getOption(Character sw) {
        return getOption( (int) sw.charValue());
    }

    /**
     * getOption
     *
     * @param sw int value switch whose option is requested
     */
    public String getOption(int sw) {
        Character opt = new Character((char) sw);
        return getOption(opt);
    }

    // swList

    /**
     * Method to return an Enumeration of the switches
     * that the Getopts object is able to parse (according
     * to its initialization).
     *

     * May be later used to step through the OptSwitch objects.
     */
    public Enumeration swList() {
        return switchtab.keys();
    }

    // argList

    /**
     * Method to return an array of the actual arguments of the
     * command line invocation.
     */
    public String[] argList() {
        return arglist;
    }

    // Getopts

    /**
     * Wrapper Constructor
     * @param flags a string with the valid switch names
     * @param args  array of strings (usually args)
     *

     * @throws InvalidSwitch thrown when invalid options are found
     *

     */
    public Getopts(String flags, String args[])
            throws InvalidSwitch
    {
	this((String)null,flags,args);
    }

    /**
     * Basic class constructor.  Gets the flags passed, in a
     * notation similar to the one used by the sh, ksh, bash,
     * and perl getopts.
     *

     * String array 'args' is passed and
     * is parsed according to the flags.
     *

     *
     * @param progname program name for producing error messages
     * @param flags a string with the valid switch names
     * @param args  array of strings (usually args)
     *

     * @throws InvalidSwitch thrown when invalid options are found
     *

     */
    public Getopts(String progname, String flags, String args[])
            throws InvalidSwitch {
        initialize(progname,flags,args);
    }

    protected void initialize(String progname, String flags, String args[])
            throws InvalidSwitch {
        String throwstring =
	    progname == null?"Invalid Getopts switch(s): "
	                    : progname+": Invalid Getopts switch(s): ";
        String usage = "Usage: Getopts(String flags, String args[])"
		       + "or Usage: Getopts(String progname, String flags, String args[])";
	this.progname = progname;

        switchtab = new Hashtable(1, 1);

        for (int i = 0; i < flags.length(); i++) {
            boolean found;
            int cc = flags.charAt(i);
            Character c = new Character((char) cc);
            char alpha[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
                    'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
                    's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};

            //  space characters or punctuation marks are not allowed
            //  as switch values
            //
            if (cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r') {
                throw new InvalidSwitch(throwstring + "Spaces not allowed\n" +
                        usage + "\n");
            }

            found = false;
            for (int j = 0; j < alpha.length; j++) {
                Character ch = new Character(alpha[j]);
                char uc = Character.toUpperCase(ch.charValue());
                if (alpha[j] == cc || uc == cc) {
                    found = true;
                    break;
                }
            }

            //  if the character was not found on the set, throw the exception
            //
            if (! found && cc != ':') {
                throw new InvalidSwitch(throwstring + "Invalid Flag Character " + c + "\n");
            }

            //  ':' appears when the preceding character accepts a value
            //
            if (cc == ':') {
                if (i > 0) {
                    int prv = flags.charAt(i - 1);

                    if (prv == ':') {
                        throw new InvalidSwitch(throwstring +
                                "Can't have consecutive ':'\n" +
                                usage + "\n");
                    } else {
                        Character cp = new Character((char) prv);
                        OptSwitch sw = (OptSwitch) switchtab.get(cp);
                        sw.SetHasValue(OptSwitch.VAL);
                    }
                }
            } else {
                OptSwitch sw = new OptSwitch(cc, OptSwitch.BOOL);
                switchtab.put(c, sw);
            }
        }

        //
        //  Now, step through the arguments in the argument list
        //  and identify the options and values
        //
        int i;
        for (i = 0; i < args.length; i++) {
            char cc = args[i].charAt(0);    // first character

            // check end of options
	    if(args[i].equals("--")) {
		optind = i+1;
                break;
            } else if( cc != '-') {
		optind = i;
                break;
            }

            // more options, iterate them
            for (int j = 1; j < args[i].length(); j++) {
                cc = args[i].charAt(j);
                Character fc = new Character(cc);
                OptSwitch cs = (OptSwitch) switchtab.get(fc);
                if (cs == null) {
                    // The supplied switch wasn't recognised.
		    if(opterr)
                        throw new InvalidSwitch(throwstring + "invalid switch " +
                            cc + "\n2 Valid switches are: " + flags + "\n" + usage + "\n");
		    else
			illegals.append((char)cc);
                } else if (!cs.acceptVal()) {
                    // the option is a switch and takes no value
                    cs.SetVal(true);
                } else if (j + 1 < args[i].length()) {
                    //  the value may follow immediately after the switch
                    // (not as a separate token) set value to remainder of string...
                    cs.SetVal(args[i].substring(j + 1));
                    // ... and move pointer to end, thus consuming the string
                    j = args[i].length();
                } else if (++i >= args.length) {
                    // there wasn't another token
                    throw new InvalidSwitch(throwstring +
                            "missing value from switch " + cc +
                            "\n1 Valid switches are: " + flags +
                            "\n" + usage + "\n");
                } else if (args[i].charAt(0) == '-') {
                    // there was no value, next token starts with flags
                    throw new InvalidSwitch(
                            throwstring + "missing value from switch " + cc +
                                    "\n0 Valid switches are: " + flags + "\n" +
                                    usage + "\n");
                } else {
                    // the next token is the value
                    cs.SetVal(args[i]);
                    // and move j to the end to mark it consumed
                    j = args[i].length();
                }
            } // end for j
	    optind = i;
        } // end for i

        //  Now insert the array of arguments into the object
        //
        arglist = new String[args.length - i];
        System.arraycopy(args, i, arglist, 0, args.length - i);
    }

    /**
     * method for class testing.
     *

     * Invocation:
     *

     * java Getopts "option set" arg0 arg1 ... argn
     *

     *
     * @param args arguments passed
     */
    public static void main(String args[]) {
        int i;
        String args1[] = new String[args.length - 1];
        System.arraycopy(args, 1, args1, 0, args.length - 1);

        for (i = 0; i < args.length; i++) {
            System.out.println("args[" + i + "] : " + args[i]);
        }

        try {
            Getopts opts = new Getopts(args[0], args1);
            Enumeration names = opts.swList();

            i = 0;
            while (names.hasMoreElements()) {
                OptSwitch cs = opts.getSwitch((Character) names.nextElement());
                System.out.println("args[" + i + "] : " +
                        (char) cs.sw + " " + cs.type + " " +
                        cs.set + " " + cs.val);
                i++;
            }

            String argp[] = opts.argList();
            for (i = 0; i < argp.length; i++) {
                System.out.println("argv[" + i + "] : " + argp[i]);
            }
        }
        catch (InvalidSwitch e) {
            System.out.print(e);
        }
    }

    /**************************************************/
    /* Mimic gnu Getopt */

    String optarg = null;
    Enumeration optenum = null;
    boolean opterr = true; /* we report errors */
    int optind = 0;
    char optopt = '\0';
    StringBuilder illegals = new StringBuilder();
    String[] optargv = null;
    String optflags = null;

    /**
     * Wrapper Constructor
     * @param progname program name for producing error messages
     * @param flags a string with the valid switch names
     * @param args  array of strings (usually args)
     *

     */
    public Getopts(String progname, String args[], String flags)
    {
	this.progname = progname;
	optargv = args;
	optflags = flags;
    }

    public int getopt()
    {
        optarg = null;
	optopt = '\0';
	if(optenum == null) {
	    /* Process the arguments */
	    try {
	        initialize(progname,optflags,optargv);
	    } catch (InvalidSwitch ise) {
	        System.err.println("new Getopts: "+ise.getMessage());
	    }
	    optenum = swList();
	}
	if(optenum.hasMoreElements()) {
	    while(optenum.hasMoreElements()) {
	        Character c = (Character)optenum.nextElement();
	        OptSwitch sw = getSwitch(c);
	        if(sw.set) {
		    if(sw.acceptVal()) {
		        optarg = sw.val;
		    }
		    return (int)c;
	        }
	    }
	} else if(!opterr && illegals.length() > 0) {// Generate illegals */
	    optopt = illegals.charAt(0);
	    illegals.deleteCharAt(0);
	    return (int)'?';
        }
        return -1;
    }

    public String getOptarg() {return optarg;}
    public void setOpterr(boolean b) {opterr = b;}
    public int getOptind() {return optind;}
    public int getOptopt() {return optopt;}

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.nc2.dt.image.image;

import java.awt.image.BufferedImage;
import java.net.URL;
import java.net.MalformedURLException;
import java.io.IOException;
import java.io.File;
import java.util.List;
import java.util.ArrayList;

import ucar.nc2.dt.GridDatatype;
import ucar.nc2.dt.GridCoordSystem;
import ucar.ma2.Array;

/**
 * A factory for buffered images
 * @author caron
 */
public class ImageDatasetFactory {

  private StringBuffer log;
  public String getErrorMessages() { return log == null ? "" : log.toString(); }
  private File currentFile, currentDir = null;
  private java.util.List<File> currentDirFileList;
  private int currentDirFileNo = 0;

  // grid stuff
  private GridDatatype grid = null;
  private int time = 0;
  private int ntimes = 1;

  public BufferedImage openDataset( GridDatatype grid) throws java.io.IOException {
    this.grid = grid;
    this.time = 0;
    GridCoordSystem gcsys = grid.getCoordinateSystem();
    if (gcsys.getTimeAxis() != null)
      ntimes = (int) gcsys.getTimeAxis().getSize();
    Array data = grid.readDataSlice( this.time, 0, -1, -1);
    return ImageArrayAdapter.makeGrayscaleImage( data, grid);
  }

  /**
   * Open from a URL:
   *   adde: use  AddeImage.factory()
   *   http: use javax.imageio.ImageIO.read()
   *   file: javax.imageio.ImageIO.read()
   * @param location open from this location
   * @return a BufferedImage
   * @throws java.io.IOException on read error
   */
  public BufferedImage open( String location) throws java.io.IOException {
    log = new StringBuffer();

    if (location.startsWith("http:")) {
      try {
        URL url = new URL(location);
        currentFile = null;
        return javax.imageio.ImageIO.read(url);

      } catch (MalformedURLException e) {
        log.append(e.getMessage());
        //e.printStackTrace();
        return null;
      } catch (IOException e) {
        log.append(e.getMessage());
        //e.printStackTrace();
        return null;
      }
    }

    else  {
      if (location.startsWith("file:)"))
        location = location.substring(5);

      try {
        File f = new File(location);
        if (!f.exists()) {
          return null;
        }

        currentFile = f;
        currentDir = null;
        return javax.imageio.ImageIO.read(f);

      } catch (MalformedURLException e) {
        log.append(e.getMessage());
        //e.printStackTrace();
        return null;
      } catch (IOException e) {
        log.append(e.getMessage());
        //e.printStackTrace();
        return null;
      }
    }
  }

  /**
   * This assumes you have opened a file. looks in the parent directory.
   * @param forward if true got to next, else previous
   * @return  next file in the directory, as a BufferedImage.
   */
  public BufferedImage getNextImage(boolean forward) {
    if (grid != null) {
      if (forward) {
        this.time++;
        if (this.time >= this.ntimes) this.time = 0;
      } else {
        this.time--;
        if (this.time < 0) this.time = this.ntimes-1;
      }

      Array data;
      try {
        data = grid.readDataSlice( this.time, 0, -1, -1);
        return ImageArrayAdapter.makeGrayscaleImage( data, grid);
      } catch (IOException e) {
        e.printStackTrace();
        return null;
      }
    }

    if (currentFile == null)
      return null;

    if (currentDir == null) {
      currentDirFileNo = 0;
      currentDir = currentFile.getParentFile();
      currentDirFileList = new ArrayList<>();
      addToList( currentDir, currentDirFileList);
      //Arrays.asList(currentDir.listFiles());
      //Collections.sort(currentDirFileList);
      for (int i = 0; i < currentDirFileList.size(); i++) {
        File file = currentDirFileList.get(i);
        if (file.equals(currentFile))
          currentDirFileNo = i;
      }
    }

    if (forward) {
      currentDirFileNo++;
      if (currentDirFileNo >= currentDirFileList.size())
        currentDirFileNo = 0;
    } else {
      currentDirFileNo--;
      if (currentDirFileNo < 0)
        currentDirFileNo = currentDirFileList.size()-1;
    }

    File nextFile = currentDirFileList.get(currentDirFileNo);
    try {
      System.out.println("Open image "+nextFile);
      return javax.imageio.ImageIO.read(nextFile);
    } catch (IOException e) {
      System.out.println("Failed to open image "+nextFile);
      return getNextImage( forward);
    }

  }

  private void addToList( File dir, List<File> list) {
    for (File file : dir.listFiles()) {
      if (file.isDirectory())
        addToList(file, list);
      else if (file.getName().endsWith(".jpg") || file.getName().endsWith(".JPG"))
        list.add(file);
    }
  }

}


/* Copyright 2012, UCAR/Unidata.
   See the LICENSE file for more information.
*/

package dap4.dap4lib;

import dap4.core.util.DapException;
import dap4.core.dmr.DapEnumeration;
import dap4.core.dmr.DapType;
import dap4.core.dmr.TypeSort;
import dap4.core.util.DapUtil;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;

/**
 * This Class is used to isolate as many as possible
 * of the switch statements using TypeSort enums.
 * Singleton
 */

abstract public class LibTypeFcns
{
    //////////////////////////////////////////////////
    // Static Methods

    /**
     * Conmpute the size, in databuffer,
     * of the daptype wrt to a serialization;
     * 0 if undefined.
     *
     * @param type The type of interest
     * @return the size, in databuffer
     */
    static public int
    size(DapType type)
    {
        switch (type.getTypeSort()) {
        case Char: // remember serial size is 1, not 2.
        case UInt8:
        case Int8:
            return 1;
        case Int16:
        case UInt16:
            return 2;
        case Int32:
        case UInt32:
        case Float32:
            return 4;
        case Int64:
        case UInt64:
        case Float64:
            return 8;
        case Enum:
            return size(((DapEnumeration)type).getBaseType());
        default:
            break;
        }
        return 0;
    }

    static public Object
    newVector(DapType type, long count)
            throws DapException
    {
        int icount = (int) count;
        Object vector = null;
        switch (type.getTypeSort()) {
        case Char:
            vector = new char[icount];
            break;
        case UInt8:
        case Int8:
            vector = new byte[icount];
            break;
        case Int16:
        case UInt16:
            vector = new short[icount];
            break;
        case Int32:
        case UInt32:
            vector = new int[icount];
            break;
        case Int64:
        case UInt64:
            vector = new long[icount];
            break;
        case Float32:
            vector = new float[icount];
            break;
        case Float64:
            vector = new double[icount];
            break;
        case String:
        case URL:
            vector = new String[icount];
            break;
        case Opaque:
            vector = new ByteBuffer[icount];
            break;
        case Enum:
            return newVector(((DapEnumeration)type).getBaseType(),count);
        default:
            throw new DapException("Illegal Conversion");
        }
        return vector;
    }

    /**
     * Convert an array of one type of values to another type
     *
     * @param dsttype target type
     * @param srctype source type
     * @param src     array of values to convert
     * @return resulting array of converted values as an object
     */

    static public Object
    convertVector(DapType dsttype, DapType srctype, Object src)
            throws DapException
    {
        int i;

        TypeSort srcatomtype = srctype.getAtomicType();
        TypeSort dstatomtype = dsttype.getAtomicType();

        if(srcatomtype == dstatomtype)
            return src;
        if(srcatomtype.isIntegerType()
                && TypeSort.getSignedVersion(srcatomtype) == TypeSort.getSignedVersion(dstatomtype))
            return src;

        Object result = null;
        boolean ok = true;
        int len = 0;
        char[] csrc;
        byte[] bsrc;
        short[] shsrc;
        int[] isrc;
        long[] lsrc;
        float[] fsrc;
        double[] dsrc;
        char[] cresult;
        byte[] bresult;
        short[] shresult;
        int[] iresult;
        long[] lresult;
        float[] fresult;
        double[] dresult;
        BigInteger bi;
        boolean srcunsigned = srcatomtype.isUnsigned();
        boolean dstunsigned = dstatomtype.isUnsigned();

        // Do a double switch src X dst (ugh!)

        switch (srcatomtype) {
        case Char: //Char->
            csrc = (char[]) src;
            len = csrc.length;
            switch (dstatomtype) {
            case Int8: //char->int8
            case UInt8: //char->uint8
                return src;
            case Int16: //char->Int16
            case UInt16://char->UInt16;
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) (((int) csrc[i]) & 0xFF);
                }
                break;
            case Int32: //char->Int32
            case UInt32://char->UInt32;
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    iresult[i] = (int) (((int) csrc[i]) & 0xFF);
                }
                break;
            case Int64: //char->Int64
            case UInt64://char->UInt64;
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    lresult[i] = (long) (((int) csrc[i]) & 0xFF);
                }
                break;
            case Float32:
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) (((int) csrc[i]) & 0xFF);
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) (((int) csrc[i]) & 0xFF);
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case Int8: //Int8->
            bsrc = (byte[]) src;
            len = bsrc.length;
            switch (dstatomtype) {
            case Char: //int8->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (((int) bsrc[i]) & 0xFF);
                }
                break;
            case Int16: //int8->Int16
            case UInt16://int8->UInt16;
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) bsrc[i];
                }
                if(dstunsigned) {
                    for(i = 0; i < len; i++) {
                        shresult[i] &= (short) 0xFF;
                    }
                }
                break;
            case Int32: //int8->Int32
            case UInt32://int8->UInt32;
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    iresult[i] = (int) bsrc[i];
                }
                if(dstunsigned) {
                    for(i = 0; i < len; i++) {
                        iresult[i] &= 0xFF;
                    }
                }
                break;
            case Int64: //int8->Int64
            case UInt64://int8->UInt64;
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    lresult[i] = (long) bsrc[i];
                }
                if(dstunsigned) {
                    for(i = 0; i < len; i++) {
                        lresult[i] &= 0xFFL;
                    }
                }
                break;
            case Float32: //int8->float
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) bsrc[i];
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) bsrc[i];
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case UInt8: //UInt8->
            bsrc = (byte[]) src;
            len = bsrc.length;
            switch (dstatomtype) {
            case Char: //Byte->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (((int) bsrc[i]) & 0xFF);
                }
                break;
            case Int16: //Byte->Int16
            case UInt16://Byte->UInt16;
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) (((int) bsrc[i]) & 0xFF);
                }
                break;
            case Int32: //Byte->Int32
            case UInt32://Byte->UInt32;
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    iresult[i] = ((int) bsrc[i]) & 0xFF;
                }
                break;
            case Int64: //Byte->Int64
            case UInt64://Byte->UInt64;
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    lresult[i] = ((long) bsrc[i]) & 0xFFL;
                }
                break;
            case Float32: //Byte->float
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) ((int) bsrc[i] & 0xFF);
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) ((int) bsrc[i] & 0xFF);
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case Int16: //Int16->
            shsrc = (short[]) src;
            len = shsrc.length;
            switch (dstatomtype) {
            case Char: //int16->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (((int) shsrc[i]) & 0xFF);
                }
                break;
            case Int8: //int16->Int8
            case UInt8://int16->UInt8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    bresult[i] = (byte) shsrc[i];
                }
                break;
            case Int32: //int16->Int32
            case UInt32://int16->UInt32;
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    iresult[i] = (int) shsrc[i];
                }
                if(dstunsigned) {
                    for(i = 0; i < len; i++) {
                        iresult[i] &= 0xFFFF;
                    }
                }
                break;
            case Int64: //int16->Int64
            case UInt64://int16->UInt64;
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    lresult[i] = (long) shsrc[i];
                }
                if(dstunsigned) {
                    for(i = 0; i < len; i++) {
                        lresult[i] &= 0xFFFFL;
                    }
                }
                break;
            case Float32: //int16->float
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) shsrc[i];
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) shsrc[i];
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case UInt16: //UInt16->
            shsrc = (short[]) src;
            len = shsrc.length;
            switch (dstatomtype) {
            case Char: //UInt16->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (((int) shsrc[i]) & 0xFF);
                }
                break;
            case Int8: //UInt16->Int8
            case UInt8://UInt16->UInt8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    bresult[i] = (byte) shsrc[i];
                }
                break;
            case Int32: //UInt16->Int32
            case UInt32://UInt16->UInt32;
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    iresult[i] = ((int) shsrc[i]) & 0xFFFF;
                }
                break;
            case Int64: //UInt16->Int64
            case UInt64://UInt16->UInt64;
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    lresult[i] = ((long) shsrc[i]) & 0xFFFFL;
                }
                break;
            case Float32: //UInt16->float
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) ((int) shsrc[i] & 0xFFFF);
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) ((int) shsrc[i] & 0xFFFF);
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case Int32: //Int32->
            isrc = (int[]) src;
            len = isrc.length;
            switch (dstatomtype) {
            case Char: //int32->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (isrc[i] & 0xFF);
                }
                break;
            case Int8: //Int32->Int8
            case UInt8://Int32->UInt8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    bresult[i] = (byte) isrc[i];
                }
                break;
            case Int16: //Int32->Int16
            case UInt16://Int32->UInt16;
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) isrc[i];
                }
                break;
            case Int64: //Int32->Int64
            case UInt64://Int32->UInt64
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    lresult[i] = (long) isrc[i];
                }
                if(dstunsigned) {
                    for(i = 0; i < len; i++) {
                        lresult[i] &= 0xFFFFL;
                    }
                }
                break;
            case Float32: //int32->float
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) isrc[i];
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) isrc[i];
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case UInt32: //UInt32->
            isrc = (int[]) src;
            len = isrc.length;
            switch (dstatomtype) {
            case Char: //UInt32->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (((int) isrc[i]) & 0xFF);
                }
                break;
            case Int8: //Int32->Int8
            case UInt8://UInt32->UInt8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    bresult[i] = (byte) isrc[i];
                }
                break;
            case Int16: //Int32->Int16
            case UInt16://UInt32->UInt16
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) isrc[i];
                }
                break;
            case Int64: //Int32->Int64
            case UInt64://UInt32->UInt64;
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    lresult[i] = (long) isrc[i];
                }
                if(dstunsigned) {
                    for(i = 0; i < len; i++) {
                        lresult[i] &= 0xFFFFFFFFL;
                    }
                }
                break;
            case Float32: //UInt32->float
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) ((int) isrc[i] & 0xFFFF);
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) ((int) isrc[i] & 0xFFFF);
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case Int64: //Int64->
            lsrc = (long[]) src;
            len = lsrc.length;
            switch (dstatomtype) {
            case Char: //Int64->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (lsrc[i] & 0xFF);
                }
                break;
            case Int8: //Int64->Int8
            case UInt8://Int64->UInt8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    bresult[i] = (byte) lsrc[i];
                }
                break;
            case Int16: //Int64->Int16
            case UInt16://Int64->UInt16;
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) lsrc[i];
                }
                break;
            case Int32: //Int64->Int32
            case UInt32://Int64->UInt32;
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    iresult[i] = (int) lsrc[i];
                }
                break;
            case Float32: //Int64->float
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) lsrc[i];
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) lsrc[i];
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case UInt64: //UInt64->
            lsrc = (long[]) src;
            len = lsrc.length;
            switch (dstatomtype) {
            case Char: //UInt64->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (lsrc[i] & 0xFFL);
                }
                break;
            case Int8: //Int64->Int8
            case UInt8://UInt64->UInt8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    bresult[i] = (byte) lsrc[i];
                }
                break;
            case Int16: //Int64->Int16
            case UInt16://UInt64->UInt16
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) lsrc[i];
                }
                break;
            case Int32: //Int64->Int32
            case UInt32://UInt64->UInt32
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    iresult[i] = (int) lsrc[i];
                }
                break;
            case Float32: //UInt64->float
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    bi = BigInteger.valueOf(lsrc[i]);
                    bi = bi.and(DapUtil.BIG_UMASK64);
                    fresult[i] = bi.floatValue();
                }
                break;
            case Float64:
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    bi = BigInteger.valueOf(lsrc[i]);
                    bi = bi.and(DapUtil.BIG_UMASK64);
                    dresult[i] = bi.doubleValue();
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case Float32: //Float32->
            fsrc = (float[]) src;
            len = fsrc.length;
            switch (dstatomtype) {
            case Char: //Float32->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (((int) fsrc[i]) & 0xFF);
                }
                break;
            case Int8: //Float32->Int8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    bresult[i] = (byte) fsrc[i];
                }
                break;
            case UInt8://Float32->UInt8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    if(fsrc[i] < 0) {
                        ok = false;
                        break;
                    }
                    bresult[i] = (byte) fsrc[i];
                }
                break;
            case Int16: //Float32->Int16
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) fsrc[i];
                }
                break;
            case UInt16://Float32->UInt16
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    if(fsrc[i] < 0) {
                        ok = false;
                        break;
                    }
                    shresult[i] = (short) fsrc[i];
                }
                break;
            case Int32: //Float32->Int32
            case UInt32://Float32->UInt32
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    if(fsrc[i] < 0) {
                        ok = false;
                        break;
                    }
                    iresult[i] = (int) fsrc[i];
                }
                break;
            case Int64: //Float32->Int64
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    BigDecimal bd = new BigDecimal(fsrc[i]);
                    lresult[i] = bd.toBigInteger().longValue();
                }
                break;
            case UInt64://Float32->UInt64
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    if(fsrc[i] < 0) {
                        ok = false;
                        break;
                    } // not convertible
                    BigDecimal bd = new BigDecimal(fsrc[i]);
                    lresult[i] = bd.toBigInteger().longValue();
                }
                break;
            case Float64://Float32->Float64
                result = (dresult = new double[len]);
                for(i = 0; i < len; i++) {
                    dresult[i] = (double) fsrc[i];
                }
                break;
            default:
                ok = false;
                break;
            }
            break;
        case Float64: //Float64->
            dsrc = (double[]) src;
            len = dsrc.length;
            switch (dstatomtype) {
            case Char: //Float64->char
                result = (cresult = new char[len]);
                for(i = 0; i < len; i++) {
                    cresult[i] = (char) (((int) dsrc[i]) & 0xFF);
                }
                break;
            case Int8: //Float64->Int8
            case UInt8://Float64->UInt8
                result = (bresult = new byte[len]);
                for(i = 0; i < len; i++) {
                    bresult[i] = (byte) dsrc[i];
                }
                break;
            case Int16: //Float64->Int16
            case UInt16://Float64->UInt16
                result = (shresult = new short[len]);
                for(i = 0; i < len; i++) {
                    shresult[i] = (short) dsrc[i];
                }
                break;
            case Int32: //Float64->Int32
            case UInt32://Float64->UInt32
                result = (iresult = new int[len]);
                for(i = 0; i < len; i++) {
                    iresult[i] = (int) dsrc[i];
                }
                break;
            case Int64: //Float64->Int64
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    BigDecimal bd = new BigDecimal(dsrc[i]);
                    lresult[i] = bd.toBigInteger().longValue();
                }
                break;
            case UInt64://Float64->UInt64
                result = (lresult = new long[len]);
                for(i = 0; i < len; i++) {
                    if(dsrc[i] < 0) {
                        ok = false;
                        break;
                    } // not convertible
                    BigDecimal bd = new BigDecimal(dsrc[i]);
                    lresult[i] = bd.toBigInteger().longValue();
                }
                break;
            case Float32://Float32->Float64
                result = (fresult = new float[len]);
                for(i = 0; i < len; i++) {
                    fresult[i] = (float) dsrc[i];
                }
                break;
            default:
                ok = false;
                break;
            }
            break;

        default:
            throw new DapException(String.format("Illegal Conversion: %s->%s", srctype, dsttype));
        }
        if(!ok)
            throw new DapException(String.format("Illegal Conversion: %s->%s", srctype, dsttype));
        return result;
    }
}