// *****************************************************************************
// ** TIL.CS V1.3.3
// ** Copyright (c) 2001-2011 Tellert Elektronik GmbH
// ** Copyright (c) 2014-2018 Rudy Tellert Elektronik
// *****************************************************************************
//
// The minimum required TIL_TMS.DLL version is 1.2.0 (due to the strict result 
// check of TilGetNextIteratorItems for tilDataMeasGroupSignalDataIterator). 
// This limitation can be bypassed by ignoring the corresponding function 
// result:
// #define SUPPORT_OLDER_TILTMS_VERSIONS

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;

namespace Tellert.Til
{
    /// <summary>Provides read-only access to TEMES files.</summary>
    public class Til : IDisposable
    {
        /// <value>Represents the TEMES file name.</value>
        public string FileName { get; private set; }

        /// <value>Represents the root variables of a TEMES file.</value>
        public VariableDictionary Variables { get; private set; }

        /// <summary>Represents the signal groups of a TEMES file.</summary>
        public Group[] Groups { get; private set; }

        /// <summary>Represents the view items of a TEMES file.</summary>
        public View[] Views { get; private set; }

        /// <summary>Represents the version of TIL_TMS.DLL.</summary>
        public uint Version { get { return version; } }

        private IntPtr obj;
        private uint version;

        ~Til() 
        { 
            Dispose(false); 
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing) 
        {
            if (obj != IntPtr.Zero) {
                TilMethods.DestroyObject(obj);
                obj = IntPtr.Zero;
            }
        }

        /// <summary>Unloads a possibly loaded TEMES file.</summary>
        public void Clear() 
        {
            Dispose(false);
            FileName = null;
            Variables = null;
            Groups = null;
            Views = null;
        }

        /// <summary>
        /// Loads a TEMES file. 
        /// TEMES files ("*.tms") require only the additional platform-dependent file "til_tms.dll". 
        /// Compressed TEMES files ("*.tma") require the additional two platform-dependent files "til_tms.dll" and "zlibwapi.dll".
        /// </summary>
        public bool Load(string fileName)
        {
            IntPtr root, info, control, file, data, meas, meas0, comm, groups, views;

            Clear();
            if (fileName != null) fileName = System.IO.Path.GetFullPath(fileName);
            obj = TilMethods.CreateObject(TemesTilGuid);
            root = TilMethods.GetRootProperty(obj);
            info = TilMethods.GetProperty(root, Tag.Info);
            version = TilMethods.GetUInt(TilMethods.GetProperty(info, Tag.InfoVersion));
            control = TilMethods.GetProperty(root, Tag.Control);
            file = TilMethods.GetProperty(control, Tag.ControlFilename);
            if (!TilMethods.SetString(file, fileName)) { Clear(); return false; }
            data = TilMethods.GetProperty(root, Tag.Data);
            meas = TilMethods.GetProperty(data, Tag.DataMeas);
            meas0 = TilMethods.GetProperty(meas, 0);
            comm = TilMethods.GetProperty(meas0, Tag.DataMeasComm);
            Variables = new VariableDictionary(TilMethods.GetProperty(comm, Tag.DataMeasCommVariable));
            groups = TilMethods.GetProperty(meas0, Tag.DataMeasGroup);
            Groups = new Group[TilMethods.GetVectorSize(groups)];
            for (uint g = 0; g < Groups.Length; g++) Groups[g] = new Group(TilMethods.GetVectorItem(groups, g));
            views = TilMethods.GetProperty(data, Tag.DataView);
            Views = new View[TilMethods.GetVectorSize(views)];
            for (uint v = 0; v < Views.Length; v++) Views[v] = new View(TilMethods.GetVectorItem(views, v), Groups);
            FileName = fileName;
            
            return true;
        }

        /// <summary>Represents a signal group.</summary>
        public class Group
        {
            /// <summary>Describes the name of a measurement group. Group names may be empty and may not be unambiguous.</summary>
            public readonly string Name;

            /// <summary>Represents the measurement comment.</summary>
            public readonly string Comment;

            /// <summary>Represents the group variables.</summary>
            public readonly VariableDictionary Variables;

            /// <summary>Represents the total number of samples for this group.</summary>
            public readonly ulong SampleCount;

            /// <summary>Represents the group sample rate in seconds.</summary>
            public readonly double SampleRate;

            /// <summary>Contains all group date and time information.</summary>
            public readonly Time Time;

            /// <summary>Represents the group signals.</summary>
            public readonly Signal[] Signals;

            public Group(IntPtr group)
            {
                IntPtr comm, info, signals;

                comm = TilMethods.GetProperty(group, Tag.DataMeasGroupComm);
                Name = TilMethods.GetString(TilMethods.GetProperty(comm, Tag.DataMeasGroupCommName));
                Comment = TilMethods.GetString(TilMethods.GetProperty(comm, Tag.DataMeasGroupCommComment));
                Variables = new VariableDictionary(TilMethods.GetProperty(comm, Tag.DataMeasGroupCommVariable));
                SampleCount = TilMethods.GetULong(TilMethods.GetProperty(group, Tag.DataMeasGroupSamplecount));
                SampleRate = TilMethods.GetDouble(TilMethods.GetProperty(group, Tag.DataMeasGroupSamplerate));
                info = TilMethods.GetProperty(group, Tag.DataMeasGroupInfo);
                Time = new Time(TilMethods.GetProperty(info, Tag.DataMeasGroupInfoTime));
                signals = TilMethods.GetProperty(group, Tag.DataMeasGroupSignal);
                Signals = new Signal[TilMethods.GetVectorSize(signals)];
                for (uint s = 0; s < Signals.Length; s++) Signals[s] = new Signal(TilMethods.GetVectorItem(signals, s), this);
            }
        }

        /// <summary>Represents a dictionary of string keys and string/double values.</summary>
        public class VariableDictionary : SortedDictionary<string, object>
        {
            public VariableDictionary(IntPtr variables)
            {
                uint count = TilMethods.GetVectorSize(variables);

                for (uint i = 0; i < count; i++) {

                    IntPtr variable, value;
                    string key;

                    variable = TilMethods.GetVectorItem(variables, i);
                    key = TilMethods.GetString(TilMethods.GetProperty(variable, Tag.DataCommVariableKey));
                    if (ContainsKey(key)) continue;
                    value = TilMethods.GetProperty(variable, Tag.DataCommVariableValue);
                    switch (TilMethods.GetType(value))
                    {
                        case PropertyType.AnsiString:
                        case PropertyType.WideString:
                            Add(key, TilMethods.GetString(value));
                            break;

                        case PropertyType.Float64:
                        case PropertyType.Float80:
                            Add(key, TilMethods.GetDouble(value));
                            break;
                    }
                }
            }
        }

        /// <summary>Represents the date and time information of a group.</summary>
        public struct Time
        {
            /// <summary>Represents the time point when the device was set up.</summary>
            public readonly Timestamp DeviceSetup;

            /// <summary>Represents the time point of the first sample.</summary>
            public readonly Timestamp Start;

            /// <summary>Represents the time point when the device was read out.</summary>
            public readonly Timestamp DeviceReadOut;

            /// <summary>Represents the mutual exclusive subdivisions of the group samples. The sample offsets are strong monotonically increasing.</summary>
            public readonly Section[] Sections;

            public Time(IntPtr time) : this()
            {
                IntPtr sections;

                DeviceSetup = new Timestamp(TilMethods.GetTime64(TilMethods.GetProperty(time, Tag.DataMeasGroupInfoTimeHwsetup)));
                Start = new Timestamp(TilMethods.GetTime64(TilMethods.GetProperty(time, Tag.DataMeasGroupInfoTimeStart)));
                DeviceReadOut = new Timestamp(TilMethods.GetTime64(TilMethods.GetProperty(time, Tag.DataMeasGroupInfoTimeHwreadout)));
                sections = TilMethods.GetProperty(time, Tag.DataMeasGroupInfoTimeSection);
                Sections = new Section[TilMethods.GetVectorSize(sections)];
                for (uint s = 0; s < Sections.Length; s++) Sections[s] = new Section(TilMethods.GetVectorItem(sections, s));
            }
        }

        /// <summary>Represents the mutual exclusive subdivisions of the group samples. The sample offsets are strong monotonically increasing.</summary>
        public struct Section
        {
            /// <summary>Represents the zero-based index of the first section sample within the group samples.</summary>
            public readonly ulong SampleOffset;

            /// <summary>Represents the time point of the first section value.</summary>
            public readonly Timestamp Start;

            public Section(IntPtr section) : this()
            {
                SampleOffset = TilMethods.GetULong(TilMethods.GetProperty(section, Tag.DataMeasGroupInfoTimeSectionSampleindex));
                Start = new Timestamp(TilMethods.GetTime64(TilMethods.GetProperty(section, Tag.DataMeasGroupInfoTimeSectionStart)));
            }
        }

        /// <summary>Represents the group signal.</summary>
        public class Signal
        {
            /// <summary>Represents the nonempty and unambigous signal name.</summary>
            public readonly string Name;

            /// <summary>Represents the signal comment. The signal comment is empty at the moment.</summary>
            public readonly string Comment;

            /// <summary>Represents the signal variables. The signal variables are empty at the moment.</summary>
            public readonly VariableDictionary Variables;

            /// <summary>Represents the signal unit.</summary>
            public readonly string Unit;

            /// <summary>Represents all signal attributes.</summary>
            public readonly Attributes Attributes;

            /// <summary>Represents the default settings for the signal view.</summary>
            public readonly ViewDefaults ViewDefaults;

            /// <summary>Represents the signal markers.</summary>
            public readonly Marker[] Markers;

            /// <summary>Represents the raw values of a signal. The floating-point values of a signal are obtained by <c>Factor</c> * RawValue + <c>Offset</c>.</summary>
            public readonly Raw Raw;

            /// <summary>Represents the floating-point values of a signal. <c>Iterator</c> should not be confused with the native iterator concept of c#.</summary>
            public readonly Iterator Iterator;

            /// <summary>Represents the signal group.</summary>
            public readonly Group Group;

            public Signal(IntPtr signal, Group group)
            {
                IntPtr comm, data, markers;

                comm = TilMethods.GetProperty(signal, Tag.DataMeasGroupSignalComm);
                Name = TilMethods.GetString(TilMethods.GetProperty(comm, Tag.DataMeasGroupSignalCommName));
                Comment = TilMethods.GetString(TilMethods.GetProperty(comm, Tag.DataMeasGroupSignalCommComment));
                Variables = new VariableDictionary(TilMethods.GetProperty(comm, Tag.DataMeasGroupSignalCommVariable));
                Unit = TilMethods.GetString(TilMethods.GetProperty(signal, Tag.DataMeasGroupSignalUnit));
                data = TilMethods.GetProperty(signal, Tag.DataMeasGroupSignalData);
                Raw = new Raw(TilMethods.GetProperty(data, Tag.DataMeasGroupSignalDataRaw), group.SampleCount);
                Attributes = new Attributes(TilMethods.GetProperty(data, Tag.DataMeasGroupSignalDataFlags));
                ViewDefaults = new ViewDefaults(TilMethods.GetProperty(signal, Tag.DataMeasGroupSignalView));
                Iterator = new Iterator(TilMethods.GetProperty(data, Tag.DataMeasGroupSignalDataIterator), DataType.LeFloat64, group.SampleCount);
                markers = TilMethods.GetProperty(signal, Tag.DataMeasGroupSignalMarker);
                Markers = new Marker[TilMethods.GetVectorSize(markers)];
                for (uint m = 0; m < Markers.Length; m++) Markers[m] = new Marker(TilMethods.GetVectorItem(markers, m));
                Group = group;
            }
        }

        /// <summary>Represents all signal attributes.</summary>
        public struct Attributes
        {
            /// <summary>Represents the "non-interpolatable" property. E.g., this attribute is set for a "register value" or a "segment counter" signal.</summary>
            public readonly bool Quantized;

            public Attributes(IntPtr attributes) : this()
            {
                Quantized = TilMethods.GetUInt(TilMethods.GetProperty(attributes, Tag.DataMeasGroupSignalDataFlagsQuantized)) != 0;
            }
        }

        /// <summary>Represents the default settings for the signal views.</summary>
        public struct ViewDefaults
        {
            /// <summary>Represents the minimal representable signal value.</summary>
            public readonly double Min;

            /// <summary>Represents the maximal representable signal value.</summary>
            public readonly double Max;

            public ViewDefaults(IntPtr view) : this()
            {
                Min = TilMethods.GetDouble(TilMethods.GetProperty(view, Tag.DataMeasGroupSignalViewMin));
                Max = TilMethods.GetDouble(TilMethods.GetProperty(view, Tag.DataMeasGroupSignalViewMax));
            }
        }

        /// <summary>Represents a marker.</summary>
        public struct Marker
        {
            /// <summary>Represents the marker's identifier.</summary>
            public readonly string Name;

            /// <summary>Represents the marker's zero-based sample index or -1 if invalid.</summary>
            public readonly long SampleIndex;

            /// <summary>Represents the marker's visibile attribute. See also the <c>IsVisible</c> property.</summary>
            public readonly bool VisibleAttribute;

            /// <summary>Represents the marker type. The weighing is similar to "header levels".</summary>
            public readonly uint Type;

            /// <summary>Represents the marker text.</summary>
            public readonly string Text;

            /// <summary>Represents the text angle with 0.01 degree per bit (starting counterclockwise from 3 o'clock position) or -1 if invalid.</summary>
            public readonly int TextAngle;

            /// <summary>Represents the text distance with 0.01 mm per bit or -1 if invalid.</summary>
            public readonly int TextDistance;

            /// <summary>Represents the validity of the marker's zero-based sample index.</summary>
            public bool IsSampleIndexValid { get { return SampleIndex >= 0; } }

            /// <summary>Represents the marker's visibility.</summary>
            public bool IsVisible { get { return VisibleAttribute && IsSampleIndexValid; } }

            /// <summary>Represents the validity of the text angle.</summary>
            public bool IsTextAngleValid { get { return TextAngle >= 0; } }

            /// <summary>Represents the validity of the text distance.</summary>
            public bool IsTextDistanceValid { get { return TextDistance >= 0; } }

            public Marker(IntPtr marker) : this()
            {
                Name = TilMethods.GetString(TilMethods.GetProperty(marker, Tag.DataMeasGroupSignalMarkerName));
                SampleIndex = TilMethods.GetLong(TilMethods.GetProperty(marker, Tag.DataMeasGroupSignalMarkerSampleindex), -1);
                VisibleAttribute = TilMethods.GetUInt(TilMethods.GetProperty(marker, Tag.DataMeasGroupSignalMarkerVisible)) != 0;
                Type = TilMethods.GetUInt(TilMethods.GetProperty(marker, Tag.DataMeasGroupSignalMarkerType));
                Text = TilMethods.GetString(TilMethods.GetProperty(marker, Tag.DataMeasGroupSignalMarkerText));
                TextAngle = TilMethods.GetInt(TilMethods.GetProperty(marker, Tag.DataMeasGroupSignalMarkerTextangle), -1);
                TextDistance = TilMethods.GetInt(TilMethods.GetProperty(marker, Tag.DataMeasGroupSignalMarkerTextdistance), -1);
            }
        }

        /// <summary>Represents the raw values of a signal. The floating-point values of a signal are obtained by <c>Factor</c> * RawValue + <c>Offset</c>.</summary>
        public struct Raw
        {
            /// <summary>Represents the data type of the raw values.</summary>
            public readonly DataType DataType;

            /// <summary>Represents the raw value multiplier for the floating-point values of the signal.</summary>
            public readonly double Factor;

            /// <summary>Represents the raw value offset for the floating-point values of the signal.</summary>
            public readonly double Offset;

            /// <summary>Represents the storage location of the signal raw values.</summary>
            public readonly Storage Storage;

            /// <summary>Represents the raw values of a signal. <c>Iterator</c> should not be confused with the native iterator concept of c#.</summary>
            public readonly Iterator Iterator;

            public Raw(IntPtr raw, ulong sampleCount) : this()
            {
                DataType = (DataType)TilMethods.GetUInt(TilMethods.GetProperty(raw, Tag.DataMeasGroupSignalDataRawType));
                Factor = TilMethods.GetDouble(TilMethods.GetProperty(raw, Tag.DataMeasGroupSignalDataRawFactor), 1);
                Offset = TilMethods.GetDouble(TilMethods.GetProperty(raw, Tag.DataMeasGroupSignalDataRawOffset));
                Storage = new Storage(TilMethods.GetProperty(raw, Tag.DataMeasGroupSignalDataRawFile));
                Iterator = (DataType != DataType.SampleNumber) ? 
                    new Iterator(TilMethods.GetProperty(raw, Tag.DataMeasGroupSignalDataRawIterator), DataType, sampleCount) : 
                    new Iterator(IntPtr.Zero, DataType, sampleCount);
            }

#if DEBUG
            /// <summary>Converts all raw signal values to floating-point values</summary>
            public bool ConvertTo(out double[] values)
            {
                values = new double[Iterator.ItemCount];
                Iterator.Rewind();
                if (!ConvertNextItemsTo(ref values, 0)) {
                    values = null;
                    return false;
                }

                return true;
            }

            /// <summary>Converts (a subset of) raw values to floating-point values.
            /// <example>Example to query 2500 values in blocks of 1000 values:<code>
            /// double[] valuesFromRaw = double[1000];
            /// Iterator.Rewind();
            /// ConvertNextItemsTo(valuesFromRaw, 0);
            /// ConvertNextItemsTo(valuesFromRaw, 1000);
            /// ConvertNextItemsTo(valuesFromRaw, 2000);
            /// </code></example></summary>
            /// <param name="values">Is the target array which also determines the maximal number of iterated values.</param>
            /// <param name="sampleIndex">Is the zero-based start sample number.</param>
            public bool ConvertNextItemsTo(ref double[] values, ulong sampleIndex)
            {
                if (values == null) return false;

                if (DataType == DataType.SampleNumber) {
                    for (int k = 0; k < values.Length; k++) values[k] = Factor * sampleIndex++ + Offset;
                    return true;
                }
                else {
                    switch (DataType)
                    {
                        case DataType.LeInt8:
                        case DataType.RsvdBeInt8: {
                            sbyte[] rawValues = new sbyte[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeUInt8:
                        case DataType.RsvdBeUInt8: {
                            byte[] rawValues = new byte[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeInt16: {
                            short[] rawValues = new short[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeUInt16: {
                            ushort[] rawValues = new ushort[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeInt32: {
                            int[] rawValues = new int[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeUInt32: {
                            uint[] rawValues = new uint[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeInt64: {
                            long[] rawValues = new long[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeUInt64: {
                            ulong[] rawValues = new ulong[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeFloat32: {
                            float[] rawValues = new float[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }

                        case DataType.LeFloat64: {
                            double[] rawValues = new double[values.Length];
                            if (!Iterator.CopyNextItemsTo(ref rawValues)) return false;
                            for (int k = 0; k < values.Length; k++) values[k] = Factor * rawValues[k] + Offset;
                            return true; }
                    }
                }

                return false;
            }
#endif
        }

        /// <summary>Represents the storage location of the signal raw values.</summary>
        public struct Storage
        {
            /// <summary>Represents the file name of the signal raw values.</summary>
            public readonly string FileName;

            /// <summary>Represents the file offset of the first signal raw value.</summary>
            public readonly ulong Offset;

            /// <summary>Represents the data size of one signal raw value in bytes.</summary>
            public readonly uint DataSize;

            /// <summary>Represents the number of bytes which separates two adjacent raw values.</summary>
            public readonly uint Gap;

            public Storage(IntPtr file) : this()
            {
                FileName = TilMethods.GetString(TilMethods.GetProperty(file, Tag.DataMeasGroupSignalDataRawFileName));
                Offset = TilMethods.GetULong(TilMethods.GetProperty(file, Tag.DataMeasGroupSignalDataRawFileOffset));
                DataSize = TilMethods.GetUInt(TilMethods.GetProperty(file, Tag.DataMeasGroupSignalDataRawFileDatasize));
                Gap = TilMethods.GetUInt(TilMethods.GetProperty(file, Tag.DataMeasGroupSignalDataRawFileGap));
            }
        }

        /// <summary>Provides access to the signal values. <c>Iterator</c> should not be confused with the native iterator concept of c#.</summary>
        public struct Iterator
        {
            /// <summary>Represents the data type of the iterator's values.</summary>
            public readonly DataType DataType;

            /// <summary>Represents the size of one iterator value in bytes.</summary>
            public readonly uint ItemSize;

            /// <summary>Represents the total number of iterator values.</summary>
            public readonly ulong ItemCount;

            public bool IsAvailable { get { return iterator != IntPtr.Zero; } }

            private IntPtr iterator;

            public Iterator(IntPtr iteratorProperty, DataType dataType, ulong itemCount) : this()
            {
                DataType = dataType;
                ItemCount = itemCount;
                ItemSize = TilMethods.GetIteratorItemSize(iteratorProperty);
                iterator = iteratorProperty;
            }

            /// <summary>Resets the internal iterator's position counter.</summary>
            public bool Rewind()
            {
                return TilMethods.RewindIterator(iterator);
            }

            /// <summary>Copies all values of an iterator.</summary>
            public bool CopyTo<T>(out T[] values) 
            {
                if (!IsAvailable) { values = null; return false; }
                values = new T[ItemCount]; 
                Rewind();
                if (!CopyNextItemsTo<T>(ref values)) { values = null; return false; }

                return true; 
            }

            /// <summary>Copies (a subset of) iterator values.
            /// <example>Example to query 2500 values in blocks of 1000 values:<code>
            /// double[] values = double[1000];
            /// Rewind();
            /// CopyNextItemsTo(values);
            /// CopyNextItemsTo(values);
            /// CopyNextItemsTo(values);
            /// </code></example></summary>
            /// <param name="values">Is the target array which also determines the maximal number of iterated values.</param>
            public bool CopyNextItemsTo<T>(ref T[] values) { return TilMethods.GetNextIteratorItems<T>(iterator, ref values); }
        }

        public struct View
        {
            /// <summary>Represents the name of the view. The name of the first view (= default view) is empty.</summary>
            public readonly string Name;

            /// <summary>Represents the view items.</summary>
            public readonly ViewItem[] Items;

            public View(IntPtr view, Group[] groups) : this()
            {
                IntPtr comm, items;

                comm = TilMethods.GetProperty(view, Tag.DataViewComm);
                Name = TilMethods.GetString(TilMethods.GetProperty(comm, Tag.DataViewCommName));
                items = TilMethods.GetProperty(view, Tag.DataViewItem);
                Items = new ViewItem[TilMethods.GetVectorSize(items)];
                for (uint i = 0; i < Items.Length; i++) Items[i] = new ViewItem(TilMethods.GetVectorItem(items, i), groups);                
            }
        }

        public struct ViewItem
        {
            /// <summary>Represents the view item's signal.</summary>
            public readonly SignalInfo Info;

            /// <summary>Represents the name of the displayed signal name.</summary>
            public readonly string Name;

            /// <summary>Represents the name of the displayed physical unit.</summary>
            public readonly string Unit;

            /// <summary>Represents the additional signal multiplier.
            /// The item value is obtained by ItemValue(t) = Factor * SignalValue(t - RefOffset) + Offset</summary>
            public readonly double Factor;

            /// <summary>Represents the additional signal offset.
            /// The item value is obtained by ItemValue(t) = Factor * SignalValue(t - RefOffset) + Offset</summary>
            public readonly double Offset;

            /// <summary>Represents the reference multiplier. This property is always 1 at the moment.</summary>
            public readonly double RefFactor;

            /// <summary>Represents the signal delay in units of the reference signal.
            /// The item value is obtained by ItemValue(t) = Factor * SignalValue(t - RefOffset) + Offset</summary>
            public readonly double RefOffset;

            /// <summary>Represents the view item's slot visibile attribute.</summary>
            public readonly bool SlotVisibleAttribute;

            /// <summary>Represents the view item's curve visibile attribute.</summary>
            public readonly bool CurveVisibleAttribute;

            /// <summary>Represents the view item's decimal places.</summary>
            public readonly int Digits;

            /// <summary>Represents the view item's curve color.</summary>
            public readonly uint CurveColor;

            /// <summary>Represents the view item's curve thickness in 1/100 mm.</summary>
            public readonly uint CurveThickness;

            /// <summary>Represents the view item's curve line pattern.</summary>
            public readonly uint CurveLinePattern;

            /// <summary>Represents the view item's begin index.</summary>
            public readonly double Begin;

            /// <summary>Represents the view item's end index.</summary>
            public readonly double End;

            /// <summary>Represents the view item's y axis position.</summary>
            public readonly int AxisPosition;

            /// <summary>Represents the view item's x axis position.</summary>
            public readonly int XAxisPosition;

            /// <summary>Represents the view item's axis minimum value.</summary>
            public readonly double AxisMin;

            /// <summary>Represents the view item's axis maximum value.</summary>
            public readonly double AxisMax;

            /// <summary>Represents the view item's axis name.</summary>
            public readonly string AxisName;

            /// <summary>Represents the view item's axis title.</summary>
            public readonly string AxisTitle;

            /// <summary>Represents the view item's default axis minimum value.</summary>
            public readonly double AxisDefaultMin;

            /// <summary>Represents the view item's default axis maximal value.</summary>
            public readonly double AxisDefaultMax;

            /// <summary>Represents the view item's slot visibility.</summary>
            public bool IsSlotVisible { get { return SlotVisibleAttribute; } }

            /// <summary>Represents the view item's curve visibility.</summary>
            public bool IsCurveVisible { get { return CurveVisibleAttribute; } }

            public ViewItem(IntPtr item, Group[] groups) : this()
            {
                IntPtr comm;

                Info = new SignalInfo(TilMethods.GetProperty(item, Tag.DataViewItemSignalinfo), groups);
                comm = TilMethods.GetProperty(item, Tag.DataViewItemComm);
                Name = TilMethods.GetString(TilMethods.GetProperty(comm, Tag.DataViewItemCommName));
                Unit = TilMethods.GetString(TilMethods.GetProperty(item, Tag.DataViewItemUnit));
                Factor = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemFactor), 1);
                Offset = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemOffset));
                RefFactor = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemReffactor), 1);
                RefOffset = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemRefoffset));
                SlotVisibleAttribute = TilMethods.GetUInt(TilMethods.GetProperty(item, Tag.DataViewItemSlotVisible)) != 0;
                Digits = TilMethods.GetInt(TilMethods.GetProperty(item, Tag.DataViewItemDigits));
                CurveVisibleAttribute = TilMethods.GetUInt(TilMethods.GetProperty(item, Tag.DataViewItemCurveVisible)) != 0;
                CurveColor = TilMethods.GetUInt(TilMethods.GetProperty(item, Tag.DataViewItemColor));
                CurveThickness = TilMethods.GetUInt(TilMethods.GetProperty(item, Tag.DataViewItemThickness));
                CurveLinePattern = TilMethods.GetUInt(TilMethods.GetProperty(item, Tag.DataViewItemPattern));
                Begin = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemBegin));
                End = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemEnd));
                AxisPosition = TilMethods.GetInt(TilMethods.GetProperty(item, Tag.DataViewItemAxisPos));
                XAxisPosition = TilMethods.GetInt(TilMethods.GetProperty(item, Tag.DataViewItemXAxisPos));
                AxisMin = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemAxisMin));
                AxisMax = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemAxisMax));
                AxisName = TilMethods.GetString(TilMethods.GetProperty(item, Tag.DataViewItemAxisName));
                AxisTitle = TilMethods.GetString(TilMethods.GetProperty(item, Tag.DataViewItemAxisTitle));
                AxisDefaultMin = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemAxisDefMin));
                AxisDefaultMax = TilMethods.GetDouble(TilMethods.GetProperty(item, Tag.DataViewItemAxisDefMax));
            }
        }

        public struct SignalInfo
        {
            /// <summary>Represents the zero-based measurement index. This property is always zero at the moment.</summary>
            public readonly uint MeasIndex;

            /// <summary>Represents the zero-based group index within the respective measurement.</summary>
            public readonly uint GroupIndex;

            /// <summary>Represents the zero-based signal index within the respective group.</summary>
            public readonly uint SignalIndex;

            /// <summary>Represents the signal.</summary>
            public readonly Signal Signal;

            /// <summary>Represents the reference signal.</summary>
            public readonly Signal RefSignal;

            public bool IsRefSignal { get { return SignalIndex == 0; } }

            public SignalInfo(IntPtr info, Group[] groups) : this()
            {
                MeasIndex = TilMethods.GetUInt(TilMethods.GetProperty(info, Tag.DataViewItemSignalinfoMeasindex));
                GroupIndex = TilMethods.GetUInt(TilMethods.GetProperty(info, Tag.DataViewItemSignalinfoGroupindex));
                SignalIndex = TilMethods.GetUInt(TilMethods.GetProperty(info, Tag.DataViewItemSignalinfoSignalindex));
                if (MeasIndex == 0 && groups.Length > GroupIndex) {
                    if (groups[GroupIndex].Signals.Length > 0) RefSignal = groups[GroupIndex].Signals[0];
                    if (groups[GroupIndex].Signals.Length > SignalIndex) Signal = groups[GroupIndex].Signals[SignalIndex];
                }
            }
        }

        public struct Timestamp
        {
            public readonly int Year;
            public readonly int Month;
            public readonly int Day;
            public readonly int Hour;
            public readonly int Minute;
            public readonly int Second;
            public readonly int Microsecond;

            /// <summary>Represents the number of days since December 30, 1899.</summary>
            public readonly double Time64;

            public bool IsAvailable { get { return Year != 0; } }

            public Timestamp(double timestamp) : this()
            {
#if DEBUG
                // CLR consistency check
                DateTime roundingCheck = new DateTime(29999);
                if (roundingCheck.Millisecond != 2) throw new Exception("Unexpected tick rounding in DateTime!");
#endif
                Time64 = timestamp;

                if (timestamp == 0 || timestamp < -693593 || timestamp > 2958465) {
                    Year = Month = Day = Hour = Minute = Second = Microsecond = 0;
                    return;
                }

                long ticks = (long)(timestamp * 864000000000);
                ticks += 599264352000000005;
                DateTime dt = new DateTime(ticks);
                Year = dt.Year;
                Month = dt.Month;
                Day = dt.Day;
                Hour = dt.Hour;
                Minute = dt.Minute;
                Second = dt.Second;
                Microsecond = ((int)(ticks % 10000000)) / 20;
                Microsecond *= 2;
            }

            public override string ToString()
            {
                return IsAvailable ? string.Format(CultureInfo.InvariantCulture,
                    "{0:D4}-{1:D2}-{2:D2} {3:D2}:{4:D2}:{5:D2}{6}{7:D6}",
                    Year, Month, Day, Hour, Minute, Second, 
                    CultureInfo.CurrentCulture.NumberFormat.PercentDecimalSeparator, 
                    Microsecond) : String.Empty;
            }
        }

#if DEBUG
        [Serializable()]
        public class Exception : System.Exception
        {
            public Exception() { }
            public Exception(string Message) : base(Message) { }
            //public Exception(string Message, Exception InnerException) : base(Message, InnerException) { }
            protected Exception(SerializationInfo Info, StreamingContext Context) : base(Info, Context) { }
        }
#endif

        /// <summary>Represents signal data types with following prefixes:
        /// Le = little endian (Intel), Be = big endian (Motorola), Rsvd = reserved for.</summary>
        public enum DataType
        {
            Unknown, SampleNumber, 
            LeUInt8, LeInt8, LeUInt16, LeInt16, LeUInt32, LeInt32, LeUInt64, LeInt64, 
            LeFloat32 = 32, LeFloat64, RsvdLeFloat80, 
            RsvdBeUInt8 = 16, RsvdBeInt8, RsvdBeUInt16, RsvdBeInt16, RsvdBeUInt32, RsvdBeInt32, RsvdBeUInt64, RsvdBeInt64,
            RsvdBeFloat32 = 48, RsvdBeFloat64, RsvdBeFloat80,
            RsvdChar8 = 64, RsvdChar16, 
            RsvdTime64 = 80
        }

        private const string TemesTilGuid = "{35F0EC67-B7ED-4627-9EF6-563016B176B5}";

        private enum PropertyType
        {
            Void, Bool, Int8, Int16, Int32, Int64, UInt8 = 8, UInt16, UInt32, UInt64,
            Float32 = 32, Float64, Float80, Time64 = 40, Data = 64, AnsiString, WideString, 
            Collection = 128, Vector, Iterator
        }
   
        private enum Tag
        {
            Local = 1,
              LocalLangid = 1,
              LocalResolvedlangid = 2,
              LocalSupportedlangids = 3,
            Info = 2,
              InfoSupportedtypes = 1,
                InfoSupportedtypesGuid = 1,
                InfoSupportedtypesDescription = 2,
                InfoSupportedtypesExtension = 3,
                InfoSupportedtypesArgguids = 4,
                InfoSupportedtypesFeatures = 5,
                  InfoSupportedtypesFeaturesGuid = 1,
                  InfoSupportedtypesFeaturesData = 2,
              InfoVersion = 2,
            Control = 3,
              ControlTypeindex = 1,
              ControlArgs = 2,
                ControlArgsGuid = 1,
                ControlArgsData = 2,
              ControlFilename = 3,
            Data = 4,
              DataComm = 1,
                DataCommName = 1,
                DataCommComment = 2,
                DataCommVariable = 3,
                  DataCommVariableKey = 1,
                  DataCommVariableValue = 2,
              DataMeas = 3,
                DataMeasComm = 1,
                  DataMeasCommName = 1,
                  DataMeasCommComment = 2,
                  DataMeasCommVariable = 3,
                    DataMeasCommVariableKey = 1,
                    DataMeasCommVariableValue = 2,
                DataMeasGroup = 2,
                  DataMeasGroupComm = 1,
                    DataMeasGroupCommName = 1,
                    DataMeasGroupCommComment = 2,
                    DataMeasGroupCommVariable = 3,
                      DataMeasGroupCommVariableKey = 1,
                      DataMeasGroupCommVariableValue = 2,
                  DataMeasGroupSamplecount = 2,
                  DataMeasGroupSamplerate = 3,
                  DataMeasGroupInfo = 4,
                    DataMeasGroupInfoTime = 1,
                      DataMeasGroupInfoTimeHwsetup = 1,
                      DataMeasGroupInfoTimeStart = 2,
                      DataMeasGroupInfoTimeHwreadout = 3,
                      // start of legacy support for V1.1.1 downwards
                      DataMeasGroupInfoTimeStamp = 4,
                        DataMeasGroupInfoTimeStampSampleindex = 1,
                        DataMeasGroupInfoTimeStampTime = 2,
                      // end of legacy support for V1.1.1 downwards 
                      DataMeasGroupInfoTimeSection = 4,
                        DataMeasGroupInfoTimeSectionSampleindex = 1,
                        DataMeasGroupInfoTimeSectionStart = 2,
                  DataMeasGroupSignal = 5,
                    DataMeasGroupSignalComm = 1,
                      DataMeasGroupSignalCommName = 1,
                      DataMeasGroupSignalCommComment = 2,
                      DataMeasGroupSignalCommVariable = 3,
                        DataMeasGroupSignalCommVariableKey = 1,
                        DataMeasGroupSignalCommVariableValue = 2,
                    DataMeasGroupSignalInternalname = 2,
                    DataMeasGroupSignalUnit = 3,
                    DataMeasGroupSignalData = 4,
                      DataMeasGroupSignalDataRaw = 1,
                        DataMeasGroupSignalDataRawType = 1,
                        DataMeasGroupSignalDataRawFactor = 2,
                        DataMeasGroupSignalDataRawOffset = 3,
                        DataMeasGroupSignalDataRawFile = 4,
                          DataMeasGroupSignalDataRawFileName = 1,
                          DataMeasGroupSignalDataRawFileOffset = 2,
                          DataMeasGroupSignalDataRawFileDatasize = 3,
                          DataMeasGroupSignalDataRawFileGap = 4,
                        DataMeasGroupSignalDataRawIterator = 5,
                      DataMeasGroupSignalDataInvalidvalue = 2,
                      DataMeasGroupSignalDataIterator = 3,
                      DataMeasGroupSignalDataFlags = 4,
                        DataMeasGroupSignalDataFlagsQuantized = 1,
                        DataMeasGroupSignalDataFlagsWraparoundcounter = 2,
                    DataMeasGroupSignalRefsignal = 5,
                      DataMeasGroupSignalRefsignalFactor = 1,
                      DataMeasGroupSignalRefsignalOffset = 2,
                    DataMeasGroupSignalView = 6,
                      DataMeasGroupSignalViewMin = 1,
                      DataMeasGroupSignalViewMax = 2,
                    DataMeasGroupSignalMarker = 7,
                      DataMeasGroupSignalMarkerName = 1,
                      DataMeasGroupSignalMarkerSampleindex = 2,
                      DataMeasGroupSignalMarkerVisible = 3,
                      DataMeasGroupSignalMarkerType = 4,
                      DataMeasGroupSignalMarkerText = 5,
                      DataMeasGroupSignalMarkerTextangle = 6,
                      DataMeasGroupSignalMarkerTextdistance = 7,
              DataView = 4,
                DataViewComm = 1,
                  DataViewCommName = 1,
                DataViewItem = 2,
                  DataViewItemComm = 1,
                    DataViewItemCommName = 1,
                  DataViewItemSignalinfo = 2,
                    DataViewItemSignalinfoMeasindex = 1,
                    DataViewItemSignalinfoGroupindex = 2,
                    DataViewItemSignalinfoSignalindex = 3,
                  DataViewItemUnit = 3,
                  DataViewItemFactor = 4,
                  DataViewItemOffset = 5,
                  DataViewItemReffactor = 6,
                  DataViewItemRefoffset = 7,
                  DataViewItemVisible = 8,
                  DataViewItemSlotVisible = 8,
                  DataViewItemDigits = 9,
                  DataViewItemCurveVisible = 10,
                  DataViewItemColor = 11,
                  DataViewItemThickness = 12,
                  DataViewItemPattern = 13,
                  DataViewItemBegin = 14,
                  DataViewItemEnd = 15,
                  DataViewItemAxisPos = 16,
                  DataViewItemXAxisPos = 17,
                  DataViewItemAxisMin = 18,
                  DataViewItemAxisMax = 19,
                  DataViewItemAxisName = 20,
                  DataViewItemAxisTitle = 21,
                  DataViewItemAxisDefMin = 22,
                  DataViewItemAxisDefMax = 23
        }

        // -----------------------------------------------------------------
        // "til_tms.dll" parameters are allowed to be NULL (to indicate non-
        // existing parameters). However, NULL parameters are catched to 
        // maintain a nice looking debug log when the debug version of 
        // "til_tms.dll" is used.
        //
        // It is possible to replace the system wide "til_tms.dll" by 
        // copying the "til_tms.dll" in the executable subfolder "\x86" or 
        // "\x64" respectively.
        //
        // Platform | Correct "til_tms.dll"/"zlipwapi.dll" version
        // ---------+-------------------------------------------------------
        // Any CPU  | 32-bit or x64 (depending on the running OS)
        // x86      | 32-bit
        // x64      | x64

        private static class TilMethods
        {
            private static IntPtr missingParameter = IntPtr.Zero;
            private static readonly IntPtr queryStringLength = new IntPtr(-1);

            internal static IntPtr CreateObject(string guid)
            {
                if (guid == null) return IntPtr.Zero;
                
                return NativeMethods.TilCreateObject(guid);
            }

            internal static void DestroyObject(IntPtr obj)
            {
                if (obj == IntPtr.Zero) return;

                NativeMethods.TilDestroyObject(obj);
            }

            internal static IntPtr GetRootProperty(IntPtr obj)
            {
                if (obj == IntPtr.Zero) return IntPtr.Zero;

                return NativeMethods.TilGetRootProperty(obj);
            }

            internal static IntPtr GetProperty(IntPtr property, Tag tag)
            {
                if (property == IntPtr.Zero) return IntPtr.Zero;

                return NativeMethods.TilGetProperty(property, (int)tag);
            }

            internal static PropertyType GetType(IntPtr property)
            {
                if (property == IntPtr.Zero) return PropertyType.Void;

                return (PropertyType)NativeMethods.TilGetType(property);
            }

            internal static ulong GetULong(IntPtr property, ulong defaultValue)
            {
                ulong value;

                if (property == IntPtr.Zero) return defaultValue;

                if (NativeMethods.TilGetUInt64(property, out value) == 0) return defaultValue;

                return value;
            }

            internal static ulong GetULong(IntPtr property)
            {
                return GetULong(property, 0);
            }

            internal static uint GetUInt(IntPtr property, uint defaultValue)
            {
                return (uint)GetULong(property, (ulong)defaultValue);
            }
            
            internal static uint GetUInt(IntPtr property)
            {
                return GetUInt(property, 0);
            }

            internal static long GetLong(IntPtr property, long defaultValue)
            {
                long value;

                if (property == IntPtr.Zero) return defaultValue;

                if (NativeMethods.TilGetInt64(property, out value) == 0) return defaultValue;

                return value;
            }

            internal static long GetLong(IntPtr property)
            {
                return GetLong(property, 0);
            }

            internal static int GetInt(IntPtr property, int defaultValue)
            {
                return (int)GetLong(property, (long)defaultValue);
            }

            internal static int GetInt(IntPtr property)
            {
                return GetInt(property, 0);
            }

            internal static double GetDouble(IntPtr property, double defaultValue)
            {
                double value;

                if (property == IntPtr.Zero) return defaultValue;

                if (NativeMethods.TilGetFloat64(property, out value) == 0) return defaultValue;

                return value;
            }

            internal static double GetDouble(IntPtr property)
            {
                return GetDouble(property, 0);
            }

            internal static double GetTime64(IntPtr property)
            {
                double value;

                if (property == IntPtr.Zero) return 0;

                if (NativeMethods.TilGetTime64(property, out value) == 0) return 0;

                return value;
            }

            internal static uint GetVectorSize(IntPtr property)
            {
                return (property != IntPtr.Zero) ? (uint)NativeMethods.TilGetVectorSize(property) : 0;
            }

            internal static IntPtr GetVectorItem(IntPtr property, uint index)
            {
                return NativeMethods.TilGetVectorItem(property, (IntPtr)index);
            }

            internal static string GetString(IntPtr property, string defaultValue)
            {
                if (property == IntPtr.Zero) return defaultValue;

                IntPtr length = queryStringLength;
#pragma warning disable 0642 // CS0642: "Possible mistaken empty statement"
                if (NativeMethods.TilGetString(property, null, queryStringLength, ref length) == 0) /* ignore function result */ ;
#pragma warning restore 0642
                if (length == queryStringLength) return defaultValue;

                StringBuilder value = new StringBuilder((int)length);
                if (NativeMethods.TilGetString(property, value, length, ref missingParameter) == 0) return defaultValue;

                return value.ToString();
            }

            internal static string GetString(IntPtr property)
            {
                return GetString(property, string.Empty);
            }

            internal static bool SetString(IntPtr property, string value)
            {
                if (property == IntPtr.Zero || value == null) return false;

                return NativeMethods.TilSetString(property, value) != 0;
            }

            internal static uint GetIteratorItemSize(IntPtr iterator)
            {
                if (iterator == IntPtr.Zero) return 0;

                return (uint)NativeMethods.TilGetIteratorItemSize(iterator);
            }

            internal static bool RewindIterator(IntPtr iterator)
            {
                if (iterator == IntPtr.Zero) return false;

                return NativeMethods.TilRewindIterator(iterator) != 0;
            }

            internal static bool GetNextIteratorItems<T>(IntPtr iterator, ref T[] values)
            {
                if (iterator == IntPtr.Zero || values == null) return false;
                if (GetIteratorItemSize(iterator) != Marshal.SizeOf(typeof(T))) return false;

                if (typeof(T) == typeof(double)) {
#if !SUPPORT_OLDER_TILTMS_VERSIONS
                    return NativeMethods.TilGetNextIteratorItems(iterator, (double[])(object)values, (IntPtr)values.Length) != 0;
#else
                    NativeMethods.TilGetNextIteratorItems(iterator, (double[])(object)values, (IntPtr)values.Length);
                    return true;
#endif
                } else if (typeof(T) == typeof(ushort)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (ushort[])(object)values, (IntPtr)values.Length) != 0;
                } else if (typeof(T) == typeof(uint)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (uint[])(object)values, (IntPtr)values.Length) != 0;
                } else if (typeof(T) == typeof(int)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (int[])(object)values, (IntPtr)values.Length) != 0;
                } else if (typeof(T) == typeof(ulong)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (ulong[])(object)values, (IntPtr)values.Length) != 0;
                } else if (typeof(T) == typeof(byte)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (byte[])(object)values, (IntPtr)values.Length) != 0;
                } else if (typeof(T) == typeof(long)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (long[])(object)values, (IntPtr)values.Length) != 0;
                } else if (typeof(T) == typeof(short)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (short[])(object)values, (IntPtr)values.Length) != 0;
                } else if (typeof(T) == typeof(sbyte)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (sbyte[])(object)values, (IntPtr)values.Length) != 0;
                } else if (typeof(T) == typeof(float)) {
                    return NativeMethods.TilGetNextIteratorItems(iterator, (float[])(object)values, (IntPtr)values.Length) != 0;
                }

                return false;
            }
        }

        private static class NativeMethods
        {
            static NativeMethods()
            {
                string path = new Uri(typeof(NativeMethods).Assembly.CodeBase).LocalPath;
                string folder = Path.GetDirectoryName(path);

                string subFolder = (IntPtr.Size == 8) ? "\\x64\\" : "\\x86\\";

                LoadLibrary(folder + subFolder + "til_tms.dll");
            }

            [DllImport("kernel32.dll")]
            private static extern IntPtr LoadLibrary(string libFileName);

            [DllImport("til_tms.dll", CharSet = CharSet.Unicode)]
            internal static extern IntPtr TilCreateObject(string guid);

            [DllImport("til_tms.dll")]
            internal static extern void TilDestroyObject(IntPtr obj);

            [DllImport("til_tms.dll")]
            internal static extern IntPtr TilGetRootProperty(IntPtr obj);

            [DllImport("til_tms.dll")]
            internal static extern IntPtr TilGetProperty(IntPtr property, int tag);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetType(IntPtr property);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetUInt64(IntPtr property, out ulong value);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetInt64(IntPtr property, out long value);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetFloat64(IntPtr property, out double value);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetTime64(IntPtr property, out double value);

            [DllImport("til_tms.dll")]
            internal static extern IntPtr TilGetVectorSize(IntPtr property);

            [DllImport("til_tms.dll")]
            internal static extern IntPtr TilGetVectorItem(IntPtr property, IntPtr index);

            [DllImport("til_tms.dll", CharSet = CharSet.Unicode)]
            internal static extern int TilGetString(IntPtr property, StringBuilder value, IntPtr maxLength, ref IntPtr valueLength);

            [DllImport("til_tms.dll", CharSet = CharSet.Unicode)]
            internal static extern int TilSetString(IntPtr property, string value);

            [DllImport("til_tms.dll")]
            internal static extern int TilRewindIterator(IntPtr iterator);

            [DllImport("til_tms.dll")]
            internal static extern IntPtr TilGetIteratorItemSize(IntPtr iterator);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] byte[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] sbyte[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] ushort[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] short[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] uint[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] int[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] ulong[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] long[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] float[] values, IntPtr count);

            [DllImport("til_tms.dll")]
            internal static extern int TilGetNextIteratorItems(IntPtr iterator, [Out()] double[] values, IntPtr count);
        }
    }
}
