package usda.weru.mcrew;

import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

/**
 *
 * @author cademccumber
 * The purpose of this class is to provide the data structure for WEPP json files to be 
 * read using Google's GSON library. This functionality will allow for more information 
 * to be pulled from these files in the future if necessary. Currently, It will be configured
 * just to retrieve the information necessary to build a .skel WEPS XML file. All the 
 * Classes in this file are for GSON to generate Objects with the same data structure as
 * the JSON files. KEEP NO-ARGS CONSTRUCTORS - GSON needs them to write these objects
 */

public class RotationData {
    // All values inside a rotation object
    private Rotation rotation;
    private OperationData[] m_opData;
    
    public RotationData() { }
    
    protected Integer getDuration() {
        return this.rotation.duration;
    }
    
    /**
     * Path is used for name in skel files
     * @return 
     */
    protected String getName() { 
        if (this.rotation.managements == null)
            return null;
        return this.rotation.managements.get(0).name;
    }
    
    /**
     * @return a list of operation data objects with information for skel file
     * Current uses crop or residue as the <veg><name> value </name></veg> tag
    */ 
    protected OperationData[] getOperationDataArray() {
        // if already done, return 
        if (m_opData != null)
            return m_opData;
        try {
            // Gson writes managements as a list based on layout, hence hard coded '0' here.
            OperationData[] opData = new OperationData[this.rotation.managements.get(0).events.size()];
            List<Event> opList = this.rotation.managements.get(0).events; // list of events
            
            // This original String is for modifying dates
            int startingDate = Integer.parseInt(opList.get(0).date.substring(0, 4));

            for (int i = 0; i < opData.length; i++) {
                OperationData op = new OperationData();
                op.setDate(modifyDateString(startingDate, opList.get(i).date));
                op.setName(opList.get(i).operation.name);
                if (opList.get(i).operation.opGroup1 != null) {
                    op.setVeg_names(getVegName(opList.get(i)));
                }
                
                // set yield if present
                if (opList.get(i).crop != null) {
                    if (opList.get(i).crop.yield != null) {
                        // Only if begin_growth is true should we get the yeild.
                        op.setYield(opList.get(i).crop.yield);
                    }
                    
                }
                
                // res_added
                if (opList.get(i).residue != null) {
                    if (opList.get(i).residue.res_added != null) {
                        if (opList.get(i).residue.res_added.intValue() != 0) {
                            // assuming only set if change has occured
                            op.setRes_added(opList.get(i).residue.res_added.intValue());
                        }
                    }
                }
                
                opData[i] = op;
            }
            m_opData = opData;
            return opData;
        } catch (NumberFormatException format) {
            System.err.println("Failed to parse date year in \'getOperationData'Array\'");
            format.printStackTrace();
        } catch (NullPointerException nullP) {
            // Null pointers generated here come from incorrectly formatted json files in every instance thus far.
            // When thrown up, it will print an error from which file caused the error. File name cannot be accessed from here.
            throw new NullPointerException("From getOperationDataArray()");
        } catch (Exception e) {
            System.err.println("Failed to get list of OperationData "); 
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * This function modifies the date String to a 2 digit year format. It uses the 
     * original or starting date to determine the value of that 2-digit date string
     * @param originalDate
     * @param dateString
     * @return 
     */
    private String modifyDateString(int originalDate, String dateString) throws NumberFormatException {
        
        try {
            StringTokenizer thedate = new StringTokenizer(dateString, "-");             // dates are split by '-'
            String year = thedate.nextToken();                                          // dates enter in form yyyy-mm-dd
            String month = thedate.nextToken();
            String day = thedate.nextToken();

            int dateYear = Integer.parseInt(year);                                      // the number we do change
            dateYear -= originalDate;                                                   // get how many years have passed
            dateYear++;                                                                 // start years at 01

            String finishedYear = String.valueOf(dateYear).length() > 1 ? String.valueOf(dateYear) : "0" + String.valueOf(dateYear);

            // Checks to make sure follows 2 digit schema.
            if (finishedYear.length() > 2) {
                finishedYear = finishedYear.substring(finishedYear.length() - 2);       // only get the 2 digits
            }
            else if (finishedYear.length() < 1) {
                throw new NumberFormatException("Empty year String");
            }
                    // format mm/dd/yy - year starts at 0
            return (month + "/" + day + "/" + finishedYear);
        
        } catch (NoSuchElementException tokenError) {
            // tokenError.printStackTrace(); // for more information
            // error parsing date string
            throw new NumberFormatException("Error parsing date String in \'modifyDateString()\'");
        }
    }
    
    /**
     * The <veg><name> tag uses the 'name' tag if either the "crop" or "Residue" 
     * objects 'name' value. 
     * @return String[] with [0] being crop name and [1] being residue name
     * returns null
     */
    private String[] getVegName(Event event) {
        String[] vegNames = new String[2];
        // Check for crop name
        if (event.crop != null) {
            vegNames[0] = event.crop.name != null ? event.crop.name : null;
        }
        // Check for residue name
        if (event.residue != null) {
            vegNames[1] = event.residue.name != null ? event.residue.name : null;
        } 
        return vegNames;
    }
    
    /* PRIVATE CLASSES FOR STORAGE OF JSON DATA 
       Used by GSON to store json data
       only necessary getters are present above for the data needed currently
       if more data needed, create more getters
    */
    
    private class Rotation {
        private Integer duration;
        private List<Management> managements;
    }
    
    private class Management {
        private String path;                                                        
        private Double stir;
        private String id;                                                          
        private String name;
        private List<Event> events;
    }
    
    private class Event {
        private String date;
        private Operation operation;
        private Crop crop;
        private Residue residue;
        
    }
    
    /**
     * Used for <op> tags
     */
    private class Operation {
        private Integer defaultResidueAdded;
        private String name;
        @SerializedName("begin growth")
        private Boolean begin_growth;
        @SerializedName("kill crop")
        private Boolean kill_crop;
        @SerializedName("add residue")
        private Boolean add_residue;
        private String opGroup1;
        private String opGroup2;
        private String opGroup3;
        private String opGroup4;
        private String opGroup5;
        private Double stir;
        private String id;      
    }
     /**
      * Crop objects are not always present in every Event
      */
    private class Crop {
        private String yieldUnit;
        private String name;
        private String cropGroup1;
        private Number defaultYield;
        private Integer yield;
    }
    
    /**
     * Residue objects are not always present in every event
     */
    private class Residue {
        private String resGroup1;
        private Number res_added;
        private String name;
        private String id;
    }  
}


