//Program to convert FRAM data to standard IMMA1 format
//Author: Eric Freeman
//Affiliation: STG, Inc./NOAA/NCDC

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;


public class fram_imma1
{
	private static String IpPath = "";
	private static String OpPath = "";

	/*** File ***/
	public static File InputFile, OutputFile;
	public static BufferedReader ipbr;
	public static BufferedWriter opbw;

	private static boolean bdebug=true;

	static boolean Eflag;

	/*******************************************************************************************************************/
	public static void main(String[] args)
	{
		try {
			//Process
			process(args);
		} 
		catch (Exception e) {
			e.printStackTrace();
		}
	}	

	public static void process(String[] args) throws IOException, ParseException		
	{	
		if(args.length == 2)
		{
			IpPath = args[0];
			OpPath = args[1];
		}
		else if(bdebug)
		{
			System.out.println("*** Warning *** \nProgram Is Running In Debug Mode");
			IpPath = "sea_Sep_1_to_Sep_19_1898.csv";
			OpPath = "sea_Sep_1_to_Sep_19_1898_imma1_v8";
		}
		else
		{
			System.out.println("*** ARGUMENT ERROR ***  Num of Arguments = " + args.length + "\nRequired Arguments: InPut Path, OutPut Path, and  Log Path");
			System.exit(1);
		}

		// Open IP File
		openFiles();

		String str = "";

		while ((str = ipbr.readLine()) != null) {
			try {

				String [] cols = str.split(",");

				String yr = (cols.length <= 0) ? "" : cols[0];
				String mo = (cols.length <= 1) ? "" : cols[1];
				String day = (cols.length <= 2) ? "" : cols[2];
				String hr = (cols.length <= 3) ? "" : cols[3];
				String lat = (cols.length <= 4) ? "" : cols[4];
				String lon = (cols.length <= 5) ? "" : cols[5];
				String wd = (cols.length <= 6) ? "" : cols[6];
				String ws = (cols.length <= 7) ? "" : cols[7];
				String slp = (cols.length <= 8) ? "" : cols[8];
				String at = (cols.length <= 9) ? "" : cols[9];
				String vapor = (cols.length <= 10) ? "" : cols[10];
				String rh = (cols.length <= 11) ? "" : cols[11];
				String c_amt = (cols.length <= 12) ? "" : cols[12];
				String c_type = (cols.length <= 13) ? "" : cols[13];
				String c_dir = (cols.length <= 14) ? "" : cols[14];
				String wx = (cols.length <= 15) ? "" : cols[15];
				String sea_dir = (cols.length <= 16) ? "" : cols[16];
				String sea_state = (cols.length <= 17) ? "" : cols[17];
				String sst = (cols.length <= 18) ? "" : cols[18];

				
				
				
				
				//Table C0 - Core
				String b4 = yr.trim();  // year   1-4
				try {
					Integer I = new Integer(b4);
					int i = I.intValue();
					if (i<1898 | i>1902) Eflag=true; //
				}
				catch (NumberFormatException e) {
					Eflag = true;
				}


				String b5 = mo.trim();  // month  5-6
				if (b5.trim().length() == 1){
					b5 = " " + b5;
				}
				try {
					Integer I = new Integer(b5.trim());
					int i = I.intValue();
					if (i<1 | i>12) Eflag=true;
				}
				catch (NumberFormatException e) {
					Eflag = true;
				}


				String b6 = day.trim();  // day    7-8
				if (b6.trim().length() == 1){
					b6 = " " + b6;
				}
				try {
					Integer I = new Integer(b6.trim());
					int i = I.intValue();
					if (i<1 | i>31) Eflag=true;

				}
				catch (NumberFormatException e) {
					Eflag = true;
				}


				String b7 = hr.trim();  // hr   9-10; plus checks on hour 24 and changing dates
				try {
					Integer I = new Integer(b7.trim());
					int i = I.intValue();
					if (i<0 | i>24) Eflag=true;
				}
				catch (NumberFormatException e) {
					Eflag = true;
				}
				if (b7.trim().length() == 0) {b7 = "    ";} //Hour not reported
				else if (b7.trim().length() == 3) {  // 1-digit hour + minutes
					String hour = " " + b7.trim().substring(0,1);
					String minutes = b7.trim().substring(1,3);
					if (minutes.equals("15")) {minutes = "25";}
					if (minutes.equals("30")) {minutes = "50";}
					if (minutes.equals("45")) {minutes = "75";}
					b7 = hour+minutes;
				}
				else if (b7.trim().length() == 4) {  // 2-digit hour + minutes
					String hour = b7.trim().substring(0,2);
					String minutes = b7.trim().substring(2,4);
					if (minutes.equals("15")) {minutes = "25";}
					if (minutes.equals("30")) {minutes = "50";}
					if (minutes.equals("45")) {minutes = "75";}
					b7 = hour+minutes;
				}
				else if (b7.trim().length() == 1) {b7 = " " + b7 + "00";} //One digit hour reported, e.g. '4'
				else {       // Two digit hour reported, e.g. '12'
					if (b7.equals("24")) {
						b7 = "   0"; // if '24' then convert to '   0'

						Integer Y = new Integer(b4.trim());
						int y = Y.intValue();
						Integer M = new Integer(b5.trim());
						int m = M.intValue();
						Integer D = new Integer(b6.trim());
						int d = D.intValue();
						//month has 31 days (Jan, Mar, May, July, Aug, Oct, Dec)
						if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) {
							if (d == 31) {  //last day of the month
								if (m == 12) {  //and December (new year coming up); increment year; start month 1 day 1
									y = y+1;
									m = 1;
									d = 1;
									b4 = Integer.toString(y).trim();
									b5 = Integer.toString(m).trim();
									if (b5.length() == 1) {b5 = " " + b5;}
									b6 = Integer.toString(d).trim();
									if (b6.length() == 1) {b6 = " " + b6;}
								}
								else {    //all other months  w/ 31 days increment month by 1; start on day 1
									m = m+1;
									d = 1;
									b5 = Integer.toString(m).trim();
									if (b5.length() == 1) {b5 = " " + b5;}
									b6 = Integer.toString(d).trim();
									if (b6.length() == 1) {b6 = " " + b6;}
								}
							}
							else { //less than 31 days, month & year stay the same; increment day by 1
								d = d+1;
								b6 = Integer.toString(d).trim();
								if (b6.length() == 1) {b6 = " " + b6;}
							}
						}
						// month has 30 days (Apr, June, Sept, Nov)
						else if (m == 4 || m == 6 || m == 9 || m == 11) {
							if (d == 30) {  //last day of the month
								m = m+1;
								d = 1;
								b5 = Integer.toString(m).trim();
								if (b5.length() == 1) {b5 = " " + b5;}
								b6 = Integer.toString(d).trim();
								if (b6.length() == 1) {b6 = " " + b6;}
							}
							else {
								d = d+1;
								b6 = Integer.toString(d).trim();
								if (b6.length() == 1) {b6 = " " + b6;}
							}	
						}
						// month has 28 days (Feb)
						else if (m == 2) {
							if (d == 28) {  //last day of the month
								if (y == 1900) {   //leap year
									d = d+1; //day 29
									b6 = Integer.toString(d).trim();
									if (b6.length() == 1) {b6 = " " + b6;}
								}
								else {  //next month (March); day 1
									m = 3;
									d = 1;
									b5 = Integer.toString(m).trim();
									if (b5.length() == 1) {b5 = " " + b5;}
									b6 = Integer.toString(d).trim();
									if (b6.length() == 1) {b6 = " " + b6;}
								}
							}
							else {  // increment day by 1
								d = d+1;
								b6 = Integer.toString(d).trim();
								if (b6.length() == 1) {b6 = " " + b6;}
							}
						}	
					}  
					else {b7 = b7 + "00";}  // hour not 24 so add "00" minutes
				}				

				//Platform type - This is set in the lat/lon sections below.
				String pt = null; 
				
				//Latitude
				String b2; //lat
				if (lat.trim().length() == 0) {
					b2 = "     ";
				}
				else {
					if (lat.trim().equals("Gaasefjord")) {
						b2 = " 7682";
						pt= " 9";
					}
					else if (lat.trim().equals("Godhavn")) {
						b2 = " 6925";
						pt= " 9";
					}
					else if (lat.trim().equals("at_Godhavn")) {
						b2 = " 6925";
						pt= " 5";
					}
					else if (lat.trim().equals("Havenfjord") || lat.trim().equals("Havnefjord")) {
						b2 = " 7649";
						pt= " 9";
					}
					else if (lat.trim().length() == 0) {
						b2 = "     ";
						pt=" 5";
					}
					else {
						String [] lat_split = lat.split("°");
						String lat_deg = lat_split[0].trim(); //whole degrees
						String lat_min = lat_split[1].trim(); //minutes
						if (lat_min.contains("'")) {
							lat_min = lat_min.replace("'","").trim();
							lat_min = conv_MinToDecimal(lat_min);//convert lat_min to hundredths of degree
						}
						else {
							lat_min = conv_MinToDecimal(lat_min);//convert lat_min to hundredths of degree
						}

						b2 = lat_deg + lat_min;
						b2 = StringUtils.leftPad(b2, 5); //pad length to 5 chars
						pt=" 5";
					}
				}


				//Longitude
				String b3;
				if (lat.trim().equals("at_Godhavn")){
					b3 = " 30647";
				}
				else if (lon.trim().length() == 0) {
					b3 = "      ";
				}	
				else {
					if (lon.trim().equals("Gaasefjord")) {
						b3 = " 27134";
					}
					else if (lon.trim().equals("Godhavn") || lat.trim().equals("at_Godhavn")) {
						b3 = " 30647";
					}
					else if (lon.trim().equals("Havenfjord") || lon.trim().equals("Havnefjord")) {
						b3 = " 27594";
					}
					else {
						String [] lon_split = lon.split("°");
						String lon_deg = lon_split[0].trim(); //whole degrees
						String lon_min = lon_split[1].trim(); //minutes
						if (lon_min.contains("'")) {
							lon_min = lon_min.replace("'","").trim();

							lon_min = conv_MinToDecimal(lon_min);//convert lon_min to hundredths of degree
						}
						else {
							lon_min = conv_MinToDecimal(lon_min);//convert lon_min to hundredths of degree
						}

						String lon_full = lon_deg + lon_min; // lon decimal degrees W  for UTC conversion 18-23
						b3 = conv_Lon(lon_full);  // lon degrees E for IMMA1
					}
				}

				//Special case for PT in file 'sea_Sep_1_to_Sep_19_1898.csv; lat/lons are provided but it is noted in the publication that this is overwintering station.
				//All PT=9 for ice station/overwintering ship
				if (IpPath.equals("sea_Sep_1_to_Sep_19_1898.csv")) {
					pt= " 9";
				}
				
				//Special case for lat/lon in file 'sea_Aug_30_to_Oct_23_1899.csv; must populate blank entries with lat/lon of Havenfjord
				if (IpPath.equals("sea_Aug_30_to_Oct_23_1899.csv") && b2.trim().length() == 0 && b3.trim().length() == 0) {
					b2 = " 7649";
					b3 = " 27594";
					pt= " 9";
				}

				//Special case for lat/lon in file 'sea_1900.csv; must populate blank entries with lat/lon of Gaasefjord
				if (IpPath.equals("sea_1900.csv") && b2.trim().length() == 0 && b3.trim().length() == 0) {
					b2 = " 7682";
					b3 = " 27134";
					pt= " 9";
				}
				
				String gmtDate = null;
				//Convert local date/time to UTC
				if (b7.equals("   0")) {
					b7=" 000";
				}
				if (b7.trim().length() == 0) {
					gmtDate = b4+b5+b6+"    ";
				}
                //Calculate hour offset and convert time to UTC (Based on subroutine rxltut from lmrlib: http://icoads.noaa.gov/software/lmrlib
                else {
                   
                	int elon = Integer.parseInt(b3.trim());  //convert imma longitude [b3] string to integer
                    int wlon = 36000 - elon;
                    int dhr = (wlon + 749)/1500;  //calculate hour offset

					String localDate = b4+b5+b6+b7; //local date yyyymmddhhmm
                    if (b7.substring(2,4).trim().equals("75")) {
                    	localDate = b4+b5+b6+b7.substring(0,2)+"45"; //local date yyyymmddhhmm
                    }
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
                    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
                    Date date = sdf.parse(localDate);
//                    System.out.println(date);
                    Date newDate = DateUtils.addHours(date, dhr);
                    gmtDate = sdf.format(newDate);
                    
                    if (gmtDate.substring(10,12).trim().equals("45")) {
                    	gmtDate = gmtDate.substring(0,10)+"75"; //gmt date yyyymmddhhmm
                    }
                    
					if (gmtDate.substring(4,5).equals("0")) {
						gmtDate = gmtDate.substring(0,4) + " " + gmtDate.substring(5);
					}
					if (gmtDate.substring(6,7).equals("0")) {
						gmtDate = gmtDate.substring(0,6) + " " + gmtDate.substring(7);
					}
					if (gmtDate.substring(8,9).equals("0") && gmtDate.substring(9,10).equals("0") && gmtDate.substring(10,11).equals("0")) {
						gmtDate = gmtDate.substring(0,8) + "   " + gmtDate.substring(11);
					}
					if (gmtDate.substring(8,9).equals("0") && gmtDate.substring(9,10).equals("0")) {
						gmtDate = gmtDate.substring(0,8) + "  " + gmtDate.substring(10);
					}
					if (gmtDate.substring(8,9).equals("0")) {
						gmtDate = gmtDate.substring(0,8) + " " + gmtDate.substring(9);
					}
				}



				//22222233333
				//45678901234   Hard code Data
				String f1 = " 1204    10";	// 24-34: IM(24-25);ATTC(26);TI(27);LI(28);DS(29);VS(30);NID(31-32);II(33-34)   II=10=composite info from early ship data


				String b1 = "FRAM     ";  // call sign 35-43

				//44444455  
				//45678901   Create Filler Space
				String f2 = "  ";   //   44-45 Country Code


				String fwdi = "1";  // DI - wind dir ind 50      DI = 1 = 32 Point Compass


				String b13 = wd.trim();
				if (wd.trim().length() == 0) {     // wind dir 47-49
					b13 = "   ";
					fwdi = " ";
				}
				else if (b13.trim().equals("0")) {
					b13 = "  0";
				}
				else {
					b13 = conv_WD(wd); //convert from alpha to numeric direction
				}


				String b14 = ws.trim();   // wind speed in m/s 51-53
				if (b14.length() > 0) {
					BigDecimal ms = new BigDecimal(b14);  //convert string to BigDecimal
					BigDecimal d_ms = ms.divide(BigDecimal.valueOf(0.1));   //remove decimal      
					BigDecimal i_ms = d_ms.setScale(0,RoundingMode.UNNECESSARY); //scale = 0 (no values right of decimal); no rounding needed
					String new_ms = i_ms.toString(); //convert back to string

					b14 = StringUtils.leftPad(new_ms, 3); //pad length to 3 chars
				}
				else { b14 = "   ";}

				// if wind speed = 0 and wind direction is blank assume calm, IMMA output wind direction = 361 for calm
				if (b13.trim().length() == 0 && b14.trim().equals("0")) {
					b13 = "361";
					fwdi = "1";
				}

				String b12; // wind Sp Ind. 50  WI=1=m/s
				if (b14.trim().length() == 0) {
					b12 = " ";
				}
				else {b12 = "1";}  


				//55555555566
				//12345678901   Create Filler Space
				String f3 = "   ";   //  54-56: VI(54);VV(55-56)


				String b23 = "  ";  //pres wx 57-58; Not mapped to IMMA1
				String b24 = " ";  // past wx 59; not populated


				String b11 = slp.trim();  // slp 60-64
				if (b11.length() > 0) {
					BigDecimal mm = new BigDecimal(b11);  //convert string to BigDecimal
					BigDecimal D_hpa = mm.multiply(BigDecimal.valueOf(1.333224));   //hPa = mm * 1.333224 per fxmmmb from lmrlib.f (http://icoads.noaa.gov/software/lmrlib)
					BigDecimal I_hpa = D_hpa.setScale(1,RoundingMode.HALF_UP); //scale = 1 (keep 1 decimal value); round accordingly

					BigDecimal d_hpa = I_hpa.divide(BigDecimal.valueOf(0.1));   //remove decimal      
					BigDecimal i_hpa = d_hpa.setScale(0,RoundingMode.UNNECESSARY); //scale = 0 (no values right of decimal); no rounding needed

					String new_hpa = i_hpa.toString(); //convert back to string
					b11 = StringUtils.leftPad(new_hpa, 5); //pad length to 5 chars
				}
				else {b11 = "     ";}


				//6666
				//5678      Create Filler Space
				String f4 = "    ";  //    65-68: A(65);PPP(66-68) 


				String b17 = at.trim();   // air temp 70-73
				if (b17.length() > 0) {
					BigDecimal celsius = new BigDecimal(b17);  //convert string to BigDecimal
					BigDecimal d_celsius = celsius.divide(BigDecimal.valueOf(0.1));   //remove decimal      
					BigDecimal i_celsius = d_celsius.setScale(0,RoundingMode.UNNECESSARY); //scale = 0 (no values right of decimal); no rounding needed
					String new_celsius = i_celsius.toString(); //convert back to string

					b17 = StringUtils.leftPad(new_celsius, 4); //pad length to 4 chars
				}
				else {b17 = "    ";}


				String b18 = " "; //wetBI 74
				String b19 = "    "; // wetBulb 75-78


				//DPTI (79) and DPT (80-83)
//				String f5 = " ";   // DPTI 79
//				String b20 = "    ";  // dwpt  80-83
				//DPTI (79) and DPT (80-83)
				String f5,b20;
				if (vapor.trim().length()==0 || slp.trim().length()==0){
					f5 = " ";   // DPTI 79
					b20 = "    ";  // dwpt  80-83
				}
				else {
					//Calculate dwpt using caclDwpt method; 2 args: slp and vapor
					String dwpt = calcDwpt(slp,vapor); 

					f5 = "1";   // DPTI 79: DPTI = 1 = Calculated
					b20 = dwpt;  // dwpt  80-83
				}


				String b33;  // SI 84-85
				if (sst.trim().length() == 0) {
					b33 = "  ";
				}
				else {b33 = "10"; } //  SI = 10 = implied bucket (indirectly disclosed in description of thermometers used. See transpec for more info.

				String b34 = sst.trim(); //SST
				if (b34.length() > 0) {
					BigDecimal sstC = new BigDecimal(b34);  //convert string to BigDecimal
					BigDecimal d_sstC = sstC.divide(BigDecimal.valueOf(0.1));   //remove decimal      
					BigDecimal i_sstC = d_sstC.setScale(0,RoundingMode.UNNECESSARY); //scale = 0 (no values right of decimal); no rounding needed
					String new_sstC = i_sstC.toString(); //convert back to string

					b34 = StringUtils.leftPad(new_sstC, 4); //pad length to 4 chars
				}
				else {
					b34 = "    ";
				}


				String fIT = null;   // IT (temp indicator) 69
				if (b17.trim().length() == 0 && b34.trim().length() == 0) {
					fIT = " ";
				}
				else {
					fIT = "0";  // IT = 0 = temps in tenths °C
				}


				String b25 = c_amt.trim();  // tot cld amt 90  - Mohn 1907 values are 'tenths of sky covered'; use ixt1ok from lmrlib to convert to oktas (see amt_Cloud)
				if (b25.trim().length() == 0) {          // ck for missing
					b25 = " ";
				}
				else {
					b25 = amt_Cloud(b25);
				}


				String b26 = " "; //NH Low Cld Amt 91 (Not reported)


				String b27 = "  "; // HI,H 93-94 (Not reported)


				String b28 = " "; // CL - low cloud type 92     //Cloud types taken from WMO Codes 0513 (low), 0515 (med), 0509 (high)
				String b29 = " "; // CM - mid cloud type 95
				String b30 = " "; // CH - high cloud type 96
				if (c_type.trim().length() == 0 || c_type.trim().equals("Fr-Nb")|| c_type.trim().equals("Nb") || 
						c_type.trim().equals("Nb&Fr-Nb")) {
					b28 = " "; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("Ci-St") || c_type.trim().equals("\"Ci-St") || c_type.trim().equals("Ci-St3")) {
					b28 = " "; b29 = " "; b30 = "7";
				}
				else if (c_type.trim().equals("A-Cu")) {
					b28 = " "; b29 = "3"; b30 = " ";
				}
				else if (c_type.trim().equals("A-Cu&St") || c_type.trim().equals("A-Cu-St")) {
					b28 = "6"; b29 = "3"; b30 = " ";
				}
				else if (c_type.trim().equals("Ci")) {
					b28 = " "; b29 = " "; b30 = "1";
				}
				else if (c_type.trim().equals("Ci&St")) {
					b28 = "6"; b29 = " "; b30 = "1";
				}
				else if (c_type.trim().equals("Ci-Cu")) {
					b28 = " "; b29 = " "; b30 = "9";
				}
				else if (c_type.trim().equals("Ci-Cu&St")) {
					b28 = "6"; b29 = " "; b30 = "9";
				}
				else if (c_type.trim().equals("Ci-St&St") || c_type.trim().equals("Ci-St1&St1") || c_type.trim().equals("Ci-St2&St2") || 
						c_type.trim().equals("Ci-St2&St3") || c_type.trim().equals("Ci-St2&St7") || c_type.trim().equals("Ci-St3&St1") || 
						c_type.trim().equals("Ci-St4&St1") || c_type.trim().equals("Ci-St4&St3") || c_type.trim().equals("Ci-St5&St3") || 
						c_type.trim().equals("Ci-St7&St1") || c_type.trim().equals("Ci-St7&St3")) {
					b28 = "6"; b29 = " "; b30 = "7";
				}
				else if (c_type.trim().equals("Ci-StSt-Cu")) {
					b28 = "5"; b29 = " "; b30 = "7";
				}
				else if (c_type.trim().equals("Ci2&St-Cu1")) {
					b28 = "5"; b29 = " "; b30 = "1";
				}
				else if (c_type.trim().equals("Cu")) {
					b28 = "1"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("Cu-Nb")) {
					b28 = "3"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("Fr-St")) {
					b28 = "7"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("St")) {
					b28 = "6"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("St&Fr-St") || c_type.trim().equals("St3&Fr-St0-1") || 
						c_type.trim().equals("St4&Fr-St")) {    //stratus trumps stratus fractus
					b28 = "6"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("St&St-Cu") || c_type.trim().equals("St-Cu&St") || 
						c_type.trim().equals("St6&St-Cu3")) {    //stratus trumps strato-cu
					b28 = "6"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("St-Cu")) {
					b28 = "5"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("St-Cu&Nb")) {
					b28 = "5"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("St-Cu2&Cu-Nb7")) {
					b28 = "9"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("St-Cu2&Cu-Nb7")) {
					b28 = "9"; b29 = " "; b30 = " ";
				}
				else if (c_type.trim().equals("St7&Cu2")) { //stratus trumps cumulus
					b28 = "6"; b29 = " "; b30 = " ";
				}


				String waves = "            "; // 97-108 Sea/Swell State reported but indeterminable which of the 2 was being reported: 
				//WD(97-98);WP(99-100);WH(101-102);SD(103-104);SP(105-106);SH(107-108)


				// Table C1 - Icoads attm (Most fields populated by ICOADS QC software)


				String icoadsPt1= 
						//111111111111111111111111111111111111
						//0111111111122222222223333333333444444
						//9012345678901234567890123456789012345   Beg
						 " 165      734128"; // 109-124 
				//DCK=734
				//SID=128
				//PT=5 (ship) or PT=9 (ice station/overwintering) - This is set in lat/lon sections.
				String icoadsPt2="                                               "; //  127-173
				
				


				// Table C5 - Immt attm	(remove attm if RH blank)			    
				String immt;
				if (rh.trim().length() > 0) {
					//Part 1
					String immt1 = " 594                               "+  // 174-208
							"                                              ";  // 209-254
					//Part 2
					String hum = rh.trim(); //255-258 RH
					if (hum.trim().length() == 1) {
						hum = "  " + hum + "0";
					}
					else if (hum.trim().length() == 2) {
						hum = " " + hum + "0";
					}
					else if (hum.trim().length() == 3) {
						hum = hum + "0";
					}
					//Part 3
					String humI = "4"; //  259 RHI = 4 = Whole percentage;Computed
					//Part 4
					String immt2 = "        "; // 260-267 (AWSI [260];IMONO [261-267])

					immt = immt1+hum+humI+immt2;
				}
				else {
					immt = "";
				}

				//Supplemental Attm - Original observation attached here
				String s50 = "99 0 "; //  


				//Full IMMA record assembled here
				String outrec = gmtDate+b2+b3+f1+b1+f2+fwdi
						+b13+b12+b14+f3+b23+b24+b11+f4+fIT+b17+b18+b19
						+f5+b20+b33+b34+b25+b26+b28+b27+b29+b30+waves
						+icoadsPt1+pt+icoadsPt2+immt+s50+str+","+IpPath;

				//Full IMMA record assembled here
				//								String outrec = b4+b5+b6+b7+b2+b3+f1+b1+f2+fwdi
				//										+b13+b12+b14+f3+b23+b24+b11+f4+fIT+b17+b18+b19
				//										+f5+b20+b33+b34+b25+b26+b28+b27+b29+b30+waves
				//										+icoads+immt+s50+str+","+IpPath;


				//write IMMA record to output file
				writeString(opbw,outrec);
				System.out.println(outrec);

			}
			catch (NumberFormatException e) {
				System.out.println(e);
				System.out.println(str);
				e.printStackTrace();
				break;
			}
		}//end while 
		closeUp();
	}


	private static boolean ValidCheck(int i, String localDate) {
		return false;
	}

	/******************************************************************************************************************************/
	public static void openFiles()
	{

		try    /*** IP File ***/
		{
			System.out.println("Opening For Read:   " + IpPath + "\n");

			InputFile = new File(IpPath);
			ipbr = new BufferedReader(new FileReader(InputFile));
		}
		catch(IOException e)
		{
			System.out.println("*** Error ***    " + e.toString());
			System.out.println("Could Not Open " + IpPath);
			System.exit(1);
		}

		try    /*** OP File ***/
		{
			System.out.println("Opening For Write:   " + OpPath + "\n");

			OutputFile = new File(OpPath);
			opbw = new BufferedWriter(new FileWriter(OutputFile));
		}
		catch(IOException e)
		{
			System.out.println("*** Error ***    " + e.toString());
			System.out.println("Could Not Open " + OpPath);
			System.exit(1);
		}
	}

	/******************************************************************************************************************************/
	public static boolean writeString(BufferedWriter locbw, String locString)
	{
		try
		{
			locbw.write(locString + "\n");
			return(true);
		}
		catch(IOException e)
		{
			System.out.println("*** Error ***    " + e.toString());
		}
		return(false);
	}
	/******************************************************************************************************************************/
	public static void closeUp()
	{
		try
		{
			ipbr.close();
			opbw.close();
		}
		catch(IOException e)
		{
			System.out.println("*** ERROR Closing Files ***\n");
		}
	}

	/***********************************************
	 * String Function to convert longitude from E-W to degrees East (IMMA convention).
	 */

	public static String conv_MinToDecimal(String latlon_minutes)
	{
		BigDecimal whole = new BigDecimal(latlon_minutes);  //convert string to BigDecimal
		BigDecimal d_decimal = whole.divide(BigDecimal.valueOf(60),2,RoundingMode.HALF_UP);   //convert minutes to hundredths of degree  
		BigDecimal remove_decimal = d_decimal.divide(BigDecimal.valueOf(0.01)); //remove decimal
		String new_decimal = remove_decimal.toString(); //convert back to string

		String decimalLength = new_decimal.trim(); //Check string length and pad where needed
		int l = decimalLength.length();
		if (l==1) {new_decimal = "0" + decimalLength;}

		return new_decimal;
	}

	/***********************************************
	 * String Function to convert longitude from E-W to degrees East (IMMA convention).
	 */

	public static String conv_Lon(String longitude)
	{

		Double D_lon = new Double(longitude); //Create double obj
		double d_lon = D_lon.doubleValue(); //Create double num

		d_lon = 36000 - d_lon; // Convert to degrees east (only in case of positive longitude)
		d_lon = Math.round(d_lon); //Round to whole#

		D_lon = new Double(d_lon); //Convert to int
		int i_lon = D_lon.intValue(); 

		Integer I_lon = new Integer(i_lon); // Convert back to string
		String lon = I_lon.toString();

		lon = StringUtils.leftPad(lon, 6); //pad length to 6 chars

		return lon;
	}

	/****************************************************************   
	 * Integer Function to calculate time zone offset in hours for conversion of local date/time to GMT
	 */

	public static int getTimezoneOffsetInHours(String longitude)
	{
		Integer lon_int = new Integer(longitude);
		int y = lon_int.intValue();
		int lon_offset = (y/100)/24;

		return lon_offset;
	}



	/***********************************************
	 * String Function to convert units of mmHg to hPa (millibars) for SLP or moisture calculations
	 */

	public static String conv_hPa(String mmHg)
	{

		BigDecimal mm = new BigDecimal(mmHg);  //convert string to BigDecimal
		BigDecimal D_hpa = mm.multiply(BigDecimal.valueOf(1.333224));   //hPa = mm * 1.333224 per fxmmmb from lmrlib.f (http://icoads.noaa.gov/software/lmrlib)
		BigDecimal I_hpa = D_hpa.setScale(1,RoundingMode.HALF_UP); //scale = 1 (keep 1 decimal value); round accordingly

		BigDecimal d_hpa = I_hpa.divide(BigDecimal.valueOf(0.1));   //remove decimal      
		BigDecimal i_hpa = d_hpa.setScale(0,RoundingMode.UNNECESSARY); //scale = 0 (no values right of decimal); no rounding needed

		String new_hpa = i_hpa.toString(); //convert back to string

		new_hpa = StringUtils.leftPad(new_hpa, 5); //pad length to 5 chars

		return new_hpa;
	}



	/****************************************************************   
	 * String Function to convert 32 point Alpha Wind directions to degrees
	 */
	public static String conv_WD(String AlphaWD)
	{
		String NumWD="   ";

		if (AlphaWD.trim().equalsIgnoreCase("NbE") || AlphaWD.trim().equalsIgnoreCase("NxE") || AlphaWD.trim().equalsIgnoreCase("NXE")) NumWD=" 11";		   
		if (AlphaWD.trim().equalsIgnoreCase("NNE")) NumWD=" 23";		   
		if (AlphaWD.trim().equalsIgnoreCase("NEbN") || AlphaWD.trim().equalsIgnoreCase("NExN") || AlphaWD.trim().equalsIgnoreCase("NEXN")) NumWD=" 34";		   
		if (AlphaWD.trim().equalsIgnoreCase("NE")) NumWD=" 45";		   
		if (AlphaWD.trim().equalsIgnoreCase("NEbE") || AlphaWD.trim().equalsIgnoreCase("NExE") || AlphaWD.trim().equalsIgnoreCase("NEXE")) NumWD=" 56";		   
		if (AlphaWD.trim().equalsIgnoreCase("ENE")) NumWD=" 68";		   
		if (AlphaWD.trim().equalsIgnoreCase("EbN") || AlphaWD.trim().equalsIgnoreCase("ExN") || AlphaWD.trim().equalsIgnoreCase("EXN")) NumWD=" 79";		   
		if (AlphaWD.trim().equalsIgnoreCase("E")) NumWD=" 90";		   
		if (AlphaWD.trim().equalsIgnoreCase("EbS") || AlphaWD.trim().equalsIgnoreCase("ExS") || AlphaWD.trim().equalsIgnoreCase("EXS")) NumWD="101";		   
		if (AlphaWD.trim().equalsIgnoreCase("ESE")) NumWD="113";		   
		if (AlphaWD.trim().equalsIgnoreCase("SEbE") || AlphaWD.trim().equalsIgnoreCase("SExE") || AlphaWD.trim().equalsIgnoreCase("SEXE")) NumWD="124";		   
		if (AlphaWD.trim().equalsIgnoreCase("SE")) NumWD="135";		   
		if (AlphaWD.trim().equalsIgnoreCase("SEbS") || AlphaWD.trim().equalsIgnoreCase("SExS") || AlphaWD.trim().equalsIgnoreCase("SEXS")) NumWD="146";		   
		if (AlphaWD.trim().equalsIgnoreCase("SSE")) NumWD="158";		   
		if (AlphaWD.trim().equalsIgnoreCase("SbE") || AlphaWD.trim().equalsIgnoreCase("SxE") || AlphaWD.trim().equalsIgnoreCase("SXE")) NumWD="169";		   
		if (AlphaWD.trim().equalsIgnoreCase("S")) NumWD="180";		   
		if (AlphaWD.trim().equalsIgnoreCase("SbW") || AlphaWD.trim().equalsIgnoreCase("SxW") || AlphaWD.trim().equalsIgnoreCase("SXW")) NumWD="191";		   
		if (AlphaWD.trim().equalsIgnoreCase("SSW")) NumWD="203";		   
		if (AlphaWD.trim().equalsIgnoreCase("SWbS") || AlphaWD.trim().equalsIgnoreCase("SWxS") || AlphaWD.trim().equalsIgnoreCase("SWXS")) NumWD="214";		   
		if (AlphaWD.trim().equalsIgnoreCase("SW")) NumWD="225";		   
		if (AlphaWD.trim().equalsIgnoreCase("SWbW") || AlphaWD.trim().equalsIgnoreCase("SWxW") || AlphaWD.trim().equalsIgnoreCase("SWXW")) NumWD="236";		   
		if (AlphaWD.trim().equalsIgnoreCase("WSW")) NumWD="248";		   
		if (AlphaWD.trim().equalsIgnoreCase("WbS") || AlphaWD.trim().equalsIgnoreCase("WxS") || AlphaWD.trim().equalsIgnoreCase("WXS")) NumWD="259";		   
		if (AlphaWD.trim().equalsIgnoreCase("W")) NumWD="270";		   
		if (AlphaWD.trim().equalsIgnoreCase("WbN") || AlphaWD.trim().equalsIgnoreCase("WxN") || AlphaWD.trim().equalsIgnoreCase("WXN")) NumWD="281";		   
		if (AlphaWD.trim().equalsIgnoreCase("WNW")) NumWD="293";		   
		if (AlphaWD.trim().equalsIgnoreCase("NWbW") || AlphaWD.trim().equalsIgnoreCase("NWxW") || AlphaWD.trim().equalsIgnoreCase("NWXW")) NumWD="304";		   
		if (AlphaWD.trim().equalsIgnoreCase("NW")) NumWD="315";		   
		if (AlphaWD.trim().equalsIgnoreCase("NWbN") ||AlphaWD.trim().equalsIgnoreCase("NWxN") || AlphaWD.trim().equalsIgnoreCase("NWXN")) NumWD="326";		   
		if (AlphaWD.trim().equalsIgnoreCase("NNW")) NumWD="338";		   
		if (AlphaWD.trim().equalsIgnoreCase("NbW") || AlphaWD.trim().equalsIgnoreCase("NxW") || AlphaWD.trim().equalsIgnoreCase("NXW")) NumWD="349";
		if (AlphaWD.trim().equalsIgnoreCase("N")) NumWD="360";		   
		if (AlphaWD.trim().equalsIgnoreCase("C")) NumWD="361";
		if (AlphaWD.trim().equalsIgnoreCase("V") || AlphaWD.trim().equalsIgnoreCase("Var.") || AlphaWD.trim().equalsIgnoreCase("VAR")) NumWD="362";

		return NumWD;
	}

	/****************************************************************
	 * String Function to convert amt sky (covered) from tenths to oktas
	 */

	public static String amt_Cloud (String amt)
	{
		String NC = null;

		if (amt.trim().equals("0")) {
			NC="0";
			return NC;
		}

		if (amt.trim().equals("1") || amt.trim().equals("0-1") || amt.trim().equals("1+") || amt.trim().equals("1-")) {
			NC="1";
			return NC;
		}

		if (amt.trim().equals("2") || amt.trim().equals("3") || amt.trim().equals("1-2") || amt.trim().equals("1-2-") || 
				amt.trim().equals("2-") || amt.trim().equals("2-3") || amt.trim().equals("2-Jan") || amt.trim().equals("3-") ||
				amt.trim().equals("3-Feb")) {
			NC="2";
			return NC;
		}

		if (amt.trim().equals("4") || amt.trim().equals("4+") || amt.trim().equals("4-")) {
			NC="3";
			return NC;
		}

		if (amt.trim().equals("5") || amt.trim().equals("5-")) {
			NC="4";
			return NC;
		}

		if (amt.trim().equals("6") || amt.trim().equals("6-") || amt.trim().equals("6+")) {
			NC="5";
			return NC;
		}

		if (amt.trim().equals("7") || amt.trim().equals("8") || amt.trim().equals("7-") || amt.trim().equals("8-") || 
				amt.trim().equals("8+")) {
			NC="6";
			return NC;
		}

		if (amt.trim().equals("9") || amt.trim().equals("9-") || amt.trim().equals("9+") || amt.trim().equals("90")) {
			NC="7";
			return NC;
		}

		if (amt.trim().equals("10") || amt.trim().equals("10+") || amt.trim().equals("10-") || amt.trim().equals("9-10") || 
				amt.trim().equals("10-Sep")) {
			NC="8";
			return NC;
		}
		else {
			System.err.println("Cloud Amt = "+amt);
			NC=" ";
			return NC;
		}
	}

	/***********************************************
	 * String Function to calculate dwpt from available dry bulb, pressure and vapor tension
	 */

	public static String calcDwpt(String pressure, String vapTension)
	{

		//convert slp in mmHg to mb 
		double slp_mmHg = Double.parseDouble(pressure); //Create double num
		double slp_mb = slp_mmHg * 1.333224; // convert vapor tension from mmHg to mb
		//convert vapor tension in mmHg to mb  
		double vap_mmHg = Double.parseDouble(vapTension); //Create double num
		double vap_mb = vap_mmHg * 1.333224; // convert vapor tension from mmHg to mb

		//Sat. Vap. Pressure calculation
		double calc_fp = 1.0016 + (3.15 * Math.pow(10,-6) * slp_mb) - 0.074 * Math.pow(slp_mb, -1);
		
		//dwpt calculation
		double calc_dwpt = (243.12 * Math.log(vap_mb / (6.112 * calc_fp))) / (17.62 - Math.log(vap_mb / (6.112 * calc_fp)));

		BigDecimal i_dwpt = new BigDecimal(calc_dwpt);
		BigDecimal I_dwpt = i_dwpt.setScale(1,RoundingMode.HALF_UP); //scale = 1 (keep 1 decimal value); round accordingly
		BigDecimal d_dwpt = I_dwpt.divide(BigDecimal.valueOf(0.1));   //remove decimal      
		String new_dwpt = d_dwpt.toString(); //convert back to string
		new_dwpt = StringUtils.leftPad(new_dwpt, 4); //pad length to 4 chars

		return new_dwpt;
	}


	/******************************************************************************
	  Returns false if any character in CheckString is not also in ValidChars
	 *******************************************************************************/
	public static boolean ValidCheck(String ValidChars, String CheckString)
	{
		for(int n=0;n<CheckString.length();n++)
		{
			char c = CheckString.charAt(n);
			if(ValidChars.indexOf(c) == -1)
				return(false);
		}

		return(true);
	} 

	/******************************************************************************
	 *******************************************************************************/
}


