// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
namespace ICSharpCode.Decompiler
{
	/// 
	/// Settings for the decompiler.
	/// 
	public class DecompilerSettings : INotifyPropertyChanged
	{
		/// 
		/// Equivalent to new DecompilerSettings(LanguageVersion.Latest)
		/// 
		public DecompilerSettings()
		{
		}
		/// 
		/// Creates a new DecompilerSettings instance with initial settings
		/// appropriate for the specified language version.
		/// 
		/// 
		/// This does not imply that the resulting
		/// 
		public DecompilerSettings(CSharp.LanguageVersion languageVersion)
		{
			// By default, all decompiler features are enabled.
			// Disable some of them based on language version:
			if (languageVersion < CSharp.LanguageVersion.CSharp2) {
				anonymousMethods = false;
				liftNullables = false;
				yieldReturn = false;
			}
			if (languageVersion < CSharp.LanguageVersion.CSharp3) {
				anonymousTypes = false;
				objectCollectionInitializers = false;
				automaticProperties = false;
				queryExpressions = false;
				expressionTrees = false;
			}
			if (languageVersion < CSharp.LanguageVersion.CSharp4) {
				// * dynamic (not supported yet)
				// * named and optional arguments (not supported yet)
			}
			if (languageVersion < CSharp.LanguageVersion.CSharp5) {
				asyncAwait = false;
			}
			if (languageVersion < CSharp.LanguageVersion.CSharp6) {
				awaitInCatchFinally = false;
				useExpressionBodyForCalculatedGetterOnlyProperties = false;
				nullPropagation = false;
				stringInterpolation = false;
				dictionaryInitializers = false;
			}
			if (languageVersion < CSharp.LanguageVersion.CSharp7) {
				outVariables = false;
				discards = false;
			}
			if (languageVersion < CSharp.LanguageVersion.CSharp7_2) {
				introduceRefAndReadonlyModifiersOnStructs = false;
			}
		}
		bool anonymousMethods = true;
		/// 
		/// Decompile anonymous methods/lambdas.
		/// 
		public bool AnonymousMethods {
			get { return anonymousMethods; }
			set {
				if (anonymousMethods != value) {
					anonymousMethods = value;
					OnPropertyChanged();
				}
			}
		}
		bool anonymousTypes = true;
		/// 
		/// Decompile anonymous types.
		/// 
		public bool AnonymousTypes {
			get { return anonymousTypes; }
			set {
				if (anonymousTypes != value) {
					anonymousTypes = value;
					OnPropertyChanged();
				}
			}
		}
		bool expressionTrees = true;
		/// 
		/// Decompile expression trees.
		/// 
		public bool ExpressionTrees {
			get { return expressionTrees; }
			set {
				if (expressionTrees != value) {
					expressionTrees = value;
					OnPropertyChanged();
				}
			}
		}
		bool yieldReturn = true;
		/// 
		/// Decompile enumerators.
		/// 
		public bool YieldReturn {
			get { return yieldReturn; }
			set {
				if (yieldReturn != value) {
					yieldReturn = value;
					OnPropertyChanged();
				}
			}
		}
		bool asyncAwait = true;
		/// 
		/// Decompile async methods.
		/// 
		public bool AsyncAwait {
			get { return asyncAwait; }
			set {
				if (asyncAwait != value) {
					asyncAwait = value;
					OnPropertyChanged();
				}
			}
		}
		bool awaitInCatchFinally = true;
		/// 
		/// Decompile await in catch/finally blocks.
		/// Only has an effect if  is enabled.
		/// 
		public bool AwaitInCatchFinally {
			get { return awaitInCatchFinally; }
			set {
				if (awaitInCatchFinally != value) {
					awaitInCatchFinally = value;
					OnPropertyChanged();
				}
			}
		}
		bool fixedBuffers = true;
		/// 
		/// Decompile C# 1.0 'public unsafe fixed int arr[10];' members.
		/// 
		public bool FixedBuffers {
			get { return fixedBuffers; }
			set {
				if (fixedBuffers != value) {
					fixedBuffers = value;
					OnPropertyChanged();
				}
			}
		}
		bool liftNullables = true;
		/// 
		/// Use lifted operators for nullables.
		/// 
		public bool LiftNullables {
			get { return liftNullables; }
			set {
				if (liftNullables != value) {
					liftNullables = value;
					OnPropertyChanged();
				}
			}
		}
		bool nullPropagation = true;
		/// 
		/// Decompile C# 6 ?. and ?[] operators.
		/// 
		public bool NullPropagation {
			get { return nullPropagation; }
			set {
				if (nullPropagation != value) {
					nullPropagation = value;
					OnPropertyChanged();
				}
			}
		}
		bool automaticProperties = true;
		/// 
		/// Decompile automatic properties
		/// 
		public bool AutomaticProperties {
			get { return automaticProperties; }
			set {
				if (automaticProperties != value) {
					automaticProperties = value;
					OnPropertyChanged();
				}
			}
		}
		bool automaticEvents = true;
		/// 
		/// Decompile automatic events
		/// 
		public bool AutomaticEvents {
			get { return automaticEvents; }
			set {
				if (automaticEvents != value) {
					automaticEvents = value;
					OnPropertyChanged();
				}
			}
		}
		bool usingStatement = true;
		/// 
		/// Decompile using statements.
		/// 
		public bool UsingStatement {
			get { return usingStatement; }
			set {
				if (usingStatement != value) {
					usingStatement = value;
					OnPropertyChanged();
				}
			}
		}
		bool alwaysUseBraces = true;
		/// 
		/// Gets/Sets whether to use braces for single-statement-blocks. 
		/// 
		public bool AlwaysUseBraces {
			get { return alwaysUseBraces; }
			set {
				if (alwaysUseBraces != value) {
					alwaysUseBraces = value;
					OnPropertyChanged();
				}
			}
		}
		bool forEachStatement = true;
		/// 
		/// Decompile foreach statements.
		/// 
		public bool ForEachStatement {
			get { return forEachStatement; }
			set {
				if (forEachStatement != value) {
					forEachStatement = value;
					OnPropertyChanged();
				}
			}
		}
		bool lockStatement = true;
		/// 
		/// Decompile lock statements.
		/// 
		public bool LockStatement {
			get { return lockStatement; }
			set {
				if (lockStatement != value) {
					lockStatement = value;
					OnPropertyChanged();
				}
			}
		}
		bool switchStatementOnString = true;
		public bool SwitchStatementOnString {
			get { return switchStatementOnString; }
			set {
				if (switchStatementOnString != value) {
					switchStatementOnString = value;
					OnPropertyChanged();
				}
			}
		}
		bool usingDeclarations = true;
		public bool UsingDeclarations {
			get { return usingDeclarations; }
			set {
				if (usingDeclarations != value) {
					usingDeclarations = value;
					OnPropertyChanged();
				}
			}
		}
		bool queryExpressions = true;
		public bool QueryExpressions {
			get { return queryExpressions; }
			set {
				if (queryExpressions != value) {
					queryExpressions = value;
					OnPropertyChanged();
				}
			}
		}
		bool useImplicitMethodGroupConversion = true;
		/// 
		/// Gets/Sets whether to use C# 2.0 method group conversions.
		/// true: EventHandler h = this.OnClick;
		/// false: EventHandler h = new EventHandler(this.OnClick);
		/// 
		public bool UseImplicitMethodGroupConversion {
			get { return useImplicitMethodGroupConversion; }
			set {
				if (useImplicitMethodGroupConversion != value) {
					useImplicitMethodGroupConversion = value;
					OnPropertyChanged();
				}
			}
		}
		bool fullyQualifyAmbiguousTypeNames = true;
		public bool FullyQualifyAmbiguousTypeNames {
			get { return fullyQualifyAmbiguousTypeNames; }
			set {
				if (fullyQualifyAmbiguousTypeNames != value) {
					fullyQualifyAmbiguousTypeNames = value;
					OnPropertyChanged();
				}
			}
		}
		bool useDebugSymbols = true;
		/// 
		/// Gets/Sets whether to use variable names from debug symbols, if available.
		/// 
		public bool UseDebugSymbols {
			get { return useDebugSymbols; }
			set {
				if (useDebugSymbols != value) {
					useDebugSymbols = value;
					OnPropertyChanged();
				}
			}
		}
		bool arrayInitializers = true;
		/// 
		/// Gets/Sets whether to use array initializers.
		/// If set to false, might produce non-compilable code.
		/// 
		public bool ArrayInitializers
		{
			get { return arrayInitializers; }
			set
			{
				if (arrayInitializers != value)
				{
					arrayInitializers = value;
					OnPropertyChanged();
				}
			}
		}
		bool objectCollectionInitializers = true;
		/// 
		/// Gets/Sets whether to use C# 3.0 object/collection initializers.
		/// 
		public bool ObjectOrCollectionInitializers {
			get { return objectCollectionInitializers; }
			set {
				if (objectCollectionInitializers != value) {
					objectCollectionInitializers = value;
					OnPropertyChanged();
				}
			}
		}
		bool dictionaryInitializers = true;
		/// 
		/// Gets/Sets whether to use C# 6.0 dictionary initializers.
		/// Only has an effect if ObjectOrCollectionInitializers is enabled.
		/// 
		public bool DictionaryInitializers {
			get { return dictionaryInitializers; }
			set {
				if (dictionaryInitializers != value) {
					dictionaryInitializers = value;
					OnPropertyChanged();
				}
			}
		}
		bool stringInterpolation = true;
		/// 
		/// Gets/Sets whether to use C# 6.0 string interpolation
		/// 
		public bool StringInterpolation {
			get { return stringInterpolation; }
			set {
				if (stringInterpolation != value) {
					stringInterpolation = value;
					OnPropertyChanged();
				}
			}
		}
		bool showXmlDocumentation = true;
		/// 
		/// Gets/Sets whether to include XML documentation comments in the decompiled code.
		/// 
		public bool ShowXmlDocumentation {
			get { return showXmlDocumentation; }
			set {
				if (showXmlDocumentation != value) {
					showXmlDocumentation = value;
					OnPropertyChanged();
				}
			}
		}
		bool foldBraces = false;
		public bool FoldBraces {
			get { return foldBraces; }
			set {
				if (foldBraces != value) {
					foldBraces = value;
					OnPropertyChanged();
				}
			}
		}
		bool expandMemberDefinitions = false;
		public bool ExpandMemberDefinitions {
			get { return expandMemberDefinitions; }
			set {
				if (expandMemberDefinitions != value) {
					expandMemberDefinitions = value;
					OnPropertyChanged();
				}
			}
		}
		bool decompileMemberBodies = true;
		/// 
		/// Gets/Sets whether member bodies should be decompiled.
		/// 
		public bool DecompileMemberBodies {
			get { return decompileMemberBodies; }
			set {
				if (decompileMemberBodies != value) {
					decompileMemberBodies = value;
					OnPropertyChanged();
				}
			}
		}
		bool useExpressionBodyForCalculatedGetterOnlyProperties = true;
		/// 
		/// Gets/Sets whether simple calculated getter-only property declarations should use expression body syntax.
		/// 
		public bool UseExpressionBodyForCalculatedGetterOnlyProperties {
			get { return useExpressionBodyForCalculatedGetterOnlyProperties; }
			set {
				if (useExpressionBodyForCalculatedGetterOnlyProperties != value) {
					useExpressionBodyForCalculatedGetterOnlyProperties = value;
					OnPropertyChanged();
				}
			}
		}
		bool outVariables = true;
		/// 
		/// Gets/Sets whether out variable declarations should be used when possible.
		/// 
		public bool OutVariables {
			get { return outVariables; }
			set {
				if (outVariables != value) {
					outVariables = value;
					OnPropertyChanged();
				}
			}
		}
		bool discards = true;
		/// 
		/// Gets/Sets whether discards should be used when possible.
		/// Only has an effect if  is enabled.
		/// 
		public bool Discards {
			get { return discards; }
			set {
				if (discards != value) {
					discards = value;
					OnPropertyChanged();
				}
			}
		}
		bool introduceRefAndReadonlyModifiersOnStructs = true;
		/// 
		/// Gets/Sets whether IsByRefLikeAttribute and IsReadOnlyAttribute should be replaced with 'ref' and 'readonly' modifiers on structs.
		/// 
		public bool IntroduceRefAndReadonlyModifiersOnStructs {
			get { return introduceRefAndReadonlyModifiersOnStructs; }
			set {
				if (introduceRefAndReadonlyModifiersOnStructs != value) {
					introduceRefAndReadonlyModifiersOnStructs = value;
					OnPropertyChanged();
				}
			}
		}
		#region Options to aid VB decompilation
		bool introduceIncrementAndDecrement = true;
		/// 
		/// Gets/Sets whether to use increment and decrement operators
		/// 
		public bool IntroduceIncrementAndDecrement {
			get { return introduceIncrementAndDecrement; }
			set {
				if (introduceIncrementAndDecrement != value) {
					introduceIncrementAndDecrement = value;
					OnPropertyChanged();
				}
			}
		}
		bool makeAssignmentExpressions = true;
		/// 
		/// Gets/Sets whether to use assignment expressions such as in while ((count = Do()) != 0) ;
		/// 
		public bool MakeAssignmentExpressions {
			get { return makeAssignmentExpressions; }
			set {
				if (makeAssignmentExpressions != value) {
					makeAssignmentExpressions = value;
					OnPropertyChanged();
				}
			}
		}
		bool alwaysGenerateExceptionVariableForCatchBlocks = false;
		/// 
		/// Gets/Sets whether to always generate exception variables in catch blocks
		/// 
		public bool AlwaysGenerateExceptionVariableForCatchBlocks {
			get { return alwaysGenerateExceptionVariableForCatchBlocks; }
			set {
				if (alwaysGenerateExceptionVariableForCatchBlocks != value) {
					alwaysGenerateExceptionVariableForCatchBlocks = value;
					OnPropertyChanged();
				}
			}
		}
		bool showDebugInfo;
		public bool ShowDebugInfo {
			get { return showDebugInfo; }
			set {
				if (showDebugInfo != value) {
					showDebugInfo = value;
					OnPropertyChanged();
				}
			}
		}
		#endregion
		#region Options to aid F# decompilation
		bool removeDeadCode = false;
		public bool RemoveDeadCode {
			get { return removeDeadCode; }
			set {
				if (removeDeadCode != value) {
					removeDeadCode = value;
					OnPropertyChanged();
				}
			}
		}
		#endregion
		#region Assembly Load and Resolve options
		bool loadInMemory = false;
		public bool LoadInMemory {
			get { return loadInMemory; }
			set {
				if (loadInMemory != value) {
					loadInMemory = value;
					OnPropertyChanged();
				}
			}
		}
		bool throwOnAssemblyResolveErrors = true;
		public bool ThrowOnAssemblyResolveErrors {
			get { return throwOnAssemblyResolveErrors; }
			set {
				if (throwOnAssemblyResolveErrors != value) {
					throwOnAssemblyResolveErrors = value;
					OnPropertyChanged();
				}
			}
		}
		#endregion
		CSharpFormattingOptions csharpFormattingOptions;
		public CSharpFormattingOptions CSharpFormattingOptions {
			get {
				if (csharpFormattingOptions == null) {
					csharpFormattingOptions = FormattingOptionsFactory.CreateAllman();
					csharpFormattingOptions.IndentSwitchBody = false;
					csharpFormattingOptions.ArrayInitializerWrapping = Wrapping.WrapAlways;
				}
				return csharpFormattingOptions;
			}
			set {
				if (value == null)
					throw new ArgumentNullException();
				if (csharpFormattingOptions != value) {
					csharpFormattingOptions = value;
					OnPropertyChanged();
				}
			}
		}
		public event PropertyChangedEventHandler PropertyChanged;
		protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
		{
			if (PropertyChanged != null) {
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}
		public DecompilerSettings Clone()
		{
			DecompilerSettings settings = (DecompilerSettings)MemberwiseClone();
			if (csharpFormattingOptions != null)
				settings.csharpFormattingOptions = csharpFormattingOptions.Clone();
			settings.PropertyChanged = null;
			return settings;
		}
	}
}