1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.Linq;
5 using System.Reflection;
6 using System.Text;
7 using System.Text.RegularExpressions;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
10 using Mesen.GUI.Controls;
11 using Mesen.GUI.Forms.Config;
12 
13 namespace Mesen.GUI.Forms
14 {
15 	public class EntityBinder
16 	{
17 		private Dictionary<string, Control> _bindings = new Dictionary<string, Control>();
18 		private Dictionary<string, eNumberFormat> _fieldFormat = new Dictionary<string, eNumberFormat>();
19 		private Dictionary<string, FieldInfoWrapper> _fieldInfo = null;
20 
21 		public object Entity { get; set; }
22 
23 		protected virtual Type BindedType
24 		{
25 			get { return Entity.GetType(); }
26 		}
27 
28 		public bool Updating { get; private set; }
29 
AddBinding(string fieldName, Control bindedField, eNumberFormat format = eNumberFormat.Default)30 		public void AddBinding(string fieldName, Control bindedField, eNumberFormat format = eNumberFormat.Default)
31 		{
32 			if(BindedType == null) {
33 				throw new Exception("Need to override BindedType to use bindings");
34 			}
35 
36 			if(_fieldInfo == null) {
37 				_fieldInfo = new Dictionary<string, FieldInfoWrapper>();
38 				PropertyInfo[] properties = BindedType.GetProperties();
39 				foreach(PropertyInfo info in properties) {
40 					_fieldInfo[info.Name] = new FieldInfoWrapper(info);
41 				}
42 
43 				FieldInfo[] members = BindedType.GetFields();
44 				foreach(FieldInfo info in members) {
45 					_fieldInfo[info.Name] = new FieldInfoWrapper(info);
46 				}
47 			}
48 
49 			if(_fieldInfo.ContainsKey(fieldName)) {
50 				Type fieldType = _fieldInfo[fieldName].FieldType;
51 				if(fieldType.IsSubclassOf(typeof(Enum)) && bindedField is ComboBox) {
52 					BaseConfigForm.InitializeComboBox(((ComboBox)bindedField), fieldType);
53 				}
54 				_bindings[fieldName] = bindedField;
55 				_fieldFormat[fieldName] = format;
56 			} else {
57 				throw new Exception("Invalid field name");
58 			}
59 		}
60 
UpdateUI()61 		public void UpdateUI()
62 		{
63 			this.Updating = true;
64 
65 			foreach(KeyValuePair<string, Control> kvp in _bindings) {
66 				if(!_fieldInfo.ContainsKey(kvp.Key)) {
67 					throw new Exception("Invalid binding key");
68 				} else {
69 					FieldInfoWrapper field = _fieldInfo[kvp.Key];
70 					eNumberFormat format = _fieldFormat[kvp.Key];
71 					object value = field.GetValue(this.Entity);
72 					if(kvp.Value is TextBox) {
73 						if(value is IFormattable) {
74 							kvp.Value.Text = ((IFormattable)value).ToString(format == eNumberFormat.Decimal ? "" : "X", System.Globalization.CultureInfo.InvariantCulture);
75 						} else {
76 							kvp.Value.Text = value == null ? "" : ((string)value).Replace(Environment.NewLine, "\n").Replace("\n", Environment.NewLine);
77 						}
78 					} else if(kvp.Value is ctrlPathSelection) {
79 						kvp.Value.Text = (string)value;
80 					} else if(kvp.Value is CheckBox) {
81 						((CheckBox)kvp.Value).Checked = Convert.ToBoolean(value);
82 					} else if(kvp.Value is ctrlRiskyOption) {
83 						((ctrlRiskyOption)kvp.Value).Checked = Convert.ToBoolean(value);
84 					} else if(kvp.Value is RadioButton) {
85 						((RadioButton)kvp.Value).Checked = (bool)value;
86 					} else if(kvp.Value is Panel) {
87 						RadioButton radio = kvp.Value.Controls.OfType<RadioButton>().FirstOrDefault(r => r.Tag.Equals(value));
88 						if(radio != null) {
89 							radio.Checked = true;
90 						} else {
91 							throw new Exception("No radio button matching value found");
92 						}
93 					} else if(kvp.Value is ctrlTrackbar) {
94 						if(field.FieldType == typeof(Int32)) {
95 							((ctrlTrackbar)kvp.Value).Value = (int)value;
96 						} else {
97 							((ctrlTrackbar)kvp.Value).Value = (int)(uint)value;
98 						}
99 					} else if(kvp.Value is ctrlHorizontalTrackbar) {
100 						((ctrlHorizontalTrackbar)kvp.Value).Value = (int)value;
101 					} else if(kvp.Value is TrackBar) {
102 						if(field.FieldType == typeof(Int32)) {
103 							((TrackBar)kvp.Value).Value = (int)value;
104 						} else {
105 							((TrackBar)kvp.Value).Value = (int)(uint)value;
106 						}
107 					} else if(kvp.Value is MesenNumericUpDown) {
108 						MesenNumericUpDown nud = kvp.Value as MesenNumericUpDown;
109 						decimal val;
110 						if(field.FieldType == typeof(UInt32)) {
111 							val = (UInt32)value;
112 						} else if(field.FieldType == typeof(Int32)) {
113 							val = (Int32)value;
114 						} else {
115 							val = (decimal)(double)value;
116 						}
117 						val = Math.Min(Math.Max(val, nud.Minimum), nud.Maximum);
118 						nud.Value = val;
119 					} else if(kvp.Value is ComboBox) {
120 						ComboBox combo = kvp.Value as ComboBox;
121 						if(value is Enum) {
122 							combo.SelectedItem = ResourceHelper.GetEnumText((Enum)value);
123 						} else if(field.FieldType == typeof(UInt32)) {
124 							for(int i = 0, len = combo.Items.Count; i < len; i++) {
125 								UInt32 numericValue;
126 								string item = Regex.Replace(combo.Items[i].ToString(), "[^0-9]", "");
127 								if(UInt32.TryParse(item, out numericValue)) {
128 									if(numericValue == (UInt32)value) {
129 										combo.SelectedIndex = i;
130 										break;
131 									}
132 								}
133 							}
134 						} else if(field.FieldType == typeof(string)) {
135 							combo.SelectedItem = value;
136 							if(combo.SelectedIndex < 0 && combo.Items.Count > 0) {
137 								combo.SelectedIndex = 0;
138 							}
139 						}
140 					}
141 				}
142 			}
143 
144 			this.Updating = false;
145 		}
146 
UpdateObject()147 		public void UpdateObject()
148 		{
149 			foreach(KeyValuePair<string, Control> kvp in _bindings) {
150 				if(!_fieldInfo.ContainsKey(kvp.Key)) {
151 					throw new Exception("Invalid binding key");
152 				} else {
153 					try {
154 						FieldInfoWrapper field = _fieldInfo[kvp.Key];
155 						eNumberFormat format = _fieldFormat[kvp.Key];
156 						if(kvp.Value is TextBox) {
157 							object value = kvp.Value.Text;
158 							NumberStyles numberStyle = format == eNumberFormat.Decimal ? NumberStyles.Integer : NumberStyles.HexNumber;
159 							if(field.FieldType != typeof(string)) {
160 								value = ((string)value).Trim().Replace("$", "").Replace("0x", "");
161 								if(string.IsNullOrWhiteSpace((string)value)) {
162 									value = "0";
163 								}
164 							}
165 							if(field.FieldType == typeof(UInt32)) {
166 								UInt32 result;
167 								if(!UInt32.TryParse((string)value, numberStyle, null, out result)) {
168 									continue; //Invalid value, ignore it
169 								}
170 								value = result;
171 							} else if(field.FieldType == typeof(Int32)) {
172 								Int32 result;
173 								if(!Int32.TryParse((string)value, numberStyle, null, out result)) {
174 									continue; //Invalid value, ignore it
175 								}
176 								value = result;
177 							} else if(field.FieldType == typeof(Byte)) {
178 								Byte result;
179 								if(!Byte.TryParse((string)value, numberStyle, null, out result)) {
180 									continue; //Invalid value, ignore it
181 								}
182 								value = result;
183 							} else if(field.FieldType == typeof(UInt16)) {
184 								UInt16 result;
185 								if(!UInt16.TryParse((string)value, numberStyle, null, out result)) {
186 									continue; //Invalid value, ignore it
187 								}
188 								value = result;
189 							} else if(field.FieldType == typeof(UInt64)) {
190 								UInt64 result;
191 								if(!UInt64.TryParse((string)value, numberStyle, null, out result)) {
192 									continue; //Invalid value, ignore it
193 								}
194 								value = result;
195 							} else if(field.FieldType == typeof(Int64)) {
196 								Int64 result;
197 								if(!Int64.TryParse((string)value, numberStyle, null, out result)) {
198 									continue; //Invalid value, ignore it
199 								}
200 								value = result;
201 							}
202 							field.SetValue(Entity, value);
203 						} else if(kvp.Value is ctrlPathSelection) {
204 							field.SetValue(Entity, ((ctrlPathSelection)kvp.Value).Text);
205 						} else if(kvp.Value is CheckBox) {
206 							if(field.FieldType == typeof(bool)) {
207 								field.SetValue(Entity, ((CheckBox)kvp.Value).Checked);
208 							} else if(field.FieldType == typeof(byte)) {
209 								field.SetValue(Entity, ((CheckBox)kvp.Value).Checked ? (byte)1 : (byte)0);
210 							}
211 						} else if(kvp.Value is ctrlRiskyOption) {
212 							if(field.FieldType == typeof(bool)) {
213 								field.SetValue(Entity, ((ctrlRiskyOption)kvp.Value).Checked);
214 							} else if(field.FieldType == typeof(byte)) {
215 								field.SetValue(Entity, ((ctrlRiskyOption)kvp.Value).Checked ? (byte)1 : (byte)0);
216 							}
217 						} else if(kvp.Value is RadioButton) {
218 							field.SetValue(Entity, ((RadioButton)kvp.Value).Checked);
219 						} else if(kvp.Value is Panel) {
220 							field.SetValue(Entity, kvp.Value.Controls.OfType<RadioButton>().FirstOrDefault(r => r.Checked).Tag);
221 						} else if(kvp.Value is ctrlTrackbar) {
222 							if(field.FieldType == typeof(Int32)) {
223 								field.SetValue(Entity, (Int32)((ctrlTrackbar)kvp.Value).Value);
224 							} else {
225 								field.SetValue(Entity, (UInt32)((ctrlTrackbar)kvp.Value).Value);
226 							}
227 						} else if(kvp.Value is ctrlHorizontalTrackbar) {
228 							field.SetValue(Entity, (Int32)((ctrlHorizontalTrackbar)kvp.Value).Value);
229 						} else if(kvp.Value is TrackBar) {
230 							if(field.FieldType == typeof(Int32)) {
231 								field.SetValue(Entity, ((TrackBar)kvp.Value).Value);
232 							} else {
233 								field.SetValue(Entity, (UInt32)((TrackBar)kvp.Value).Value);
234 							}
235 						} else if(kvp.Value is MesenNumericUpDown) {
236 							if(field.FieldType == typeof(UInt32)) {
237 								field.SetValue(Entity, (UInt32)((MesenNumericUpDown)kvp.Value).Value);
238 							} else if(field.FieldType == typeof(Int32)) {
239 								field.SetValue(Entity, (Int32)((MesenNumericUpDown)kvp.Value).Value);
240 							} else {
241 								field.SetValue(Entity, (double)((MesenNumericUpDown)kvp.Value).Value);
242 							}
243 						} else if(kvp.Value is ComboBox) {
244 							if(field.FieldType.IsSubclassOf(typeof(Enum))) {
245 								Enum enumValue = ((ComboBox)kvp.Value).GetEnumValue(field.FieldType);
246 								if(enumValue != null) {
247 									field.SetValue(Entity, enumValue);
248 								}
249 							} else if(field.FieldType == typeof(UInt32)) {
250 								UInt32 numericValue;
251 								string item = Regex.Replace(((ComboBox)kvp.Value).SelectedItem.ToString(), "[^0-9]", "");
252 								if(UInt32.TryParse(item, out numericValue)) {
253 									field.SetValue(Entity, numericValue);
254 								}
255 							} else if(field.FieldType == typeof(string)) {
256 								field.SetValue(Entity, ((ComboBox)kvp.Value).SelectedItem);
257 							}
258 						}
259 					} catch {
260 						//Ignore exceptions caused by bad user input
261 					}
262 				}
263 			}
264 		}
265 		private class FieldInfoWrapper
266 		{
267 			private FieldInfo _fieldInfo;
268 			private PropertyInfo _propertyInfo;
269 
FieldInfoWrapper(PropertyInfo info)270 			public FieldInfoWrapper(PropertyInfo info)
271 			{
272 				_propertyInfo = info;
273 			}
274 
FieldInfoWrapper(FieldInfo info)275 			public FieldInfoWrapper(FieldInfo info)
276 			{
277 				_fieldInfo = info;
278 			}
279 
280 			public Type FieldType
281 			{
282 				get
283 				{
284 					if(_fieldInfo != null) {
285 						return _fieldInfo.FieldType;
286 					} else {
287 						return _propertyInfo.PropertyType;
288 					}
289 				}
290 			}
291 
SetValue(object obj, object value)292 			public void SetValue(object obj, object value)
293 			{
294 				if(_fieldInfo != null) {
295 					_fieldInfo.SetValue(obj, value);
296 				} else {
297 					_propertyInfo.SetValue(obj, value);
298 				}
299 			}
300 
GetValue(object obj)301 			public object GetValue(object obj)
302 			{
303 				if(_fieldInfo != null) {
304 					return _fieldInfo.GetValue(obj);
305 				} else {
306 					return _propertyInfo.GetValue(obj);
307 				}
308 			}
309 		}
310 	}
311 
312 	public enum eNumberFormat
313 	{
314 		Default,
315 		Hex,
316 		Decimal,
317 	}
318 }
319