using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using TradeIdeas.ServerConnection;
using TradeIdeas.TIProData.Interfaces;
using TradeIdeas.XML;
namespace TradeIdeas.TIProGUI.Charting
{
public delegate void ColumnLibraryReceivedHandler();
public class TiqColumnLibraryManager
{
public event ColumnLibraryReceivedHandler ColumnLibraryReceived;
///
/// Flex command to use when retrieving indicator library from the server.
///
private const string INDICATORS_FLEX_COMMAND = "chart_indicators";
///
/// The indicator cache maximum age seconds. If last retrieved longer ago than this, then re-retrieve.
///
private readonly int _indicatorCacheMaxAgeSeconds;
private readonly IConnectionMaster _connectionMaster;
private TiqColumnLibraryManager()
{
_connectionMaster = GuiEnvironment.FindConnectionMaster("");
// TODO: Change the below to pull from GlobalSettings so we can override with Custom4.xml
var prodCacheMaxAgeSeconds = GuiEnvironment.XmlConfig.Node("CHARTS").Node("INDICATOR_CACHE_MAX_AGE").Property("THRESHOLD", 300);
var isProd = !GuiEnvironment.DevelopmentMode;
_indicatorCacheMaxAgeSeconds = isProd ? prodCacheMaxAgeSeconds : 0;
}
private static TiqColumnLibraryManager _instance;
public static TiqColumnLibraryManager Instance()
{
return _instance ?? (_instance = new TiqColumnLibraryManager());
}
///
/// True = running in indicator admin mode. This mode turns on access to the ModifyIndicator form.
///
public bool IsIndicatorAdmin()
{
return GuiEnvironment.DevelopmentMode;
}
private TalkWithServer.CancelToken _indicatorCancelToken;
///
/// Initiates the request to load the indicator library from the server. Cancels any existing previous
/// requests that might be still uncanceled.
///
public void LoadIndicatorLibrary()
{
if (IsIndicatorAdmin() || !GuiEnvironment.LastRetrievedIndicatorLibrary.HasValue || (DateTime.Now - GuiEnvironment.LastRetrievedIndicatorLibrary.Value).TotalSeconds > _indicatorCacheMaxAgeSeconds || GuiEnvironment.TiqColumns.Count == 0)
{
if (null != _indicatorCancelToken)
_connectionMaster.SendManager.CancelMessage(_indicatorCancelToken);
var message = new object[]
{
"command", INDICATORS_FLEX_COMMAND,
"subcommand", "me_get_config"
};
_indicatorCancelToken = new TalkWithServer.CancelToken();
var messageToSend = TalkWithServer.CreateMessage(message);
_connectionMaster.SendManager.SendMessage(messageToSend, IndicatorResponse, true, null, _indicatorCancelToken);
}
else if (null != ColumnLibraryReceived)
{
if (GuiEnvironment.TiqColumns.Count > 0)
ColumnLibraryReceived();
}
}
///
/// Handles the response from the server which defines the indicator library.
///
/// The body.
/// The client identifier.
private void IndicatorResponse(byte[] body, object clientId)
{
if (null == body)
LoadIndicatorLibrary();
else
{
string unused = System.Text.Encoding.UTF8.GetString(body);
XmlNode message = XmlHelper.Get(body).Node(0);
string type = message.Property("type");
if (type == "columns")
{
GuiEnvironment.TiqColumns.Clear();
int columnCount = message.Property("COUNT", 0);
for (int i = 1; i <= columnCount; i++)
{
var indicator = GetChartIndicator(message, i);
if (null != indicator)
GuiEnvironment.TiqColumns.Add(indicator);
}
GroupIndicatorSets();
}
else if (type == "parameters")
{
int parameterCount = message.Property("COUNT", 0);
for (int i = 1; i <= parameterCount; i++)
{
var parameter = GetIndicatorParameter(message, i);
if (null != parameter)
AssignIndicatorParameter(parameter);
}
}
else if (type == "config_complete")
{
GuiEnvironment.LastRetrievedIndicatorLibrary = DateTime.Now;
ColumnLibraryReceived?.Invoke();
if (null == _indicatorCancelToken)
return;
_connectionMaster.SendManager.CancelMessage(_indicatorCancelToken);
_indicatorCancelToken = null;
}
}
}
///
/// When parameters come from the server they are in a separate list. This function assigns them to the
/// appropriate indicator using the ColumnId.
///
/// The parameter.
private void AssignIndicatorParameter(IndicatorParameter parameter)
{
var matchingIndicators = GetIndicatorLibrary(true).Where(x => x.Id == parameter.ColumnId).ToList();
if (matchingIndicators.Count > 0)
matchingIndicators[0].Parameters.Add(parameter);
}
///
/// Creates an Indicator Parameter object from the XML source.
///
/// The source.
/// The index.
///
private IndicatorParameter GetIndicatorParameter(XmlNode source, int index)
{
if (null == source)
return null;
var parameter = new IndicatorParameter();
parameter.Restore(source, index);
return parameter;
}
///
/// Creates a Chart Indicator object from the XML source from the server.
///
/// The source.
/// The index.
///
private TiqColumn GetChartIndicator(XmlNode source, int index)
{
if (null == source)
return null;
var indicator = new TiqColumn();
indicator.RestoreFromServer(source, index);
return indicator;
}
///
/// Goes through all received indicators and links together any indicator sets that should be grouped together.
/// For example, the top and bottom of a channel or band.
///
private void GroupIndicatorSets()
{
foreach (var indicator in GuiEnvironment.TiqColumns.ToList())
{
LinkParentIndicator(indicator);
}
}
///
/// Links indicator to its parent indicator if it has one. This can be applied to the master list
/// or the locally stored copies that the user has selected to appear on the chart. When using
/// the local list, set makeCopy to true.
///
/// The indicator.
/// if set to true [make copy].
private void LinkParentIndicator(TiqColumn indicator, bool makeCopy = false)
{
if (indicator.ParentId.HasValue)
{
var parentIndicators = GuiEnvironment.TiqColumns.Where(x => x.Id == indicator.ParentId.Value).ToList();
if (parentIndicators.Count > 0)
{
var parent = parentIndicators[0];
if (makeCopy)
parent = parentIndicators[0].DeepCopy();
indicator.Parent = parent;
if (!parent.ChildIndicators.Contains(indicator))
parent.ChildIndicators.Add(indicator);
}
}
}
///
/// Link this indicator with any child indicators that are associated with it.
///
/// The indicator.
public void LinkChildIndicators(TiqColumn indicator)
{
var masterCopies = GuiEnvironment.TiqColumns.Where(x => x.Id == indicator.Id).ToList();
if (masterCopies.Count > 0)
{
foreach (var childIndicator in masterCopies[0].ChildIndicators.ToList())
{
var copy = childIndicator.DeepCopy();
copy.Parent = indicator;
indicator.ChildIndicators.Add(copy);
}
}
}
///
/// Retrieves the indicator library.
///
/// List of available indicators.
public List GetIndicatorLibrary(bool chartable)
{
var toReturn = GuiEnvironment.TiqColumns.Where(x => x.Chartable && x.Category != "DEVELOPMENT").OrderBy(x => x.DisplayName).ToList();
if (GuiEnvironment.DevelopmentMode)
toReturn = GuiEnvironment.TiqColumns.Where(x => x.Chartable).OrderBy(x => x.DisplayName).ToList();
if (!chartable)
{
toReturn = GuiEnvironment.TiqColumns.Where(x => x.Category != "DEVELOPMENT").OrderBy(x => x.DisplayName).ToList();
if (GuiEnvironment.DevelopmentMode)
toReturn = GuiEnvironment.TiqColumns.OrderBy(x => x.DisplayName).ToList();
}
return toReturn;
}
///
/// Gets the indicator code.
///
/// The indicator.
///
public string GetIndicatorCode(TiqColumn indicator)
{
string toReturn = "";
toReturn += " {{" + indicator.ResolvedReferenceName() + "}";
if (indicator.Code != "")
{
string indicatorCode = " {{" + indicator.ResolvedCode() + "}}";
toReturn += " " + indicatorCode;
}
else
toReturn += " {{}}";
toReturn += "} ";
return toReturn;
}
}
}