// Copyright 2009-2022 Josh Close
// This file is a part of CsvHelper and is dual licensed under MS-PL and Apache 2.0.
// See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0.
// https://github.com/JoshClose/CsvHelper
using CsvHelper.TypeConversion;
using System;
using System.Diagnostics;
using System.Reflection;
namespace CsvHelper.Configuration
{
	/// 
	/// Mapping for a constructor parameter.
	/// This may contain value type data, a constructor type map,
	/// or a reference map, depending on the type of the parameter.
	/// 
	[DebuggerDisplay("Data = {Data}")]
	public class ParameterMap
	{
		/// 
		/// Gets the parameter map data.
		/// 
		public virtual ParameterMapData Data { get; protected set; }
		/// 
		/// Type converter options.
		/// 
		public virtual ParameterMapTypeConverterOption TypeConverterOption { get; protected set; }
		/// 
		/// Gets or sets the map for a constructor type.
		/// 
		public virtual ClassMap ConstructorTypeMap { get; set; }
		/// 
		/// Gets or sets the map for a reference type.
		/// 
		public virtual ParameterReferenceMap ReferenceMap { get; set; }
		/// 
		/// Creates an instance of  using
		/// the given information.
		/// 
		/// The parameter being mapped.
		public ParameterMap(ParameterInfo parameter)
		{
			TypeConverterOption = new ParameterMapTypeConverterOption(this);
			Data = new ParameterMapData(parameter);
		}
		/// 
		/// When reading, is used to get the field
		/// at the index of the name if there was a
		/// header specified. It will look for the
		/// first name match in the order listed.
		/// When writing, sets the name of the 
		/// field in the header record.
		/// The first name will be used.
		/// 
		/// The possible names of the CSV field.
		public virtual ParameterMap Name(params string[] names)
		{
			if (names == null || names.Length == 0)
			{
				throw new ArgumentNullException(nameof(names));
			}
			Data.Names.Clear();
			Data.Names.AddRange(names);
			Data.IsNameSet = true;
			return this;
		}
		/// 
		/// When reading, is used to get the 
		/// index of the name used when there 
		/// are multiple names that are the same.
		/// 
		/// The index of the name.
		public virtual ParameterMap NameIndex(int index)
		{
			Data.NameIndex = index;
			return this;
		}
		/// 
		/// When reading, is used to get the field at
		/// the given index. When writing, the fields
		/// will be written in the order of the field
		/// indexes.
		/// 
		/// The index of the CSV field.
		public virtual ParameterMap Index(int index)
		{
			Data.Index = index;
			Data.IsIndexSet = true;
			return this;
		}
		/// 
		/// Ignore the parameter when reading and writing.
		/// 
		public virtual ParameterMap Ignore()
		{
			Data.Ignore = true;
			return this;
		}
		/// 
		/// Ignore the parameter when reading and writing.
		/// 
		/// True to ignore, otherwise false.
		public virtual ParameterMap Ignore(bool ignore)
		{
			Data.Ignore = ignore;
			return this;
		}
		/// 
		/// The default value that will be used when reading when
		/// the CSV field is empty.
		/// 
		/// The default value.
		public virtual ParameterMap Default(object? defaultValue)
		{
			if (defaultValue == null && Data.Parameter.ParameterType.IsValueType)
			{
				throw new ArgumentException($"Parameter of type '{Data.Parameter.ParameterType.FullName}' can't have a default value of null.");
			}
			if (defaultValue != null && defaultValue.GetType() != Data.Parameter.ParameterType)
			{
				throw new ArgumentException($"Default of type '{defaultValue.GetType().FullName}' does not match parameter of type '{Data.Parameter.ParameterType.FullName}'.");
			}
			Data.Default = defaultValue;
			Data.IsDefaultSet = true;
			return this;
		}
		/// 
		/// The constant value that will be used for every record when 
		/// reading and writing. This value will always be used no matter 
		/// what other mapping configurations are specified.
		/// 
		/// The constant value.
		public virtual ParameterMap Constant(object? constantValue)
		{
			if (constantValue == null && Data.Parameter.ParameterType.IsValueType)
			{
				throw new ArgumentException($"Parameter of type '{Data.Parameter.ParameterType.FullName}' can't have a constant value of null.");
			}
			if (constantValue != null && constantValue.GetType() != Data.Parameter.ParameterType)
			{
				throw new ArgumentException($"Constant of type '{constantValue.GetType().FullName}' does not match parameter of type '{Data.Parameter.ParameterType.FullName}'.");
			}
			Data.Constant = constantValue;
			Data.IsConstantSet = true;
			return this;
		}
		/// 
		/// The field is optional.
		/// 
		public virtual ParameterMap Optional()
		{
			Data.IsOptional = true;
			return this;
		}
		/// 
		/// Specifies the  to use
		/// when converting the parameter to and from a CSV field.
		/// 
		/// The TypeConverter to use.
		public virtual ParameterMap TypeConverter(ITypeConverter typeConverter)
		{
			Data.TypeConverter = typeConverter;
			return this;
		}
		/// 
		/// Specifies the  to use
		/// when converting the parameter to and from a CSV field.
		/// 
		/// The  of the 
		///  to use.
		public virtual ParameterMap TypeConverter() where TConverter : ITypeConverter
		{
			TypeConverter(ObjectResolver.Current.Resolve());
			return this;
		}
		internal int GetMaxIndex()
		{
			return ReferenceMap?.GetMaxIndex() ?? Data.Index;
		}
	}
}