/* 

   Copyright (C) Arnaud Houdelette		2012-2014
   Copyright (C) Emmanuel Langlois		2012-2014

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;

namespace DMX2
{
	/// <summary>
	/// Univers DMX.
	/// Chaque univers DMX représente un patch des circuits virtuels sur 512 dimmers DMX
	/// </summary>
	public class UniversDMX
	{
		/// <summary> structure indiquant le mode de calcul d'un octet sur la tramme DMX </summary> 
		public struct Dimmer {
			public Circuit circuitAssocié;
			public FTransfer fonctionTransfert;
			public float param1; // Paramètres pour fonction de transfert
			public float param2;
		}


		public static int maxid=1;

		public UniversDMX ()
							
		{
			Nom = "Univers DMX n°" + maxid++.ToString ();

			for (int i = 0; i<_dimmers.Length; i++) 
			{
				_dimmers[i].fonctionTransfert = FTransfer.lineaire;
				_dimmers[i].param1 = 100;
				_dimmers[i].param2 = 500;
			}

		}

		Dimmer[] _dimmers = new Dimmer[512];

		int allumageForce = -1;

		public int AllumageForce {
			get {
				return allumageForce;
			}
			set {
				allumageForce = value;
			}
		}

		int allumageForceVal= 255;

		public int AllumageForceVal {
			get {
				return allumageForceVal;
			}
			set {
				allumageForceVal = value;
			}
		}

		public string Nom;
		public enum FTransfer {
			lineaire,
			log,
			exp
		}

		public Dimmer[] Dimmers  {
			get
			{
				return _dimmers;
			}
		}

		/// <summary> Calcule la trame DMX </summary>
		/// <param name='valeurs'> tampon pour le stockage de la trame calculée </param>
		/// <param name='offset'> Offset de début de trame dans le tampon </param>
		/// <param name='count'> Nombre d'octets a calculer </param>
		public void CalculUnivers(byte[] valeurs, int offset, int count)
		{
			Dimmer g;
			double val;
			Debug.Assert(valeurs.Length == _dimmers.Length);
			for(int i = 0 ; i<count; i++)
			{
				if(allumageForce==i) {
					valeurs[i+offset] = (byte)allumageForceVal;
					continue;
				}

				g= _dimmers[i];
				if(g.circuitAssocié !=null) {
					switch (g.fonctionTransfert) {
					case FTransfer.lineaire:
						val = g.circuitAssocié.ValeurCourante * g.param1 /100;
						valeurs[i+offset] = (byte)(val>255?255:val);
						break;
					case FTransfer.log:
						val=Math.Log10(g.circuitAssocié.ValeurCourante/g.param2+1)/Math.Log10(255/g.param2+1);
						val = val * 255 * g.param1 /100;
						valeurs[i+offset] = (byte)(val>255?255:val);
						break;
					case FTransfer.exp:
						val=(Math.Exp(g.circuitAssocié.ValeurCourante/g.param2)-1)/(Math.Exp(255/g.param2)-1);
						val = val * 255 * g.param1 /100;
						valeurs[i+offset] = (byte)(val>255?255:val);
						break;
					default:
						throw new ArgumentOutOfRangeException ();
					}
				}
				else 
					valeurs[i+offset] = 0;
			}
		}

		public void Save (XmlElement parent)
		{
			XmlElement el = parent.OwnerDocument.CreateElement ("UniversDMX");
			parent.AppendChild (el);

			el.SetAttribute ("nom", Nom);
			XmlElement xmlDim; int dim;
			for(dim=0; dim < _dimmers.Length; dim++)
			{
				if(_dimmers[dim].circuitAssocié!=null)
				{
					el.AppendChild(xmlDim = el.OwnerDocument.CreateElement("Dimmer"));
					xmlDim.SetAttribute("num",dim.ToString());
					xmlDim.SetAttribute("circuit",_dimmers[dim].circuitAssocié.ID.ToString());
					xmlDim.SetAttribute("ft",_dimmers[dim].fonctionTransfert.ToString());
					xmlDim.SetAttribute("param1",_dimmers[dim].param1.ToString() );
					xmlDim.SetAttribute("param2",_dimmers[dim].param2.ToString() );
				}

			}
		}

		public static UniversDMX Load (Conduite c, XmlElement el)
		{
			if (el.Name != "UniversDMX")
				throw new ErreurLectureFichier ("<UniversDMX> attendu.");
			UniversDMX univ = new UniversDMX ();

			univ.Nom = el.GetAttribute("nom");

			XmlElement xmlDim; Dimmer dim;
			foreach (var xd in el.GetElementsByTagName("Dimmer")) {
				xmlDim = xd as XmlElement;
				dim.circuitAssocié = c.GetCircuitByID(int.Parse(xmlDim.GetAttribute("circuit")));
				FTransfer.TryParse(xmlDim.GetAttribute("ft"),out dim.fonctionTransfert);
				dim.param1 = float.Parse(xmlDim.GetAttribute("param1"));
				dim.param2 = float.Parse(xmlDim.GetAttribute("param2"));
				univ.Dimmers[int.Parse(xmlDim.GetAttribute("num"))] = dim;
			}
			return univ;
		}
	}

}