package edu.uprm.walsaip.vte.core.loader.terrain;

import static java.nio.ByteOrder.LITTLE_ENDIAN;

import com.c5corp.c5dem.*;

//import java.awt.Point;
//import java.awt.Point.*;
import java.io.*;
import java.nio.*;
//import java.lang.Object.*;
import java.nio.channels.*;

//import java.nio.DoubleBuffer.*;

/*
 * Hector Irizarry
 * capstone project
 */
public class DEMHandeler {
	private static int Padding = 190;

	private String InputFilename;

	private static String BTVersion = "binterr1.3";

	private int Columns;

	private int Rows;

	private short Datasize;

	private short FloatingPointFlag;

	private short HorizontalUnits;

	private short UTMZone;

	private short Datum;

	private double LeftExtent;

	private double RightExtent;

	private double BottomExtent;

	private double TopExtent;

	private short ExternalProjection;

	private float Scale;

	private int[][] ElevationData;

	//public String getBTVersion() {
	///	return this.BTVersion;
	//}

	public int getRows() {
		return this.Rows;
	}

	public int getColumns() {
		return this.Columns;
	}

	public short getDatasize() {
		return this.Datasize;
	}

	public short getFloatingPointFlag() {
		return this.FloatingPointFlag;
	}

	public short getHorizontalUnits() {
		return this.HorizontalUnits;
	}

	public short getUTMZone() {
		return this.UTMZone;
	}

	public short getDatum() {
		return this.Datum;
	}

	public double getLeftExtent() {
		return this.LeftExtent;
	}

	public double getRightExtent() {
		return this.RightExtent;
	}

	public double getBottomExtent() {
		return this.BottomExtent;
	}

	public double getTopExtent() {
		return this.TopExtent;
	}

	public float getExternalProjection() {
		return this.ExternalProjection;
	}

	public float getScale() {
		return this.Scale;
	}

	public int[][] getElevationData() {
		return this.ElevationData;
	}

	/*
	 * Constructor: Inicializa las instancias necesarias para crear un .bt
	 * extrayendo la data del Input file
	 */
	public DEMHandeler(String InputFile) {
		this.InputFilename = InputFile;
		this.initBT();

	}

	/* Inicializa los campos para construir un bt */
	public void initBT() {
		this.initHeaderBT();
		this.initDataBT();
		//System.out.println("initBT");
	}

	/* Devuelve las coord de mas al norte */

	private double MostNorth() {

		DemTable dem = new DemTable(this.InputFilename);
		PreciseUTMcoordPair NE = dem.getNEcorner();
		PreciseUTMcoordPair NW = dem.getNWcorner();

		double NED = NE.getNorthing();

		double NWD = NW.getNorthing();

		if (NWD > NED) {
			return NWD;
		} else {
			return NED;
		}

	}

	/* devuelve las coord del punto mas al sur */
	private double MostSouth() {

		DemTable dem = new DemTable(this.InputFilename);
		PreciseUTMcoordPair SE = dem.getSEcorner();
		PreciseUTMcoordPair SW = dem.getSWcorner();

		double SED = SE.getNorthing();

		double SWD = SW.getNorthing();

		if (SWD < SED) {
			return SWD;
		} else {
			return SED;
		}

	}

	/* devuelve las coord del punto mas al este */
	private double MostEast() {

		DemTable dem = new DemTable(this.InputFilename);
		PreciseUTMcoordPair NE = dem.getNEcorner();
		PreciseUTMcoordPair SE = dem.getSEcorner();

		double NED = NE.getEasting();

		double SED = SE.getEasting();

		if (SED > NED) {
			return SED;
		} else {
			return NED;
		}

	}

	/* devuelve las coord del punto mas al oeste */
	private double MostWest() {

		DemTable dem = new DemTable(this.InputFilename);
		PreciseUTMcoordPair NW = dem.getNWcorner();
		PreciseUTMcoordPair SW = dem.getSWcorner();

		double NWD = NW.getEasting();

		double SWD = SW.getEasting();

		if (NWD < SWD) {
			return NWD;
		} else {
			return SWD;
		}

	}

	/* Inicializa el campo que contiene la data de elevacion */

	private void initDataBT() {
		DemTable dem = new DemTable(this.InputFilename);
		UtmCoordinatePairElev[][] table = dem.getTable();

		// System.out.println("cols: "+Integer.toString(table.length));
		// System.out.println("rows: "+Integer.toString(table[0].length));
		ElevationData = new int[table.length][table[0].length];
		for (int i = 0; i < table.length; i = i + 1) {
			for (int j = 0; j < table[i].length; j = j + 1) {
				if (table[i][j] == null) {
					// this.ElevationData[i][j]=(int)(this.Datum);
				} else {
					this.ElevationData[i][j] = (table[i][j]).getElevation();
				}
			}
		}

	}

	/*
	 * private int MaxRows(Dem image){
	 * 
	 * short max=0; TypeB Bfile=image.getTypeB(0);
	 * 
	 * for(int i=1;i<(this.Columns);i=i+1){ Bfile=image.getTypeB(i); short []
	 * DimsB=Bfile.get_number_of_elevations(); if(max<DimsB[0]){ max=DimsB[0]; } }
	 * return max; }
	 */

	public void initHeaderBT() {

		Dem image = new Dem(this.InputFilename);
		// Columns
		short Dims[] = image.get_rows_and_columns();
		this.Columns = Dims[1]; // number of profiles OK

		DemTable dem = new DemTable(this.InputFilename);
		UtmCoordinatePairElev[][] table = dem.getTable();

		// Rows
		// this.Rows=this.MaxRows(image);// number of elevations FALTA!!!!!!!!!
		this.Rows = (table[0]).length;
		//System.out.println(this.Rows); // Display the string.

		// DataSize

		this.Datasize = 4; // dado que es el numero de bytes por punto de
							// elevacion y en dem la data esta en ints 4 bytes

		// Floating pointFlag
		this.FloatingPointFlag = 0; // la data en los dem es int

		// HorizontalUnits
		short unit = image.get_planimetric_unit();
		switch (unit) {
		case 0:
			this.HorizontalUnits = 0; // si es degress cambiar la data a
										// radianes??
			break;
		case 1:
			this.HorizontalUnits = 3;
			break;
		case 2:
			this.HorizontalUnits = 1;
			break;
		case 3:
			this.HorizontalUnits = 3;
			break;
		}
		//System.out.println(""); // Display the string.
		//System.out.println(unit); // Display the string.
		//System.out.println(this.HorizontalUnits); // Display the string.

		// UTM
		this.UTMZone = image.get_planimetric_zone();

		// Datum
		this.Datum = 6326;

		// LeftExtent mas al west?
		this.LeftExtent = (double) ((int) this.MostWest());

		// RightExtent mas al east?
		this.RightExtent = (double) ((int) this.MostEast());

		// BottomExtent mas al sur?
		this.BottomExtent = (double) ((int) this.MostSouth());

		// TopExtent mas al norte? unidades imp?
		this.TopExtent = (double) ((int) this.MostNorth());

		// ExternalProjection
		this.ExternalProjection = 0; // no requiere file de projeccion
										// externo

		// Scale
		unit = image.get_elevation_unit();
		if (unit == 1) {
			this.Scale = (float) 3.2808;
		} else {
			this.Scale = (float) 1.0;
		}

		//System.out.println("initHearderBT");

	}

	/* escribe in file de BT */
	public boolean CreateBT(String outFile) {

		// Funciona ok
		try {
			// Creating Output Stream
			FileOutputStream file = new FileOutputStream(outFile);
			// creating output channel
			FileChannel outC = file.getChannel();
			// Version buffer
			ByteBuffer VBuffer = ByteBuffer.allocate(20);
			VBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// columns buffer
			ByteBuffer ColBuffer = ByteBuffer.allocate(4);
			ColBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// rows buffer
			ByteBuffer RowBuffer = ByteBuffer.allocate(4);
			RowBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Datasize Buffer
			ByteBuffer DSBuffer = ByteBuffer.allocate(2);
			DSBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Floating point flag buffer
			ByteBuffer FPBuffer = ByteBuffer.allocate(2);
			FPBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Horizontal units buffer
			ByteBuffer HUBuffer = ByteBuffer.allocate(2);
			HUBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Utm zone buffer
			ByteBuffer UTMBuffer = ByteBuffer.allocate(2);
			UTMBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Datum Buffer
			ByteBuffer DatumBuffer = ByteBuffer.allocate(2);
			DatumBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Left extent buffer
			ByteBuffer LeftBuffer = ByteBuffer.allocate(8);
			LeftBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Right extent buffer
			ByteBuffer RightBuffer = ByteBuffer.allocate(8);
			RightBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Bottom extent buffer
			ByteBuffer BottomBuffer = ByteBuffer.allocate(8);
			BottomBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Top extent buffer
			ByteBuffer TopBuffer = ByteBuffer.allocate(8);
			TopBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// External projection flag buffer
			ByteBuffer EPBuffer = ByteBuffer.allocate(2);
			EPBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// Scale buffer
			ByteBuffer ScaleBuffer = ByteBuffer.allocate(4);
			ScaleBuffer.order(ByteOrder.LITTLE_ENDIAN);

			// Padding buffer
			// ByteBuffer PadBuffer= ByteBuffer.allocate(this.Padding);
			ByteBuffer PadBuffer = ByteBuffer.allocate(DEMHandeler.Padding);
			PadBuffer.order(ByteOrder.LITTLE_ENDIAN);
			// elevation data buffer
			ByteBuffer[][] ElevBuffer = new ByteBuffer[this.Columns][this.Rows];
			for (int i = 0; i < this.Columns; i = i + 1) {
				for (int j = 0; j < this.Rows; j = j + 1) {
					ElevBuffer[i][j] = ByteBuffer.allocate(4);
					ElevBuffer[i][j].order(ByteOrder.LITTLE_ENDIAN);
				}
			}

			// initialazing & writing the VERSION
			VBuffer.put(DEMHandeler.BTVersion.getBytes());

			VBuffer.flip();
			outC.write(VBuffer);

			// writing the COLUMNS
			outC.position(10);
			ColBuffer.putInt((this.Columns));
			ColBuffer.flip();
			outC.write(ColBuffer);

			// writing the ROWS
			outC.position(14);
			RowBuffer.putInt(this.Rows);
			RowBuffer.flip();
			outC.write(RowBuffer);

			// writing the DATASIZE
			outC.position(18);
			DSBuffer.putShort(this.Datasize);
			DSBuffer.flip();
			outC.write(DSBuffer);

			// writing the FLOATINGPOINTFLAG
			outC.position(20);
			FPBuffer.putShort(this.FloatingPointFlag);
			FPBuffer.flip();
			outC.write(FPBuffer);

			// writing the HORIZONTAL UNITS
			outC.position(22);
			HUBuffer.putShort(this.HorizontalUnits);
			HUBuffer.flip();
			outC.write(HUBuffer);

			// writing the UTM ZONE
			outC.position(24);
			UTMBuffer.putShort(this.UTMZone);
			UTMBuffer.flip();
			outC.write(UTMBuffer);

			// writing the DATUM
			outC.position(26);
			DatumBuffer.putShort(this.Datum);
			DatumBuffer.flip();
			outC.write(DatumBuffer);

			// writing the LEFTEXTENT
			outC.position(28);
			LeftBuffer.putDouble(this.LeftExtent);
			LeftBuffer.flip();
			outC.write(LeftBuffer);

			// writing the RIGHTEXTENT
			outC.position(36);
			RightBuffer.putDouble(this.RightExtent);
			RightBuffer.flip();
			outC.write(RightBuffer);

			// writing the BOTTOMEXTENT
			outC.position(44);
			BottomBuffer.putDouble(this.BottomExtent);
			BottomBuffer.flip();
			outC.write(BottomBuffer);

			// writing the TOPEXTENT
			outC.position(52);
			TopBuffer.putDouble(this.TopExtent);
			TopBuffer.flip();
			outC.write(TopBuffer);

			// writng the EXTERNAL PROJECTION FLAG
			outC.position(60);
			EPBuffer.putShort(this.ExternalProjection);
			EPBuffer.flip();
			outC.write(EPBuffer);

			// writing the SCALE
			outC.position(62);
			ScaleBuffer.putFloat(this.Scale);
			ScaleBuffer.flip();
			outC.write(ScaleBuffer);

			// writng the PADDING
			byte pad = 0;

			for (int i = 0; i < (DEMHandeler.Padding); i++) {
				PadBuffer.put(pad);
			}

			PadBuffer.rewind();
			outC.write(PadBuffer);

			for (int i = 0; i < (this.Columns); i++) {
				for (int j = 0; j < (this.Rows); j++) {
					ElevBuffer[i][j].putInt((this.ElevationData[i][j]));
					ElevBuffer[i][j].flip();
					outC.write(ElevBuffer[i][j]);
				}

			}

			outC.close();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return true;

	}

	public int[] getDIMS(String outFile) {
		int[] size = new int[2];
		size[0] = 1;
		size[1] = 1;
		try {
			FileInputStream file = new FileInputStream(outFile);
			FileChannel fileChannel = file.getChannel();
			ByteBuffer fileBuffer = null;
			fileBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0,
					fileChannel.size());
			fileBuffer.order(LITTLE_ENDIAN);

			fileBuffer.position(10);
			size[0] = fileBuffer.getInt();
			fileBuffer.position(14);
			size[1] = fileBuffer.getInt();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return size;

	}

}
