using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Diagnostics;
using TradeIdeas.ServerConnection;
using TradeIdeas.XML;
using TradeIdeas.MiscSupport;
namespace TradeIdeas.TIProData
{
///
/// Used when we have received new historical alerts.
///
/// One or more alerts. The most recent one is on top. A user could iterate through
/// this list in the normal order. There is no gaurentee about how many groups the alerts will come in.
/// The history request that generated this data.
/// Compare that to the currently outstanding request to make sure you are not
/// receiving alerts from a request you already canceled.
public delegate void HistoryData(List alerts, History sender);
///
/// Status has changed. Read the status directly from the History object.
///
/// The history request that generated this event.
/// Compare that to the currently outstanding request to make sure you are not
/// receiving events from a request you already canceled.
public delegate void HistoryStatus(History sender);
///
/// NotSent - The request has not been sent to the server.
/// Working - The server is handling the request.
/// MoreAvailable - The server stopped sending data because it sent all we asked for.
/// Done - There is no more data or the client terminated the request.
///
public enum HistoryDisposition
{
NotSent,
Working,
MoreAvailable,
Done
}
///
/// Use this to specify the data you want to see.
///
public struct HistoryRequest
{
///
/// This is a standard collaborate string. Do not include the leading "http://www.trade-ideas.com/View.php?" part.
///
public string Config;
///
/// The most recent alerts you want to see. Set this to null to disable this filter and see
/// the most recent alerts available.
///
public DateTime? StartTime;
///
/// The oldest alerts you want to see. Set this to null to disable this filter and go back
/// as far as the server will let you.
///
public DateTime? EndTime;
///
/// This is the number of alerts you want to see.
///
/// You might get fewer. Maybe not enough alerts match your criteria. Or you run into some other
/// limit. If you ask for 2 billion, your not going to get 2 billion.
///
/// You might actually get more. For simplicity we don't make a guarantee. But you probably won't.
///
/// This is the maximum you can get for each time you call start(). You can always ask for more.
///
/// Set this to null to get a default value.
///
public int? MaxCount;
///
/// If you set MaxCount to null, that's the same as setting it to this value.
///
public const int DEFAULT_COUNT = 1000;
}
///
/// Use this to interact with a history request.
/// Use to create one of these.
///
public interface History
{
///
/// New data is available.
///
/// Like all callbacks from TIProData this can come from any thread.
///
event HistoryData HistoryData;
///
/// The status of this request has changed.
///
/// Like all callbacks from TIProData this can come from any thread. This
/// includes some possibly reentrant calls. If you cancel a history request,
/// you are likely to see this callback before returns.
///
event HistoryStatus HistoryStatus;
///
/// There are strict rules about how history can change states. You can't reuse
/// a history object. You might ask for more history, if you are in the appropriate
/// state.
///
HistoryDisposition HistoryDisposition { get; }
///
/// Note: It's possible that we will go into this state on our own at any time.
/// (More precisely, we can go from Working to MoreAvailable at any time.)
/// Watch the status callback if you need to know that. However, we won't exit this
/// state unless someone calls Start() or Stop().
///
/// True if it is safe and legal to call .
bool CanStart();
///
/// Call start to start receiving the data. Nothing will be sent to the server until
/// you do this. This gives you time to register for the callbacks. Also, this gives
/// you time to store a copy of this object so when you get a response you are sure
/// it is from the current object. If this object is in the MoreAvailable state, this
/// will continue the last request from where we left off. This is like the user
/// selecting "More" from the GUI.
///
void Start();
///
/// Call this to stop the request early. This will save resources on the server and
/// client side.
///
/// It is always safe to call this. It doesn't make sense to ask for the current state
/// because the state could change right after we ask.
///
void Stop();
///
/// This gets updated with a connonical form, or "short form," by the server, just like
/// the realtime stuff.
///
string Config { get; }
///
/// WindowName is null before the first update. "" means that (we know that) the user
/// requested "".
///
string WindowName { get; }
///
/// This might be null before we hear from the server.
///
IList Columns { get; }
}
///
/// The main program / API user should use this to create a new request.
///
/// This class does a lot of stuff internally. All history requests share some resources. However,
/// that is an implemention detail which is hidden.
///
public class HistoryManager
{
private readonly object _mutex = new object();
private readonly Dictionary _windows =
new Dictionary();
private class HistoryImpl : History
{
private static Int64 _lastId = 0;
private readonly string _id;
private HistoryManager _manager;
public String Config { get; private set; }
public String WindowName { get; private set; }
public IList Columns { get; private set; }
private int _maxCount;
private string _startId = "";
private DateTime? _startTime;
private DateTime? _endTime;
private readonly int _perRequestCount;
private const int MAX_PER_REQUEST_COUNT = 1000;
public HistoryImpl(HistoryRequest request, HistoryManager manager)
{
lock (typeof(HistoryImpl))
{
_lastId++;
_id = "HA" + _lastId.ToString();
}
Config = request.Config;
_startTime = request.StartTime;
_endTime = request.EndTime;
_manager = manager;
_perRequestCount = request.MaxCount??HistoryRequest.DEFAULT_COUNT;
if (_perRequestCount > MAX_PER_REQUEST_COUNT)
_perRequestCount = MAX_PER_REQUEST_COUNT;
else if (_perRequestCount < 1)
_perRequestCount = 1;
}
public void SendRequestNow()
{ // Assume we are already in the mutex.
List