context: using System;
  using System.Text;
  using System.Windows.Forms;
  using System.Linq;
  using Newtonsoft.Json;
  using Newtonsoft.Json.Linq;
  using System.Drawing;
  using System.Globalization;
  
  namespace TOUI
  {
      public class JSONSerializer: ISerializer
      {
          public byte[] Serialize(Packet packet)
          {
              var json = "";
              try
              {
                  var serializerSettings = new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore };
                  json = JsonConvert.SerializeObject(packet/*, serializerSettings*/);
              }
              catch (Exception e)
              {
                  MessageBox.Show(e.ToString());
              }
              return Encoding.UTF8.GetBytes(json);
          }
          
          public Packet Deserialize(byte[] bytes)
          {
              var json = Encoding.UTF8.GetString(bytes);
  
              //first decode as dynamic to check for the datatype
              dynamic p = JsonConvert.DeserializeObject(json);
              //MessageBox.Show(p.command.ToString());
              var packet = new Packet();
              Command c;
                  
              if (Command.TryParse(p.command.ToString(), true, out c))
              {
                  packet.Command = c;
                      
                  switch(packet.Command)
                  {
                      case Command.Init: break;
                      case Command.Remove:
                      {
                          packet.Data = new Parameter(p.data.id.ToString());
                          break;
                      }
                      case Command.Update: 
                      {
                          packet.Data = new Parameter(p.data.id.ToString());
                          //TODO: does an update also always come with a typdef
                          //so we can properly deserialize here?
                          packet.Data.Value = p.data.value;
                          break;
                      }
                      case Command.Add:
                      {
                          packet.Data = new Parameter(p.data.id.ToString());
                              
                          //decode to specific datatype
                          var param = p.data;
                          var name = param.type.name.ToString();
                          var typedefinition = param.type.ToString();
                          var value = param.value.ToString();
      
  //                        MessageBox.Show(p.data.ToString());
                              
                          packet.Data.Type = DecodeTypeDefinition(param.type);
                      	///TODO: set TOUI defaults of typedefinition for properties that have been omitted in serialization
                      	
                          packet.Data.Value = DecodeValue(packet.Data.Type, value);
                      	if (param.widget != null)
                      	    packet.Data.Widget = DecodeWidget(packet.Data.Type, param.widget);
                          else //create default widget
                      		packet.Data.Widget = GetDefaultWidget(packet.Data.Type);
                      	
                      	if (param.description != null)
                      		packet.Data.Description = param.description.ToString();
                      	if (param.label != null)
                      		packet.Data.Label = param.label.ToString();
                      	if (param.group != null)
                      		packet.Data.Group = param.group.ToString();
                      	if (param.order != null)
                      		packet.Data.Order = (int) param.order;
                      	if (param.userdata != null)
                      		packet.Data.UserData = param.userdata.ToString();
                      	
                          break;
                      }
                  }
              }
              return packet;
          }
  
          private TypeDefinition DecodeTypeDefinition(dynamic typedef)
          {
          	var name = typedef.name.ToString();
              var typedefinition = typedef.ToString();
                  
              if (name == "boolean")
                  return JsonConvert.DeserializeObject<TOUIBoolean>(typedefinition);
              else if (name == "number")
              {
                  if (typedef.subtype == "byte")
                      return JsonConvert.DeserializeObject<TOUINumber<byte>>(typedefinition);
              	else if (typedef.subtype == "sbyte")
                      return JsonConvert.DeserializeObject<TOUINumber<sbyte>>(typedefinition);
              	else if (typedef.subtype == "int16")
                      return JsonConvert.DeserializeObject<TOUINumber<Int16>>(typedefinition);
              	else if (typedef.subtype == "uint16")
                      return JsonConvert.DeserializeObject<TOUINumber<UInt16>>(typedefinition);
              	else if (typedef.subtype == "int32")
                      return JsonConvert.DeserializeObject<TOUINumber<int>>(typedefinition);
              	else if (typedef.subtype == "uint32")
                      return JsonConvert.DeserializeObject<TOUINumber<UInt32>>(typedefinition);
              	else if (typedef.subtype == "int64")
                      return JsonConvert.DeserializeObject<TOUINumber<Int64>>(typedefinition);
              	else if (typedef.subtype == "uint64")
                      return JsonConvert.DeserializeObject<TOUINumber<UInt64>>(typedefinition);
              	else if (typedef.subtype == "float64")
              		return JsonConvert.DeserializeObject<TOUINumber<double>>(typedefinition);
              	else
              		return JsonConvert.DeserializeObject<TOUINumber<float>>(typedefinition);
              }                
              else if (name == "vector2")
                  return JsonConvert.DeserializeObject<TOUIVector2>(typedefinition);
  //        	else if (name == "vector3")
  //                return JsonConvert.DeserializeObject<TOUIVector3>(typedefinition);
  //        	else if (name == "vector4")
  //                return JsonConvert.DeserializeObject<TOUIVector4>(typedefinition);
              else if (name == "string")
                  return JsonConvert.DeserializeObject<TOUIString>(typedefinition);
              else if (name == "color")
                  return JsonConvert.DeserializeObject<TOUIColor>(typedefinition);
              else if (name == "enum")
                  return JsonConvert.DeserializeObject<TOUIEnum>(typedefinition);
              else if (name == "array")
              {
                  var type = DecodeTypeDefinition(typedef.subtype);
                  return new TOUIArray(type);
              }
              else
                  return null;
          }
  
          private object DecodeValue(TypeDefinition typedefinition, string value)
          {
              if (typedefinition is TOUIBoolean)
                  return bool.Parse(value);
          	else if (typedefinition is TOUINumber<byte>)
                  return byte.Parse(value);
          	else if (typedefinition is TOUINumber<sbyte>)
                  return sbyte.Parse(value);
          	else if (typedefinition is TOUINumber<Int16>)
                  return Int16.Parse(value);
          	else if (typedefinition is TOUINumber<UInt16>)
                  return UInt16.Parse(value);
          	else if (typedefinition is TOUINumber<int>)
                  return int.Parse(value);
          	else if (typedefinition is TOUINumber<uint>)
                  return uint.Parse(value);
          	else if (typedefinition is TOUINumber<Int64>)
                  return Int64.Parse(value);
          	else if (typedefinition is TOUINumber<UInt64>)
                  return UInt64.Parse(value);
              else if (typedefinition is TOUINumber<float>)
                  return float.Parse(value);
          	else if (typedefinition is TOUINumber<double>)
                  return double.Parse(value); //, NumberStyles.Float, CultureInfo.InvariantCulture);
              else if (typedefinition is TOUIVector2)
                  return value;
  //        	else if (typedefinition is TOUIVector3)
  //                return value;
  //        	else if (typedefinition is TOUIVector4)
  //                return value;
              else if (typedefinition is TOUIString)
                  return value;
              else if (typedefinition is TOUIColor)
              {
                  return value;
  //              var comps = value.Split(',');
  //              return Color.FromArgb(0, int.Parse(comps[0]), int.Parse(comps[1]), int.Parse(comps[2]));
  //              return JsonConvert.DeserializeObject<System.Drawing.Color>(value + ", 0");
              }                
              else if (typedefinition is TOUIEnum)
                  return int.Parse(value);
              else if (typedefinition is TOUIArray)
                  return JsonConvert.DeserializeObject(value);
              else 
                  return null;
          }
      	
          private Widget DecodeWidget(TypeDefinition typeDefinition, dynamic widget)
          {
          	var widgetType = widget.type.ToString();
              var w = widget.ToString();
          
              if (widgetType == "button")
                  return JsonConvert.DeserializeObject<TOUIButton>(w);
          	if (widgetType == "numberbox")
          	{
          		///TODO: deserialize numberbox for specific type
          		//see if its stepsize is set and a multiple of Value.MultipleOf
          		//if so, leave stepsize as is
          		//else if Value.MultipleOf > 0 take that as stepsize
          		//else assume 0.01 for reals and 1 for ints
  //        		var generictype = typeDefinition.GetType().GetGenericTypeDefinition();
  //        		var numberBox = JsonConvert.DeserializeObject<TOUINumberBox<generictype>>(w);
  //        		
  //        		if (generictype == typeof(float))
  //        			numberBox.Stepsize = 0.01f;
  //        		else
  //        			numberBox.Stepsize = 1;
          		var numberBox = JsonConvert.DeserializeObject<TOUINumberBox<float>>(w);
         			return numberBox;
          	}        		
              else
                  return null;
          }
      	
      	private Widget GetDefaultWidget(TypeDefinition typeDefinition)
      	{
      		if (typeDefinition is TOUIBoolean)
                  return new TOUIButton();
          	else if (typeDefinition is TOUINumber<byte>)
                  return new TOUINumberBox<byte>(){ Stepsize = 1 };
          	else if (typeDefinition is TOUINumber<sbyte>)
                  return new TOUINumberBox<sbyte>(){ Stepsize = 1 };
          	else if (typeDefinition is TOUINumber<Int16>)
  
gt:                 return new TOUINumberBox<Int16>(){ Stepsize = 1 };