using DTS.Common.Interface.DASFactory;
using DTS.Common.Utilities.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace DTS.Common.Classes.DASFactory
{
///
/// this class attempts to prevent delays in processing commands by controlling how many devices are talked to at once
/// testing with python has shown that greatest efficiency on ATD side is by talking to one device on each port at once
/// testing on DataPRO side has shown that talking to too many devices simultaneously can cause delays in device communication
/// 16237 Add in ATD sequencing/staggering for services
/// Add in ATD sequencing/staggering for services
///
//public class ATDStagger
//{
// ///
// /// this maps from a S6DB to a lookup of port to a sorted list of devices.
// /// this allows us to get the next device on a port of a S6DB
// ///
// private Dictionary>> _s6DBToPortToDevices =
// new Dictionary>>();
// ///
// /// any das that need to be operated on that aren't attached to a distributor
// /// this makes sure we don't lose anybody in the service
// ///
// private List _restOfTheDAS = new List();
// public enum States
// {
// Waiting, //has not started
// Started, //has started but not finished
// Finished //has already finished
// };
// ///
// /// constructs a new stagger instance, with given devices [all devices enterring service]
// /// and a SQL Command structure (used for querying device layout)
// ///
// ///
// ///
// public ATDStagger(IDASCommunication[] devices, IDbCommand cmd)
// {
// try
// {
// cmd.CommandType = CommandType.Text;
// cmd.CommandText = "SELECT [SerialNumber], [PositionOnDistributor], [PositionOnChain], [Port], [ParentDAS] FROM [DataPro].[dbo].[DAS] WHERE [Port] > 0";
// var serialToInfo = new Dictionary>();
// var parentDASToSerials = new Dictionary>();
// //the goal here is to determine S6DB->port->position on chain mapping for all devices in the service
// using (var reader = cmd.ExecuteReader())
// {
// while (reader.Read())
// {
// var serial = Convert.ToString(reader["SerialNumber"]);
// var positionOnDistributor = Convert.ToInt32(reader["PositionOnDistributor"]);
// var positionOnChain = Convert.ToInt32(reader["PositionOnChain"]);
// var port = Convert.ToInt32(reader["Port"]);
// var parentDAS = Convert.ToString(reader["ParentDAS"]);
// var tuple = new Tuple(serial, positionOnDistributor, positionOnChain, port, parentDAS);
// serialToInfo[serial] = tuple;
// if (!parentDASToSerials.ContainsKey(parentDAS))
// {
// parentDASToSerials[parentDAS] = new List();
// }
// parentDASToSerials[parentDAS].Add(serial);
// }
// }
// var serialsDealtWith = new HashSet();
// //tool to make it easier to look up a device given a serial number
// var dasSerialToIDAS = new Dictionary();
// foreach (var das in devices)
// {
// dasSerialToIDAS[das.SerialNumber] = das;
// }
// //tool to make it easier to lookup a S6DB given an ip address
// //this is used for associating S6 which aren't stored in the db
// //associated to a S6DB, but are connected to one
// var ipAddressToS6DB = new Dictionary();
// lock (MyLock)
// {
// //process every unit which is in the db attached to a S6DB
// foreach (var das in devices)
// {
// //initialize every device enterring the service as currently waiting to start service
// _deviceStates[das] = States.Waiting;
// //now look for any units attached to this unit (assuming this unit is a S6DB)
// if (das.IsSlice6Distributor())
// {
// //store the ip address to device mapping incase there are units attached but not associated in db
// var ip = ((ICommunication) das).ConnectString.Split(new[] {':'})[0];
// ipAddressToS6DB[ip] = das;
// _s6DBToPortToDevices[das] = new Dictionary>();
// //S6DB normally has 4 ports, but lists them as 2,3,4,5, just for safety I'm doing 1-5
// for (int i = 0; i <= 5; i++)
// {
// _s6DBToPortToDevices[das][i] = new List();
// }
// if (parentDASToSerials.ContainsKey(das.SerialNumber))
// {
// using (var eSerials = parentDASToSerials[das.SerialNumber].GetEnumerator())
// {
// while (eSerials.MoveNext())
// {
// if (dasSerialToIDAS.ContainsKey(eSerials.Current) && serialToInfo.ContainsKey(eSerials.Current))
// {
// var tuple = serialToInfo[eSerials.Current];
// //item 3 is the port, only ports 2-5 are really valid, but here we just check <=5
// if (tuple.Item3 <= 5)
// {
// _s6DBToPortToDevices[das][tuple.Item3]
// .Add(dasSerialToIDAS[eSerials.Current]);
// serialsDealtWith.Add(eSerials.Current);
// }
// }
// }
// }
// }
// }
// }
// //now every unit which is in the db attached to a distributor has been handled, handle any extra units
// //starting with the units attached to any S6DB but not associated in the db, we'll determine those using ip addresses
// foreach (var das in devices)
// {
// if (!serialsDealtWith.Contains(das.SerialNumber))
// {
// var ip = ((ICommunication) das).ConnectString.Split(new[] {':'})[0];
// if (ipAddressToS6DB.ContainsKey(ip))
// {
// var s6DB = ipAddressToS6DB[ip];
// //we don't know what port the devices is on, make it up trying to distribute evenely across ports
// //even if we end up talking to 4 devices on one port it'll be better than talking to 26 devices all at once
// var insertAt = 0;
// var min = int.MaxValue;
// for (var i = 2; i <= 5; i++)
// {
// if (_s6DBToPortToDevices[s6DB][i].Count < min)
// {
// insertAt = i;
// min = _s6DBToPortToDevices[s6DB][i].Count;
// }
// }
// _s6DBToPortToDevices[s6DB][insertAt].Add(das);
// serialsDealtWith.Add(das.SerialNumber);
// }
// else
// {
// _restOfTheDAS.Add(das);
// }
// }
// }
// }
// }
// catch (Exception ex)
// {
// APILogger.Log(ex);
// }
// cmd.Connection.Close();
// cmd.Dispose();
// }
// ///
// /// returns an array of all devices which have started (but not finished)
// /// this is used for checking for finished devices
// ///
// ///
// public IDASCommunication[] GetStartedUnits()
// {
// lock (MyLock)
// {
// var any = _deviceStates.Any(device => device.Value == States.Started);
// if (!any)
// {
// return new IDASCommunication[0];
// }
// return _deviceStates.Where(device => device.Value == States.Started).Select(device => device.Key)
// .ToArray();
// }
// }
// ///
// /// returns the next device that should start the service
// /// or null if no devices are ready to start
// /// we want to run one device on every port on every S6DB, but ideally only 1
// /// then when one device is done we can start another
// ///
// /// all units in the service
// ///
// public IDASCommunication GetNextDevice(IDASCommunication [] units)
// {
// lock (MyLock)
// {
// using (var dasEnum = _s6DBToPortToDevices.GetEnumerator())
// {
// while (dasEnum.MoveNext())
// {
// var s6DB = dasEnum.Current.Key;
// using (var ports = dasEnum.Current.Value.GetEnumerator())
// {
// while (ports.MoveNext())
// {
// var devicesOnPort = ports.Current.Value;
// if (!devicesOnPort.Any())
// {
// continue;
// }
// var started = devicesOnPort.Count(device => _deviceStates[device] == States.Started);
// //if there's a started device on this port, don't send from this port
// if( started >= 3){ continue; }
// var anyWaiting = devicesOnPort.Any(device => _deviceStates[device] == States.Waiting && units.Contains(device));
// if( !anyWaiting ){ continue; }
// var selected = devicesOnPort.First(device => _deviceStates[device] == States.Waiting && units.Contains(device));
// return selected;
// }
// }
// if (units.Contains(s6DB) && _deviceStates[s6DB] == States.Waiting)
// {
// return s6DB;
// }
// }
// }
// //finally there could be devices in the service which aren't attached to any distributor (USB devices, etc)
// //they have to get start too ...
// var anyWaitingFromRest = _restOfTheDAS.Any(device => _deviceStates[device] == States.Waiting && units.Contains(device));
// if( !anyWaitingFromRest ){ return null; }
// return _restOfTheDAS.First(device =>_deviceStates[device] == States.Waiting && units.Contains(device));
// }
// }
// ///
// /// holds the device state (waiting/started/finished)
// ///
// private Dictionary _deviceStates = new Dictionary();
// ///
// /// returns whether all devices are finished or not
// ///
// ///
// public bool AreAllDevicesFinished()
// {
// lock (MyLock)
// {
// return !_deviceStates.Values.Any(state => state != States.Finished);
// }
// }
// ///
// /// lock used to control access to device states/etc
// ///
// private static readonly object MyLock = new object();
// ///
// /// marks a devices as started
// ///
// ///
// public void MarkDeviceStared(IDASCommunication das)
// {
// lock(MyLock){ _deviceStates[das] = States.Started;}
// }
// ///
// /// marks a device as finished
// ///
// ///
// public void MarkDeviceFinished(IDASCommunication das)
// {
// lock (MyLock)
// {
// _deviceStates[das] = States.Finished;
// }
// }
//}
}