<commit_before>using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Text;

public class TreePlanterScript : MonoBehaviour
{
    System.Random m_random = new System.Random();
    string m_simulationId = "none";
    int m_generations = 201; //Number of time steps to simulate
    int[,,] m_cellStatus; //Tree species for each cell in each generation [gen,x,z]
    bool[,] m_permanentDisturbanceMap; //Whether each cell is marked as permanently disturbed
    int[] m_speciesList = new int[6]; //Unity tree prototypes to include in the community
                                      //(-1 represents a gap with no tree)
    //Replacement Matrix.  The probability of replacement of a tree of one prototype
    //by another prototype if it is entirely surrounded by the other species.
    //Example [1,2] is the probability that species 2 will be replaced by species 1, if
    //species 2 is entirely surrounded by species 1.  Row one is all 0.0 because gaps don't replace plants.
    //Row  and column indices are 1+ the prototype number (because row 1 and column 1 are for gaps.)
    float[,] m_masterReplacementMatrix = new float[21,21]{
        {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.5f, 0.12f, 0.1f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f}};
    //This smaller replacement matrix is for convenience.  We only have to dig through
    //the master replacement matrix once (to generate this smaller matrix) when we
    //import simulation settings rather than everytime we need a replacement value.
    //The first row will always be zero's because a gap never replaces a plant (that
    //only happens when the plant dies due to age or environment). But by having that
    //row of zero's the index numbers can correspond to the species numbers.
    float[,] m_replacementMatrix = new float[6,6];
    //Store the human-readable plant names so we can display them later
    string[] m_prototypeNames = new string[20] {"Alder", "Aspen", "Starthistle", "Juniper",
                                       "Serviceberry", "Sagebrush", "Sumac", "Obsolete1",
                                       "Obsolete2", "Obsolete3", "Obsolete4", "Obsolete5",
                                       "Wildrose", "Fern", "Maple", "Elderberry",
                                       "Obsolete6", "Pine", "Cottonwood", "Willow"};
    //Adjust the default sizes of the plants
    float[] m_prototypeScales = new float[20] {1.2f, 1.0f, 3.0f, 80.0f,
                                               5.0f, 7.0f, 6.0f, 7.0f,
                                               5.0f, 2.0f, 5.0f, 2.0f,
                                               7.0f, 3.0f, 0.5f, 0.3f,
                                               75.0f, 1.0f, 1.2f, 1.5f};
    //Maximum age for each prototype
    int[] m_lifespans = new int[20] {25, 25, 25, 25,
                                     25, 25, 25, 25,
                                     25, 25, 25, 25,
                                     25, 25, 25, 25,
                                     25, 25, 25, 25};
    //Optimal values and shape parameters for each prototype
    float[] m_altitudeOptimums = new float[20] {35f, 35f, 35f, 35f,
                                                35f, 35f, 35f, 35f,
                                                35f, 35f, 35f, 35f,
                                                35f, 35f, 35f, 35f,
                                                35f, 35f, 35f, 35f};
    float[] m_altitudeEffects = new float[20] {0.0f, 0.0f, 0.0f, 0.0f,
                                               0.0f, 0.0f, 0.0f, 0.0f,
                                               0.0f, 0.0f, 0.0f, 0.0f,
                                               0.0f, 0.0f, 0.0f, 0.0f,
                                               0.0f, 0.0f, 0.0f, 0.0f};
    float[] m_waterLevelOptimums = new float[20] {0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.75f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,};
    float[] m_waterLevelEffects = new float[20] {0.1f, 0.1f, 0.1f, 0.1f,
                                                 0.1f, 0.1f, 0.1f, 0.1f,
                                                 0.1f, 0.1f, 0.1f, 0.1f,
                                                 0.1f, 0.1f, 0.1f, 0.1f,
                                                 0.1f, 0.1f, 0.1f, 0.1f};
    float[] m_lightLevelOptimums = new float[20] {0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,};
    float[] m_lightLevelEffects = new float[20] {0f, 0f, 0f, 0f,
                                                 0f, 0f, 0f, 0f,
                                                 0f, 0f, 0f, 0f,
                                                 0f, 0f, 0f, 0f,
                                                 0f, 0f, 0f, 0f};
    float[] m_temperatureLevelOptimums = new float[20] {0.5f, 0.5f, 0.5f, 0.5f,
                                                   0.5f, 0.5f, 0.5f, 0.5f,
                                                   0.5f, 0.5f, 0.5f, 0.5f,
                                                   0.5f, 0.5f, 0.5f, 0.5f,
                                                   0.5f, 0.5f, 0.5f, 0.5f,};
    float[] m_temperatureLevelEffects = new float[20] {0f, 0f, 0f, 0f,
                                                  0f, 0f, 0f, 0f,
                                                  0f, 0f, 0f, 0f,
                                                  0f, 0f, 0f, 0f,
                                                  0f, 0f, 0f, 0f};
    float m_ongoingDisturbanceRate = 0.0f;
    //int m_terrainMap = 0; //Which terrain map we are using
    //These levels range from 0-1.0.  Default to 0.5 (mid-range or normal)
    float m_waterLevel = 0.5f;
    float m_lightLevel = 0.5f;
    float m_temperatureLevel = 0.5f;
    //Number of x and z cells (horizontal plane is xz in Unity3D)
    int m_xCells = 150;
    int m_zCells = 150;
    int[,,] m_age; //Tracks the age of each plant in each generation.
    int[,] m_totalSpeciesCounts; //Total species counts for each generation.
    float[,] m_averageSpeciesAges; //Species average ages for each generation
    Vector3[,] m_cellPositions; //Keeps track of the region coordinates where each plant will be placed.
    int m_displayedGeneration = 0; //Which generation number is currently visualized
    string m_chosenGeneration = "0"; //The generation number the user selects from the GUI
    string m_chosenSimulationId = "";
    string m_countLogString = "";
    string m_ageLogString = "";
    bool m_showCountLogWindow = false; //Whether to display the window with species count log data
    bool m_showAgeLogWindow = false; //Whether to display the window with age log data
    Rect m_countLogWindow = new Rect(200, 5, 400, 400);
    Rect m_ageLogWindow = new Rect(210, 5, 400, 400);
    string m_parameterPath = "http://vpcsim.appspot.com"; //Base URL of parameter webapp
    string m_debugString = ""; //Debug errors to display on the HUD
    WWW m_www; //Stores xml data downloaded from the web
    bool m_showDebugWindow = false; //Whether to display the window with debug messages
    Rect m_debugWindow = new Rect(300, 10, 400, 400);
    string m_currentDataString = "";
    //Convert values from the webapp to numbers the simulation can use
    //Ongoing disturbance values - None, Very Low, Low, High, Very High
    float[] m_convertDisturbance = new float[5] {0f, 0.01f, 0.03f, 0.1f, 0.25f};
    //Environmental parameter values - Very Low, Low, Normal, High, Very High
    float[] m_convertWaterLevel = new float[5] {0f, 0.25f, 0.5f, 0.75f, 1.0f};
    float[] m_convertLightLevel = new float[5] {0f, 0.25f, 0.5f, 0.75f, 1.0f};
    float[] m_convertTemperatureLevel = new float[5] {0f, 0.25f, 0.5f, 0.75f, 1.0f};


    #region Unity3D specific functions

    void Start()
    {
        m_debugString += "Start\n"; //Debug
        //There shouldn't be trees in the scene at startup, but just in case...
        DeleteAllTrees();
    }

    void Update()
    {
        //Called once per frame
    }

    void OnApplicationQuit()
    {
        DeleteAllTrees();
    }

    void OnGUI()
    {
        //Generate the GUI controls and HUD
        GUI.Box(new Rect(5, 10, 155, 330), "Simulation");
        //Create an unused button off-screen so we can move focus out of the TextFields
        GUI.SetNextControlName("focusBuster");
        bool focusBusterButton = GUI.Button(new Rect(-10, -10, 1, 1),
                                            new GUIContent("",
                                            ""));
        //Buttons to run a simulation
        bool defaultsButton = GUI.Button(new Rect(10, 35, 55, 20),
                                         new GUIContent("Default",
                                         "Load the default ecosystem"));
        bool createButton = GUI.Button(new Rect(68, 35, 40, 20),
                                       new GUIContent("New",
                                       "Create a new ecosystem"));
        bool showParametersButton = GUI.Button(new Rect(111, 35, 44, 20),
                                               new GUIContent("Show",
                                               "Show ecosystem parameters"));
        m_chosenSimulationId = GUI.TextField(new Rect(10, 60, 90, 20),
                                             m_chosenSimulationId, 10);
        bool loadButton = GUI.Button(new Rect(105, 60, 50, 20),
                                     new GUIContent("Load",
                                     "Load new ecosystem parameters"));
        //Buttons to move between simulation steps
        bool firstButton = GUI.Button(new Rect (10, 95, 22, 20),
                                      new GUIContent("[<",
                                      "First time step"));
        bool reverse10Button = GUI.Button(new Rect (34, 95, 25, 20),
                                          new GUIContent("<<",
                                          "Skip backward 10 time steps"));
        bool reverseButton = GUI.Button(new Rect (61, 95, 20, 20),
                                        new GUIContent("<",
                                        "Previous time step"));
        bool forwardButton = GUI.Button(new Rect (83, 95, 20, 20),
                                        new GUIContent(">",
                                        "Next time step"));
        bool forward10Button = GUI.Button(new Rect (105, 95, 25, 20),
                                          new GUIContent(">>",
                                          "Skip forward 10 time steps"));
        bool lastButton = GUI.Button(new Rect (132, 95, 22, 20),
                                     new GUIContent(">]",
                                     "Last time step"));
        GUI.Label(new Rect(10, 120, 35, 20), "Step:");
        m_chosenGeneration = GUI.TextField(new Rect(42, 120, 40, 20),
                                           m_chosenGeneration, 4);
        GUI.Label(new Rect(83, 120, 35, 20), "/ " + (m_generations - 1).ToString());
        bool goButton = GUI.Button(new Rect(120, 120, 35, 20),
                                   new GUIContent("Go",
                                   "View the selected time step"));
        GUI.Label(new Rect(10, 145, 135, 100), m_currentDataString);
        //Buttons to display log data or plots
        GUI.Label(new Rect(10, 250, 35, 20), "Plots:");
        bool countPlotButton = GUI.Button(new Rect(10, 270, 43, 20),
                                     new GUIContent("Count",
                                     "Show species count plots"));
        bool agePlotButton = GUI.Button(new Rect(55, 270, 35, 20),
                                     new GUIContent("Age",
                                     "Show average age plots"));
        bool biomassPlotButton = GUI.Button(new Rect(92, 270, 63, 20),
                                     new GUIContent("Biomass",
                                     "Show biomass plots"));
        GUI.Label(new Rect(10, 290, 35, 20), "Logs:");
        bool countLogButton = GUI.Button(new Rect(10, 310, 43, 20),
                                    new GUIContent("Count",
                                    "Show species count log data"));
        bool ageLogButton = GUI.Button(new Rect(55, 310, 35, 20),
                                     new GUIContent("Age",
                                     "Show average age log data"));
        bool biomassLogButton = GUI.Button(new Rect(92, 310, 63, 20),
                                     new GUIContent("Biomass",
                                     "Show biomass log data"));
        //Button to display debug messages - TODO: Remove?  This is not for students.
        bool debugButton = GUI.Button(new Rect(10, 490, 60, 20),
                                      new GUIContent("Debug",
                                      "Show/Hide debug messages"));
        if (!System.String.IsNullOrEmpty(GUI.tooltip))
        {
            GUI.Box(new Rect(165 , 35, 220, 20),"");
        }
        GUI.Label(new Rect(170, 35, 210, 20), GUI.tooltip);
        if (m_showCountLogWindow)
        {
            //Setup species count log data window
            m_countLogWindow = GUI.Window(0, m_countLogWindow, DisplayCountLogWindow, "Species Counts");
        }
        if (m_showAgeLogWindow)
        {
            //Setup age log data window
            m_countLogWindow = GUI.Window(0, m_ageLogWindow, DisplayAgeLogWindow, "Average Ages");
        }
        if (countLogButton)
        {
            //Show or hide the count log data window
            m_showCountLogWindow = !m_showCountLogWindow;
            m_chosenGeneration = m_displayedGeneration.ToString();
            GUI.FocusControl("focusBuster");
        }
        if (ageLogButton)
        {
            //Show or hide the age log data window
            m_showAgeLogWindow = !m_showAgeLogWindow;
            m_chosenGeneration = m_displayedGeneration.ToString();
            GUI.FocusControl("focusBuster");
        }
        if (m_showDebugWindow)
        {
            //Setup the debug message window
            m_debugWindow = GUI.Window(0, m_debugWindow, DisplayDebugWindow, "Debug");
        }
        if (debugButton)
        {
            //Show or hide the debug message window
            m_showDebugWindow = !m_showDebugWindow;
            GUI.FocusControl("focusBuster");
        }
        if (defaultsButton)
        {
            //Run a simulation using the default settings
            DeleteAllTrees();
            GenerateRandomCommunity();
            RunSimulation();
            m_countLogString = GetCountLogData(false);
            m_ageLogString = GetAgeLogData(false);
            VisualizeGeneration(0);
            m_chosenGeneration = m_displayedGeneration.ToString();
            m_chosenSimulationId = m_simulationId;
            GUI.FocusControl("focusBuster");
        }
        if (createButton)
        {
            //Open the parameters webapp in a new browser window or tab
            Application.ExternalCall("window.open('http://vpcsim.appspot.com','_blank')");
        }
        if (showParametersButton)
        {
            //Open the parameters webapp to show the current parameters in a new browser window or tab
            Application.ExternalCall("window.open('http://vpcsim.appspot.com/show?id=" + m_chosenSimulationId + "','_blank')");
        }
        if (loadButton)
        {
            //Run a simulation using the parameters for the simulation id entered in the textbox
            bool isValidId = false;
            try
            {
                //Make sure the new simulation id can be converted to an integer (it is
                //supposed to be a datecode.)
                int newSimulationId = System.Int32.Parse(m_chosenSimulationId);
                isValidId = true;
                m_debugString += "Valid ID\n";
            }
            catch
            {
                isValidId = false;
                m_debugString += "Not Valid ID\n";
            }
            if (isValidId)
            {
                //Retrieve simulation parameters from the web
                StartCoroutine(GetNewSimulationParameters(m_chosenSimulationId));
            }
            else
            {
                //TODO - Error message: Invalid simulation ID
            }
            GUI.FocusControl("focusBuster");
        }
        if (firstButton)
        {
            //Visualize first simulation step
            if (m_displayedGeneration != 0)
            {
                VisualizeGeneration(0);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (reverse10Button)
        {
            //Move the visualization back 10 simulation steps
            int newGeneration = m_displayedGeneration - 10;
            if (newGeneration < 0)
            {
                newGeneration = 0;
            }
            if (newGeneration != m_displayedGeneration)
            {
                VisualizeGeneration(newGeneration);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (reverseButton)
        {
            //Visualize the previous simulation step
            int newGeneration = m_displayedGeneration - 1;
            if (newGeneration >= 0)
            {
                VisualizeGeneration(newGeneration);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (forwardButton)
        {
            //Visualize the next simulation step
            int newGeneration = m_displayedGeneration + 1;
            if (newGeneration <= m_generations - 1)
            {
                VisualizeGeneration(newGeneration);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (forward10Button)
        {
            //Move the visualization forward 10 steps
            int newGeneration = m_displayedGeneration + 10;
            if (newGeneration > m_generations - 1)
            {
                newGeneration = m_generations - 1;
            }
            if (newGeneration != m_displayedGeneration)
            {
                VisualizeGeneration(newGeneration);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (lastButton)
        {
            //Visualize the last simulation step
            if (m_displayedGeneration != m_generations - 1)
            {
                VisualizeGeneration(m_generations - 1);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (goButton)
        {
            //Visualize the simulation step entered in the text box
            int newGeneration;
            try
            {
                newGeneration = System.Int32.Parse(m_chosenGeneration);
            }
            catch
            {
                newGeneration = m_displayedGeneration;
            }
            if (newGeneration < 0)
            {
                newGeneration = 0;
            }
            if (newGeneration > m_generations - 1)
            {
                newGeneration = m_generations - 1;
            }
            if (newGeneration != m_displayedGeneration)
            {
                VisualizeGeneration(newGeneration);
            }
            m_chosenGeneration = m_displayedGeneration.ToString();
            GUI.FocusControl("focusBuster");
        }
        if (countPlotButton)
        {
            //Display plots of the species count data
            if (m_simulationId != "none")
            {
                DisplayCountPlot();
            }
            GUI.FocusControl("focusBuster");
        }
        if (agePlotButton)
        {
            //Display plots of the average age data
            if (m_simulationId != "none")
            {
                DisplayAgePlot();
            }
            GUI.FocusControl("focusBuster");
        }
    }

    void DisplayCountPlot()
    {
        //Send species count logdata to the surrounding web page where it will be redirected to the webapp
        //which will generate plots in a new browser window or tab.
        //TODO - generate plots of other data types (age, biomass, etc)
        string logData = GetCountLogData(true);
        Application.ExternalCall("OpenCountsPlotPage", logData);
    }

    void DisplayAgePlot()
    {
        //Send average age logdata to the surrounding web page where it will be redirected to the webapp
        //which will generate plots in a new browser window or tab.
        //TODO - generate plots of other data types (biomass)
        string logData = GetAgeLogData(true);
        Application.ExternalCall("OpenAgePlotPage", logData);
    }

    void DisplayCountLogWindow(int windowID)
    {
        if (GUI.Button(new Rect(330,370,50,20), "Close"))
        {
            m_showCountLogWindow = !m_showCountLogWindow;
        }
        if (m_simulationId != "none")
        {
            GUI.TextArea(new Rect(5, 20, 390, 350), m_countLogString);
        }
        else
        {
            GUI.TextArea(new Rect(5, 20, 390, 350), "No simulation loaded");
        }
        GUI.DragWindow();
    }

    void DisplayAgeLogWindow(int windowID)
    {
        if (GUI.Button(new Rect(330,370,50,20), "Close"))
        {
            m_showAgeLogWindow = !m_showAgeLogWindow;
        }
        if (m_simulationId != "none")
        {
            GUI.TextArea(new Rect(5, 20, 390, 350), m_ageLogString);
        }
        else
        {
            GUI.TextArea(new Rect(5, 20, 390, 350), "No simulation loaded");
        }
        GUI.DragWindow();
    }

    void DisplayDebugWindow(int windowID)
    {
        GUI.TextArea(new Rect(5, 20, 390, 350), m_debugString);
        GUI.DragWindow();
    }

    string GetCurrentCounts()
    {
        //Generates a string to display the current species counts formatted for the HUD
        StringBuilder currentDataBuilder = new StringBuilder();
        string currentData;
        currentDataBuilder.Append("Gaps: ");
        currentDataBuilder.Append(m_totalSpeciesCounts[m_displayedGeneration, 0].ToString() + "\n");
        for (int i=1; i<6; i++)
        {
            currentDataBuilder.Append(m_prototypeNames[m_speciesList[i]] + ": ");
            currentDataBuilder.Append(m_totalSpeciesCounts[m_displayedGeneration, i].ToString() + "\n");
        }
        currentData = currentDataBuilder.ToString();
        return currentData;
    }

    string GetCountLogData(bool isForPlotting)
    {
        //Generates a string of log data suitable for either displaying to humans or
        //for sending out for plotting
        StringBuilder logDataBuilder = new StringBuilder();
        string logData;
        if (isForPlotting)
        {
            //Generate string for sending out for plotting
            logDataBuilder.Append("\"time step,Gaps,");
            for (int i=1; i<6; i++)
            {
                logDataBuilder.Append(m_prototypeNames[m_speciesList[i]]);
                if (i != 5)
                {
                    logDataBuilder.Append(",");
                }
            }
            logDataBuilder.Append("\\\n\" + ");
            for(int generation=0; generation<m_generations; generation++)
            {
                logDataBuilder.Append("\"");
                logDataBuilder.Append(generation.ToString() + ',');
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 0].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 1].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 2].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 3].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 4].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 5].ToString() + "\\\n\"");
                if (generation != m_generations - 1)
                {
                    logDataBuilder.Append(" + ");
                }
            }
        }
        else
        {
            //Generate string for displaying to humans
            logDataBuilder.Append("Time_step, Gaps, ");
            for (int i=1; i<6; i++)
            {
                logDataBuilder.Append(m_prototypeNames[m_speciesList[i]]);
                if (i != 5)
                {
                    logDataBuilder.Append(", ");
                }
            }
            logDataBuilder.Append("\n");
            for(int generation=0; generation<m_generations; generation++)
            {
                logDataBuilder.Append(generation.ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 0].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 1].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 2].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 3].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 4].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 5].ToString());
                if (generation != m_generations - 1)
                {
                    logDataBuilder.Append("\n");
                }
            }
        }
        logData = logDataBuilder.ToString();
        print(logData);
        return logData;
    }

    string GetAgeLogData(bool isForPlotting)
    {
        //Generates a string of log data suitable for either displaying to humans or
        //for sending out for plotting
        StringBuilder logDataBuilder = new StringBuilder();
        string logData;
        if (isForPlotting)
        {
            //Generate string for sending out for plotting
            logDataBuilder.Append("\"time step,");
            for (int i=1; i<6; i++)
            {
                logDataBuilder.Append(m_prototypeNames[m_speciesList[i]]);
                if (i != 5)
                {
                    logDataBuilder.Append(",");
                }
            }
            logDataBuilder.Append("\\\n\" + ");
            for(int generation=0; generation<m_generations; generation++)
            {
                logDataBuilder.Append("\"");
                logDataBuilder.Append(generation.ToString() + ',');
                logDataBuilder.Append(m_averageSpeciesAges[generation, 1].ToString() + ",");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 2].ToString() + ",");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 3].ToString() + ",");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 4].ToString() + ",");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 5].ToString() + "\\\n\"");
                if (generation != m_generations - 1)
                {
                    logDataBuilder.Append(" + ");
                }
            }
        }
        else
        {
            //Generate string for displaying to humans
            logDataBuilder.Append("Time_step, ");
            for (int i=1; i<6; i++)
            {
                logDataBuilder.Append(m_prototypeNames[m_speciesList[i]]);
                if (i != 5)
                {
                    logDataBuilder.Append(", ");
                }
            }
            logDataBuilder.Append("\n");
            for(int generation=0; generation<m_generations; generation++)
            {
                logDataBuilder.Append(generation.ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 1].ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 2].ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 3].ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 4].ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 5].ToString());
                if (generation != m_generations - 1)
                {
                    logDataBuilder.Append("\n");
                }
            }
        }
        logData = logDataBuilder.ToString();
        print(logData);
        return logData;
    }


    #endregion

    #region Visualization functions

    void VisualizeGeneration(int generation)
    {
        //Update the visualization with plants from a particular generation.
        //Remove old trees
        Terrain.activeTerrain.terrainData.treeInstances = new TreeInstance[0];
        //Add new trees
        for (int z=0; z<m_zCells; z++)
        {
            for (int x=0; x<m_xCells; x++)
            {
                int newTreeSpecies = m_cellStatus[generation, x, z];
                Vector3 newTreeLocation = m_cellPositions[x, z];
                AddTree(newTreeLocation, newTreeSpecies, m_age[generation, x, z]);
            }
        }
        Terrain.activeTerrain.Flush();
        m_displayedGeneration = generation;
        m_currentDataString = GetCurrentCounts();
    }

    void AddTree(Vector3 position, int treeSpecies, int age)
    {
        //Add a tree to the terrain sized according to its age
        if (treeSpecies != 0) // 0 would be a gap (no tree)
        {
            int treePrototype = m_speciesList[treeSpecies];
            TreeInstance tree = new TreeInstance();
            tree.position = position;
            tree.prototypeIndex = treePrototype;
            //Vary the height and width of individual trees according to age
            float scale = m_prototypeScales[treePrototype] * ((age / (float)m_lifespans[treePrototype]) + 0.5f);
            tree.widthScale = scale;
            tree.heightScale = scale;
            tree.color = Color.white;
            tree.lightmapColor = Color.white;
            Terrain.activeTerrain.AddTreeInstance(tree);
        }
    }

    void DeleteAllTrees()
    {
        //Remove all trees from the terrain
        Terrain.activeTerrain.terrainData.treeInstances = new TreeInstance[0];
        Terrain.activeTerrain.Flush();
    }

    #endregion

    #region Simulation functions

    IEnumerator GetNewSimulationParameters(string fileName)
    {
        //Get simulation parameters from the parameters webapp
        string url = System.IO.Path.Combine(m_parameterPath,"data?id=" + fileName);
        m_www = new WWW(url);
        yield return m_www;
        if (m_www.isDone)
        {
            m_debugString += "WWW read isDone\n";
            bool unpackSuccess = UnpackParameters();
            if (unpackSuccess)
            {
                m_debugString += "Unpacked success\n";
                DeleteAllTrees();
                RunSimulation();
                m_debugString += "RunSimulation success\n";
                m_countLogString = GetCountLogData(false);
                m_ageLogString = GetAgeLogData(false);
                VisualizeGeneration(0);
                m_debugString += "VisualizeGeneration success\n";
                m_chosenGeneration = m_displayedGeneration.ToString();
                m_chosenSimulationId = m_simulationId;
            }
            else
            {
                m_debugString += "Unpack fail\n";
            }
        }
        else
        {
            m_debugString += "WWW not isDone\n";
        }
    }

    bool UnpackParameters()
    {
        //Unpack the parameter data into appropriate variables
        Dictionary<string, string> newParameters = new Dictionary<string, string>();
        XmlTextReader reader;
        try
        {
            reader = new XmlTextReader(new System.IO.StringReader(m_www.text));
            m_debugString += "opened stream\n";
            reader.WhitespaceHandling = WhitespaceHandling.Significant;
        }
        catch
        {
            return false;
        }
        while (reader.Read())
        {
            if (reader.Name == "property")
            {
                string parameterName = reader.GetAttribute("name");
                string parameterValue = reader.ReadString();
                newParameters.Add(parameterName, parameterValue);
            }
        }
        m_simulationId = newParameters["id"];
        //m_terrainMap = System.Int32.Parse(newParameters["terrain"]);
        int waterLevelCode = System.Int32.Parse(newParameters["water_level"]);
        m_waterLevel = m_convertWaterLevel[waterLevelCode];
        int lightLevelCode = System.Int32.Parse(newParameters["light_level"]);<commit_msg>We are not going to give the option of changing the terrain.  The choice has already been removed from the webapp and it had never been implemented here so I am just removing some commented code.<commit_after>using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Text;

public class TreePlanterScript : MonoBehaviour
{
    System.Random m_random = new System.Random();
    string m_simulationId = "none";
    int m_generations = 201; //Number of time steps to simulate
    int[,,] m_cellStatus; //Tree species for each cell in each generation [gen,x,z]
    bool[,] m_permanentDisturbanceMap; //Whether each cell is marked as permanently disturbed
    int[] m_speciesList = new int[6]; //Unity tree prototypes to include in the community
                                      //(-1 represents a gap with no tree)
    //Replacement Matrix.  The probability of replacement of a tree of one prototype
    //by another prototype if it is entirely surrounded by the other species.
    //Example [1,2] is the probability that species 2 will be replaced by species 1, if
    //species 2 is entirely surrounded by species 1.  Row one is all 0.0 because gaps don't replace plants.
    //Row  and column indices are 1+ the prototype number (because row 1 and column 1 are for gaps.)
    float[,] m_masterReplacementMatrix = new float[21,21]{
        {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.5f, 0.12f, 0.1f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f, 0.12f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f},
        {0.4f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f}};
    //This smaller replacement matrix is for convenience.  We only have to dig through
    //the master replacement matrix once (to generate this smaller matrix) when we
    //import simulation settings rather than everytime we need a replacement value.
    //The first row will always be zero's because a gap never replaces a plant (that
    //only happens when the plant dies due to age or environment). But by having that
    //row of zero's the index numbers can correspond to the species numbers.
    float[,] m_replacementMatrix = new float[6,6];
    //Store the human-readable plant names so we can display them later
    string[] m_prototypeNames = new string[20] {"Alder", "Aspen", "Starthistle", "Juniper",
                                       "Serviceberry", "Sagebrush", "Sumac", "Obsolete1",
                                       "Obsolete2", "Obsolete3", "Obsolete4", "Obsolete5",
                                       "Wildrose", "Fern", "Maple", "Elderberry",
                                       "Obsolete6", "Pine", "Cottonwood", "Willow"};
    //Adjust the default sizes of the plants
    float[] m_prototypeScales = new float[20] {1.2f, 1.0f, 3.0f, 80.0f,
                                               5.0f, 7.0f, 6.0f, 7.0f,
                                               5.0f, 2.0f, 5.0f, 2.0f,
                                               7.0f, 3.0f, 0.5f, 0.3f,
                                               75.0f, 1.0f, 1.2f, 1.5f};
    //Maximum age for each prototype
    int[] m_lifespans = new int[20] {25, 25, 25, 25,
                                     25, 25, 25, 25,
                                     25, 25, 25, 25,
                                     25, 25, 25, 25,
                                     25, 25, 25, 25};
    //Optimal values and shape parameters for each prototype
    float[] m_altitudeOptimums = new float[20] {35f, 35f, 35f, 35f,
                                                35f, 35f, 35f, 35f,
                                                35f, 35f, 35f, 35f,
                                                35f, 35f, 35f, 35f,
                                                35f, 35f, 35f, 35f};
    float[] m_altitudeEffects = new float[20] {0.0f, 0.0f, 0.0f, 0.0f,
                                               0.0f, 0.0f, 0.0f, 0.0f,
                                               0.0f, 0.0f, 0.0f, 0.0f,
                                               0.0f, 0.0f, 0.0f, 0.0f,
                                               0.0f, 0.0f, 0.0f, 0.0f};
    float[] m_waterLevelOptimums = new float[20] {0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.75f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,};
    float[] m_waterLevelEffects = new float[20] {0.1f, 0.1f, 0.1f, 0.1f,
                                                 0.1f, 0.1f, 0.1f, 0.1f,
                                                 0.1f, 0.1f, 0.1f, 0.1f,
                                                 0.1f, 0.1f, 0.1f, 0.1f,
                                                 0.1f, 0.1f, 0.1f, 0.1f};
    float[] m_lightLevelOptimums = new float[20] {0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,
                                                  0.5f, 0.5f, 0.5f, 0.5f,};
    float[] m_lightLevelEffects = new float[20] {0f, 0f, 0f, 0f,
                                                 0f, 0f, 0f, 0f,
                                                 0f, 0f, 0f, 0f,
                                                 0f, 0f, 0f, 0f,
                                                 0f, 0f, 0f, 0f};
    float[] m_temperatureLevelOptimums = new float[20] {0.5f, 0.5f, 0.5f, 0.5f,
                                                   0.5f, 0.5f, 0.5f, 0.5f,
                                                   0.5f, 0.5f, 0.5f, 0.5f,
                                                   0.5f, 0.5f, 0.5f, 0.5f,
                                                   0.5f, 0.5f, 0.5f, 0.5f,};
    float[] m_temperatureLevelEffects = new float[20] {0f, 0f, 0f, 0f,
                                                  0f, 0f, 0f, 0f,
                                                  0f, 0f, 0f, 0f,
                                                  0f, 0f, 0f, 0f,
                                                  0f, 0f, 0f, 0f};
    float m_ongoingDisturbanceRate = 0.0f;
    //These levels range from 0-1.0.  Default to 0.5 (mid-range or normal)
    float m_waterLevel = 0.5f;
    float m_lightLevel = 0.5f;
    float m_temperatureLevel = 0.5f;
    //Number of x and z cells (horizontal plane is xz in Unity3D)
    int m_xCells = 150;
    int m_zCells = 150;
    int[,,] m_age; //Tracks the age of each plant in each generation.
    int[,] m_totalSpeciesCounts; //Total species counts for each generation.
    float[,] m_averageSpeciesAges; //Species average ages for each generation
    Vector3[,] m_cellPositions; //Keeps track of the region coordinates where each plant will be placed.
    int m_displayedGeneration = 0; //Which generation number is currently visualized
    string m_chosenGeneration = "0"; //The generation number the user selects from the GUI
    string m_chosenSimulationId = "";
    string m_countLogString = "";
    string m_ageLogString = "";
    bool m_showCountLogWindow = false; //Whether to display the window with species count log data
    bool m_showAgeLogWindow = false; //Whether to display the window with age log data
    Rect m_countLogWindow = new Rect(200, 5, 400, 400);
    Rect m_ageLogWindow = new Rect(210, 5, 400, 400);
    string m_parameterPath = "http://vpcsim.app<EMAIL>"; //Base URL of parameter webapp
    string m_debugString = ""; //Debug errors to display on the HUD
    WWW m_www; //Stores xml data downloaded from the web
    bool m_showDebugWindow = false; //Whether to display the window with debug messages
    Rect m_debugWindow = new Rect(300, 10, 400, 400);
    string m_currentDataString = "";
    //Convert values from the webapp to numbers the simulation can use
    //Ongoing disturbance values - None, Very Low, Low, High, Very High
    float[] m_convertDisturbance = new float[5] {0f, 0.01f, 0.03f, 0.1f, 0.25f};
    //Environmental parameter values - Very Low, Low, Normal, High, Very High
    float[] m_convertWaterLevel = new float[5] {0f, 0.25f, 0.5f, 0.75f, 1.0f};
    float[] m_convertLightLevel = new float[5] {0f, 0.25f, 0.5f, 0.75f, 1.0f};
    float[] m_convertTemperatureLevel = new float[5] {0f, 0.25f, 0.5f, 0.75f, 1.0f};


    #region Unity3D specific functions

    void Start()
    {
        m_debugString += "Start\n"; //Debug
        //There shouldn't be trees in the scene at startup, but just in case...
        DeleteAllTrees();
    }

    void Update()
    {
        //Called once per frame
    }

    void OnApplicationQuit()
    {
        DeleteAllTrees();
    }

    void OnGUI()
    {
        //Generate the GUI controls and HUD
        GUI.Box(new Rect(5, 10, 155, 330), "Simulation");
        //Create an unused button off-screen so we can move focus out of the TextFields
        GUI.SetNextControlName("focusBuster");
        bool focusBusterButton = GUI.Button(new Rect(-10, -10, 1, 1),
                                            new GUIContent("",
                                            ""));
        //Buttons to run a simulation
        bool defaultsButton = GUI.Button(new Rect(10, 35, 55, 20),
                                         new GUIContent("Default",
                                         "Load the default ecosystem"));
        bool createButton = GUI.Button(new Rect(68, 35, 40, 20),
                                       new GUIContent("New",
                                       "Create a new ecosystem"));
        bool showParametersButton = GUI.Button(new Rect(111, 35, 44, 20),
                                               new GUIContent("Show",
                                               "Show ecosystem parameters"));
        m_chosenSimulationId = GUI.TextField(new Rect(10, 60, 90, 20),
                                             m_chosenSimulationId, 10);
        bool loadButton = GUI.Button(new Rect(105, 60, 50, 20),
                                     new GUIContent("Load",
                                     "Load new ecosystem parameters"));
        //Buttons to move between simulation steps
        bool firstButton = GUI.Button(new Rect (10, 95, 22, 20),
                                      new GUIContent("[<",
                                      "First time step"));
        bool reverse10Button = GUI.Button(new Rect (34, 95, 25, 20),
                                          new GUIContent("<<",
                                          "Skip backward 10 time steps"));
        bool reverseButton = GUI.Button(new Rect (61, 95, 20, 20),
                                        new GUIContent("<",
                                        "Previous time step"));
        bool forwardButton = GUI.Button(new Rect (83, 95, 20, 20),
                                        new GUIContent(">",
                                        "Next time step"));
        bool forward10Button = GUI.Button(new Rect (105, 95, 25, 20),
                                          new GUIContent(">>",
                                          "Skip forward 10 time steps"));
        bool lastButton = GUI.Button(new Rect (132, 95, 22, 20),
                                     new GUIContent(">]",
                                     "Last time step"));
        GUI.Label(new Rect(10, 120, 35, 20), "Step:");
        m_chosenGeneration = GUI.TextField(new Rect(42, 120, 40, 20),
                                           m_chosenGeneration, 4);
        GUI.Label(new Rect(83, 120, 35, 20), "/ " + (m_generations - 1).ToString());
        bool goButton = GUI.Button(new Rect(120, 120, 35, 20),
                                   new GUIContent("Go",
                                   "View the selected time step"));
        GUI.Label(new Rect(10, 145, 135, 100), m_currentDataString);
        //Buttons to display log data or plots
        GUI.Label(new Rect(10, 250, 35, 20), "Plots:");
        bool countPlotButton = GUI.Button(new Rect(10, 270, 43, 20),
                                     new GUIContent("Count",
                                     "Show species count plots"));
        bool agePlotButton = GUI.Button(new Rect(55, 270, 35, 20),
                                     new GUIContent("Age",
                                     "Show average age plots"));
        bool biomassPlotButton = GUI.Button(new Rect(92, 270, 63, 20),
                                     new GUIContent("Biomass",
                                     "Show biomass plots"));
        GUI.Label(new Rect(10, 290, 35, 20), "Logs:");
        bool countLogButton = GUI.Button(new Rect(10, 310, 43, 20),
                                    new GUIContent("Count",
                                    "Show species count log data"));
        bool ageLogButton = GUI.Button(new Rect(55, 310, 35, 20),
                                     new GUIContent("Age",
                                     "Show average age log data"));
        bool biomassLogButton = GUI.Button(new Rect(92, 310, 63, 20),
                                     new GUIContent("Biomass",
                                     "Show biomass log data"));
        //Button to display debug messages - TODO: Remove?  This is not for students.
        bool debugButton = GUI.Button(new Rect(10, 490, 60, 20),
                                      new GUIContent("Debug",
                                      "Show/Hide debug messages"));
        if (!System.String.IsNullOrEmpty(GUI.tooltip))
        {
            GUI.Box(new Rect(165 , 35, 220, 20),"");
        }
        GUI.Label(new Rect(170, 35, 210, 20), GUI.tooltip);
        if (m_showCountLogWindow)
        {
            //Setup species count log data window
            m_countLogWindow = GUI.Window(0, m_countLogWindow, DisplayCountLogWindow, "Species Counts");
        }
        if (m_showAgeLogWindow)
        {
            //Setup age log data window
            m_countLogWindow = GUI.Window(0, m_ageLogWindow, DisplayAgeLogWindow, "Average Ages");
        }
        if (countLogButton)
        {
            //Show or hide the count log data window
            m_showCountLogWindow = !m_showCountLogWindow;
            m_chosenGeneration = m_displayedGeneration.ToString();
            GUI.FocusControl("focusBuster");
        }
        if (ageLogButton)
        {
            //Show or hide the age log data window
            m_showAgeLogWindow = !m_showAgeLogWindow;
            m_chosenGeneration = m_displayedGeneration.ToString();
            GUI.FocusControl("focusBuster");
        }
        if (m_showDebugWindow)
        {
            //Setup the debug message window
            m_debugWindow = GUI.Window(0, m_debugWindow, DisplayDebugWindow, "Debug");
        }
        if (debugButton)
        {
            //Show or hide the debug message window
            m_showDebugWindow = !m_showDebugWindow;
            GUI.FocusControl("focusBuster");
        }
        if (defaultsButton)
        {
            //Run a simulation using the default settings
            DeleteAllTrees();
            GenerateRandomCommunity();
            RunSimulation();
            m_countLogString = GetCountLogData(false);
            m_ageLogString = GetAgeLogData(false);
            VisualizeGeneration(0);
            m_chosenGeneration = m_displayedGeneration.ToString();
            m_chosenSimulationId = m_simulationId;
            GUI.FocusControl("focusBuster");
        }
        if (createButton)
        {
            //Open the parameters webapp in a new browser window or tab
            Application.ExternalCall("window.open('http://vpcsim.appspot.com','_blank')");
        }
        if (showParametersButton)
        {
            //Open the parameters webapp to show the current parameters in a new browser window or tab
            Application.ExternalCall("window.open('http://vpcsim.appspot.com/show?id=" + m_chosenSimulationId + "','_blank')");
        }
        if (loadButton)
        {
            //Run a simulation using the parameters for the simulation id entered in the textbox
            bool isValidId = false;
            try
            {
                //Make sure the new simulation id can be converted to an integer (it is
                //supposed to be a datecode.)
                int newSimulationId = System.Int32.Parse(m_chosenSimulationId);
                isValidId = true;
                m_debugString += "Valid ID\n";
            }
            catch
            {
                isValidId = false;
                m_debugString += "Not Valid ID\n";
            }
            if (isValidId)
            {
                //Retrieve simulation parameters from the web
                StartCoroutine(GetNewSimulationParameters(m_chosenSimulationId));
            }
            else
            {
                //TODO - Error message: Invalid simulation ID
            }
            GUI.FocusControl("focusBuster");
        }
        if (firstButton)
        {
            //Visualize first simulation step
            if (m_displayedGeneration != 0)
            {
                VisualizeGeneration(0);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (reverse10Button)
        {
            //Move the visualization back 10 simulation steps
            int newGeneration = m_displayedGeneration - 10;
            if (newGeneration < 0)
            {
                newGeneration = 0;
            }
            if (newGeneration != m_displayedGeneration)
            {
                VisualizeGeneration(newGeneration);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (reverseButton)
        {
            //Visualize the previous simulation step
            int newGeneration = m_displayedGeneration - 1;
            if (newGeneration >= 0)
            {
                VisualizeGeneration(newGeneration);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (forwardButton)
        {
            //Visualize the next simulation step
            int newGeneration = m_displayedGeneration + 1;
            if (newGeneration <= m_generations - 1)
            {
                VisualizeGeneration(newGeneration);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (forward10Button)
        {
            //Move the visualization forward 10 steps
            int newGeneration = m_displayedGeneration + 10;
            if (newGeneration > m_generations - 1)
            {
                newGeneration = m_generations - 1;
            }
            if (newGeneration != m_displayedGeneration)
            {
                VisualizeGeneration(newGeneration);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (lastButton)
        {
            //Visualize the last simulation step
            if (m_displayedGeneration != m_generations - 1)
            {
                VisualizeGeneration(m_generations - 1);
                m_chosenGeneration = m_displayedGeneration.ToString();
            }
            GUI.FocusControl("focusBuster");
        }
        if (goButton)
        {
            //Visualize the simulation step entered in the text box
            int newGeneration;
            try
            {
                newGeneration = System.Int32.Parse(m_chosenGeneration);
            }
            catch
            {
                newGeneration = m_displayedGeneration;
            }
            if (newGeneration < 0)
            {
                newGeneration = 0;
            }
            if (newGeneration > m_generations - 1)
            {
                newGeneration = m_generations - 1;
            }
            if (newGeneration != m_displayedGeneration)
            {
                VisualizeGeneration(newGeneration);
            }
            m_chosenGeneration = m_displayedGeneration.ToString();
            GUI.FocusControl("focusBuster");
        }
        if (countPlotButton)
        {
            //Display plots of the species count data
            if (m_simulationId != "none")
            {
                DisplayCountPlot();
            }
            GUI.FocusControl("focusBuster");
        }
        if (agePlotButton)
        {
            //Display plots of the average age data
            if (m_simulationId != "none")
            {
                DisplayAgePlot();
            }
            GUI.FocusControl("focusBuster");
        }
    }

    void DisplayCountPlot()
    {
        //Send species count logdata to the surrounding web page where it will be redirected to the webapp
        //which will generate plots in a new browser window or tab.
        //TODO - generate plots of other data types (age, biomass, etc)
        string logData = GetCountLogData(true);
        Application.ExternalCall("OpenCountsPlotPage", logData);
    }

    void DisplayAgePlot()
    {
        //Send average age logdata to the surrounding web page where it will be redirected to the webapp
        //which will generate plots in a new browser window or tab.
        //TODO - generate plots of other data types (biomass)
        string logData = GetAgeLogData(true);
        Application.ExternalCall("OpenAgePlotPage", logData);
    }

    void DisplayCountLogWindow(int windowID)
    {
        if (GUI.Button(new Rect(330,370,50,20), "Close"))
        {
            m_showCountLogWindow = !m_showCountLogWindow;
        }
        if (m_simulationId != "none")
        {
            GUI.TextArea(new Rect(5, 20, 390, 350), m_countLogString);
        }
        else
        {
            GUI.TextArea(new Rect(5, 20, 390, 350), "No simulation loaded");
        }
        GUI.DragWindow();
    }

    void DisplayAgeLogWindow(int windowID)
    {
        if (GUI.Button(new Rect(330,370,50,20), "Close"))
        {
            m_showAgeLogWindow = !m_showAgeLogWindow;
        }
        if (m_simulationId != "none")
        {
            GUI.TextArea(new Rect(5, 20, 390, 350), m_ageLogString);
        }
        else
        {
            GUI.TextArea(new Rect(5, 20, 390, 350), "No simulation loaded");
        }
        GUI.DragWindow();
    }

    void DisplayDebugWindow(int windowID)
    {
        GUI.TextArea(new Rect(5, 20, 390, 350), m_debugString);
        GUI.DragWindow();
    }

    string GetCurrentCounts()
    {
        //Generates a string to display the current species counts formatted for the HUD
        StringBuilder currentDataBuilder = new StringBuilder();
        string currentData;
        currentDataBuilder.Append("Gaps: ");
        currentDataBuilder.Append(m_totalSpeciesCounts[m_displayedGeneration, 0].ToString() + "\n");
        for (int i=1; i<6; i++)
        {
            currentDataBuilder.Append(m_prototypeNames[m_speciesList[i]] + ": ");
            currentDataBuilder.Append(m_totalSpeciesCounts[m_displayedGeneration, i].ToString() + "\n");
        }
        currentData = currentDataBuilder.ToString();
        return currentData;
    }

    string GetCountLogData(bool isForPlotting)
    {
        //Generates a string of log data suitable for either displaying to humans or
        //for sending out for plotting
        StringBuilder logDataBuilder = new StringBuilder();
        string logData;
        if (isForPlotting)
        {
            //Generate string for sending out for plotting
            logDataBuilder.Append("\"time step,Gaps,");
            for (int i=1; i<6; i++)
            {
                logDataBuilder.Append(m_prototypeNames[m_speciesList[i]]);
                if (i != 5)
                {
                    logDataBuilder.Append(",");
                }
            }
            logDataBuilder.Append("\\\n\" + ");
            for(int generation=0; generation<m_generations; generation++)
            {
                logDataBuilder.Append("\"");
                logDataBuilder.Append(generation.ToString() + ',');
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 0].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 1].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 2].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 3].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 4].ToString() + ",");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 5].ToString() + "\\\n\"");
                if (generation != m_generations - 1)
                {
                    logDataBuilder.Append(" + ");
                }
            }
        }
        else
        {
            //Generate string for displaying to humans
            logDataBuilder.Append("Time_step, Gaps, ");
            for (int i=1; i<6; i++)
            {
                logDataBuilder.Append(m_prototypeNames[m_speciesList[i]]);
                if (i != 5)
                {
                    logDataBuilder.Append(", ");
                }
            }
            logDataBuilder.Append("\n");
            for(int generation=0; generation<m_generations; generation++)
            {
                logDataBuilder.Append(generation.ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 0].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 1].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 2].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 3].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 4].ToString() + ", ");
                logDataBuilder.Append(m_totalSpeciesCounts[generation, 5].ToString());
                if (generation != m_generations - 1)
                {
                    logDataBuilder.Append("\n");
                }
            }
        }
        logData = logDataBuilder.ToString();
        print(logData);
        return logData;
    }

    string GetAgeLogData(bool isForPlotting)
    {
        //Generates a string of log data suitable for either displaying to humans or
        //for sending out for plotting
        StringBuilder logDataBuilder = new StringBuilder();
        string logData;
        if (isForPlotting)
        {
            //Generate string for sending out for plotting
            logDataBuilder.Append("\"time step,");
            for (int i=1; i<6; i++)
            {
                logDataBuilder.Append(m_prototypeNames[m_speciesList[i]]);
                if (i != 5)
                {
                    logDataBuilder.Append(",");
                }
            }
            logDataBuilder.Append("\\\n\" + ");
            for(int generation=0; generation<m_generations; generation++)
            {
                logDataBuilder.Append("\"");
                logDataBuilder.Append(generation.ToString() + ',');
                logDataBuilder.Append(m_averageSpeciesAges[generation, 1].ToString() + ",");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 2].ToString() + ",");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 3].ToString() + ",");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 4].ToString() + ",");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 5].ToString() + "\\\n\"");
                if (generation != m_generations - 1)
                {
                    logDataBuilder.Append(" + ");
                }
            }
        }
        else
        {
            //Generate string for displaying to humans
            logDataBuilder.Append("Time_step, ");
            for (int i=1; i<6; i++)
            {
                logDataBuilder.Append(m_prototypeNames[m_speciesList[i]]);
                if (i != 5)
                {
                    logDataBuilder.Append(", ");
                }
            }
            logDataBuilder.Append("\n");
            for(int generation=0; generation<m_generations; generation++)
            {
                logDataBuilder.Append(generation.ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 1].ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 2].ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 3].ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 4].ToString() + ", ");
                logDataBuilder.Append(m_averageSpeciesAges[generation, 5].ToString());
                if (generation != m_generations - 1)
                {
                    logDataBuilder.Append("\n");
                }
            }
        }
        logData = logDataBuilder.ToString();
        print(logData);
        return logData;
    }


    #endregion

    #region Visualization functions

    void VisualizeGeneration(int generation)
    {
        //Update the visualization with plants from a particular generation.
        //Remove old trees
        Terrain.activeTerrain.terrainData.treeInstances = new TreeInstance[0];
        //Add new trees
        for (int z=0; z<m_zCells; z++)
        {
            for (int x=0; x<m_xCells; x++)
            {
                int newTreeSpecies = m_cellStatus[generation, x, z];
                Vector3 newTreeLocation = m_cellPositions[x, z];
                AddTree(newTreeLocation, newTreeSpecies, m_age[generation, x, z]);
            }
        }
        Terrain.activeTerrain.Flush();
        m_displayedGeneration = generation;
        m_currentDataString = GetCurrentCounts();
    }

    void AddTree(Vector3 position, int treeSpecies, int age)
    {
        //Add a tree to the terrain sized according to its age
        if (treeSpecies != 0) // 0 would be a gap (no tree)
        {
            int treePrototype = m_speciesList[treeSpecies];
            TreeInstance tree = new TreeInstance();
            tree.position = position;
            tree.prototypeIndex = treePrototype;
            //Vary the height and width of individual trees according to age
            float scale = m_prototypeScales[treePrototype] * ((age / (float)m_lifespans[treePrototype]) + 0.5f);
            tree.widthScale = scale;
            tree.heightScale = scale;
            tree.color = Color.white;
            tree.lightmapColor = Color.white;
            Terrain.activeTerrain.AddTreeInstance(tree);
        }
    }

    void DeleteAllTrees()
    {
        //Remove all trees from the terrain
        Terrain.activeTerrain.terrainData.treeInstances = new TreeInstance[0];
        Terrain.activeTerrain.Flush();
    }

    #endregion

    #region Simulation functions

    IEnumerator GetNewSimulationParameters(string fileName)
    {
        //Get simulation parameters from the parameters webapp
        string url = System.IO.Path.Combine(m_parameterPath,"data?id=" + fileName);
        m_www = new WWW(url);
        yield return m_www;
        if (m_www.isDone)
        {
            m_debugString += "WWW read isDone\n";
            bool unpackSuccess = UnpackParameters();
            if (unpackSuccess)
            {
                m_debugString += "Unpacked success\n";
                DeleteAllTrees();
                RunSimulation();
                m_debugString += "RunSimulation success\n";
                m_countLogString = GetCountLogData(false);
                m_ageLogString = GetAgeLogData(false);
                VisualizeGeneration(0);
                m_debugString += "VisualizeGeneration success\n";
                m_chosenGeneration = m_displayedGeneration.ToString();
                m_chosenSimulationId = m_simulationId;
            }
            else
            {
                m_debugString += "Unpack fail\n";
            }
        }
        else
        {
            m_debugString += "WWW not isDone\n";
        }
    }

    bool UnpackParameters()
    {
        //Unpack the parameter data into appropriate variables
        Dictionary<string, string> newParameters = new Dictionary<string, string>();
        XmlTextReader reader;
        try
        {
            reader = new XmlTextReader(new System.IO.StringReader(m_www.text));
            m_debugString += "opened stream\n";
            reader.WhitespaceHandling = WhitespaceHandling.Significant;
        }
        catch
        {
            return false;
        }
        while (reader.Read())
        {
            if (reader.Name == "property")
            {
                string parameterName = reader.GetAttribute("name");
                string parameterValue = reader.ReadString();
                newParameters.Add(parameterName, parameterValue);
            }
        }
        m_simulationId = newParameters["id"];
        int waterLevelCode = System.Int32.Parse(newParameters["water_level"]);
        m_waterLevel = m_convertWaterLevel[waterLevelCode];
        int lightLevelCode = System.Int32.Parse(newParameters["light_level"]);
{'fraction_non_alphanumeric': 0.11601033906036187, 'fraction_numerical': 0.05110094406126031, 'mean_word_length': 1.504691016098321, 'pattern_counts': {'":': 2, '<': 44, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 10, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 300, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2821682', 'n_tokens_mistral': 24662, 'n_tokens_neox': 21720, 'n_words': 5431}