// Amplify Shader Editor - Visual Shader Editing Tool
// Copyright (c) Amplify Creations, Lda <info@amplify.pt>
using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;

namespace AmplifyShaderEditor
{
	public enum OutlineMode
	{
		VertexOffset,
		VertexScale
	}

	[Serializable]
	public sealed class OutlineOpHelper
	{

		private string[] ModeTags =
		{
			"Tags{ }",
			"Tags{ \"RenderType\" = \"TransparentCutout\"  \"Queue\" = \"AlphaTest+0\"}",
			"Tags{ \"RenderType\" = \"Transparent\"  \"Queue\" = \"Transparent+0\"}",
			"Tags{ \"RenderType\" = \"Transparent\"  \"Queue\" = \"Transparent+0\" }"
		};

		private string[] ModePragma =
		{
			string.Empty,
			string.Empty,
			"alpha:fade ",
			"alpha:premul "
		};


		private readonly string OutlineSurfaceConfig = "#pragma surface outlineSurf Outline {0} keepalpha noshadow noambient novertexlights nolightmap nodynlightmap nodirlightmap nometa noforwardadd vertex:outlineVertexDataFunc ";

		private readonly string OutlineBodyStructBegin = "struct Input {";
		private readonly string OutlineBodyStructDefault = "\thalf filler;";
		private readonly string OutlineBodyStructEnd = "};";

		private readonly string OutlineDefaultUniformColor = "half4 _ASEOutlineColor;";
		private readonly string OutlineDefaultUniformWidth = "half _ASEOutlineWidth;";
		private readonly string OutlineDefaultUniformColorInstanced = "UNITY_DEFINE_INSTANCED_PROP( half4, _ASEOutlineColor )";
		private readonly string OutlineDefaultUniformWidthInstanced = "UNITY_DEFINE_INSTANCED_PROP( half, _ASEOutlineWidth )";

		private readonly string OutlineDefaultVertexHeader = "void outlineVertexDataFunc( inout appdata_full v, out Input o )\n\t\t{";
		private readonly string OutlineTessVertexHeader = "void outlineVertexDataFunc( inout appdata_full v )\n\t\t{";

		private readonly string OutlineDefaultVertexOutputDeclaration = "\tUNITY_INITIALIZE_OUTPUT( Input, o );";

		private readonly string[] OutlineSurfBody = {
														"\to.Emission = _ASEOutlineColor.rgb;",
														"\to.Alpha = 1;"
		};

		private readonly string[] OutlineSurfBodyInstanced = {
														"\to.Emission = UNITY_ACCESS_INSTANCED_PROP(_ASEOutlineColor).rgb;",
														"\to.Alpha = 1;"
		};

		private readonly string[] OutlineBodyDefaultSurfBegin = {
														"}",
														"inline half4 LightingOutline( SurfaceOutput s, half3 lightDir, half atten ) { return half4 ( 0,0,0, s.Alpha); }",
														"void outlineSurf( Input i, inout SurfaceOutput o )",
														"{"};

		private readonly string[] OutlineBodyDefaultSurfEnd = {
														"}",
														"ENDCG",
														"\n"};

		//private const string OutlineInstancedHeader = "#pragma multi_compile_instancing";

		//private readonly string[] OutlineBodyInstancedBegin = {
		//												"UNITY_INSTANCING_CBUFFER_START({0})",
		//												"\tUNITY_DEFINE_INSTANCED_PROP( half4, _ASEOutlineColor )",
		//												"\tUNITY_DEFINE_INSTANCED_PROP(half, _ASEOutlineWidth)",
		//												"UNITY_INSTANCING_CBUFFER_END",
		//												"void outlineVertexDataFunc( inout appdata_full v, out Input o )",
		//												"{",
		//												"\tUNITY_INITIALIZE_OUTPUT( Input, o );"};

		//private readonly string[] OutlineBodyInstancedEnd = {
		//												"}",
		//												"inline half4 LightingOutline( SurfaceOutput s, half3 lightDir, half atten ) { return half4 ( 0,0,0, s.Alpha); }",
		//												"void outlineSurf( Input i, inout SurfaceOutput o ) { o.Emission = UNITY_ACCESS_INSTANCED_PROP( _ASEOutlineColor ).rgb; o.Alpha = 1; }",
		//												"ENDCG",
		//												"\n"};

		private const string WidthVariableAccessInstanced = "UNITY_ACCESS_INSTANCED_PROP( _ASEOutlineWidth )";

		private const string OutlineVertexOffsetMode = "\tv.vertex.xyz += ( v.normal * {0} );";
		private const string OutlineVertexScaleMode = "\tv.vertex.xyz *= ( 1 + {0});";
		private const string OutlineVertexCustomMode = "\tv.vertex.xyz += {0};";

		private const string OutlineColorLabel = "Color";
		private const string OutlineWidthLabel = "Width";

		private const string ColorPropertyName = "_ASEOutlineColor";
		private const string WidthPropertyName = "_ASEOutlineWidth";


		private const string WidthPropertyNameInstanced = "UNITY_ACCESS_INSTANCED_PROP(_ASEOutlineWidth)";


		
		private const string ColorPropertyDec = "_ASEOutlineColor( \"Outline Color\", Color ) = ({0})";
		private const string OutlinePropertyDec = "_ASEOutlineWidth( \"Outline Width\", Float ) = {0}";

		private const string ModePropertyStr = "Mode";

		private const string NoFogStr = "No Fog";

		private const string BillboardInstructionFormat = "\t{0};";

		private readonly char[] m_colorMaskChar = { 'R' , 'G' , 'B' , 'A' };

		[SerializeField]
		private Color m_outlineColor;

		[SerializeField]
		private float m_outlineWidth;

		[SerializeField]
		private bool m_enabled;

		[SerializeField]
		private OutlineMode m_mode = OutlineMode.VertexOffset;

		[SerializeField]
		private bool m_noFog = true;

		private bool[] m_colorMask = { true , true , true , true };
		private CullMode m_cullMode = CullMode.Front;
		private int m_zTestMode = 0;
		private int m_zWriteMode = 0;
		private bool m_dirtyInput = false;
		private string m_inputs = string.Empty;
		private List<PropertyDataCollector> m_inputList = new List<PropertyDataCollector>();
		private string m_uniforms = string.Empty;
		private List<PropertyDataCollector> m_uniformList = new List<PropertyDataCollector>();
		private List<PropertyDataCollector> m_instancedPropertiesList = new List<PropertyDataCollector>();
		private string m_instancedProperties = string.Empty;
		private string m_instructions = string.Empty;
		private string m_functions = string.Empty;
		private string m_includes = string.Empty;
		private string m_pragmas = string.Empty;
		private string m_defines = string.Empty;
		private string m_standardAdditionalDirectives = string.Empty;
		private string m_vertexData = string.Empty;
		private string m_grabPasses = string.Empty;
		private Dictionary<string, string> m_localFunctions;

		//private OutlineMode m_customMode = OutlineMode.VertexOffset;
		private int m_offsetMode = 0;
		private bool m_customNoFog = true;

		public void Draw( ParentNode owner, GUIStyle toolbarstyle, Material mat )
		{
			Color cachedColor = GUI.color;
			GUI.color = new Color( cachedColor.r, cachedColor.g, cachedColor.b, 0.5f );
			EditorGUILayout.BeginHorizontal( toolbarstyle );
			GUI.color = cachedColor;
			owner.ContainerGraph.ParentWindow.InnerWindowVariables.OutlineActiveMode = owner.GUILayoutToggle( owner.ContainerGraph.ParentWindow.InnerWindowVariables.OutlineActiveMode , EditorVariablesManager.OutlineActiveMode.LabelName, UIUtils.MenuItemToggleStyle, GUILayout.ExpandWidth( true ) );
			EditorGUI.BeginChangeCheck();
			m_enabled = owner.EditorGUILayoutToggle( string.Empty, m_enabled, UIUtils.MenuItemEnableStyle, GUILayout.Width( 16 ) );
			if( EditorGUI.EndChangeCheck() )
			{
				if( m_enabled )
					UpdateToMaterial( mat );

				UIUtils.RequestSave();
			}
			EditorGUILayout.EndHorizontal();

			if( owner.ContainerGraph.ParentWindow.InnerWindowVariables.OutlineActiveMode )
			{
				cachedColor = GUI.color;
				GUI.color = new Color( cachedColor.r, cachedColor.g, cachedColor.b, ( EditorGUIUtility.isProSkin ? 0.5f : 0.25f ) );
				EditorGUILayout.BeginVertical( UIUtils.MenuItemBackgroundStyle );
				GUI.color = cachedColor;

				EditorGUILayout.Separator();
				EditorGUI.BeginDisabledGroup( !m_enabled );

				EditorGUI.indentLevel += 1;
				{
					m_mode = (OutlineMode)owner.EditorGUILayoutEnumPopup( ModePropertyStr, m_mode );

					EditorGUI.BeginChangeCheck();
					m_outlineColor = owner.EditorGUILayoutColorField( OutlineColorLabel, m_outlineColor );
					if( EditorGUI.EndChangeCheck() && mat != null )
					{
						if( mat.HasProperty( ColorPropertyName ) )
						{
							mat.SetColor( ColorPropertyName, m_outlineColor );
						}
					}

					EditorGUI.BeginChangeCheck();
					m_outlineWidth = owner.EditorGUILayoutFloatField( OutlineWidthLabel, m_outlineWidth );
					if( EditorGUI.EndChangeCheck() && mat != null )
					{
						if( mat.HasProperty( WidthPropertyName ) )
						{
							mat.SetFloat( WidthPropertyName, m_outlineWidth );
						}
					}

					m_noFog = owner.EditorGUILayoutToggle( NoFogStr, m_noFog );
				}

				EditorGUI.indentLevel -= 1;
				EditorGUI.EndDisabledGroup();
				EditorGUILayout.Separator();
				EditorGUILayout.EndVertical();
			}
		}

		public void UpdateToMaterial( Material mat )
		{
			if( mat == null )
				return;

			if( mat.HasProperty( ColorPropertyName ) )
			{
				mat.SetColor( ColorPropertyName, m_outlineColor );
			}

			if( mat.HasProperty( WidthPropertyName ) )
			{
				mat.SetFloat( WidthPropertyName, m_outlineWidth );
			}
		}

		public void ReadFromString( ref uint index, ref string[] nodeParams )
		{
			m_enabled = Convert.ToBoolean( nodeParams[ index++ ] );
			m_outlineWidth = Convert.ToSingle( nodeParams[ index++ ] );
			m_outlineColor = IOUtils.StringToColor( nodeParams[ index++ ] );
			if( UIUtils.CurrentShaderVersion() > 5004 )
			{
				m_mode = (OutlineMode)Enum.Parse( typeof( OutlineMode ), nodeParams[ index++ ] );
			}

			if( UIUtils.CurrentShaderVersion() > 13902 )
			{
				m_noFog = Convert.ToBoolean( nodeParams[ index++ ] );
			}
		}

		public void WriteToString( ref string nodeInfo )
		{
			IOUtils.AddFieldValueToString( ref nodeInfo, m_enabled );
			IOUtils.AddFieldValueToString( ref nodeInfo, m_outlineWidth );
			IOUtils.AddFieldValueToString( ref nodeInfo, IOUtils.ColorToString( m_outlineColor ) );
			IOUtils.AddFieldValueToString( ref nodeInfo, m_mode );
			IOUtils.AddFieldValueToString( ref nodeInfo, m_noFog );
		}

		public void AddToDataCollector( ref MasterNodeDataCollector dataCollector )
		{
			if( !dataCollector.UsingCustomOutlineColor )
				dataCollector.AddToProperties( -1, string.Format( ColorPropertyDec, IOUtils.ColorToString( m_outlineColor ) ), -1 );
			if( !dataCollector.UsingCustomOutlineWidth )
				dataCollector.AddToProperties( -1, string.Format( OutlinePropertyDec, m_outlineWidth ), -1 );
		}

		public void UpdateFromMaterial( Material mat )
		{
			if( mat.HasProperty( ColorPropertyName ) )
			{
				m_outlineColor = mat.GetColor( ColorPropertyName );
			}

			if( mat.HasProperty( WidthPropertyName ) )
			{
				m_outlineWidth = mat.GetFloat( WidthPropertyName );
			}
		}

		void AddMultibodyString( string body , List<string> list )
		{
			body = body.Replace( "\t\t", string.Empty );
			string[] strArr = body.Split( '\n' );
			for( int i = 0; i < strArr.Length; i++ )
			{
				list.Add( strArr[ i ] );
			}

		}
		public string[] OutlineFunctionBody( ref MasterNodeDataCollector dataCollector, bool instanced, bool isShadowCaster, string shaderName, string[] billboardInfo, ref TessellationOpHelper tessOpHelper, string target, PrecisionType precision )
		{
			List<string> body = new List<string>();
			body.Add( ModeTags[ dataCollector.CustomOutlineSelectedAlpha ] );
			if( !string.IsNullOrEmpty( m_grabPasses ))
				body.Add( m_grabPasses.Replace( "\t\t",string.Empty ));	

			if( m_zWriteMode != 0 )
				body.Add( "ZWrite " + ZBufferOpHelper.ZWriteModeValues[ m_zWriteMode ] );
			if( m_zTestMode != 0 )
				body.Add( "ZTest " + ZBufferOpHelper.ZTestModeValues[ m_zTestMode ] );

			body.Add( "Cull " + m_cullMode );
			
			//Color Mask
			{
				int count = 0;
				string colorMask = string.Empty;
				for( int i = 0 ; i < m_colorMask.Length ; i++ )
				{
					if( m_colorMask[ i ] )
					{
						count++;
						colorMask += m_colorMaskChar[ i ];
					}
				}

				if( count != m_colorMask.Length )
				{
					body.Add( "ColorMask " + ( ( count == 0 ) ? "0" : colorMask ) );
				}
			}

			body.Add( "CGPROGRAM" );
			if( tessOpHelper.EnableTesselation )
			{
				body.Add( "#include \"" + TessellationOpHelper.TessInclude + "\"" );
				body.Add( "#pragma target " + target );
			}
			else
			{
				body.Add( "#pragma target 3.0" );
			}
			bool customOutline = dataCollector.UsingCustomOutlineColor || dataCollector.UsingCustomOutlineWidth || dataCollector.UsingCustomOutlineAlpha;
			int outlineMode = customOutline ? m_offsetMode : ( m_mode == OutlineMode.VertexOffset ? 0 : 1 );
			string extraOptions = ( customOutline ? m_customNoFog : m_noFog ) ? "nofog " : string.Empty;
			if( dataCollector.CustomOutlineSelectedAlpha > 0 )
			{
				extraOptions += ModePragma[ dataCollector.CustomOutlineSelectedAlpha ];
			}

			string surfConfig = string.Format( OutlineSurfaceConfig, extraOptions );

			if( tessOpHelper.EnableTesselation )
				tessOpHelper.WriteToOptionalParams( ref surfConfig );

			body.Add( surfConfig );
			if( !isShadowCaster )
			{
				AddMultibodyString( m_defines, body );
				AddMultibodyString( m_includes, body );
				AddMultibodyString( m_pragmas, body );
			}
			AddMultibodyString( m_standardAdditionalDirectives, body );
			//if( instanced )
			//{
			//	body.Add( OutlineInstancedHeader );
			//}

			if( customOutline )
			{
				if( isShadowCaster )
				{

					for( int i = 0; i < InputList.Count; i++ )
					{
						dataCollector.AddToInput( InputList[ i ].NodeId, InputList[ i ].PropertyName, !InputList[ i ].IsDirective );
					}
				}
				else
				{
					if( !string.IsNullOrEmpty( m_inputs ) )
						body.Add( m_inputs.Trim( '\t', '\n' ) );
				}

				if( !DirtyInput && !isShadowCaster )
					body.Add( OutlineBodyStructDefault );

				if( !isShadowCaster )
					body.Add( OutlineBodyStructEnd );
			}
			else if( !isShadowCaster )
			{
				body.Add( OutlineBodyStructBegin );
				body.Add( OutlineBodyStructDefault );
				body.Add( OutlineBodyStructEnd );
			}

			if( instanced )
			{
				//for( int i = 0; i < OutlineBodyInstancedBegin.Length; i++ )
				//{
				//	body.Add( ( i == 0 ) ? string.Format( OutlineBodyInstancedBegin[ i ], shaderName ) : OutlineBodyInstancedBegin[ i ] );
				//}

				//if( (object)billboardInfo != null )
				//{
				//	for( int j = 0; j < billboardInfo.Length; j++ )
				//	{
				//		body.Add( string.Format( BillboardInstructionFormat, billboardInfo[ j ] ) );
				//	}
				//}

				//switch( outlineMode )
				//{
				//	case 0: body.Add( string.Format( OutlineVertexOffsetMode, WidthVariableAccessInstanced ) ); break;
				//	case 1: body.Add( string.Format( OutlineVertexScaleMode, WidthVariableAccessInstanced ) ); break;
				//	case 2: body.Add( string.Format( OutlineVertexCustomMode, WidthVariableAccessInstanced ) ); break;
				//}
				//for( int i = 0; i < OutlineBodyInstancedEnd.Length; i++ )
				//{
				//	body.Add( OutlineBodyInstancedEnd[ i ] );
				//}
				
				//Instanced block name must differ from used on main shader so it won't throw a duplicate name error
				shaderName = shaderName+ "Outline";
				bool openCBuffer = true;
				if( customOutline )
				{
					if( isShadowCaster )
					{
						for( int i = 0; i < UniformList.Count; i++ )
						{
							dataCollector.AddToUniforms( UniformList[ i ].NodeId, UniformList[ i ].PropertyName );
						}

						foreach( KeyValuePair<string, string> kvp in m_localFunctions )
						{
							dataCollector.AddFunction( kvp.Key, kvp.Value );
						}
					}
					else
					{
						if( !string.IsNullOrEmpty( Uniforms ) )
							body.Add( Uniforms.Trim( '\t', '\n' ) );

						openCBuffer = false;
						body.Add( string.Format( IOUtils.InstancedPropertiesBegin, shaderName ));
						if( !string.IsNullOrEmpty( InstancedProperties ) )
							body.Add( InstancedProperties.Trim( '\t', '\n' ) );
					}
				}

				if( openCBuffer)
					body.Add( string.Format( IOUtils.InstancedPropertiesBegin, shaderName ) );

				if( !dataCollector.UsingCustomOutlineColor )
					body.Add( precision == PrecisionType.Float ? OutlineDefaultUniformColorInstanced.Replace( "half", "float" ) : OutlineDefaultUniformColorInstanced );

				if( !dataCollector.UsingCustomOutlineWidth )
					body.Add( precision == PrecisionType.Float ? OutlineDefaultUniformWidthInstanced.Replace( "half", "float" ) : OutlineDefaultUniformWidthInstanced );

				body.Add( IOUtils.InstancedPropertiesEnd );

				//Functions
				if( customOutline && !isShadowCaster )
					body.Add( Functions );

				if( tessOpHelper.EnableTesselation && !isShadowCaster )
				{
					body.Add( tessOpHelper.Uniforms().TrimStart( '\t' ) );
					body.Add( tessOpHelper.GetCurrentTessellationFunction( ref dataCollector ).Trim( '\t', '\n' ) + "\n" );
				}

				if( tessOpHelper.EnableTesselation )
				{
					body.Add( OutlineTessVertexHeader );
				}
				else
				{
					body.Add( OutlineDefaultVertexHeader );
					body.Add( OutlineDefaultVertexOutputDeclaration );
				}

				if( customOutline )
				{
					if( !string.IsNullOrEmpty( VertexData ) )
						body.Add( "\t" + VertexData.Trim( '\t', '\n' ) );
				}

				if( (object)billboardInfo != null )
				{
					for( int j = 0; j < billboardInfo.Length; j++ )
					{
						body.Add( string.Format( BillboardInstructionFormat, billboardInfo[ j ] ) );
					}
				}

				switch( outlineMode )
				{
					case 0: body.Add( string.Format( OutlineVertexOffsetMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyNameInstanced ) ); break;
					case 1: body.Add( string.Format( OutlineVertexScaleMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyNameInstanced ) ); break;
					case 2: body.Add( string.Format( OutlineVertexCustomMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyNameInstanced ) ); break;
				}

				for( int i = 0; i < OutlineBodyDefaultSurfBegin.Length; i++ )
				{
					body.Add( OutlineBodyDefaultSurfBegin[ i ] );
				}
				if( dataCollector.UsingCustomOutlineColor || dataCollector.CustomOutlineSelectedAlpha > 0 )
				{
					body.Add( "\t" + Instructions.Trim( '\t', '\n' ) );
				}
				else
				{
					for( int i = 0; i < OutlineSurfBodyInstanced.Length; i++ )
					{
						body.Add( OutlineSurfBodyInstanced[ i ] );
					}
				}

				for( int i = 0; i < OutlineBodyDefaultSurfEnd.Length; i++ )
				{
					body.Add( OutlineBodyDefaultSurfEnd[ i ] );
				}
			}
			else
			{
				if( customOutline )
				{
					if( isShadowCaster )
					{
						for( int i = 0; i < UniformList.Count; i++ )
						{
							dataCollector.AddToUniforms( UniformList[ i ].NodeId, UniformList[ i ].PropertyName );
						}

						foreach( KeyValuePair<string, string> kvp in m_localFunctions )
						{
							dataCollector.AddFunction( kvp.Key, kvp.Value );
						}
					}
					else
					{
						if( !string.IsNullOrEmpty( Uniforms ) )
							body.Add( Uniforms.Trim( '\t', '\n' ) );
					}
				}

				if( !dataCollector.UsingCustomOutlineColor )
					body.Add( precision == PrecisionType.Float ? OutlineDefaultUniformColor.Replace( "half", "float" ) : OutlineDefaultUniformColor );

				if( !dataCollector.UsingCustomOutlineWidth )
					body.Add( precision == PrecisionType.Float ? OutlineDefaultUniformWidth.Replace( "half", "float" ) : OutlineDefaultUniformWidth );

				//Functions
				if( customOutline && !isShadowCaster )
					body.Add( Functions );

				if( tessOpHelper.EnableTesselation && !isShadowCaster )
				{
					body.Add( tessOpHelper.Uniforms().TrimStart( '\t' ) );
					body.Add( tessOpHelper.GetCurrentTessellationFunction( ref dataCollector ).Trim( '\t', '\n' ) + "\n" );
				}

				if( tessOpHelper.EnableTesselation )
				{
					body.Add( OutlineTessVertexHeader );
				}
				else
				{
					body.Add( OutlineDefaultVertexHeader );
					body.Add( OutlineDefaultVertexOutputDeclaration );
				}

				if( customOutline )
				{
					if( !string.IsNullOrEmpty( VertexData ) )
						body.Add( "\t" + VertexData.Trim( '\t', '\n' ) );
				}

				if( (object)billboardInfo != null )
				{
					for( int j = 0; j < billboardInfo.Length; j++ )
					{
						body.Add( string.Format( BillboardInstructionFormat, billboardInfo[ j ] ) );
					}
				}

				switch( outlineMode )
				{
					case 0: body.Add( string.Format( OutlineVertexOffsetMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyName ) ); break;
					case 1: body.Add( string.Format( OutlineVertexScaleMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyName ) ); break;
					case 2: body.Add( string.Format( OutlineVertexCustomMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyName ) ); break;
				}
				for( int i = 0; i < OutlineBodyDefaultSurfBegin.Length; i++ )
				{
					body.Add( OutlineBodyDefaultSurfBegin[ i ] );
				}
				if( dataCollector.UsingCustomOutlineColor || dataCollector.CustomOutlineSelectedAlpha > 0 )
				{
					body.Add( "\t" + Instructions.Trim( '\t', '\n' ) );
				}
				else
				{
					for( int i = 0; i < OutlineSurfBody.Length; i++ )
					{
						body.Add( OutlineSurfBody[ i ] );
					}
				}

				for( int i = 0; i < OutlineBodyDefaultSurfEnd.Length; i++ )
				{
					body.Add( OutlineBodyDefaultSurfEnd[ i ] );
				}
			}

			string[] bodyArr = body.ToArray();
			body.Clear();
			body = null;
			return bodyArr;
		}


		public void Destroy()
		{
			m_inputList = null;
			m_uniformList = null;
			m_instancedPropertiesList = null;
			m_localFunctions = null;
		}

		public bool EnableOutline { get { return m_enabled; } }

		public bool UsingCullMode { get { return m_cullMode != CullMode.Front; } }
		public bool UsingZWrite { get { return m_zWriteMode != 0; } }
		public bool UsingZTest { get { return m_zTestMode != 0; } }
		public int ZWriteMode { get { return m_zWriteMode; } set { m_zWriteMode = value; } }
		public int ZTestMode { get { return m_zTestMode; } set { m_zTestMode = value; } }
		public CullMode OutlineCullMode { get { return m_cullMode; } set { m_cullMode = value; } }
		public bool[] ColorMask
		{
			get { return m_colorMask; }
			set
			{
				if( value.Length == m_colorMask.Length )
				{
					for( int i = 0 ; i < m_colorMask.Length ; i++ )
					{
						m_colorMask[i] = value[i];
					}
				}
			}
		}

		public bool ActiveColorMask
		{
			get
			{
				for( int i = 0 ; i < m_colorMask.Length ; i++ )
				{
					if( !m_colorMask[ i ] )
						return true;
				}
				return false;
			}
		}
		public string Inputs { get { return m_inputs; } set { m_inputs = value; } }
		public string Uniforms { get { return m_uniforms; } set { m_uniforms = value; } }
		public string InstancedProperties { get { return m_instancedProperties; } set { m_instancedProperties = value; } }
		public string Instructions { get { return m_instructions; } set { m_instructions = value; } }
		public string Functions { get { return m_functions; } set { m_functions = value; } }
		public string Includes { get { return m_includes; } set { m_includes = value; } }
		public string Pragmas { get { return m_pragmas; } set { m_pragmas = value; } }
		public string Defines { get { return m_defines; } set { m_defines = value; } }
		public string StandardAdditionalDirectives { get { return m_standardAdditionalDirectives; } set { m_standardAdditionalDirectives = value; } }
		public string VertexData { get { return m_vertexData; } set { m_vertexData = value; } }
		public string GrabPasses { get { return m_grabPasses; } set { m_grabPasses = value; } }
		public List<PropertyDataCollector> InputList { get { return m_inputList; } set { m_inputList = value; } }
		public List<PropertyDataCollector> UniformList { get { return m_uniformList; } set { m_uniformList = value; } }
		public List<PropertyDataCollector> InstancedPropertiesList { get { return m_instancedPropertiesList; } set { m_instancedPropertiesList = value; } }

		public Dictionary<string, string> LocalFunctions { get { return m_localFunctions; } set { m_localFunctions = value; } }
		public bool DirtyInput { get { return m_dirtyInput; } set { m_dirtyInput = value; } }

		//public OutlineMode CustomMode { get { return m_customMode; } set { m_customMode = value; } }
		public int OffsetMode { get { return m_offsetMode; } set { m_offsetMode = value; } }
		public bool CustomNoFog { get { return m_customNoFog; } set { m_customNoFog = value; } }
	}
}
