001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.adt;
030
031import java.io.BufferedWriter;
032import java.io.File;
033import java.io.FileWriter;
034import java.io.IOException;
035import java.util.Scanner;
036
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040class IRHistoryRecord {
041    int date;
042    int time;
043    double TrawO;
044    double Traw;
045    double Tfinal;
046    double CI;
047    double eyet;
048    double cloudt;
049    double cloudt2;
050    double cwcloudt;
051    double latitude;
052    double longitude;
053    double eyecdosize;
054    double eyestdv;
055    double cloudsymave;
056    int sattype;
057    int eyescene;
058    int cloudscene;
059    int eyesceneold;
060    int cloudsceneold;
061    int rule9;
062    int rule8;
063    int land;
064    int eyefft;
065    int cloudfft;
066    int ringcb;
067    int ringcbval;
068    int cwring;
069    int ringcbvalmax;
070    double ringcbvalmaxlat;
071    double ringcbvalmaxlon;
072    double CIadjp;
073    int autopos;
074    int LBflag;
075    int rapiddiss;
076    double rmw;
077    double mwscore;
078    int mwdate;
079    int mwtime;
080    int r34;
081    int MSLPenv;
082    int vza;
083    String comment;
084}
085
086public class History {
087
088    private static final Logger logger = LoggerFactory.getLogger(History.class);
089
090    /** ATCF Rule 8/9 array */
091    static String[] Rule89_ATCF = { "  ", "R8", "R9", "89" };
092
093    /** ATCF Rule 9 array */
094    static String[] Rule9String = { "OFF", " ON", "WKN", "N/A" };
095
096    /** rapid dissipation array */
097    static String[] RapidDissString = { "OFF", "FLG", "ON ", "ON ", "N/A" };
098
099    /** eye scenes */
100    static String[] EyeSceneString = { "EYE   ", "EYE/P ", "EYE/L ", "EYE/LR", "EYE/R ", "EYE/OB" };
101
102    /** cloud scenes */
103    static String[] CloudSceneString = { "UNIFRM", "EMBC  ", "IRRCDO", "CRVBND", "SHEAR ", "EYE MW" };
104
105    /** ATCF cloud */
106    static String[] CloudSceneString_ATCF = { " CDO", "EMBC", "ICDO", "CBND", "SHER", "MEYE" };
107
108    static String[] AutoPosStringAbbr = { " MAN ", "FCST ", "LAPL ", "WARM ", "SPRL ", "COMBO",
109            "EXTRP", "NETCDF", " N/A " };
110
111    static String[] Rule8String = { "NO LIMIT ", "0.5T/6hr ", "1.0T/6hr ", "1.7T/12hr",
112            "2.2T/18hr", "2.7T/24hr", "         ", "         ", "0.2T/hour", "0.5T/hour",
113            "NO LIMIT ", "0.5T/6hr ", "1.0T/6hr ", "2.7T/12hr", "3.2T/18hr", "3.7T/24hr",
114            "         ", "         ", "0.2T/hour", "0.5T/hour", "NO LIMIT ", "0.5T/6hr ",
115            "0.7T/6hr ", "1.2T/12hr", "1.7T/18hr", "2.2T/24hr", "         ", "         ",
116            "0.2T/hour", "0.5T/hour", "MW Adjst ", " MW ON    ", "MW ON    ", "MW HOLD  ",
117            "MW AdjEnd" };
118
119    static int HISTLEN = 194;
120
121    public static int HistoryFileRecords;
122    public static IRHistoryRecord HistoryFile[] = new IRHistoryRecord[1000];
123    public static IRHistoryRecord IRCurrentRecord = new IRHistoryRecord();
124
125    /*
126     * public static IRHistoryRecord IRCurrentRecord = new IRHistoryRecord(); /
127     * this could be public if I redo everything
128     */
129
130    public History() {
131        HistoryFileRecords = 0;
132    }
133
134    public void ReadHistoryFile(String filename) throws IOException {
135        String delims = "[ ]+";
136
137        File historyfile = new File(filename);
138        Scanner in = new Scanner(historyfile);
139
140        logger.debug("Opened history file {} SUCCESSFULY", filename);
141        while (in.hasNextLine()) {
142            HistoryFile[HistoryFileRecords] = new IRHistoryRecord();
143            String historyRec = in.nextLine();
144            logger.debug("Parsing History file line: " + historyRec);
145            String[] tokens = historyRec.split(delims);
146            /*
147             * for(String histVals : tokens) { System.out.println(histVals); }
148             */
149            /*
150             * HistoryFile[HistoryFileRecords].date =
151             * Integer.parseInt(tokens[0]);
152             */
153            HistoryFile[HistoryFileRecords].date = Functions.cmonth2julian(tokens[0]);
154            HistoryFile[HistoryFileRecords].time = Integer.parseInt(tokens[1]);
155            HistoryFile[HistoryFileRecords].TrawO = Float.parseFloat(tokens[3]);
156            HistoryFile[HistoryFileRecords].Traw = Float.parseFloat(tokens[4]);
157            HistoryFile[HistoryFileRecords].Tfinal = Float.parseFloat(tokens[5]);
158            HistoryFile[HistoryFileRecords].CI = Float.parseFloat(tokens[6]);
159            HistoryFile[HistoryFileRecords].eyet = Float.parseFloat(tokens[7]);
160            HistoryFile[HistoryFileRecords].cloudt = Float.parseFloat(tokens[8]);
161            HistoryFile[HistoryFileRecords].cloudt2 = Float.parseFloat(tokens[9]);
162            HistoryFile[HistoryFileRecords].cwcloudt = Float.parseFloat(tokens[10]);
163            HistoryFile[HistoryFileRecords].latitude = Float.parseFloat(tokens[11]);
164            HistoryFile[HistoryFileRecords].longitude = Float.parseFloat(tokens[12]);
165            HistoryFile[HistoryFileRecords].eyecdosize = Float.parseFloat(tokens[13]);
166            HistoryFile[HistoryFileRecords].eyestdv = Float.parseFloat(tokens[14]);
167            HistoryFile[HistoryFileRecords].cloudsymave = Float.parseFloat(tokens[15]);
168            HistoryFile[HistoryFileRecords].sattype = Integer.parseInt(tokens[16]);
169            HistoryFile[HistoryFileRecords].eyescene = Integer.parseInt(tokens[17]);
170            HistoryFile[HistoryFileRecords].cloudscene = Integer.parseInt(tokens[18]);
171            HistoryFile[HistoryFileRecords].eyesceneold = Integer.parseInt(tokens[19]);
172            HistoryFile[HistoryFileRecords].cloudsceneold = Integer.parseInt(tokens[20]);
173            HistoryFile[HistoryFileRecords].rule9 = Integer.parseInt(tokens[21]);
174            HistoryFile[HistoryFileRecords].rule8 = Integer.parseInt(tokens[22]);
175            HistoryFile[HistoryFileRecords].LBflag = Integer.parseInt(tokens[23]);
176            HistoryFile[HistoryFileRecords].rapiddiss = Integer.parseInt(tokens[24]);
177            HistoryFile[HistoryFileRecords].land = Integer.parseInt(tokens[25]);
178            HistoryFile[HistoryFileRecords].eyefft = Integer.parseInt(tokens[26]);
179            HistoryFile[HistoryFileRecords].cloudfft = Integer.parseInt(tokens[27]);
180            HistoryFile[HistoryFileRecords].ringcb = Integer.parseInt(tokens[28]);
181            HistoryFile[HistoryFileRecords].ringcbval = Integer.parseInt(tokens[29]);
182            HistoryFile[HistoryFileRecords].cwring = Integer.parseInt(tokens[30]);
183            HistoryFile[HistoryFileRecords].autopos = Integer.parseInt(tokens[31]);
184            HistoryFile[HistoryFileRecords].CIadjp = Float.parseFloat(tokens[32]);
185            HistoryFile[HistoryFileRecords].rmw = Float.parseFloat(tokens[33]);
186            HistoryFile[HistoryFileRecords].mwscore = Float.parseFloat(tokens[34]);
187            HistoryFile[HistoryFileRecords].mwdate = Functions.cmonth2julian(tokens[35]);
188            HistoryFile[HistoryFileRecords].mwtime = Integer.parseInt(tokens[36]);
189            HistoryFile[HistoryFileRecords].r34 = Integer.parseInt(tokens[37]);
190            HistoryFile[HistoryFileRecords].MSLPenv = Integer.parseInt(tokens[38]);
191            HistoryFile[HistoryFileRecords].vza = Integer.parseInt(tokens[39]);
192            if (tokens.length > 40) {
193                if (tokens[40] != null) {
194                    /* this won't get all comment past first space */
195                    HistoryFile[HistoryFileRecords].comment = tokens[40];
196                }
197            } else {
198                HistoryFile[HistoryFileRecords].comment = "";
199            }
200            HistoryFileRecords++;
201        }
202        in.close();
203        logger.debug("Done reading History file, number of records: " + HistoryFileRecords);
204    }
205
206    public static int HistoryNumberOfRecords() {
207        return HistoryFileRecords;
208    }
209
210    /**
211     * List the ASCII history file between given date/times.
212     *
213     * @param OutputStyle
214     *            Output one record (-1) or entire file (0).
215     * @param OutputFormatTypeID
216     *            0=ATCF, -1=History List.
217     * @param ATCFFileSourceIDString
218     *            ATCF file source identifier.
219     * @param ATCFStormIDString
220     *            ATCF storm identifier.
221     *
222     * @return Matching part of the ASCII history file.
223     */
224
225    public static String ListHistory(int OutputStyle, int OutputFormatTypeID,
226            String ATCFFileSourceIDString, String ATCFStormIDString) {
227        String HistoryFileListing = "";
228
229        boolean UseCKZTF = Env.UseCKZTF;
230        boolean Vmax1or10TF = Env.Vmax1or10TF;
231        int NumRecsHistory;
232        int DateValue;
233        int TimeValue;
234        double RawTAdj;
235        double RawTOrig;
236        double FinalT;
237        double CI;
238        double EyeTemp;
239        double CloudTemp;
240        double Latitude;
241        double Longitude;
242        int SatelliteIDValue;
243        int CloudScene;
244        int EyeScene;
245        int LandFlag;
246        int Rule8Flag;
247        int Rule9Flag;
248        int RapidDissFlag;
249        double CIPresAdj;
250        double RadiusMaxWind;
251        double MWEyeScore;
252        int AutoCenteringValue;
253        int R34Distance;
254        int EnvironMSLP;
255        double SatVZA;
256        String CommentString = null;
257
258        logger.debug("outputstyle={} outputformattype={}", OutputStyle, OutputFormatTypeID);
259
260        if (OutputStyle == 0) {
261            NumRecsHistory = HistoryNumberOfRecords(); // loop through history
262                                                       // file
263        } else {
264            NumRecsHistory = 1; // output current record
265        }
266
267        int XInc = 0;
268        while (XInc < NumRecsHistory) {
269            if (XInc == 0) {
270                if (OutputFormatTypeID == -1) {
271                    /* original format history file listing */
272                    if (UseCKZTF) {
273                        HistoryFileListing += String.format("                  ----Intensity--- "
274                                + "-Tno Values-- ---Tno/CI Rules---  -Temperature-"
275                                + "                    \n");
276                    } else {
277                        HistoryFileListing += String
278                                .format("                  --------Intensity------- "
279                                        + "-Tno Values-- ---Tno/CI Rules---  -Temperature-"
280                                        + "                    \n");
281                    }
282                    String LabelA = "";
283                    String LabelB = "";
284                    if (UseCKZTF) {
285                        if (Vmax1or10TF) {
286                            LabelA = String.format("      MSLP/Vmax   ");
287                        } else {
288                            LabelA = String.format("%18s", "      MSLP/Vmax10 ");
289                        }
290                        HistoryFileListing += String.format("           Time  %18s "
291                                + "Fnl Adj Ini   Cnstrnt Wkng Rpd    Cntr   Mean   "
292                                + "Scene  EstRMW   MW   Storm Location  Fix\n", LabelA);
293                    } else {
294                        if (Vmax1or10TF) {
295                            LabelB = String.format("%24s", "     MSLP/MSLPLat/Vmax  ");
296                        } else {
297                            LabelB = String.format("%24s", "     MSLP/MSLPLat/Vmax10");
298                        }
299                        HistoryFileListing += String.format("           Time    %24s "
300                                + "Fnl Adj Ini   Cnstrnt Wkng Rpd    Cntr   Mean   "
301                                + "Scene  EstRMW   MW   Storm Location  Fix\n", LabelB);
302                    }
303                    if (UseCKZTF) {
304                        HistoryFileListing += String.format("   Date    (UTC)   CI  (CKZ)/(kts)  "
305                                + "Tno Raw Raw    Limit  Flag Wkng  Region  Cloud  "
306                                + "Type    (km)  Score   Lat     Lon    Mthd    Sat   "
307                                + "VZA  Comments\n");
308                    } else {
309                        HistoryFileListing += String
310                                .format("   Date    (UTC)   CI  (DvT)/BiasAdj/(kts)  "
311                                        + "Tno Raw Raw    Limit  Flag Wkng  Region  Cloud  "
312                                        + "Type    (km)  Score   Lat     Lon    Mthd     Sat   "
313                                        + "VZA  Comments\n");
314                    }
315                } else {
316                    /* ATCF format listing */
317                    /* HistoryFileListing += String.format("\n"); */
318                }
319            }
320            if (OutputStyle == 0) {
321                DateValue = HistoryFile[XInc].date;
322                TimeValue = HistoryFile[XInc].time;
323                RawTAdj = HistoryFile[XInc].Traw;
324                RawTOrig = HistoryFile[XInc].TrawO;
325                FinalT = HistoryFile[XInc].Tfinal;
326                CI = HistoryFile[XInc].CI;
327                EyeTemp = HistoryFile[XInc].eyet;
328                CloudTemp = HistoryFile[XInc].cloudt;
329                Latitude = HistoryFile[XInc].latitude;
330                Longitude = HistoryFile[XInc].longitude;
331                SatelliteIDValue = HistoryFile[XInc].sattype;
332                CloudScene = HistoryFile[XInc].cloudscene;
333                EyeScene = HistoryFile[XInc].eyescene;
334                LandFlag = HistoryFile[XInc].land;
335                Rule8Flag = HistoryFile[XInc].rule8;
336                Rule9Flag = HistoryFile[XInc].rule9;
337                RapidDissFlag = HistoryFile[XInc].rapiddiss;
338                CIPresAdj = HistoryFile[XInc].CIadjp;
339                RadiusMaxWind = HistoryFile[XInc].rmw;
340                MWEyeScore = HistoryFile[XInc].mwscore;
341                AutoCenteringValue = HistoryFile[XInc].autopos;
342                R34Distance = HistoryFile[XInc].r34;
343                EnvironMSLP = HistoryFile[XInc].MSLPenv;
344                SatVZA = ((float) HistoryFile[XInc].vza) / 10.0;
345                CommentString = String.format("%s", HistoryFile[XInc].comment);
346            } else {
347                DateValue = IRCurrentRecord.date;
348                TimeValue = IRCurrentRecord.time;
349                RawTAdj = IRCurrentRecord.Traw;
350                RawTOrig = IRCurrentRecord.TrawO;
351                FinalT = IRCurrentRecord.Tfinal;
352                CI = IRCurrentRecord.CI;
353                EyeTemp = IRCurrentRecord.eyet;
354                CloudTemp = IRCurrentRecord.cloudt;
355                Latitude = IRCurrentRecord.latitude;
356                Longitude = IRCurrentRecord.longitude;
357                SatelliteIDValue = IRCurrentRecord.sattype;
358                CloudScene = IRCurrentRecord.cloudscene;
359                EyeScene = IRCurrentRecord.eyescene;
360                LandFlag = IRCurrentRecord.land;
361                Rule8Flag = IRCurrentRecord.rule8;
362                Rule9Flag = IRCurrentRecord.rule9;
363                RapidDissFlag = IRCurrentRecord.rapiddiss;
364                CIPresAdj = IRCurrentRecord.CIadjp;
365                RadiusMaxWind = IRCurrentRecord.rmw;
366                MWEyeScore = IRCurrentRecord.mwscore;
367                AutoCenteringValue = IRCurrentRecord.autopos;
368                R34Distance = IRCurrentRecord.r34;
369                EnvironMSLP = IRCurrentRecord.MSLPenv;
370                SatVZA = ((float) IRCurrentRecord.vza) / 10.0;
371                CommentString = String.format("%s", IRCurrentRecord.comment);
372            }
373
374            boolean ListLandRecordTF = true;
375            if ((LandFlag == 1) && (CI < 1.0)) {
376                ListLandRecordTF = false;
377            }
378
379            double CIPressureValue;
380            double CIWindValue;
381            String RadiusMaxWindString = "";
382            String MWScoreString = "";
383            String SceneTypeString = "";
384            if (!ListLandRecordTF) {
385                CIPresAdj = 0.0;
386                CIPressureValue = 0.0;
387                CIWindValue = 0.0;
388                SceneTypeString = String.format("LAND  ");
389                Rule8Flag = 6;
390                Rule9Flag = 3;
391                RapidDissFlag = 4;
392                /* AutoCenteringValue=7; */
393                RadiusMaxWindString = String.format("  N/A ");
394                MWScoreString = String.format("  N/A");
395            } else {
396                Env.CKZGaleRadius = R34Distance;
397                Env.CKZPenv = EnvironMSLP;
398                CIPressureValue = Functions.adt_getpwval(0, CI, Latitude, Longitude);
399                CIWindValue = Functions.adt_getpwval(1, CI, Latitude, Longitude);
400                if (!Vmax1or10TF) {
401                    /* convert 1-minute to 10-minute average Vmax for output */
402                    CIWindValue = 0.88 * CIWindValue;
403                }
404                if ((CloudScene == 3) || (CloudScene == 4)) {
405                    SceneTypeString = String.format("%s", CloudSceneString[CloudScene]);
406                } else if (EyeScene < 3) {
407                    SceneTypeString = String.format("%s", EyeSceneString[EyeScene]);
408                } else {
409                    SceneTypeString = String.format("%s", CloudSceneString[CloudScene]);
410                }
411                if ((CloudScene <= 5) && (EyeScene <= 2)) {
412                    RadiusMaxWindString = String.format("%3d IR", (int) RadiusMaxWind);
413                } else {
414                    RadiusMaxWindString = String.format("%s", "  N/A ");
415                }
416                if (MWEyeScore >= -99.0) {
417                    MWScoreString = String.format("%5.1f", MWEyeScore);
418                } else {
419                    MWScoreString = String.format("%s", "  N/A");
420                }
421            }
422
423            logger.debug("here AA {} {}", OutputFormatTypeID, NumRecsHistory);
424            if (OutputFormatTypeID == -1) {
425                /* original format history file listing */
426                String DateString = Functions.adt_julian2cmonth(DateValue);
427                String LatLonComboString = String.format("%6.2f %7.2f", Latitude, Longitude);
428                String SatelliteIDString = Functions.adt_sattypes(SatelliteIDValue);
429                if (UseCKZTF) {
430                    HistoryFileListing += String.format("%9s %06d  %3.1f %6.1f %5.1f  "
431                            + "%3.1f %3.1f %3.1f  %8s %3s  %3s  "
432                            + "%6.2f %6.2f  %6s %6s %5s %15s  %5s %7s %4.1f %s\n", DateString,
433                            TimeValue, CI, CIPressureValue, CIWindValue, FinalT, RawTAdj, RawTOrig,
434                            Rule8String[Rule8Flag], Rule9String[Rule9Flag],
435                            RapidDissString[RapidDissFlag], EyeTemp, CloudTemp, SceneTypeString,
436                            RadiusMaxWindString, MWScoreString, LatLonComboString,
437                            AutoPosStringAbbr[AutoCenteringValue], SatelliteIDString, SatVZA,
438                            CommentString);
439                } else {
440                    HistoryFileListing += String.format("%9s %06d  %3.1f %6.1f  %+5.1f  %5.1f  "
441                            + "%3.1f %3.1f %3.1f  %8s %3s  %3s  "
442                            + "%6.2f %6.2f  %6s %6s %5s %15s  %5s %7s %4.1f %s\n", DateString,
443                            TimeValue, CI, CIPressureValue + CIPresAdj, CIPresAdj, CIWindValue,
444                            FinalT, RawTAdj, RawTOrig, Rule8String[Rule8Flag],
445                            Rule9String[Rule9Flag], RapidDissString[RapidDissFlag], EyeTemp,
446                            CloudTemp, SceneTypeString, RadiusMaxWindString, MWScoreString,
447                            LatLonComboString, AutoPosStringAbbr[AutoCenteringValue],
448                            SatelliteIDString, SatVZA, CommentString);
449                }
450            } else {
451                /* ATCF format listing */
452                int RawTFlag_ATCF = OutputFormatTypeID / 1000;
453                int[] ReturnValues = Functions.adt_yddmy(DateValue);
454                int MonthValue = ReturnValues[0];
455                int DayValue = ReturnValues[1];
456                int YearValue = ReturnValues[2];
457                String TimeString_ATCF = String.format("%04d", TimeValue / 100);
458                String DateString_ATCF = String.format("%4d%02d%02d%4s", YearValue, MonthValue,
459                        DayValue, TimeString_ATCF);
460                String NSString_ATCF = (Latitude > 0.0) ? String.format("%s", "N") : String.format(
461                        "%s", "S");
462                String EWString_ATCF = (Longitude > 0.0) ? String.format("%s", "W") : String
463                        .format("%s", "E");
464                String LatitudeString = String.format("%4d%1s", (int) (Math.abs(Latitude) * 100),
465                        NSString_ATCF);
466                String LongitudeString = String.format("%5d%1s", (int) (Math.abs(Longitude) * 100),
467                        EWString_ATCF);
468
469                int StormIDNum = Integer.parseInt(ATCFStormIDString.substring(0, 2));
470                char aChar = ATCFStormIDString.charAt(2);
471                String StormIDNumString = String.format("%02d", StormIDNum);
472
473                String BasinIDString_ATCF = "";
474                if (aChar == 'L') {
475                    BasinIDString_ATCF = String.format("%s", "AL");
476                } else if (aChar == 'E') {
477                    BasinIDString_ATCF = String.format("%s", "EP");
478                } else if (aChar == 'C') {
479                    BasinIDString_ATCF = String.format("%s", "CP");
480                } else if (aChar == 'W') {
481                    BasinIDString_ATCF = String.format("%s", "WP");
482                } else if (aChar == 'S') {
483                    BasinIDString_ATCF = String.format("%s", "SH");
484                } else if (aChar == 'P') {
485                    BasinIDString_ATCF = String.format("%s", "SH");
486                } else if (aChar == 'U') {
487                    BasinIDString_ATCF = String.format("%s", "SH");
488                } else if (aChar == 'R') {
489                    BasinIDString_ATCF = String.format("%s", "SH");
490                } else if (aChar == 'F') {
491                    BasinIDString_ATCF = String.format("%s", "SH");
492                } else if (aChar == 'B') {
493                    BasinIDString_ATCF = String.format("%s", "IO");
494                } else if (aChar == 'A') {
495                    BasinIDString_ATCF = String.format("%s", "IO");
496                } else {
497                    BasinIDString_ATCF = String.format("%s", "XX");
498                    StormIDNumString = "XX";
499                }
500
501                int MaxWindSpeed_ATCF = (int) (CIWindValue);
502                int Pressure_ATCF = (int) (CIPressureValue + CIPresAdj);
503                int CI_ATCF = (int) ((CI + 0.01) * 10);
504                int RawTnoValue_ATCF = 0;
505                if (RawTFlag_ATCF == 0) {
506                    RawTnoValue_ATCF = (int) ((RawTAdj + 0.01) * 10); /*
507                                                                       * adjusted
508                                                                       * Raw T#
509                                                                       * -
510                                                                       * default
511                                                                       */
512                } else {
513                    RawTnoValue_ATCF = (int) ((RawTOrig + 0.01) * 10); /*
514                                                                        * unadjusted
515                                                                        * Raw T#
516                                                                        */
517                }
518                int FinalTnoValue_ATCF = (int) ((FinalT + 0.01) * 10);
519                String TnoAveTimeFlagString_ATCF = String.format("%s", "L");
520                int TnoAveTimeFlag_ATCF = 3; /* Final T# - default */
521                int EyeTempValue = (int) (EyeTemp);
522                int CloudTempValue = (int) (CloudTemp);
523                SceneTypeString = "";
524                int CIConfidence = 0;
525                int WindSpeedConfidence = 0;
526                int PressureConfidence = 0;
527                int PositionConfidence = 0;
528                int Rule89Value = 0;
529
530                if (ListLandRecordTF) {
531                    SceneTypeString = String.format("%s", "LAND");
532                    CIConfidence = 3;
533                    WindSpeedConfidence = 3;
534                    PressureConfidence = 3;
535                    PositionConfidence = 2;
536                    Rule89Value = 0;
537                } else {
538                    Rule89Value = 0;
539                    if ((Rule8Flag % 10) > 0) {
540                        Rule89Value = 1;
541                    }
542                    if (Rule9Flag == 1) {
543                        Rule89Value = 2;
544                    }
545                    if (((Rule8Flag % 10) > 0) && (Rule9Flag == 1)) {
546                        Rule89Value = 3;
547                    }
548                    if ((CloudScene == 3) || (CloudScene == 4)) {
549                        SceneTypeString = String.format("%s", CloudSceneString_ATCF[CloudScene]);
550                        CIConfidence = 2;
551                        WindSpeedConfidence = 2;
552                        PressureConfidence = 2;
553                        PositionConfidence = 3;
554                    } else if (EyeScene < 3) {
555                        SceneTypeString = String.format("%s", " EYE");
556                        CIConfidence = 1;
557                        WindSpeedConfidence = 1;
558                        PressureConfidence = 1;
559                        PositionConfidence = 1;
560                    } else {
561                        SceneTypeString = String.format("%s", CloudSceneString_ATCF[CloudScene]);
562                        CIConfidence = 2;
563                        WindSpeedConfidence = 2;
564                        PressureConfidence = 2;
565                        PositionConfidence = 2;
566                    }
567                }
568                String SatelliteIDString = Functions.adt_sattypes(SatelliteIDValue);
569
570                String SiteID_ATCF = String.format("%s", ATCFFileSourceIDString);
571
572                /* determine ATCF center/intensity ID */
573                String CenterFixMethodID_ATCF = "";
574                if (AutoCenteringValue >= 4) {
575                    /* center fix by autofix method */
576                    CenterFixMethodID_ATCF = String.format("%s", "CI");
577                } else {
578                    /* center fix by forecast interpolation */
579                    if (ListLandRecordTF) {
580                        /* storm is over land */
581                        CenterFixMethodID_ATCF = String.format("%s", " N");
582                    } else {
583                        /* storm is over ocean */
584                        CenterFixMethodID_ATCF = String.format("%s", " I");
585                    }
586                }
587                String AutoCenterMethodString_ATCF = "";
588                if (AutoCenteringValue == 0) {
589                    AutoCenterMethodString_ATCF = String.format("%s", "MAN");
590                } else {
591                    AutoCenterMethodString_ATCF = String.format("%s", "AUT");
592                }
593                HistoryFileListing += String.format("%2s, %2s, %12s, %3d, %4s, %10s, %1s, "
594                        + "%5s, %6s, %5s, %1d, %3d, %1d, %4d, %1d, %4s, "
595                        + "%3s, %4s, %4s, %4s, %4s, %4s, %1s, %1s, %1s, %1s, "
596                        + "%1s, %3s, %3s, %1s, " + "%5s, %3s, %4s, %2d, %1d, %2d, "
597                        + "%3d, %1s, %2d, %4d, %4d, %4s, %2s, " + "%6s, %1s, %s\n",
598                        BasinIDString_ATCF, StormIDNumString, DateString_ATCF, 20, "DVTO",
599                        CenterFixMethodID_ATCF, " ", LatitudeString, LongitudeString, " ",
600                        PositionConfidence, MaxWindSpeed_ATCF, WindSpeedConfidence, Pressure_ATCF,
601                        PressureConfidence, "DVRK", " ", " ", " ", " ", " ", " ", " ", " ", " ",
602                        " ", " ", " ", " ", " ", SiteID_ATCF, AutoCenterMethodString_ATCF, "   I",
603                        CI_ATCF, CIConfidence, FinalTnoValue_ATCF, TnoAveTimeFlag_ATCF,
604                        TnoAveTimeFlagString_ATCF, RawTnoValue_ATCF, EyeTempValue, CloudTempValue,
605                        SceneTypeString, Rule89_ATCF[Rule89Value], SatelliteIDString, "T",
606                        CommentString);
607            }
608
609            XInc++;
610        }
611        return HistoryFileListing;
612    }
613
614    /**
615     * Insert or overwrite a record in a history file.
616     *
617     * Global structure HistoryRecordPtr will be modified and the ASCII history
618     * file will be rewritten in another routine.
619     *
620     * @param RunFullAnalysis
621     *            Full analysis toggle.
622     * @param HistoryFileName
623     *            File to modify.
624     *
625     * @return Array of two integers. The first value represents the number of
626     *         inserted records. The second value is flag that describes the
627     *         {@literal "type"} of modification (possible values are 1, 2, 3,
628     *         4).
629     *         <ol>
630     *         <li>Overwritten</li>
631     *         <li>Inserted</li>
632     *         <li>Record placed at start of new history structure.</li>
633     *         <li>Record placed at end of existing history structure.</li>
634     *         </ol>
635     */
636    // TODO(jon): document what the flags mean
637    public static int[] InsertHistoryRecord(boolean RunFullAnalysis, String HistoryFileName) {
638
639        int ModifiedCount = 0;
640        int InsertOverwriteFlag = 0;
641        boolean FoundRecordTF = false;
642        IRHistoryRecord TemporaryIRCurrentRecord = IRCurrentRecord;
643
644        boolean LandFlagTF = Env.LandFlagTF;
645
646        int NumRecsHistory = HistoryNumberOfRecords();
647
648        int ImageDate = IRCurrentRecord.date;
649        int ImageTime = IRCurrentRecord.time;
650        double CurrentTime = Functions.calctime(ImageDate, ImageTime);
651
652        int XInc = 0;
653        while (XInc < NumRecsHistory) {
654            int RecDate = HistoryFile[XInc].date;
655            int RecTime = HistoryFile[XInc].time;
656            double HistoryRecTime = Functions.calctime(RecDate, RecTime);
657            if ((HistoryRecTime == CurrentTime) && (!FoundRecordTF)) {
658                /* OVERWRITE RECORD */
659                logger.debug("OVERWRITE RECORD {}", XInc);
660                HistoryFile[XInc] = IRCurrentRecord;
661                FoundRecordTF = true;
662                InsertOverwriteFlag = 1;
663            } else if ((HistoryRecTime > CurrentTime) && !FoundRecordTF) {
664                /* INSERT RECORD */
665                logger.debug("INSERT RECORD");
666                /* shift records after HistoryRecTime up one record */
667                NumRecsHistory++;
668                HistoryFileRecords++;
669                int YInc = HistoryFileRecords;
670                while (YInc > XInc) {
671                    HistoryFile[YInc] = HistoryFile[YInc - 1];
672                    YInc--;
673                }
674                HistoryFile[XInc] = IRCurrentRecord;
675                /*
676                 * System.out.printf("IRcurrentrecord.land=%d\n",IRCurrentRecord.
677                 * land);
678                 */
679                FoundRecordTF = true;
680                InsertOverwriteFlag = 2;
681            } else {
682                if (FoundRecordTF) {
683                    logger.debug("RECOMPUTING RECORD {}", XInc);
684                    /*
685                     * previously found records to insert, so all records
686                     * following the inserted record must be recalculated
687                     */
688                    /*
689                     * assign XInc history file record as "current record" and
690                     * recalculate intensity
691                     */
692                    IRCurrentRecord = HistoryFile[XInc];
693                    int RecLand = HistoryFile[XInc].land;
694                    double RecTnoRaw = HistoryFile[XInc].Traw;
695                    if ((LandFlagTF && (RecLand == 1)) || (RecTnoRaw < 1.0)) {
696                        /* assign as "missing record" */
697                        InitCurrent(false);
698                    } else {
699                        /* recompute intensity */
700                        Intensity.CalculateIntensity(1, RunFullAnalysis, HistoryFileName);
701                    }
702                    ModifiedCount++;
703                }
704                /* nothing yet... keep searching */
705            }
706            XInc++;
707        }
708        if (!FoundRecordTF) {
709            if (XInc == 0) {
710                /* record will be placed at start of new history structure */
711                logger.debug("PLACE RECORD AT START OF NEW");
712                HistoryFile[0] = IRCurrentRecord;
713                InsertOverwriteFlag = 3;
714            } else {
715                /* record will be placed at end of history structure */
716                logger.debug("PLACE RECORD AT END OF EXISTING");
717                HistoryFile[NumRecsHistory] = IRCurrentRecord;
718                InsertOverwriteFlag = 4;
719            }
720            HistoryFileRecords++;
721        } else {
722            IRCurrentRecord = TemporaryIRCurrentRecord;
723        }
724
725        return new int[] { ModifiedCount, InsertOverwriteFlag };
726    }
727
728    /**
729     * Delete record(s) in a history file.
730     *
731     * <p>
732     * Routine will modify structure HistoryRecordPtr, which will then be
733     * rewritten to ASCII history file in another subroutine.
734     * </p>
735     *
736     * @param RunFullAnalysis
737     *            Full analysis toggle.
738     * @param HistoryFileName
739     *            File to modify.
740     *
741     * @return Array containing two integer values. The first value is the
742     *         number of modified records, and the seconds value is the number
743     *         of deleted records.
744     */
745    public static int[] DeleteHistoryRecords(boolean RunFullAnalysis, String HistoryFileName) {
746        int YInc = 0;
747        int ModifiedCount = 0;
748        int DeleteRecordCount = 0;
749        boolean FoundRecordTF = false;
750        IRHistoryRecord TemporaryIRCurrentRecord = IRCurrentRecord;
751        boolean LandFlagTF = Env.LandFlagTF;
752
753        int NumRecsHistory = HistoryNumberOfRecords();
754
755        int XInc = 0;
756        int DateStart = Env.StartJulianDate;
757        int DateEnd = Env.EndJulianDate;
758        int DateStartTime = Env.StartHHMMSSTime;
759        int DateEndTime = Env.EndHHMMSSTime;
760        double AnalysisStartTime = Functions.calctime(DateStart, DateStartTime);
761        double AnalysisEndTime = Functions.calctime(DateEnd, DateEndTime);
762
763        while (XInc < NumRecsHistory) {
764            int RecDate = HistoryFile[XInc].date;
765            int RecTime = HistoryFile[XInc].time;
766            double HistoryRecTime = Functions.calctime(RecDate, RecTime);
767
768            if ((HistoryRecTime >= AnalysisStartTime) && (HistoryRecTime <= AnalysisEndTime)) {
769                /* record falls within time boundaries to delete */
770                HistoryFileRecords--;
771                NumRecsHistory--;
772                DeleteRecordCount++;
773                YInc = XInc;
774                while (YInc < NumRecsHistory) {
775                    HistoryFile[YInc] = HistoryFile[YInc + 1];
776                    YInc++;
777                }
778                FoundRecordTF = true;
779                XInc--;
780            } else {
781                /* record not within time boundaries */
782                if (FoundRecordTF) {
783                    /*
784                     * previously found records to delete, so all records
785                     * following the last deleted record must be recalculated
786                     */
787                    IRCurrentRecord = HistoryFile[XInc];
788                    int RecLand = HistoryFile[XInc].land;
789                    double RecTnoRaw = HistoryFile[XInc].Traw;
790                    if (((LandFlagTF) && (RecLand == 1)) || (RecTnoRaw < 1.0)) {
791                        /* assign as "missing record" */
792                        InitCurrent(false);
793                    } else {
794                        /* recompute intensity */
795                        Intensity.CalculateIntensity(1, RunFullAnalysis, HistoryFileName);
796                    }
797                    ModifiedCount++;
798                }
799            }
800            XInc++;
801        }
802
803        if (FoundRecordTF) {
804            IRCurrentRecord = TemporaryIRCurrentRecord;
805        }
806
807        return new int[] { ModifiedCount, DeleteRecordCount };
808    }
809
810    /**
811     * Insert comment in history file at specifified date/time.
812     *
813     * @param CommentString
814     *            Character string field to be inserted.
815     *
816     * @return If {@code -1}, there was an error finding record, if {@code >= 0}
817     *         , number of modified records.
818     */
819    public static int CommentHistoryRecords(String CommentString) {
820
821        int RecordCount = 0; /* record counter */
822        boolean FoundRecordTF = false; /* found recurd logical */
823
824        int NumRecsHistory = HistoryNumberOfRecords();
825
826        int XInc = 0;
827        int DateStart = Env.StartJulianDate;
828        int DateEnd = Env.EndJulianDate;
829        int DateStartTime = Env.StartHHMMSSTime;
830        int DateEndTime = Env.EndHHMMSSTime;
831        double AnalysisStartTime = Functions.calctime(DateStart, DateStartTime);
832        double AnalysisEndTime = Functions.calctime(DateEnd, DateEndTime);
833
834        while (XInc < NumRecsHistory) {
835            int RecDate = HistoryFile[XInc].date;
836            int RecTime = HistoryFile[XInc].time;
837            double HistoryRecTime = Functions.calctime(RecDate, RecTime);
838
839            if ((HistoryRecTime >= AnalysisStartTime) && (HistoryRecTime <= AnalysisEndTime)) {
840                RecordCount++;
841                HistoryFile[XInc].comment = String.format("%s", CommentString);
842                FoundRecordTF = true;
843            }
844            XInc++;
845        }
846        if (!FoundRecordTF) {
847            RecordCount = -1;
848        }
849        return RecordCount;
850    }
851
852    /**
853     * Write HistoryRecordPtr structure to ASCII history file.
854     *
855     * @param HistoryFileName
856     *            File to write.
857     *
858     * @return If {@code >= 0}, the value represents the number of records
859     *         written. If -2, there was an error reading
860     *         {@code HistoryFileName}. If {@code -3}, there was an error
861     *         writing {@code HistoryFileName}. Finally, if {@code -5}, there
862     *         was an error closing {@code HistoryFileName}.
863     *
864     * @throws IOException
865     *             if there was a problem writing to {@code HistoryFileName}.
866     */
867    public static int WriteHistoryFile(String HistoryFileName) throws IOException {
868        String OutputString;
869        FileWriter FileWriteStream = new FileWriter(HistoryFileName, false);
870        BufferedWriter HistoryFilePtr = new BufferedWriter(FileWriteStream);
871
872        int RecordCount = 0; /* record counter */
873
874        int NumRecsHistory = HistoryNumberOfRecords();
875
876        int XInc = 0;
877        while (XInc < NumRecsHistory) {
878            int RecDate = HistoryFile[XInc].date;
879            int RecTime = HistoryFile[XInc].time;
880            double HistoryRecTime = Functions.calctime(RecDate, RecTime);
881            String DateString = Functions.adt_julian2cmonth(RecDate);
882
883            int MWRecDate = HistoryFile[XInc].mwdate;
884            String MWDateString = Functions.adt_julian2cmonth(MWRecDate);
885
886            double JulianDate = HistoryRecTime - (double) ((int) HistoryRecTime / 1000) * 1000.0;
887
888            OutputString = String.format("%9s %06d %8.4f %3.1f %3.1f %3.1f %3.1f %6.2f "
889                    + "%6.2f %6.2f %6.2f %6.2f %7.2f %5.1f %4.1f %4.1f "
890                    + "%3d %1d %1d %2d %2d %1d %02d %1d %1d %1d %2d "
891                    + "%2d %1d %2d %3d %2d %6.2f %5.1f %5.1f " + "%9s %06d %4d %4d %3d %s\n",
892                    DateString, HistoryFile[XInc].time, JulianDate, HistoryFile[XInc].TrawO,
893                    HistoryFile[XInc].Traw, HistoryFile[XInc].Tfinal, HistoryFile[XInc].CI,
894                    HistoryFile[XInc].eyet, HistoryFile[XInc].cloudt, HistoryFile[XInc].cloudt2,
895                    HistoryFile[XInc].cwcloudt, HistoryFile[XInc].latitude,
896                    HistoryFile[XInc].longitude, HistoryFile[XInc].eyecdosize,
897                    HistoryFile[XInc].eyestdv, HistoryFile[XInc].cloudsymave,
898                    HistoryFile[XInc].sattype, HistoryFile[XInc].eyescene,
899                    HistoryFile[XInc].cloudscene, HistoryFile[XInc].eyesceneold,
900                    HistoryFile[XInc].cloudsceneold, HistoryFile[XInc].rule9,
901                    HistoryFile[XInc].rule8, HistoryFile[XInc].LBflag, HistoryFile[XInc].rapiddiss,
902                    HistoryFile[XInc].land, HistoryFile[XInc].eyefft, HistoryFile[XInc].cloudfft,
903                    HistoryFile[XInc].ringcb, HistoryFile[XInc].ringcbval,
904                    HistoryFile[XInc].cwring, HistoryFile[XInc].autopos, HistoryFile[XInc].CIadjp,
905                    HistoryFile[XInc].rmw, Math.max(-99.5, HistoryFile[XInc].mwscore),
906                    MWDateString, HistoryFile[XInc].mwtime, HistoryFile[XInc].r34,
907                    HistoryFile[XInc].MSLPenv, HistoryFile[XInc].vza, HistoryFile[XInc].comment);
908
909            HistoryFilePtr.write(OutputString);
910            XInc++;
911            RecordCount++;
912        }
913        /* need to throw exception */
914        HistoryFilePtr.close();
915        return RecordCount;
916    }
917
918    public static void InitCurrent(boolean InitialFlagTF) {
919        if (InitialFlagTF) {
920            IRCurrentRecord.date = 1900001;
921            IRCurrentRecord.time = 0;
922            IRCurrentRecord.latitude = 999.5;
923            IRCurrentRecord.longitude = 999.5;
924            IRCurrentRecord.land = 0;
925            IRCurrentRecord.autopos = 0;
926            IRCurrentRecord.sattype = 0;
927        }
928        IRCurrentRecord.TrawO = 0.0;
929        IRCurrentRecord.Traw = 0.0;
930        IRCurrentRecord.Tfinal = 0.0;
931        IRCurrentRecord.CI = 0.0;
932        IRCurrentRecord.eyet = 99.5;
933        IRCurrentRecord.cloudt = 99.5;
934        IRCurrentRecord.cloudt2 = 99.5;
935        IRCurrentRecord.cwcloudt = 99.5;
936        IRCurrentRecord.eyecdosize = 0.0;
937        IRCurrentRecord.eyestdv = 0.0;
938        IRCurrentRecord.cloudsymave = 0.0;
939        IRCurrentRecord.eyescene = 0;
940        IRCurrentRecord.cloudscene = 0;
941        IRCurrentRecord.eyesceneold = -1;
942        IRCurrentRecord.cloudsceneold = -1;
943        IRCurrentRecord.rule9 = 0;
944        IRCurrentRecord.rule8 = 0;
945        IRCurrentRecord.LBflag = 0;
946        IRCurrentRecord.rapiddiss = 0;
947        IRCurrentRecord.eyefft = 0;
948        IRCurrentRecord.cloudfft = 0;
949        IRCurrentRecord.ringcb = 0;
950        IRCurrentRecord.ringcbval = 0;
951        IRCurrentRecord.cwring = 0;
952        IRCurrentRecord.CIadjp = 0.0;
953        IRCurrentRecord.rmw = -99.5;
954        IRCurrentRecord.mwscore = -99.5;
955        IRCurrentRecord.mwdate = 1900001;
956        IRCurrentRecord.mwtime = 0;
957        IRCurrentRecord.r34 = -99;
958        IRCurrentRecord.MSLPenv = -999;
959        IRCurrentRecord.vza = 0;
960        IRCurrentRecord.comment = "";
961    }
962}