public void ImportData(Boomlagoon.JSON.JSONObject json_data, string assetNameSuffix = "")
        {
            m_letters_to_animate            = json_data["m_letters_to_animate"].Array.JSONtoListInt();
            m_letters_to_animate_custom_idx = (int)json_data["m_letters_to_animate_custom_idx"].Number;
            m_letters_to_animate_option     = (LETTERS_TO_ANIMATE)(int)json_data["m_letters_to_animate_option"].Number;

            m_letter_actions = new List <LetterAction>();
            LetterAction letter_action;

            foreach (Boomlagoon.JSON.JSONValue action_data in json_data["ACTIONS_DATA"].Array)
            {
                letter_action = new LetterAction();
                letter_action.ImportData(action_data.Obj, assetNameSuffix);
                m_letter_actions.Add(letter_action);
            }

            m_loop_cycles = new List <ActionLoopCycle>();
            if (json_data.ContainsKey("LOOPS_DATA"))
            {
                ActionLoopCycle loop_cycle;

                foreach (Boomlagoon.JSON.JSONValue loop_data in json_data["LOOPS_DATA"].Array)
                {
                    loop_cycle = new ActionLoopCycle();
                    loop_cycle.ImportData(loop_data.Obj);

                    // Check for invalid loops
                    if (loop_cycle.m_start_action_idx < m_letter_actions.Count && loop_cycle.m_end_action_idx < m_letter_actions.Count)
                    {
                        m_loop_cycles.Add(loop_cycle);
                    }
                }
            }
        }
        public ActionLoopCycle Clone(int loop_index)
        {
            ActionLoopCycle action_loop = new ActionLoopCycle(m_start_action_idx, m_end_action_idx);

            action_loop.m_active_loop_index = loop_index;
            action_loop.m_number_of_loops   = m_number_of_loops;
            action_loop.m_loop_type         = m_loop_type;
            action_loop.m_delay_first_only  = m_delay_first_only;
            action_loop.m_finish_at_end     = m_finish_at_end;

            return(action_loop);
        }
		public ActionLoopCycle Clone(int loop_index)
		{

			ActionLoopCycle action_loop = new ActionLoopCycle(m_start_action_idx,m_end_action_idx);
			
			action_loop.m_active_loop_index = loop_index;
			action_loop.m_number_of_loops = m_number_of_loops;
			action_loop.m_loop_type = m_loop_type;
			action_loop.m_delay_first_only = m_delay_first_only;
			action_loop.m_finish_at_end = m_finish_at_end;
			
			return action_loop;
		}
        public void InsertLoop(int index, ActionLoopCycle loop, bool force_insert)
        {
            if (m_loop_cycles == null)
            {
                m_loop_cycles = new List <ActionLoopCycle>();
            }

            if (!force_insert)
            {
                // Check if loop already exists
                foreach (ActionLoopCycle loop_cycle in m_loop_cycles)
                {
                    if (loop_cycle.m_start_action_idx == loop.m_start_action_idx && loop_cycle.m_end_action_idx == loop.m_end_action_idx)
                    {
                        return;
                    }
                }
            }

            m_loop_cycles.Insert(index, loop);
        }
        public void ImportPresetSectionData(Boomlagoon.JSON.JSONObject json_data, LetterSetup[] letters, int action_insert_index, int loop_insert_index, ref int num_actions_added, ref int num_loops_added, string assetNameSuffix = "")
        {
            if (m_letter_actions == null)
            {
                m_letter_actions = new List <LetterAction>();
            }

            float timing_scale = -1;

            if (m_letters_to_animate == null || m_letters_to_animate.Count == 0)
            {
                CalculateLettersToAnimate(letters);
            }

            if (json_data.ContainsKey("SAMPLE_NUM_LETTERS_ANIMATED") && m_letters_to_animate != null && m_letters_to_animate.Count > 0)
            {
                timing_scale = m_letters_to_animate.Count / ((float)json_data["SAMPLE_NUM_LETTERS_ANIMATED"].Number);
            }


            LetterAction letter_action;

            num_actions_added = 0;
            foreach (Boomlagoon.JSON.JSONValue action_data in json_data["ACTIONS_DATA"].Array)
            {
                letter_action = new LetterAction();
                letter_action.ImportData(action_data.Obj, assetNameSuffix, timing_scale: timing_scale);

                if (num_actions_added == 0 && action_insert_index > 0)
                {
                    // Inserting new actions into the middle of the animation. Set first action to continue from last
                    letter_action.m_offset_from_last = true;
                }

                InsertAction(action_insert_index + num_actions_added, letter_action);

                num_actions_added++;
            }


            if (m_loop_cycles == null)
            {
                m_loop_cycles = new List <ActionLoopCycle>();
            }


            num_loops_added = 0;

            if (json_data.ContainsKey("LOOPS_DATA"))
            {
                ActionLoopCycle loop_cycle;

                foreach (Boomlagoon.JSON.JSONValue loop_data in json_data["LOOPS_DATA"].Array)
                {
                    loop_cycle = new ActionLoopCycle();
                    loop_cycle.ImportData(loop_data.Obj);
                    loop_cycle.m_start_action_idx += action_insert_index;
                    loop_cycle.m_end_action_idx   += action_insert_index;

                    // Check for invalid loops
                    if (loop_cycle.m_start_action_idx < m_letter_actions.Count && loop_cycle.m_end_action_idx < m_letter_actions.Count)
                    {
                        m_loop_cycles.Insert(loop_insert_index + num_loops_added, loop_cycle);

                        num_loops_added++;
                    }
                }
            }
        }
 public void InsertLoop(int index, ActionLoopCycle loop)
 {
     InsertLoop(index, loop, false);
 }
        public bool DrawGUISetting(TextFxAnimationManager animation_manager, float gui_x_offset, ref float gui_y_offset, bool gui_already_changed, int action_start_offset = 0, int loop_start_offset = 0)
        {
            LetterAnimation letterAnimation = animation_manager.GetAnimation(m_animation_idx);
            LetterAction    letterAction    = letterAnimation != null?letterAnimation.GetAction(m_action_idx + action_start_offset) : null;

            if (letterAction == null)
            {
                return(false);
            }


            if ((m_data_type == ANIMATION_DATA_TYPE.POSITION && m_startState && letterAction.m_start_pos.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.POSITION && !m_startState && letterAction.m_end_pos.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.LOCAL_ROTATION && m_startState && letterAction.m_start_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.LOCAL_ROTATION && !m_startState && letterAction.m_end_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_ROTATION && m_startState && letterAction.m_global_start_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_ROTATION && !m_startState && letterAction.m_global_end_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.LOCAL_SCALE && m_startState && letterAction.m_start_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.LOCAL_SCALE && !m_startState && letterAction.m_end_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_SCALE && m_startState && letterAction.m_global_start_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_SCALE && !m_startState && letterAction.m_global_end_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.COLOUR && m_startState && letterAction.m_start_colour.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.COLOUR && !m_startState && letterAction.m_end_colour.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)))
            {
                animation_manager.PrepareAnimationData(m_data_type);

                if (!animation_manager.Playing)
                {
                    // Set the current state of the animation to show effect of changes
                    animation_manager.SetAnimationState(m_action_idx,
                                                        m_startState ? 0 : 1,
                                                        update_mesh: true);
                }
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.DURATION && letterAction.m_duration_progression.DrawQuickEditorGUI("Lerp Duration", gui_x_offset, ref gui_y_offset, gui_already_changed))
            {
                animation_manager.PrepareAnimationData(m_data_type);

                animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.DELAY);
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.DELAY && letterAction.m_delay_progression.DrawQuickEditorGUI("Delay Easing (Seconds)", gui_x_offset, ref gui_y_offset, gui_already_changed))
            {
                animation_manager.PrepareAnimationData(m_data_type);
            }
//			else if(m_data_type == ANIMATION_DATA_TYPE.COLOUR_START_END && letterAction.m_start_colour.DrawQuickEditorGUI(this, ref gui_y_offset, gui_already_changed))
//			{
//				// Set end colour to be the same at start colour
//				if(letterAction.m_start_colour.Progression == (int) ValueProgression.Constant)
//					letterAction.m_end_colour.SetConstant(letterAction.m_start_colour.ValueFrom);
//				else if(letterAction.m_start_colour.Progression == (int) ValueProgression.Eased)
//				{
//					if(letterAction.m_start_colour.UsingThirdValue)
//						letterAction.m_end_colour.SetEased(letterAction.m_start_colour.ValueFrom, letterAction.m_start_colour.ValueTo, letterAction.m_start_colour.ValueThen);
//					else
//						letterAction.m_end_colour.SetEased(letterAction.m_start_colour.ValueFrom, letterAction.m_start_colour.ValueTo);
//				}
//				else if(letterAction.m_start_colour.Progression == (int) ValueProgression.EasedCustom)
//					letterAction.m_end_colour.SetEasedCustom(letterAction.m_start_colour.ValueFrom, letterAction.m_start_colour.ValueTo);
//				else if(letterAction.m_start_colour.Progression == (int) ValueProgression.Random)
//					letterAction.m_end_colour.SetRandom(letterAction.m_start_colour.ValueFrom, letterAction.m_start_colour.ValueTo);
//
//				animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.COLOUR_END);
//
//				animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.COLOUR_START);
//
//				if(!animation_manager.Playing)
//				{
//					// Set the current state of the animation to show effect of changes
//					animation_manager.SetAnimationState(m_action_idx, Mathf.Clamp(m_action_progress_state_override, 0f, 1f), update_mesh: true);
//				}
//			}
            else if (m_data_type == ANIMATION_DATA_TYPE.EASE_TYPE)
            {
                letterAction.m_ease_type = (EasingEquation)EditorGUI.EnumPopup(new Rect(gui_x_offset, gui_y_offset, 350, LINE_HEIGHT), m_setting_name, letterAction.m_ease_type);

                if (!gui_already_changed && GUI.changed)
                {
                    animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.ALL);
                }

                gui_y_offset += LINE_HEIGHT;
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.DELAY_EASED_RANDOM_SWITCH)
            {
                bool newSelection = EditorGUI.Toggle(new Rect(gui_x_offset, gui_y_offset, 250, LINE_HEIGHT), "Randomised?", letterAction.m_delay_progression.Progression == (int)ValueProgression.Random);

                if (!gui_already_changed && GUI.changed)
                {
                    if (newSelection)
                    {
                        letterAction.m_delay_progression.SetRandom(letterAction.m_delay_progression.ValueFrom, letterAction.m_delay_progression.ValueTo, letterAction.m_delay_progression.UniqueRandomRaw);
                    }
                    else
                    {
                        letterAction.m_delay_progression.SetEased(letterAction.m_delay_progression.ValueFrom, letterAction.m_delay_progression.ValueTo);
                    }

                    animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.DELAY);
                }

                gui_y_offset += LINE_HEIGHT;
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.NUM_LOOP_ITERATIONS)
            {
                ActionLoopCycle loop_cycle = letterAnimation.GetLoop(m_action_idx + loop_start_offset);

                if (loop_cycle != null)
                {
                    loop_cycle.m_number_of_loops = EditorGUI.IntField(new Rect(gui_x_offset, gui_y_offset, 250, LINE_HEIGHT), m_setting_name, loop_cycle.m_number_of_loops);
                }


                gui_y_offset += LINE_HEIGHT;
            }

            return(!gui_already_changed && GUI.changed);
        }
		void DrawQuickSetupPanel(float gui_y_offset)
		{
#if ANIM_DESIGN_DEV_TOOLS
			m_show_raw_import_settings = EditorGUI.Toggle (new Rect (Instance.position.width - 20, gui_y_offset, 20, 20), m_show_raw_import_settings);

			if(m_show_raw_import_settings)
			{
				float xOffset = 100;
				
				// Handle Effect imports
				GUI.Label(new Rect(xOffset, gui_y_offset, 200, 20), "Import Preset Effect", EditorStyles.boldLabel);
				gui_y_offset += LINE_HEIGHT;
				
				m_selected_animation_idx = EditorGUI.Popup (new Rect (xOffset, gui_y_offset, 250, 20), m_selected_animation_idx, m_animation_config_titles);
				
				if(GUI.changed)
				{
					EditorPrefs.SetInt("SelectedTextFxAnim", m_selected_animation_idx);
				}
				
				m_animation_manager.m_import_as_section = EditorGUI.Toggle (new Rect (xOffset + 260, gui_y_offset - 20, 170, LINE_HEIGHT), "Import as Section?", m_animation_manager.m_import_as_section);
				
				if(GUI.Button(new Rect(xOffset + 260, gui_y_offset, 80, 20), "Apply") && m_animation_manager.WipeQuickSetupData(user_confirm: true))
				{
					//
					string path = "Assets/TextFx/AnimationConfigs/" + m_animation_config_titles[m_selected_animation_idx] + ".txt";

					path = path.Replace("\n", "");
					path = path.Replace("\r", "");
					
					TextAsset animation_config_data = AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset)) as TextAsset;

					Debug.Log("Importing from : " + path);
					
					if(animation_config_data != null)
					{
						if(m_animation_manager.m_import_as_section
						   //						m_animation_config_titles[m_selected_animation_idx].Contains("Intro") ||
						   //					   m_animation_config_titles[m_selected_animation_idx].Contains("Main") ||
						   //					   m_animation_config_titles[m_selected_animation_idx].Contains("Outro")
						   )
						{
							// Importing a preset animation section
							m_animation_manager.ImportPresetAnimationSectionData(animation_config_data.text, true);
						}
						else
						{
							// Importing a full effect
							m_animation_manager.ImportData(animation_config_data.text, true);
						}
						
						//					Debug.Log("TextFx animation '" + m_animation_config_titles[m_selected_animation_idx] + "' imported");
						
						m_animation_manager.m_effect_name = (m_animation_config_titles[m_selected_animation_idx].Split('/'))[1];
						
						// Wipe all Quick Setup data
						m_animation_manager.WipeQuickSetupData();
					}
				}
				
				if(GUI.Button(new Rect(xOffset + 350, gui_y_offset, 120, 20), "Refresh List"))
				{
					EditorToolsHelper.RewriteImportEffectNames();
					
					AssetDatabase.Refresh();
					
					LoadInPresetAnimationNames();
				}
				
				gui_y_offset += LINE_HEIGHT;
				gui_y_offset += LINE_HEIGHT;
				
				m_animation_manager.m_effect_name = EditorGUI.TextField (new Rect (xOffset, gui_y_offset, 320, 20), "Name", m_animation_manager.m_effect_name);
				
				gui_y_offset += LINE_HEIGHT;
				gui_y_offset += LINE_HEIGHT;
				
				if(m_animation_manager.m_preset_effect_settings != null)
				{
					foreach(PresetEffectSetting effect_setting in m_animation_manager.m_preset_effect_settings)
					{
						effect_setting.DrawGUISetting(m_animation_manager, 10, ref gui_y_offset, GUI.changed);
					}
				}
				
				// Draw copy/paste section divider line
				TextFxHelperMethods.DrawGUILine(new Rect(0, gui_y_offset, Instance.position.width, 0), Color.gray, 3);
			}
#endif


			// Play On Start option
			gui_y_offset += 10;
			m_animation_manager.m_begin_on_start = EditorGUI.Toggle(new Rect(10, gui_y_offset, 180, LINE_HEIGHT), "Play On Start", m_animation_manager.m_begin_on_start);
			gui_y_offset += LINE_HEIGHT;
			if(m_animation_manager.m_begin_on_start)
			{
				m_animation_manager.m_begin_delay = Mathf.Max( EditorGUI.FloatField(new Rect(25, gui_y_offset, 220, INPUT_FIELD_HEIGHT), "Delay", m_animation_manager.m_begin_delay), 0 );
				gui_y_offset += LINE_HEIGHT;
			}
			m_animation_manager.m_time_type = (AnimationTime) EditorGUI.EnumPopup (new Rect (10, gui_y_offset, 240, LINE_HEIGHT), "Time", m_animation_manager.m_time_type);
			gui_y_offset += LINE_HEIGHT;
			m_animation_manager.m_animation_speed_factor = EditorGUI.FloatField (new Rect (10, gui_y_offset, 180, INPUT_FIELD_HEIGHT), "Speed Factor", m_animation_manager.m_animation_speed_factor);
			gui_y_offset += LINE_HEIGHT;


			gui_y_offset += LINE_HEIGHT;
			EditorGUI.LabelField (new Rect (10, gui_y_offset, 250, 30), "Animation Sections", BigHeaderGUIStyle);
			gui_y_offset += 30;


			QUICK_SETUP_SCROLL_POS = GUI.BeginScrollView (new Rect (0, gui_y_offset, Instance.position.width, Instance.position.height - gui_y_offset), QUICK_SETUP_SCROLL_POS, new Rect(0,0,Instance.position.width - 20,m_quick_setup_panel_height) );

			bool gui_changed = GUI.changed;

			gui_y_offset = 0;


			// Draw global loop option
			if(m_animation_manager.m_preset_main.m_active && !m_animation_manager.m_preset_intro.m_active && !m_animation_manager.m_preset_outro.m_active)
			{
				GUI.color = Color.gray;
				GUI.enabled = false;
			}


			if(GUI.Button (new Rect (310, gui_y_offset - 7, 40, 40), m_animation_manager.m_repeat_all_sections ? m_repeat_on_button_texture : m_repeat_off_button_texture, ButtonImageOnlyGUIStyle))
			{
				m_animation_manager.m_repeat_all_sections = !m_animation_manager.m_repeat_all_sections;
			}

			if(!gui_changed && GUI.changed)
			{
				// Update section loop settings
				if(!m_animation_manager.m_repeat_all_sections)
				{
					// Remove global repeat loop
					m_animation_manager.m_master_animations[0].RemoveLoop(m_animation_manager.GlobalRepeatLoopStartIndex);
				}
				else
				{
					// Add in a repeat loop
					ActionLoopCycle new_loop = new ActionLoopCycle();
					new_loop.m_start_action_idx = 0;
					new_loop.m_end_action_idx = m_animation_manager.m_preset_outro.m_start_action + (m_animation_manager.m_preset_outro.m_active ? m_animation_manager.m_preset_outro.m_num_actions : -1);
					new_loop.m_number_of_loops = m_animation_manager.m_repeat_all_sections_count;
					
					m_animation_manager.m_master_animations[0].InsertLoop(m_animation_manager.GlobalRepeatLoopStartIndex, new_loop);
				}
			}
			
			if(m_animation_manager.m_repeat_all_sections)
			{
				// display repeat num field
				m_animation_manager.m_repeat_all_sections_count = Mathf.Max(EditorGUI.IntField(new Rect(355, gui_y_offset + 2, 30, LINE_HEIGHT), m_animation_manager.m_repeat_all_sections_count), 0);
				
				if(m_animation_manager.m_repeat_all_sections_count == 0)
				{
					GUI.DrawTexture(new Rect(390, gui_y_offset - 8, 40, 40), m_infinity_texture);
				}
			}

			// Return GUI to active state again
			GUI.color = Color.white;
			GUI.enabled = true;

			gui_y_offset += 45;


			const string ANIM_INTROS_FOLDER_NAME = "Intros";
			const string ANIM_MAINS_FOLDER_NAME = "Mains";
			const string ANIM_OUTROS_FOLDER_NAME = "Outros";

			DrawGUIAnimationSettings (TextFxAnimationManager.PRESET_ANIMATION_SECTION.INTRO, ANIM_INTROS_FOLDER_NAME, ref gui_y_offset, ref m_animation_manager.m_intro_animation_foldout, ref m_animation_manager.m_selected_intro_animation_idx, ref m_animation_manager.m_preset_intro, m_animation_config_intro_titles);
			DrawGUIAnimationSettings (TextFxAnimationManager.PRESET_ANIMATION_SECTION.MAIN, ANIM_MAINS_FOLDER_NAME, ref gui_y_offset, ref m_animation_manager.m_main_animation_foldout, ref m_animation_manager.m_selected_main_animation_idx, ref m_animation_manager.m_preset_main, m_animation_config_main_titles);
			DrawGUIAnimationSettings (TextFxAnimationManager.PRESET_ANIMATION_SECTION.OUTRO, ANIM_OUTROS_FOLDER_NAME, ref gui_y_offset, ref m_animation_manager.m_outro_animation_foldout, ref m_animation_manager.m_selected_outro_animation_idx, ref m_animation_manager.m_preset_outro, m_animation_config_outro_titles);


			GUI.EndScrollView ();

			// Check that current scrollview height matches the height of the content within it. Else update the value and call to redraw.
			if(m_quick_setup_panel_height != gui_y_offset)
			{
				m_quick_setup_panel_height = gui_y_offset;
				Instance.Repaint();
			}
		}
		void DrawGUIAnimationSettings(TextFxAnimationManager.PRESET_ANIMATION_SECTION section, string folder_name, ref float gui_y_offset, ref bool foldout, ref int selected_index, ref TextFxAnimationManager.PresetAnimationSection preset_anim_section, string[] anim_titles)
		{
			if(selected_index > 0)
				foldout = EditorGUI.Foldout (new Rect (20, gui_y_offset, 80, 20), foldout, TextFxAnimationManager.m_animation_section_names[(int) section], true, FoldOutHeaderGUIStyle);
			else
				EditorGUI.LabelField(new Rect (20, gui_y_offset, 80, 20), TextFxAnimationManager.m_animation_section_names[(int) section], HeaderGUIStyle);

			bool gui_changed = GUI.changed;

			// Draw Section loop options
			if(section == TextFxAnimationManager.PRESET_ANIMATION_SECTION.MAIN && selected_index > 0)
			{
				if(GUI.Button (new Rect (310, gui_y_offset - 7, 40, 40), preset_anim_section.m_repeat ? m_repeat_on_button_texture : m_repeat_off_button_texture, ButtonImageOnlyGUIStyle))
				{
					preset_anim_section.m_repeat = !preset_anim_section.m_repeat;
				}

				if(!gui_changed && GUI.changed)
				{
					// Update section loop settings
					if(!preset_anim_section.m_repeat)
					{
						// Remove section repeat loop
						m_animation_manager.m_master_animations[0].RemoveLoop(GetSectionRepeatLoopIndex(section));
					}
					else
					{
						// Add in a repeat loop
						ActionLoopCycle new_loop = new ActionLoopCycle();
						new_loop.m_start_action_idx = preset_anim_section.m_start_action;
						new_loop.m_end_action_idx = preset_anim_section.m_start_action + preset_anim_section.m_num_actions;
						new_loop.m_number_of_loops = preset_anim_section.m_repeat_count;

						m_animation_manager.m_master_animations[0].InsertLoop(GetSectionRepeatLoopIndex(section), new_loop);
					}
				}

				if(preset_anim_section.m_repeat)
				{
					gui_changed = GUI.changed;

					// display repeat num field
					preset_anim_section.m_repeat_count = Mathf.Max(EditorGUI.IntField(new Rect(355, gui_y_offset + 2, 30, LINE_HEIGHT), preset_anim_section.m_repeat_count), 0);
					
					if(preset_anim_section.m_repeat_count == 0)
					{
						GUI.DrawTexture(new Rect(390, gui_y_offset - 8, 40, 40), m_infinity_texture);
					}

					if(!gui_changed && GUI.changed)
					{
						// Update loop with current repeat count
						ActionLoopCycle repeat_loop = m_animation_manager.m_master_animations[0].GetLoop(GetSectionRepeatLoopIndex(section));
						repeat_loop.m_number_of_loops = preset_anim_section.m_repeat_count;
					}
				}
			}
			

			gui_changed = GUI.changed;

			selected_index = EditorGUI.Popup (new Rect (120, gui_y_offset, 180, 20), selected_index, anim_titles, PopupHeaderGUIStyle);
			
			if(!gui_changed && GUI.changed)
			{
				if(m_animation_manager.WipeFullEditorData(user_confirm: true))
				{
					// Handle removing any existing section anim
					if(preset_anim_section.m_num_actions > 0
					   && m_animation_manager.m_master_animations != null
					   && m_animation_manager.m_master_animations.Count > 0
					   && m_animation_manager.m_master_animations[0].NumActions >= preset_anim_section.m_num_actions + 1)
					{
						m_animation_manager.m_master_animations[0].RemoveActions(preset_anim_section.m_start_action, preset_anim_section.m_num_actions + 1);

						m_animation_manager.m_master_animations[0].RemoveLoops(preset_anim_section.m_start_loop, preset_anim_section.m_num_loops);
					}
					
					// Animation selection changed. Update animation to reflect this
					if(selected_index > 0)
					{
						preset_anim_section.m_repeat = false;
						preset_anim_section.m_repeat_count = 0;

						string path = "Assets/TextFx/AnimationConfigs/" + folder_name + "/" + anim_titles[selected_index] + ".txt";
						path = path.Replace("\n", "");
						path = path.Replace("\r", "");
						
						TextAsset animation_config_data = AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset)) as TextAsset;
						
//						Debug.Log("Importing from : " + path + ", animation_config_data : " + (animation_config_data != null));
						
						if(animation_config_data != null)
						{
							preset_anim_section.m_active = true;

							m_animation_manager.ImportData(animation_config_data.text, preset_anim_section, section, true);

							// Add in an Exit Pause action after the end of this section
							LetterAction exit_pause_action = new LetterAction();
							exit_pause_action.m_action_type = ACTION_TYPE.BREAK;

							// Initialise delay duration based on existing settings
							exit_pause_action.m_duration_progression.SetConstant(preset_anim_section.m_exit_pause ? preset_anim_section.m_exit_pause_duration : INACTIVE_EXIT_PAUSE_DURATION);

							m_animation_manager.m_master_animations[0].InsertAction(preset_anim_section.ExitPauseIndex, exit_pause_action);

							m_animation_manager.PrepareAnimationData (ANIMATION_DATA_TYPE.DURATION);
						}
					}
					else
					{
						preset_anim_section.m_active = false;
						preset_anim_section.m_num_actions = 0;
						preset_anim_section.m_num_loops = 0;
					}
					
					UpdatePresetAnimSectionActionIndexes();

					// Update global loop settings if active
					if(m_animation_manager.m_repeat_all_sections)
					{
						ActionLoopCycle global_loop = m_animation_manager.m_master_animations[0].GetLoop(m_animation_manager.GlobalRepeatLoopStartIndex);

						if(global_loop == null)
						{
							// Global loop was removed during section rearranging. Re-add one in.
							global_loop = new ActionLoopCycle();
							global_loop.m_number_of_loops = m_animation_manager.m_repeat_all_sections_count;
							m_animation_manager.m_master_animations[0].InsertLoop(m_animation_manager.GlobalRepeatLoopStartIndex, global_loop, true);
						}

						global_loop.m_start_action_idx = 0;
						global_loop.m_end_action_idx = m_animation_manager.m_preset_outro.m_start_action + (m_animation_manager.m_preset_outro.m_active ? m_animation_manager.m_preset_outro.m_num_actions : -1);
					}
				}
				else
				{
					// Set selected anim index back to NONE
					selected_index = 0;
				}
			}
			
			gui_y_offset += 35;
			
			if(foldout && selected_index > 0)
			{
				if(preset_anim_section.m_preset_effect_settings == null || preset_anim_section.m_preset_effect_settings.Count == 0)
				{
					return;
				}

				bool setting_changed = false;

				float gui_x_offset = 60;

				GUI.Label(new Rect(gui_x_offset - 5, gui_y_offset, 120, LINE_HEIGHT), "Section Settings", EditorStyles.boldLabel);

				if(GUI.Button(new Rect(gui_x_offset + 150, gui_y_offset, 100, LINE_HEIGHT), "Play Section"))
					setting_changed = true;

				gui_y_offset += 30;

				foreach(PresetEffectSetting effect_setting in preset_anim_section.m_preset_effect_settings)
				{
					if(effect_setting.DrawGUISetting(m_animation_manager, gui_x_offset, ref gui_y_offset, GUI.changed, preset_anim_section.m_start_action, preset_anim_section.m_start_loop))
						setting_changed = true;
				}

				// Display Exit Pause setting
				gui_changed = GUI.changed;
				preset_anim_section.m_exit_pause = EditorGUI.Toggle(new Rect(gui_x_offset, gui_y_offset, 200, LINE_HEIGHT), "Exit Delay", preset_anim_section.m_exit_pause);
				gui_y_offset += LINE_HEIGHT;

				if(!gui_changed && GUI.changed)
				{
					LetterAction exitPauseAction = m_animation_manager.m_master_animations[0].GetAction(preset_anim_section.ExitPauseIndex);

					exitPauseAction.m_duration_progression.SetConstant(preset_anim_section.m_exit_pause ? preset_anim_section.m_exit_pause_duration : INACTIVE_EXIT_PAUSE_DURATION);

					m_animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.DURATION);

					UpdatePresetAnimSectionActionIndexes();
				}

				if(preset_anim_section.m_exit_pause)
				{
					LetterAction exit_pause_action = m_animation_manager.m_master_animations[0].GetAction(preset_anim_section.ExitPauseIndex);

					// Force to be Constant progression type
					if(exit_pause_action.m_duration_progression.Progression != (int) ValueProgression.Constant)
						exit_pause_action.m_duration_progression.SetConstant(exit_pause_action.m_duration_progression.ValueFrom);

					gui_changed = GUI.changed;

					float exit_pause_duration = Mathf.Max(EditorGUI.FloatField(new Rect(120, gui_y_offset, 200, LINE_HEIGHT), "Duration", exit_pause_action.m_duration_progression.ValueFrom), 0);
					gui_y_offset += LINE_HEIGHT;

					if(!gui_changed && GUI.changed)
					{
						exit_pause_action.m_duration_progression.SetConstant(exit_pause_duration);

						m_animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.DURATION);

						setting_changed = true;
					}
				}


				if(setting_changed)
				{
					PlayEditorAnimation(preset_anim_section.m_start_action);
				}
			}

			gui_y_offset += LINE_HEIGHT;
		}
		public void ImportData(Boomlagoon.JSON.JSONObject json_data, string assetNameSuffix = "")
		{
			
			m_letters_to_animate = json_data["m_letters_to_animate"].Array.JSONtoListInt();
			m_letters_to_animate_custom_idx = (int) json_data["m_letters_to_animate_custom_idx"].Number;
			m_letters_to_animate_option = (LETTERS_TO_ANIMATE) (int) json_data["m_letters_to_animate_option"].Number;
			
			m_letter_actions = new List<LetterAction>();
			LetterAction letter_action;
			foreach(Boomlagoon.JSON.JSONValue action_data in json_data["ACTIONS_DATA"].Array)
			{
				letter_action = new LetterAction();
				letter_action.ImportData(action_data.Obj, assetNameSuffix);
				m_letter_actions.Add(letter_action);
			}

			m_loop_cycles = new List<ActionLoopCycle>();
			if(json_data.ContainsKey("LOOPS_DATA"))
			{
				ActionLoopCycle loop_cycle;
				
				foreach(Boomlagoon.JSON.JSONValue loop_data in json_data["LOOPS_DATA"].Array)
				{
					loop_cycle = new ActionLoopCycle();
					loop_cycle.ImportData(loop_data.Obj);
					
					// Check for invalid loops
					if(loop_cycle.m_start_action_idx < m_letter_actions.Count && loop_cycle.m_end_action_idx < m_letter_actions.Count)
					{
						m_loop_cycles.Add(loop_cycle);
					}
					
				}
			}
		}
		public void ImportPresetSectionData(Boomlagoon.JSON.JSONObject json_data, LetterSetup[] letters, int action_insert_index, int loop_insert_index, ref int num_actions_added, ref int num_loops_added, string assetNameSuffix = "")
		{
			if(m_letter_actions == null)
				m_letter_actions = new List<LetterAction>();

			float timing_scale = -1;

			if(m_letters_to_animate == null || m_letters_to_animate.Count == 0)
			{
				CalculateLettersToAnimate(letters);
			}

			if(json_data.ContainsKey("SAMPLE_NUM_LETTERS_ANIMATED") && m_letters_to_animate != null && m_letters_to_animate.Count > 0)
			{
				timing_scale = m_letters_to_animate.Count / ((float) json_data["SAMPLE_NUM_LETTERS_ANIMATED"].Number);
			}


			LetterAction letter_action;
			num_actions_added = 0;
			foreach(Boomlagoon.JSON.JSONValue action_data in json_data["ACTIONS_DATA"].Array)
			{
				letter_action = new LetterAction();
				letter_action.ImportData(action_data.Obj, assetNameSuffix, timing_scale: timing_scale);

				if(num_actions_added == 0 && action_insert_index > 0)
				{
					// Inserting new actions into the middle of the animation. Set first action to continue from last
					letter_action.m_offset_from_last = true;
				}

				InsertAction(action_insert_index + num_actions_added, letter_action);

				num_actions_added++;
			}


			if (m_loop_cycles == null)
				m_loop_cycles = new List<ActionLoopCycle>();


			num_loops_added = 0;

			if(json_data.ContainsKey("LOOPS_DATA"))
			{
				ActionLoopCycle loop_cycle;
				
				foreach(Boomlagoon.JSON.JSONValue loop_data in json_data["LOOPS_DATA"].Array)
				{
					loop_cycle = new ActionLoopCycle();
					loop_cycle.ImportData(loop_data.Obj);
					loop_cycle.m_start_action_idx += action_insert_index;
					loop_cycle.m_end_action_idx += action_insert_index;

					// Check for invalid loops
					if(loop_cycle.m_start_action_idx < m_letter_actions.Count && loop_cycle.m_end_action_idx < m_letter_actions.Count)
					{
						m_loop_cycles.Insert(loop_insert_index + num_loops_added, loop_cycle);
						
						num_loops_added++;
					}
				}
			}
		}
		public void InsertLoop(int index, ActionLoopCycle loop, bool force_insert)
		{
			if(m_loop_cycles == null)
				m_loop_cycles = new List<ActionLoopCycle>();

			if (!force_insert)
			{
				// Check if loop already exists
				foreach (ActionLoopCycle loop_cycle in m_loop_cycles)
					if (loop_cycle.m_start_action_idx == loop.m_start_action_idx && loop_cycle.m_end_action_idx == loop.m_end_action_idx)
						return;
			}

			m_loop_cycles.Insert (index, loop);
		}
		public void InsertLoop(int index, ActionLoopCycle loop)
		{
			InsertLoop (index, loop, false);
		}