function itf = btkEmulateC3Dserver()
%BTKEMULATEC3DSERVER Creates an emulated COM object for C3Dserver.
%
%  ITF = BTKEMULATEC3DSERVER() returns a structure which emulates the COM
%  object generated by the function 'actxserver('C3Dserver.C3D')'. So, it gives
%  the possibility to run scripts which use C3Dserver by using BTK.
%
%  However, BTK did't make the same choice than C3Dserver to extract C3D 
%  informations. Then, you will find some slight differences between both:
%    - This emulation use function handles and then required for every
%      functions to be used with parenthesis (you cannot call 'itf.Close'
%      but 'itf.Close()'). Use parenthesis keep your code compatible with
%      C3Dserver.
%    - A C3D file is always opened in mode 3.
%    - The event's status is not kept in BTK. Using the following code:
%      'itf.SetEventStatus(0,'0')' sends a warning and do nothing. To remove
%      the warning, you have to add this command:
%      'warning('OFF','btk:C3Dserver:SetEventStatus')'
%    - The group and parameter number are generated from the index instead
%      of using the ID stored in the C3D file. Like that, the user doesn't 
%      need to manage this number. So, if you set the group or parameter 
%      number, a warning will inform you of the impossibility to do it. To
%      remove this warning, type the following command:
%      'warning('OFF','btk:C3Dserver:UniqueNumber')'
%      Removing this warning has an effect on the functions
%      SetGroupNumber, SetParameterNumber. But also on the function AddGroup
%      when you set manually the number.
%    - The function 'CompressParameterBlocks' is alway activated in BTK as the 
%      the number of blocks needed for the groups/parameters is automatically 
%      adapted when you write the C3D. Trying to deactivate the compression 
%      (by using CompressParameterBlocks(0)) will send a warning. To remove the
%      warning, you have to add the command:
%      'warning('OFF','btk:C3Dserver:CompressParameterBlocks')'.
%    - If some groups and parameters has the same name in the C3D file (which
%      is the case with the group PROCESSING and its parameter for C3D files
%      generated with Vicon Nexus 1.4), then BTK removes the duplication 
%      (which is not the case of C3Dserver).
%    - When you delete a group, it is not possible to keep the associated 
%      parameters (They are stored in the group). If you try to do it, then
%      a warning will be displayed and the parameters are removed. To remove the 
%      warning, use the command: 'warning('OFF','btk:C3Dserver:DeleteGroup')'. 
%    - The parameters are sorted by groups. Then if you select parameter by
%      using hardcoded indices, you could have unexpected results.
%    - The parameters' values are managed automatically inside BTK. In the
%      case of parameter with the type 'Char', BTK trims (remove leading and
%      trailing white space) values when you open the C3D file. If you set
%      a number in a parameter with the type Char, then the number is
%      converted into a string.
%    - When you modify the parameter's type, then BTK adapts its values. In
%      lots of conversion you can come back to the original value. However,
%      transforming a string which doesn't contain a number creates the
%      value 0. If you convert a Real into an Integer, you lost the 
%      floating information. If you convert an Integer into a Byte and the 
%      value is not in the range [-128, 127], then you lost the most 
%      significant part of the number.
%      For example, the conversion (Char->Integer) for 'FOO' creates the
%      number 0. But the conversion (Char->Integer) for '1234' creates the
%      number 1234. The conversion (Integer->Byte) for the number 12456 
%      creates the number 168.
%    - Modifying the parameter's type, adapts also its dimensions. 
%      For example, transforming a integer parameter into a char parameter
%      add 1 dimension corresponding to the number of characters. Or, in
%      the other case, transforming a char parameter into an integer,
%      remove.
%    - Retrieving a parameter in another index than the original will keep
%      the group for this index.
%    - Trying to retrieve a parameter which has the same name than one in 
%      the destination group (other than the original group) will throw an
%      error.
%    - An AV ratio equal to 0 in BTK is automatically converted to 1. The 
%      number of analog frames is at least equal to the number of video
%      frames.
%    - Starting with BTK 0.2, the camera mask is no longer supported. Trying
%      to extract or set a mask will display a warning. The returned masks
%      from the functions GetPointMask and GetPointMaskEx are fake masks and
%      always set to '0000000'. To remove the warning, you have to add
%      the command: 'warning('OFF','btk:C3Dserver:FakeMask')'.

%  Author: A. Barré
%  Copyright 2009-2014 Biomechanical ToolKit (BTK).

%  TODO:
%    - How to set parameter's values with the type 'Char'?
%    - What is the syntax to add frames other than using -1 and -2?
%    - Check for the btkGetForceData (remove offset part)
%    - Check for the btkGetMomentData (remove offset, in the global frame)

%  HISTORY:
%    - 13/12/28: btkEmulateC3Dserver 1.0 beta 4 released with BTK 0.3
%        - Default groups/parameters are now generated within the command NewFile.
%        - The writing of a C3D file relies now on the informations stored in the
%          existing groups/parameters as C3Dserver does (instead of the default 
%          behavior of BTK which regenerate groups/parameters from acquisition data).
%        - Events are correctly saved in the C3D header section.
%        - Emulation of some commands improved to speedup them.
%        - Generation of the internal ID fixed when btkEmulateC3Dserver is called
%          more than once in the same function.
%    - 12/08/30: btkEmulateC3Dserver 1.0 beta 3 released with BTK 0.2
%        - Code updated to remove all the BTK functions related to the camera mask
%          as this information was removed from BTK 0.2.
%    - 12/04/11: btkEmulateC3Dserver 1.0 beta 2 released with BTK 0.1.10
%        - Implementation errors in the function SaveFile for the C3Dserver emulation.
%    - 11/06/28: Initial public beta

% Construct the emulated COM object by using function handles.
id = btkC3DserverRequestNewHandle_p();
itf.GetRegistrationMode = 2; % Register mode (full speed)
itf.GetRegUserName = 'Free Emulation Copy';
itf.GetRegUserOrganization = 'Biomechanical ToolKit (BTK)';
itf.GetVersion = sprintf('C3D Server Emulator 1.0 (beta 4) - BTK version %s, compatible C3Dserver 1.144.0', btkGetVersion());
itf.GetHandle = @()btkC3DserverGetHandle(id);
itf.Open = @(filename,mode)btkC3DserverOpen(id,filename,mode);
itf.Close = @()btkC3DserverClose(id);
itf.NewFile = @(file, fileType, dataType, numAnalog, avRatio, numMarkers, frequencyRate, scalingFactor, numFrames)btkC3DserverNewFile(id, file, fileType, dataType, numAnalog, avRatio, numMarkers, frequencyRate, scalingFactor, numFrames);
itf.IsModified = @()btkC3DserverIsModified(id);
itf.SaveFile = @(filename, fileType)btkC3DserverSaveFile(id, filename, fileType);
itf.GetNumber3DPoints = @()btkC3DserverGetNumber3DPoints(id);
itf.GetAnalogChannels = @()btkC3DserverGetAnalogChannels(id);
itf.GetVideoFrameHeader = @(startEnd)btkC3DserverGetVideoFrame(id, startEnd); % Give the same result than the function GetVideoFrame (in BTK, a C3D is transformed in an acquisition object)
itf.GetVideoFrame = @(startEnd)btkC3DserverGetVideoFrame(id, startEnd);
itf.GetMaxInterpolationGap = @()btkC3DserverGetMaxInterpolationGap(id); % Fake result as BTK doesn't save this value
itf.SetMaxInterpolationGap = @(maxInterGap)btkC3DserverSetMaxInterpolationGap(id, maxInterGap); % Do nothing
itf.GetHeaderScaleFactor = @()btkC3DserverGetHeaderScaleFactor(id); % Return the value stored in POINT:SCALE
itf.GetStartingRecord = @()btkC3DserverGetStartingRecord(id);
itf.GetAnalogVideoRatio = @()btkC3DserverGetAnalogVideoRatio(id);
itf.GetVideoFrameRate = @()btkC3DserverGetVideoFrameRate(id);
itf.GetFileType = @()btkC3DserverGetFileType(id);
itf.GetDataType = @()btkC3DserverGetDataType(id);
itf.GetNumberEvents = @()btkC3DserverGetNumberEvents(id);
itf.GetEventTime = @(index)btkC3DserverGetEventTime(id, index);
itf.GetEventLabel = @(index)btkC3DserverGetEventLabel(id, index);
itf.GetEventStatus = @(index)btkC3DserverGetEventStatus(id, index);
itf.AddEvent = @(label, status, time)btkC3DserverAddEvent(id, label, status, time);
itf.SetEventLabel = @(index, label)btkC3DserverSetEventLabel(id, index, label);
itf.SetEventTime = @(index, time)btkC3DserverSetEventTime(id, index, time);
itf.SetEventStatus = @(index, status)btkC3DserverSetEventStatus(id, index, status);
itf.DeleteEvent = @(index)btkC3DserverDeleteEvent(id, index);
itf.SetStrictParameterChecking = @(value)btkC3DserverSetStrictParameterChecking(id, value);
itf.CompressParameterBlocks = @(compress)btkCompressParameterBlocks(id, compress);
itf.GetNumberGroups = @()btkC3DserverGetNumberGroups(id);
itf.GetGroupIndex = @(label)btkC3DserverGetGroupIndex(id, label);
itf.GetGroupName = @(index)btkC3DserverGetGroupName(id, index);
itf.GetGroupNumber = @(index)btkC3DserverGetGroupNumber(id, index);
itf.GetGroupLock = @(index)btkC3DserverGetGroupLock(id, index);
itf.GetGroupDescription = @(index)btkC3DserverGetGroupDescription(id, index);
itf.SetGroupName = @(index, name)btkC3DserverSetGroupName(id, index, name);
itf.SetGroupNumber = @(index, num)btkC3DserverSetGroupNumber(id, index, num);
itf.SetGroupDescription = @(index, desc)btkC3DserverSetGroupDescription(id, index, desc);
itf.SetGroupLock = @(index, locked)btkC3DserverSetGroupLock(id, index, locked);
itf.AddGroup = @(num, name, desc, locked)btkC3DserverAddGroup(id, num, name, desc, locked);
itf.DeleteGroup = @(index, deleteParameters)btkC3DserverDeleteGroup(id, index, deleteParameters);
itf.GetNumberParameters = @()btkC3DserverGetNumberParameters(id);
itf.GetParameterIndex = @(groupLabel, paramLabel)btkC3DserverGetParameterIndex(id, groupLabel, paramLabel);
itf.GetParameterName = @(index)btkC3DserverGetParameterName(id, index);
itf.GetParameterNumber = @(index)btkC3DserverGetParameterNumber(id, index);
itf.GetParameterLock = @(index)btkC3DserverGetParameterLock(id, index);
itf.GetParameterDescription = @(index)btkC3DserverGetParameterDescription(id, index);
itf.GetParameterType = @(index)btkC3DserverGetParameterType(id, index);
itf.GetParameterNumberDim = @(index)btkC3DserverGetParameterNumberDim(id, index);
itf.GetParameterDimension = @(index, dim)btkC3DserverGetParameterDimension(id, index, dim);
itf.GetParameterLength = @(index)btkC3DserverGetParameterLength(id, index);
itf.GetParameterValue = @(index, value)btkC3DserverGetParameterValue(id, index, value);
itf.SetParameterName = @(index, name)btkC3DserverSetParameterName(id, index, name);
itf.SetParameterNumber = @(index, num)btkC3DserverSetParameterNumber(id, index, num);
itf.SetParameterLock = @(index, locked)btkC3DserverSetParameterLock(id, index, locked);
itf.SetParameterDescription = @(index, desc)btkC3DserverSetParameterDescription(id, index, desc);
itf.SetParameterType = @(index, t)btkC3DserverSetParameterType(id, index, t);
itf.SetParameterValue = @(index, item, value)btkC3DserverSetParameterValue(id, index, item, value);
itf.AddParameter = @(name, desc, group, locked, t, numDim, dimList, data)btkC3DserverAddParameter(id, name, desc, group, locked, t, numDim, dimList, data);
itf.DeleteParameter = @(index)btkC3DserverDeleteParameter(id, index);
itf.CopyParameter = @(index)btkC3DserverCopyParameter(id, index);
itf.RetrieveParameter = @(index)btkC3DserverRetrieveParameter(id, index);
itf.RemoveParameterData = @(index, item)btkC3DserverRemoveParameterData(id, index, item);
itf.AddParameterData = @(index, items)btkC3DserverAddParameterData(id, index, items);
itf.GetAnalogData = @(channel, frame, subFrame, scaled, offset, scale, useScale)btkC3DserverGetAnalogData(id, channel, frame, subFrame, scaled, offset, scale, useScale);
itf.GetAnalogDataEx = @(channel, startFrame, endFrame, scaled, offset, scale, useScale)btkC3DserverGetAnalogDataEx(id, channel, startFrame, endFrame, scaled, offset, scale, useScale);
itf.SetAnalogData = @(channel, frame, subFrame, data)btkC3DserverSetAnalogData(id, channel, frame, subFrame, data);
itf.SetAnalogDataEx = @(channel, frame, data)btkC3DserverSetAnalogDataEx(id, channel, frame, data);
itf.SetAVRatio = @(avRatio)btkC3DserverSetAVRatio(id, avRatio);
itf.AddAnalogChannel = @()btkC3DserverAddAnalogChannel(id);
itf.DeleteAnalogChannel = @(index)btkC3DserverDeleteAnalogChannel(id, index);
itf.DeleteAnalogChannelWithParam = @(index)btkC3DserverDeleteAnalogChannelWithParameter(id, index);
itf.DeleteAnalogChannelWithParameter = @(index)btkC3DserverDeleteAnalogChannelWithParameter(id, index);
itf.GetForceData = @(cord, fp, startFrame, endFrame)btkC3DserverGetForceData(id, cord, fp, startFrame, endFrame);
itf.GetMomentData = @(cord, fp, startFrame, endFrame)btkC3DserverGetMomentData(id, cord, fp, startFrame, endFrame);
itf.ZeroAnalogChannel = @(channel, method, frames)btkC3DserverZeroAnalogChannel(id, channel, method, frames);
itf.GetPointData = @(channel, cord, frame, scaled)btkC3DserverGetPointData(id, channel, cord, frame, scaled);
itf.GetPointDataEx = @(channel, cord, startFrame, endFrame, scaled)btkC3DserverGetPointDataEx(id, channel, cord, startFrame, endFrame, scaled);
itf.GetPointResidualEx = @(channel, startFrame, endFrame)btkC3DserverGetPointResidualEx(id, channel, startFrame, endFrame);
itf.GetPointResidual = @(channel, frame)btkC3DserverGetPointResidual(id, channel, frame);
itf.GetPointMask = @(channel, frame)btkC3DserverGetPointMask(id, channel, frame);
itf.GetPointMaskEx = @(channel, startFrame, endFrame)btkC3DserverGetPointMaskEx(id, channel, startFrame, endFrame);
itf.SetPointData = @(channel, cord, frame, data)btkC3DserverSetPointData(id, channel, cord, frame, data);
itf.SetPointDataEx = @(channel, cord, frame, data)btkC3DserverSetPointDataEx(id, channel, cord, frame, data);
itf.AddMarker = @()btkC3DserverAddMarker(id);
itf.DeleteMarker = @(index)btkC3DserverDeleteMarker(id, index);
itf.DeleteMarkerWithParam = @(index)btkC3DserverDeleteMarkerWithParameter(id, index);
itf.DeleteMarkerWithParameter = @(index)btkC3DserverDeleteMarkerWithParameter(id, index);
itf.AddFrames = @(frames, insertAt)btkC3DserverAddFrames(id, frames, insertAt);
itf.DeleteFrames = @(startAt, numFrames)btkC3DserverDeleteFrames(id, startAt, numFrames);


% -------------------------------------------------------------------------
% Public functions to extract BTK data
% -------------------------------------------------------------------------

% Return the associated acquisition's handle
function h = btkC3DserverGetHandle(id)
global btkC3DserverHandles;
idx = btkC3DserverCheckHandleID_p(id);
h = btkC3DserverHandles(idx).handle;


% -------------------------------------------------------------------------
% Public functions to emulate the methods in the COM object
% -------------------------------------------------------------------------

% The input mode has no effect in our case
function res = btkC3DserverOpen(id, filename, mode)
global btkC3DserverHandles;
idx = btkC3DserverCheckHandleID_p(id);
try
    if (btkC3DserverCheckHandleValue_p(idx) == 0)
        btkDeleteAcquisition(btkC3DserverHandles(idx).handle);
    end
    % Handle
    warning('off', 'btk:ReadAcquisition');
    [btkC3DserverHandles(idx).handle, bo, sf] = btkReadAcquisition(filename);
    warning('on', 'btk:ReadAcquisition');
    pointScale = btkGetMetaData(btkC3DserverHandles(idx).handle,'POINT','SCALE');
    btkC3DserverHandles(idx).scalingFactor = pointScale.info.values(1);
    % File type
    switch bo
        case 'IEEE_LittleEndian'
            btkC3DserverHandles(idx).fileType = 1;
        case 'VAX_LittleEndian'
            btkC3DserverHandles(idx).fileType = 2;
        case 'IEEE_BiEndian'
            btkC3DserverHandles(idx).fileType = 3;
    end
    % Data type
    switch sf
        case 'Integer'
            btkC3DserverHandles(idx).dataType = 1;
        case 'Float'
            btkC3DserverHandles(idx).dataType = 2;
    end
    res = 0; % File read
    btkC3DserverHandles(idx).file = filename;
    btkC3DserverHandles(idx).modified = 0; % Reset
    btkC3DserverHandles(idx).parameterIndexMapping = {};
    btkC3DserverHandles(idx).copiedParameter = 0;
    % Extra: The events in the EVENT group are not included by C3Dserver but BTK does
    %        The emulation tries to hide them instead of removing them in the case
    %        the user mix C3Dserver and native BTK code.
    eventUsed = btkFindMetaData(btkC3DserverHandles(idx).handle,'EVENT','USED');
    if (~isstruct(eventUsed))
        eventUsed = btkFindMetaData(btkC3DserverHandles(idx).handle,'EVENTS','USED');
    end
    if (isstruct(eventUsed))
        num = eventUsed.info.values(1);
        first = btkGetEventNumber(btkC3DserverHandles(idx).handle) - num + 1;
        btkC3DserverHandles(idx).hiddenParameterEvents = [first,num];
    end
catch
    err = lasterror();
    error('btk:C3Dserver',['Internal error: ' , err.message]);
end
md = btkGetMetaData(btkC3DserverHandles(idx).handle);
num = 0;
if (isstruct(md.children))
    labels = fieldnames(md.children);
    for i = 1:size(labels,1)
        children = md.children.(labels{i}).children;
        if isstruct(children)
            labels_ = fieldnames(children);
            for j = 1:size(labels_,1)
                btkC3DserverHandles(idx).parameterIndexMapping(end+1,:) = {[i,j],num};
                num = num + 1;
            end
        end
    end
end


function btkC3DserverClose(id)
global btkC3DserverHandles;
idx = btkC3DserverCheckHandleID_p(id);
res = btkC3DserverCheckHandleValue_p(idx);
if (res == 0)
    btkDeleteAcquisition(btkC3DserverHandles(idx).handle);
    btkC3DserverHandles(idx).handle = NaN;
    btkC3DserverHandles(idx).file = '';
    btkC3DserverHandles(idx).scalingFactor = 1;
end


function res = btkC3DserverNewFile(id, file, fileType, dataType, numAnalog, avRatio, numMarkers, frequencyRate, scalingFactor, numFrames)
global btkC3DserverHandles;
if ((fileType < 1) || (fileType > 3))
    fileType = 1;
end
if ((dataType < 1) || (dataType > 2))
    dataType = 1;
end
idx = btkC3DserverCheckHandleID_p(id);
try
    if (btkC3DserverCheckHandleValue_p(idx) == 0)
        btkDeleteAcquisition(btkC3DserverHandles(idx).handle);
    end
    btkC3DserverHandles(idx).handle = btkNewAcquisition(numMarkers, numFrames, numAnalog, avRatio);
    btkC3DserverHandles(idx).file = file;
    btkC3DserverHandles(idx).fileType = fileType;
    btkC3DserverHandles(idx).dataType = dataType;
    btkC3DserverHandles(idx).scalingFactor = scalingFactor;
    btkC3DserverHandles(idx).modified = 1;
    h = btkC3DserverHandles(idx).handle;
    % Add default groups/parameters
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'Analog Parameters');
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'GEN_SCALE', btkMetaDataInfo('Real', 1), 'General Scale Factor');
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'RATE', btkMetaDataInfo('Real', avRatio * frequencyRate), 'Analog Base Sampling Rate');
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'USED', btkMetaDataInfo('Integer', numAnalog), 'Number of analog channels used');
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'SCALE', btkMetaDataInfo('Real', ones(numAnalog,1)), 'Analog scale factors');
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'OFFSET', btkMetaDataInfo('Integer', 2048 * ones(numAnalog,1)), 'Analog Offsets');
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'UNITS', btkMetaDataInfo('Char', repmat({''},numAnalog,1)), 'Analog units');
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'LABELS', btkMetaDataInfo('Char', cellstr(num2str([1:numAnalog]','CH%03i'))), 'Analog labels');
    btkC3DserverAddMetaData_p(h, 'ANALOG', 'DESCRIPTIONS', btkMetaDataInfo('Char', cellstr(num2str([1:numAnalog]','Channel %03i'))), 'Analog descriptions');
    btkC3DserverAddMetaData_p(h, 'POINT', 'Point Parameters');
    btkC3DserverAddMetaData_p(h, 'POINT', 'USED', btkMetaDataInfo('Integer', numMarkers), 'Number of Markers');
    btkC3DserverAddMetaData_p(h, 'POINT', 'FRAMES', btkMetaDataInfo('Integer', numFrames), 'Number of Video Frames');
    btkC3DserverAddMetaData_p(h, 'POINT', 'DATA_START', btkMetaDataInfo('Integer', 10), 'Start Record for Data');
    btkC3DserverAddMetaData_p(h, 'POINT', 'SCALE', btkMetaDataInfo('Real', scalingFactor), 'Scale factor for video data');
    btkC3DserverAddMetaData_p(h, 'POINT', 'RATE', btkMetaDataInfo('Real', frequencyRate), 'Video sampling rate');
    btkC3DserverAddMetaData_p(h, 'POINT', 'LABELS', btkMetaDataInfo('Char', cellstr(num2str([1:numMarkers]','MK%03i'))), 'Marker labels');
    btkC3DserverAddMetaData_p(h, 'POINT', 'DESCRIPTIONS', btkMetaDataInfo('Char', cellstr(num2str([1:numMarkers]','Marker %03i'))), 'Point descriptions');
    btkC3DserverAddMetaData_p(h, 'FORCE_PLATFORM', 'Force Platform Parameters');
    btkC3DserverAddMetaData_p(h, 'FORCE_PLATFORM', 'USED', btkMetaDataInfo('Integer', 0), 'Number of Force Plates');
    % Others
    btkSetFrequency(h, frequencyRate);
    % File created
    res = 1;
catch
    err = lasterror();
    error('btk:C3Dserver',['An error occured during the creation of the file: ''', err.message , ''''])
end


function res = btkC3DserverIsModified(id)
global btkC3DserverHandles;
idx = btkC3DserverCheckHandleID_p(id);
res = char(btkC3DserverHandles(idx).modified);


function res = btkC3DserverSaveFile(id, filename, fileType)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
if (isempty(filename))
    filename = btkC3DserverHandles(idx).file;
end
if (fileType == -1)
    fileType = btkC3DserverHandles(idx).fileType;
end
if ((fileType < 1) || (fileType > 3))
    fileType = 1;
end
byteorder = {'IEEE_LittleEndian', 'VAX_LittleEndian', 'IEEE_BigEndian'};
storageformat = {'Integer', 'Float'};
try
    btkWriteAcquisition(h, filename, 'ByteOrder', byteorder{fileType}, 'StorageFormat', storageformat{btkC3DserverHandles(idx).dataType}, 'InternalsUpdate', 'MetaDataBasedUpdate');
    res = 1; % File wrote
catch
    err = lasterror();
    error('btk:C3Dserver',['An error occured during the saving of the file: ''', err.message , ''''])
end


function num = btkC3DserverGetNumber3DPoints(id)
h = btkC3DserverExtractHandle_p(id);
num = btkGetPointNumber(h);


function num = btkC3DserverGetAnalogChannels(id)
h = btkC3DserverExtractHandle_p(id);
num = btkGetAnalogNumber(h);


function num = btkC3DserverGetVideoFrame(id,startEnd)
h = btkC3DserverExtractHandle_p(id);
if (startEnd == 0)
    num = btkGetFirstFrame(h);
else
    num = btkGetLastFrame(h);
end;


function num = btkC3DserverGetMaxInterpolationGap(id)
h = btkC3DserverExtractHandle_p(id);
num = btkGetMaxInterpolationGap(h);


function res = btkC3DserverSetMaxInterpolationGap(id, maxInterGap)
[h, idx] = btkC3DserverExtractHandle_p(id);
btkSetMaxInterpolationGap(h, maxInterGap);
res = 1;
btkC3DserverSetModified_p(idx,1);


function scale = btkC3DserverGetHeaderScaleFactor(id)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
scale = btkC3DserverHandles(idx).scalingFactor;


function num = btkC3DserverGetStartingRecord(id)
h = btkC3DserverExtractHandle_p(id);
pointDataStart = btkFindMetaData(h, 'POINT', 'DATA_START');
num = 2;
if isstruct(pointDataStart)
    num = pointDataStart.info.values(1);
end


function ratio = btkC3DserverGetAnalogVideoRatio(id)
h = btkC3DserverExtractHandle_p(id);
ratio = btkGetAnalogSampleNumberPerFrame(h);


function f = btkC3DserverGetVideoFrameRate(id)
h = btkC3DserverExtractHandle_p(id);
f = btkGetPointFrequency(h);


function type = btkC3DserverGetFileType(id)
global btkC3DserverHandles;
idx = btkC3DserverCheckHandleID_p(id);
type = btkC3DserverHandles(idx).fileType;


function type = btkC3DserverGetDataType(id)
global btkC3DserverHandles;
idx = btkC3DserverCheckHandleID_p(id);
type = btkC3DserverHandles(idx).dataType;


function num = btkC3DserverGetNumberEvents(id)
[h,idx] = btkC3DserverExtractHandle_p(id);
num = btkC3DserverGetEventNumber_p(h,idx);


function time = btkC3DserverGetEventTime(id, index)
[h,idx] = btkC3DserverExtractHandle_p(id);
if ((index < 0) || (index >= btkC3DserverGetEventNumber_p(h,idx)))
    error('btk:C3Dserver', 'Invalid index has been used.');
end
times = btkGetEventsValues(h);
index_ = btkC3DserverGetEventIndex_p(index,idx);
time = times(index_);


function label = btkC3DserverGetEventLabel(id, index)
[h,idx] = btkC3DserverExtractHandle_p(id);
if ((index < 0) || (index >= btkC3DserverGetEventNumber_p(h,idx)))
    error('btk:C3Dserver', 'Invalid index has been used.');
end
[times, labels] = btkGetEventsValues(h);
index_ = btkC3DserverGetEventIndex_p(index,idx);
label = labels{index_};


% The status returned is always set to 1 as the the metadata EVENTS doesn't take it into account.
function status = btkC3DserverGetEventStatus(id, index)
[h,idx] = btkC3DserverExtractHandle_p(id);
if ((index < 0) || (index >= btkC3DserverGetEventNumber_p(h,idx)))
    error('btk:C3Dserver', 'Invalid index has been used.');
end
%[times, labels, descriptions, statuses] = btkGetEventsValues(h);
%status = char(statuses(index+1));
status = char(1);


% The status is not saved as the the metadata EVENTS doesn't take it into account.
function res = btkC3DserverAddEvent(id, label, status, time)
[h, idx] = btkC3DserverExtractHandle_p(id);
try
    if (status == char(0))
        warning('btk:C3Dserver:AddEvent','The library BTK didn''t save event''s status and are always set as displayed.');
    end
    btkAppendEvent(h, label, time, '', '', '', -65536); % -65536: Special ID which will set the event detection flag to the value 0x10000. The ID will be then set to the default value (i.e. 0).
    res = btkC3DserverGetEventNumber_p(h,idx) - 1; % Added
    btkC3DserverSetModified_p(idx,1);
catch
    err = lasterror();
    error('btk:C3Dserver',['An error occured during the adding of the event: ''', err.message , ''''])
end


function res = btkC3DserverSetEventLabel(id, index, label)
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((index < 0) || (index >= btkC3DserverGetEventNumber_p(h,idx)))
    error('btk:C3Dserver', 'Invalid index has been used.');
end
index_ = btkC3DserverGetEventIndex_p(index,idx);
btkSetEventLabel(h, index_, label);
res = 1;
btkC3DserverSetModified_p(idx,1);


function res = btkC3DserverSetEventTime(id, index, time)
[h, idx] = btkC3DserverExtractHandle_p(id);
if((index < 0) || (index >= btkC3DserverGetEventNumber_p(h,idx)))
    error('btk:C3Dserver', 'Invalid index has been used.');
end
index_ = btkC3DserverGetEventIndex_p(index,idx);
btkSetEventTime(h, index_, time);
res = 1; % Modified
btkC3DserverSetModified_p(idx,1);


function res = btkC3DserverSetEventStatus(id, index, status)
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((index < 0) || (index >= btkC3DserverGetEventNumber_p(h,idx)))
    error('btk:C3Dserver', 'Invalid index has been used.');
end
%btkSetEventStatus(h, index+1, status);
if (status == '0')
    warning('btk:C3Dserver:SetEventStatus', 'The library BTK doesn''t set the event''s status.');
    res = 0; % Not modified
else
    res = 1;
    btkC3DserverSetModified_p(idx,1);
end


function res = btkC3DserverDeleteEvent(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((index < 0) || (index >= btkC3DserverGetEventNumber_p(h,idx)))
    error('btk:C3Dserver', 'Invalid index has been used.');
end
index_ = btkC3DserverGetEventIndex_p(index,idx);
btkRemoveEvent(h, index_);
res = 1; % Removed
btkC3DserverSetModified_p(idx,1);


function btkC3DserverSetStrictParameterChecking(id, value)
global btkC3DserverHandles;
idx = btkC3DserverCheckHandleID_p(id);
btkC3DserverHandles(idx).strictChecking = value;


function btkCompressParameterBlocks(id, compress)
btkC3DserverCheckHandleID_p(id);
if (compress == 0)
    warning('btk:C3Dserver:CompressParameterBlocks','Compression is done automatilcaly by BTK, as it regenerates the groups/parameters when you write the C3D.');
end


function num = btkC3DserverGetNumberGroups(id)
h = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    num = size(fieldnames(md.children),1);
else
    num = 0;
end


function index = btkC3DserverGetGroupIndex(id, label)
h = btkC3DserverExtractHandle_p(id);
index = -1; % Not found
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    for i = 1:size(labels,1)
        if (strcmp(labels{i}, label))
            index = i - 1;
            break;
        end
    end
else
    error('btk:C3Dserver', 'No group and parameter.');
end


function name = btkC3DserverGetGroupName(id, index)
h = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
        error('btk:C3Dserver', 'Invalid index has been used.');
    end
    name = labels{index+1};
else
    error('btk:C3Dserver', 'No group and parameter.');
end


function num = btkC3DserverGetGroupNumber(id, index)
h = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
        error('btk:C3Dserver', 'Invalid index has been used.');
    end
    num = -1*(index+1);
else
    error('btk:C3Dserver', 'No group and parameter.');
end


function locked = btkC3DserverGetGroupLock(id, index)
h = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
        error('btk:C3Dserver', 'Invalid index has been used.');
    end
    if (md.children.(labels{index+1}).unlocked == 0)
        locked = char(1);
    else
        locked = char(0);
    end
else
    error('btk:C3Dserver', 'No group and parameter.');
end


function desc = btkC3DserverGetGroupDescription(id, index)
h = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
        error('btk:C3Dserver', 'Invalid index has been used.');
    end
    desc = md.children.(labels{index+1}).description;
else
    error('btk:C3Dserver', 'No group and parameter.');
end


function res = btkC3DserverSetGroupName(id, index, name)
global btkC3DserverHandles
[h, idx] = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
        error('btk:C3Dserver', 'Invalid index has been used.');
    end
    for i =1:length(labels)
        if ((i ~= (index+1)) && strcmp(name, labels{i}))
            error('btk:C3Dserver','Group name already exists.');
        elseif ((i ~= (index+1)) && (btkC3DserverHandles(idx).strictChecking ~= 0))
            name_ = name;
            if (length(name_) > 6)
                name_ = name_(1:6);
            end
            label_ = labels{i};
            if (length(label_) > 6)
                label_ = label_(1:6);
            end
            if (strcmp(name_, label_))
                error('btk:C3Dserver','Group name already exists.');
            end
        end
    end
    try
        btkSetMetaDataLabel(h, index+1, name);
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
else
    error('btk:C3Dserver', 'No group and parameter.');
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetGroupNumber(id, index, num)
[h, idx] = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
        error('btk:C3Dserver', 'Incorrect value for group number.');
    elseif (num >= 0)
        error('btk:C3Dserver', 'Incorrect value for group number.');
    end
    warning('btk:C3Dserver:UniqueNumber', 'The library BTK doesn''t use an unique number for groups and parameters as it is automatically set when you write the C3D file. The number is not assigned.');
else
    error('btk:C3Dserver', 'No group and parameter.');
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetGroupDescription(id, index, desc)
[h, idx] = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
        error('btk:C3Dserver', 'Incorrect value for group number.');
    end
    btkSetMetaDataDescription(h, index+1, desc);
else
    error('btk:C3Dserver', 'No group and parameter.');
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetGroupLock(id, index, locked)
[h, idx] = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
        error('btk:C3Dserver', 'Incorrect value for group number.');
    end
    if (locked == char(0))
        btkSetMetaDataUnlock(h, index+1, 1);
    else
        btkSetMetaDataUnlock(h, index+1, 0);
    end
else
    error('btk:C3Dserver', 'No group and parameter.');
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverAddGroup(id, num, name, desc, locked)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
if (num > 0)
    error('btk:C3Dserver', 'Incorrect value for group number.');
elseif (num ~= 0)
    warning('btk:C3Dserver:UniqueNumber', 'The library BTK doesn''t use an unique number for group and parameter as it is automatically set when you write the C3D file. The number is not assigned.');
end
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    for i =1:length(labels)
        if strcmp(name, labels{i})
            error('btk:C3Dserver','Group name already exists.');
        elseif (btkC3DserverHandles(idx).strictChecking ~= 0)
            name_ = name;
            if (length(name_) > 6)
                name_ = name_(1:6);
            end
            label_ = labels{i};
            if (length(label_) > 6)
                label_ = label_(1:6);
            end
            if (strcmp(name_, label_))
                error('btk:C3Dserver','Group name already exists.');
            end
        end
    end
else
    error('btk:C3Dserver', 'No group and parameter.');
end
try
    md = btkAppendMetaData(h, name);
    res = size(fieldnames(md.children),1);
    btkSetMetaDataDescription(h, res, desc);
    if (locked == char(0))
        btkSetMetaDataUnlock(h, res, 1);
    else
        btkSetMetaDataUnlock(h, res, 0);
    end
    res = res - 1;
catch
    err = lasterror();
    error('btk:C3Dserver', ['Internal error: ', err.message]);
end
btkC3DserverSetModified_p(idx,1);


function res = btkC3DserverDeleteGroup(id, index, deleteParameters)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    if ((index < 0) || (index >= size(labels,1)))
    	error('btk:C3Dserver', 'Invalid index has been used.');
    end
    if (deleteParameters == char(0))
        warning('btk:C3Dserver:DeleteGroup', 'BTK doesn''t give the possibily to remove only the group and not its parameter (they will become orphans).');
    end
    btkRemoveMetaData(h, index+1);
else
    error('btk:C3Dserver', 'No group and parameter.');
end
% Update internal parameter index
mapping = btkC3DserverHandles(idx).parameterIndexMapping;
num = size(mapping,1);
foo = [mapping{:,1}]; foo = reshape(foo, 2, length(foo)/2)';
mapping = mapping(~(foo(:,1) == (index + 1)),:);
num = num - size(mapping,1);
for i=1:size(mapping,1)
    if (mapping{i,1}(1) > (index + 1))
        mapping{i,1}(1) = mapping{i,1}(1) - 1;
        mapping{i,2} = mapping{i,2} - num;
    end
end
btkC3DserverHandles(idx).parameterIndexMapping = mapping;
btkC3DserverSetModified_p(idx,1);
res = 1;


function num = btkC3DserverGetNumberParameters(id)
h = btkC3DserverExtractHandle_p(id);
num = 0;
md = btkGetMetaData(h);
if (isstruct(md.children))
    labels = fieldnames(md.children);
    for i = 1:size(labels,1)
        children = md.children.(labels{i}).children;
        if (isstruct(children))
            num = num + size(fieldnames(children),1);
        end
    end
else
    error('btk:C3Dserver', 'No group and parameter.');
end


function index = btkC3DserverGetParameterIndex(id, groupLabel, paramLabel)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
num = 0;
index = -1;
md = btkGetMetaData(h);
if (isstruct(md.children))
    idxGr = 0;
    labels = fieldnames(md.children);
    for i = 1:size(labels,1)
        children = md.children.(labels{i}).children;
        if (strcmp(labels{i}, groupLabel))
            idxGr = i;
            break;
        elseif (isstruct(children))
            num = num + size(fieldnames(children),1);
        end
    end
    if (idxGr ~= 0)
        idxPr = 0;
        md = md.children.(labels{idxGr});
        if (isstruct(md.children))
            labels = fieldnames(md.children);
            for i = 1:size(labels,1)
                if (strcmp(labels{i}, paramLabel))
                    idxPr = i;
                    break;
                else
                    num = num + 1;
                end
            end
        end
        if (idxPr ~= 0)
            for i = 1:size(btkC3DserverHandles(idx).parameterIndexMapping,1)
                if all(btkC3DserverHandles(idx).parameterIndexMapping{i,1} == [idxGr, idxPr])
                    index = btkC3DserverHandles(idx).parameterIndexMapping{i,2};
                    break;
                end
            end
        end
    end
else
    error('btk:C3Dserver', 'No group and parameter.');
end


function name = btkC3DserverGetParameterName(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
name = parameter.label;


function num = btkC3DserverGetParameterNumber(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
num = parameter.id;


function locked = btkC3DserverGetParameterLock(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
if (parameter.unlocked == 0)
    locked = char(1);
else
    locked = char(0);
end


function description = btkC3DserverGetParameterDescription(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
description = parameter.description;


function t = btkC3DserverGetParameterType(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
if (isstruct(parameter.info))
    switch parameter.info.format
        case 'Char'
            t = -1;
        case 'Byte'
            t = 1;
        case 'Integer'
            t = 2;
        case 'Real'
            t = 4;
    end
else
    error('btk:C3Dserver','The selected metadata doesn''t contains information.')
end


function num = btkC3DserverGetParameterNumberDim(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
if (isstruct(parameter.info))
    num = length(parameter.info.dims);
else
    error('btk:C3Dserver','The selected metadata doesn''t contains information.')
end


function num = btkC3DserverGetParameterDimension(id, index, dim)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
if (isstruct(parameter.info))
    if (dim >= length(parameter.info.dims))
        error('btk:C3Dserver', 'Invalid index has been used.');
    else
        num = parameter.info.dims(dim+1);
    end
else
    error('btk:C3Dserver','The selected metadata doesn''t contains information.')
end


function num = btkC3DserverGetParameterLength(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
if (isstruct(parameter.info))
    num = numel(parameter.info.values);
else
    error('btk:C3Dserver','The selected metadata doesn''t contains information.')
end


function value = btkC3DserverGetParameterValue(id, index, item)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    parameter = btkC3DserverGetCopiedParameter_p(idx);
else
    parameter = btkC3DserverExtractParameter_p(h, idx, index);
end
if (isstruct(parameter.info))
    if (item >= numel(parameter.info.values))
        if (strcmp(parameter.info.format, 'Char'))
            value = '';
        else
            value = 0;
        end
    else
        value = parameter.info.values(item+1);
        if (iscell(value))
            value = value{1};
        end
    end
else
    error('btk:C3Dserver','The selected metadata doesn''t contains information.')
end
switch parameter.info.format
    case 'Byte'
        value = uint8(value);
    case 'Integer'
        value = int32(value);
    case 'Real'
        value = single(value);
end

function res = btkC3DserverSetParameterName(id, index, name)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    btkC3DserverModifyCopiedParameter_p(idx, 'label', name);
else
    [parameter, indices, parent] = btkC3DserverExtractParameter_p(h, idx, index);
    labels = fieldnames(parent.children);
    for i =1:length(labels)
        if ((i ~= (index+1)) && strcmp(name, labels{i}))
            error('btk:C3Dserver','This parameter already exists within the group.');
        elseif ((i ~= (index+1)) && (btkC3DserverHandles(idx).strictChecking ~= 0))
            name_ = name;
            if (length(name_) > 6)
                name_ = name_(1:6);
            end
            label_ = labels{i};
            if (length(label_) > 6)
                label_ = label_(1:6);
            end
            if (strcmp(name_, label_))
                error('btk:C3Dserver','This parameter already exists within the group.');
            end
        end
    end
    try
        btkSetMetaDataLabel(h, indices(1), indices(2), name);
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetParameterNumber(id, index, num)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index ~= -1)
    [temp indices] = btkC3DserverExtractParameter_p(h, idx, index);
end
if (num <= 0)
    error('btk:C3Dserver', 'Incorrect value for parameter number.');
end
warning('btk:C3Dserver:UniqueNumber', 'The library BTK doesn''t use an unique number for group and parameter as it is automatically set when you write the C3D file. The number is not assigned.');
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetParameterLock(id, index, locked)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    unlocked = 0;
    if (locked == char(0))
        unlocked = 1;
    end
    btkC3DserverModifyCopiedParameter_p(idx, 'unlocked', unlocked);
else
    [temp indices] = btkC3DserverExtractParameter_p(h, idx, index);
    try
        if (locked == char(0))
            btkSetMetaDataUnlock(h, indices(1), indices(2), 1);
        else
            btkSetMetaDataUnlock(h, indices(1), indices(2), 0);
        end
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetParameterDescription(id, index, desc)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    btkC3DserverModifyCopiedParameter_p(idx, 'description', desc);
else
    [temp indices] = btkC3DserverExtractParameter_p(h, idx, index);
    try
        btkSetMetaDataDescription(h, indices(1), indices(2), desc);
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetParameterType(id, index, t)
[h, idx] = btkC3DserverExtractHandle_p(id);
switch t
    case -1
        t_ = 'Char';
    case 1
        t_ = 'Byte';
    case 2
        t_ = 'Integer';
    case 4
        t_ = 'Real';
    otherwise
        error('btk:C3Dserver', 'The given parameter''s type is unsupported.')
end
if (index == -1)
    btkC3DserverModifyCopiedParameter_p(idx, {'info','format'}, t_);
else
    [temp indices] = btkC3DserverExtractParameter_p(h, idx, index);
    try
        btkSetMetaDataFormat(h, indices(1), indices(2), t_);
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetParameterValue(id, index, item, value)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (index == -1)
    btkC3DserverModifyCopiedParameter_p(idx, {'info','values'}, value, item);
else
    [parameter indices] = btkC3DserverExtractParameter_p(h, idx, index);
    try
        if ((item < 0) || (item > numel(parameter.info.values)))
            error('btk:C3Dserver', 'Invalid index has been used.')
        end
        btkSetMetaDataValue(h, indices(1), indices(2), item+1, value);
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
end
% Why C3Dserver doesn't set the flag modified to true?
%btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverAddParameter(id, name, desc, group, locked, t, numDim, dimList, data)
global btkC3DserverHandles
[h, idx] = btkC3DserverExtractHandle_p(id);
md = btkGetMetaData(h);
if (~isstruct(md.children))
    error('btk:C3Dserver', 'No group and parameter.');
end
labels = fieldnames(md.children);
parent = [];
    for i = 1:length(labels)
        if strcmp(group, labels{i})
            parent = md.children.(labels{i});
            idxGr = i;
            break;
        end
    end
if isempty(parent)
    error('btk:C3Dserver','Could not find the selected group.');
end
if (isstruct(parent.children))
    labels = fieldnames(parent.children);
    for i = 1:length(labels)
        if (strcmp(name, labels{i}))
            error('btk:C3Dserver','This parameter already exists within the group.');
        elseif (btkC3DserverHandles(idx).strictChecking ~= 0)
            name_ = name;
            if (length(name_) > 6)
                name_ = name_(1:6);
            end
            label_ = labels{i};
            if (length(label_) > 6)
                label_ = label_(1:6);
            end
            if (strcmp(name_, label_))
                error('btk:C3Dserver','This parameter already exists within the group.');
            end
        end
    end
    idxPr = length(labels) + 1;
end
switch t
    case -1
        format = 'Char';
    case 1
        format = 'Byte';
    case 2
        format = 'Integer';
    case 4
        format = 'Real';
    otherwise
        error('btk:C3Dserver','The given parameter''s type is unsupported.');
end
if (numDim ~= numel(dimList))
    dimList = ones(1,numDim);
    if (t == -1)
        data = {};
    else
        data = 1;
    end
end
if (t ~= -1)
    data = double(data); % Adapt the data for btkAppendMetaData
end
try
    btkAppendMetaData(h, group, name, btkMetaDataInfo(format, data(:)));
    btkSetMetaDataDimensions(h, group, name, double(dimList));
    if (locked == char(0))
        btkSetMetaDataUnlock(h, group, name, 1);
    else
        btkSetMetaDataUnlock(h, group, name, 0);
    end
    btkSetMetaDataDescription(h, group, name, desc);
    res = btkC3DserverGetNumberParameters(id)-1;
    btkC3DserverHandles(idx).parameterIndexMapping(end+1,:) = {[idxGr, idxPr], res};
    %res = btkC3DserverGetParameterIndex(id, group, name);
catch
    err = lasterror();
    error('btk:C3Dserver', ['Internal error: ', err.message]);
end
% Why C3Dserver doesn't set the flag modified to true?
%btkC3DserverSetModified_p(idx,1);


function res = btkC3DserverDeleteParameter(id, index)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
[temp indices] = btkC3DserverExtractParameter_p(h, idx, index);
try
    btkRemoveMetaData(h, indices(1), indices(2));
    res = 1; % Deleted
catch
    err = lasterror();
    error('btk:C3Dserver', ['Internal error: ', err.message]);
end
% Update internal parameter index
mapping = btkC3DserverHandles(idx).parameterIndexMapping;
foo = [mapping{:,1}]; foo = reshape(foo, 2, length(foo)/2)';
mapping = mapping(~((foo(:,1) == indices(1)) & (foo(:,2) == indices(2))),:);
for i=1:size(mapping,1)
    if ((mapping{i,1}(1) == indices(1)) && (mapping{i,1}(2) > indices(2)))
        mapping{i,1}(2) = mapping{i,1}(2) - 1;
        mapping{i,2} = mapping{i,2} - 1;
    elseif (mapping{i,1}(1) > indices(1))
        mapping{i,2} = mapping{i,2} - 1;
    end
end
btkC3DserverHandles(idx).parameterIndexMapping = mapping;
btkC3DserverSetModified_p(idx,1);


function res = btkC3DserverCopyParameter(id, index)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
parameter = btkC3DserverExtractParameter_p(h, idx, index);
if ~iscell(parameter.info.values)
    parameter.info.values = {parameter.info.values};
end
btkC3DserverHandles(idx).copiedParameter = parameter;
res = 1; % Copied

function res = btkC3DserverRetrieveParameter(id, index)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
res = 0; % Not retrieved
if isstruct(btkC3DserverHandles(idx).copiedParameter)
    indices = [];
    for i = 1:length(btkC3DserverHandles(idx).parameterIndexMapping)
        if (btkC3DserverHandles(idx).parameterIndexMapping{i,2} == index)
            indices = btkC3DserverHandles(idx).parameterIndexMapping{i,1};
            break;
        end
    end
    if isempty(indices)
        error('btk:C3Dserver', 'Invalid index has been used.');
    end
    try
        parameter = btkC3DserverHandles(idx).copiedParameter;
        btkSetMetaDataLabel(h, indices(1), indices(2), parameter.label);
        btkSetMetaDataFormat(h, indices(1), indices(2), parameter.info.format);
        btkSetMetaDataDimensions(h, indices(1), indices(2), parameter.info.dims);
        for i = 1:numel(parameter.info.values);
            btkSetMetaDataValue(h, indices(1), indices(2), i, parameter.info.values{i});
        end
        btkSetMetaDataUnlock(h, indices(1), indices(2), parameter.unlocked);
        btkSetMetaDataDescription(h, indices(1), indices(2), parameter.description);
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
    % No modification
    %btkC3DserverSetModified_p(idx,1);
    res = 1;
end
 

function res = btkC3DserverRemoveParameterData(id, index, item)
[h, idx] = btkC3DserverExtractHandle_p(id);
[parameter indices] = btkC3DserverExtractParameter_p(h, idx, index);
if isstruct(parameter.info)
    if ~isempty(parameter.info.dims)
        if (~(strcmp(parameter.info.format, 'Char') && (numel(parameter.info.dims) == 1)) && (item < parameter.info.dims(end)))
            if (item < 0)
                error('btk:C3Dserver', 'Invalid (negative) index has been used.');
            end
            if (strcmp(parameter.info.format, 'Char'))
                idxStart = 2;
            else
                idxStart = 1;
            end
            p = prod(parameter.info.dims(idxStart:end-1));
            offset = p * item;
            values = parameter.info.values;
            dims = parameter.info.dims;
            dims(end) = dims(end) - 1;
            values = values(:);
            values = values([1:offset,offset+p+1:end]);
            try
                btkSetMetaDataDimensions(h,indices(1),indices(2),dims);
                for j=1:length(values);
                    btkSetMetaDataValue(h,indices(1),indices(2),j,values(j));
                end;
            catch
                err = lasterror();
                error('btk:C3Dserver', ['Internal error: ', err.message]);
            end
            % Not modified?
            %btkC3DserverSetModified_p(idx,1);
        end
        res = 1;
        return;
    end
end
res = 0;


function res = btkC3DserverAddParameterData(id, index, items)
[h, idx] = btkC3DserverExtractHandle_p(id);
[parameter indices] = btkC3DserverExtractParameter_p(h, idx, index);
if (items <= 0)
    error('btk:C3Dserver', 'Invalid value for the dimension.');
end
res = -1; % Not added
if isstruct(parameter.info) && ~isempty(parameter.info.dims)
    dims = parameter.info.dims;
    dims(end) = dims(end) + items;
    try
        btkSetMetaDataDimensions(h,indices(1),indices(2),dims);
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
    % Not modified?
    %btkC3DserverSetModified_p(idx,1);
    res = 1;
end


function data = btkC3DserverGetAnalogData(id, channel, frame, subFrame, scaled, offset, scale, useScale)
h = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetAnalogNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((frame < btkGetFirstFrame(h)) || (frame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid frame number.');
elseif ((subFrame < 1) || (subFrame > btkGetAnalogSampleNumberPerFrame(h)))
    error('btk:C3Dserver','Invalid subframe number.');
end
frame_ = (frame - btkGetFirstFrame(h)) * btkGetAnalogSampleNumberPerFrame(h) + subFrame;
[data, info] = btkGetAnalog(h, channel+1);
data = data(frame_);
% Unscale the data
offset_ = info.offset;
scale_ = info.scale;
data = data / scale_ + offset_;
if (scaled == char(0)) % Unscaled data
    % Nothing to do
elseif (useScale == char(0)) % Scaled data with acquisition's offset and scale
    % Rescale
    [rescale, reoffset] = btkC3DserverExtractAnalogInfos_p(h,channel+1);
    data = (data - reoffset) * rescale;
else % Scaled with the given offset and scale
    % Rescale
    data = (data - offset) * scale;
end
data = single(data);


function data = btkC3DserverGetAnalogDataEx(id, channel, startFrame, endFrame, scaled, offset, scale, useScale)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetAnalogNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif (endFrame < startFrame)
    error('btk:C3Dserver','End frame number lower than start frame number.');
elseif ((startFrame < btkGetFirstFrame(h)) || (startFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid start frame number.');
elseif ((endFrame < btkGetFirstFrame(h)) || (endFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid end frame number.');
end
sF = (startFrame - btkGetFirstFrame(h)) * btkGetAnalogSampleNumberPerFrame(h) + 1;
eF = (endFrame - btkGetFirstFrame(h) + 1) * btkGetAnalogSampleNumberPerFrame(h);
frames_ = sF:eF;
[data_, info] = btkGetAnalog(h, channel+1);
data_ = data_(frames_);
% Unscale the data
offset_ = info.offset;
scale_ = info.scale;
data_ = data_ / scale_ + offset_;
if (scaled == 0) % Unscaled data
    if (btkC3DserverHandles(idx).dataType == 1) % Integer format
        data_ = int16(data_);
    else
        data_ = single(data_);
    end
elseif (useScale == 0) % Scaled data with acquisition's offset and scale
    % Rescale
    [rescale, reoffset] = btkC3DserverExtractAnalogInfos_p(h,channel+1);
    data_ = (data_ - reoffset) * rescale;
    data_ = single(data_);
else % Scaled with the given offset and scale
    % Rescale
    data_ = (data_ - offset) * scale;
    if (btkC3DserverHandles(idx).dataType == 1) % Integer format
        data_ = single(data_);
    end
end
data = num2cell(data_);


function res = btkC3DserverSetAnalogData(id, channel, frame, subFrame, data)
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetAnalogNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((frame < btkGetFirstFrame(h)) || (frame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid frame number.');
elseif ((subFrame < 1) || (subFrame > btkGetAnalogSampleNumberPerFrame(h)))
    error('btk:C3Dserver','Invalid subframe number.');
end
frame_ = (frame - btkGetFirstFrame(h)) * btkGetAnalogSampleNumberPerFrame(h) + subFrame;
[data_, info] = btkGetAnalog(h, channel+1);
offset_ = info.offset;
scale_ = info.scale;
data_(frame_) = (data - offset_) * scale_ ;
btkSetAnalogValues(h, channel+1, data_);
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetAnalogDataEx(id, channel, frame, data)
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetAnalogNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((frame < btkGetFirstFrame(h)) || (frame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid frame number.');
elseif (size(data,1) ~= 1)
    error('btk:C3Dserver','Invalid data size. Must contain only one row.');
elseif ~isa(data,'int16')
    error('btk:C3Dserver','Incorrect data type passed to the function.');
end
sF = (frame - btkGetFirstFrame(h)) * btkGetAnalogSampleNumberPerFrame(h) + 1;
eF = btkGetAnalogFrameNumber(h);
if ((sF + length(data)) <= eF)
    frames_ = sF:sF+length(data)-1;
else
    frames_ = sF:eF;
end
[d, info] = btkGetAnalog(h, channel+1);
offset_ = info.offset;
scale_ = info.scale;
d(frames_) = (double(data(1:length(frames_))) - offset_) * scale_ ;
btkSetAnalogValues(h, channel+1, d);
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetAVRatio(id, avRatio)
[h, idx] = btkC3DserverExtractHandle_p(id);
if (btkGetAnalogNumber(h) > 0)
    error('btk:C3Dserver','Analog channels already exists in the file.');
elseif (avRatio < 0)
    error('btk:C3Dserver','You have to provide a ratio greater than or equal to zero.');
end
if (avRatio == 0)
    avRatio = 1;
end
btkSetAnalogSampleNumberPerFrame(h, round(avRatio));
btkC3DserverSetModified_p(idx,1);
res = 1;


function index = btkC3DserverAddAnalogChannel(id)
[h, idx] = btkC3DserverExtractHandle_p(id);
data = zeros(btkGetAnalogFrameNumber(h),1);
num = btkGetAnalogNumber(h)+1;
[scale, offset, label, description, gain] = btkC3DserverExtractAnalogInfos_p(h,num);
btkAppendAnalog(h, label, data, description);
btkSetAnalogOffset(h, num, offset);
btkSetAnalogScale(h, num, scale);
btkSetAnalogGain(h, num, gain);
analogUsed = btkFindMetaData(h, 'ANALOG', 'USED');
btkAppendMetaData(h, 'ANALOG', 'USED', btkMetaDataInfo('Integer', num));
if (isstruct(analogUsed) && ~isempty(analogUsed.description))
    btkSetMetaDataDescription(h,'ANALOG','USED',analogUsed.description);
end
index = num - 1;
btkC3DserverSetModified_p(idx,1);


function res = btkC3DserverDeleteAnalogChannel(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((index < 0) || (index >= btkGetAnalogNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
end
btkRemoveAnalog(h, index+1);
btkAppendMetaData(h, 'ANALOG', 'USED', btkMetaDataInfo('Integer', btkGetAnalogNumber(h)));
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverDeleteAnalogChannelWithParameter(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((index < 0) || (index >= btkGetAnalogNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
end
md = btkFindMetaData(h, 'FORCE_PLATFORM', 'CHANNEL');
if (isstruct(md))
    if (find(md.info.values == index + 1))
        error('btk:C3Dserver','Based on C3dServer, it seems not possible to delete an analog channel used for the force platforms. Contact the developers if is not the case.');
    end
end
btkRemoveAnalog(h, index+1);
btkAppendMetaData(h, 'ANALOG', 'USED', btkMetaDataInfo('Integer', btkGetAnalogNumber(h)));
if (isstruct(md))
    channels = md.info.values;
    indices = find(channels > index + 1);
    channels(indices) = channels(indices) - 1;
    btkAppendMetaData(h, 'FORCE_PLATFORM', 'CHANNEL', btkMetaDataInfo('Integer', channels));
end
md = btkFindMetaData(h, 'ANALOG', 'LABELS');
if (isstruct(md))
    labels = md.info.values;
    labels = labels(setdiff(1:length(labels),index+1));
    btkAppendMetaData(h, 'ANALOG', 'LABELS', btkMetaDataInfo('Char', labels));
end
md = btkFindMetaData(h, 'ANALOG', 'DESCRIPTIONS');
if (isstruct(md))
    descs = md.info.values;
    descs = descs(setdiff(1:length(descs),index+1));
    btkAppendMetaData(h, 'ANALOG', 'DESCRIPTIONS', btkMetaDataInfo('Char', descs));
end
md = btkFindMetaData(h, 'ANALOG', 'SCALE');
if (isstruct(md))
    scales = md.info.values;
    scales = scales(setdiff(1:length(scales),index+1));
    btkAppendMetaData(h, 'ANALOG', 'SCALE', btkMetaDataInfo('Real', scales));
end
md = btkFindMetaData(h, 'ANALOG', 'OFFSET');
if (isstruct(md))
    offsets = md.info.values;
    offsets = offsets(setdiff(1:length(offsets),index+1));
    btkAppendMetaData(h, 'ANALOG', 'OFFSET', btkMetaDataInfo('Integer', offsets));
end
md = btkFindMetaData(h, 'ANALOG', 'UNITS');
if (isstruct(md))
    units = md.info.values;
    units = units(setdiff(1:length(units),index+1));
    btkAppendMetaData(h, 'ANALOG', 'UNITS', btkMetaDataInfo('Char', units));
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function data = btkC3DserverGetForceData(id, cord, fp, startFrame, endFrame)
[h, frames] = btkC3DserverCheckFPInputs_p(id, 'F', cord, fp, startFrame, endFrame);
try
    fpws = btkGetForcePlatformWrenches(h, 0); % Local frame
catch
    err = lasterror();
    error('btk:C3Dserver', ['Internal error: ', err.message]);
end
if ((fp < 0) || (fp > length(fpws)))
    error('btk:C3Dserver', 'Incorrect force platform index.');
end
data_ = fpws(fp+1).F;
data_ = single(data_(frames,cord+1));
data = cell(length(frames),1);
for i=1:length(frames)
    data{i} = data_(i);
end


function data = btkC3DserverGetMomentData(id, cord, fp, startFrame, endFrame)
% error('btk:C3Dserver','Method GetMomentData not yet implemented.');
[h, frames] = btkC3DserverCheckFPInputs_p(id, 'M', cord, fp, startFrame, endFrame);
try
    grws = btkGetGroundReactionWrenches(h, 5); % Check for the threshold!
catch
    err = lasterror();
    error('btk:C3Dserver', ['Internal error: ', err.message]);
end
if ((fp < 0) || (fp > length(grws)))
    error('btk:C3Dserver', 'Incorrect force platform index.');
end
if (cord == 0)
    data_ = grws(fp+1).P(:,1); % Px
elseif (cord == 1)
    data_ = grws(fp+1).P(:,2); % Py
else
    data_ = grws(fp+1).M(:,3); % Tz
end
data_ = single(data_(frames));
data = cell(length(frames),1);
for i=1:length(frames)
    data{i} = data_(i);
end


function res = btkC3DserverZeroAnalogChannel(id, channel, method, frames)
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetAnalogNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((frames < 0) || (frames > btkGetPointFrameNumber(h)))
    error('btk:C3Dserver','Invalid frame number.');
elseif isempty(intersect(method, 1:4))
    error('btk:C3Dserver','Incorrect method supplied to zero the analog channel.');
elseif (~isempty(intersect(method, 3:4)) && ((frames < 1) || (frames > btkGetPointFrameNumber(h))))
    error('btk:C3Dserver','Incorrect number of frames supplied to zero the analog channel.');
end
[data, info] = btkGetAnalog(h,channel+1);
% Unscale the data
data = data / info.scale + info.offset;
% Remove baseline
if (method == 1)
    base = ceil(mean(data)) - info.offset;
elseif (method == 2)
    base = 0;
    md = btkFindMetaData(h,'FORCE_PLATFORM','ZERO');
    if isstruct(md)
        sF = md.info.values(1);
        eF = md.info.values(2);
        if (sF == 0)
            sF = 1;
        end
        sF = 1 + (sF - 1) * btkGetAnalogSampleNumberPerFrame(h);
        eF = eF * btkGetAnalogSampleNumberPerFrame(h);
        base = ceil(mean(data(sF:eF))) - info.offset;
    end
elseif (method == 3)
    base = ceil(mean(data(1:frames*btkGetAnalogSampleNumberPerFrame(h)))) - info.offset;
elseif (method == 4)
    num = btkGetAnalogFrameNumber(h);
    base = ceil(mean(data(num-frames*btkGetAnalogSampleNumberPerFrame(h)+1:num))) - info.offset;
end
data = data - base;
% Rescale data
data = (data - info.offset) * info.scale;
% Store modified data
btkSetAnalogValues(h, channel+1, data);
btkC3DserverSetModified_p(idx,1);
res = 1;


function data = btkC3DserverGetPointData(id, channel, cord, frame, scaled)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetPointNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((frame < btkGetFirstFrame(h)) || (frame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid frame number.');
elseif ((cord < 0) || (cord > 2))
    error('btk:C3Dserver','Invalid Index has been used.');
end
frame = frame - btkGetFirstFrame(h) + 1;
data = btkGetPoint(h, channel+1);
data = data(frame,cord+1);
% Unscale the data (if necessary)
if ((btkC3DserverHandles(idx).dataType == 1) && (scaled == char(0)))
    data = data / btkC3DserverHandles(idx).scalingFactor;
end
data = single(data);


function data = btkC3DserverGetPointDataEx(id, channel, cord, startFrame, endFrame, scaled)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetPointNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif (endFrame < startFrame)
    error('btk:C3Dserver','End frame number lower than start frame number.');
elseif ((startFrame < btkGetFirstFrame(h)) || (startFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid start frame number.');
elseif ((endFrame < btkGetFirstFrame(h)) || (endFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid end frame number.');
elseif ((cord < 0) || (cord > 2))
    error('btk:C3Dserver','Invalid Index has been used.');
end
start_ = startFrame - btkGetFirstFrame(h) + 1;
end_ = endFrame - btkGetFirstFrame(h) + 1;
data_ = btkGetPoint(h, channel+1);
frames = start_:end_;
data_ = data_(frames,cord+1);
% Unscale the data (if necessary)
if ((btkC3DserverHandles(idx).dataType == 1) && (scaled == char(0)))
    data_ = data_ / btkC3DserverHandles(idx).scalingFactor;
    data_ = int16(data_);
else
    data_ = single(data_);
end
data = num2cell(data_);


function data = btkC3DserverGetPointResidualEx(id, channel, startFrame, endFrame)
h = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetPointNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif (endFrame < startFrame)
    error('btk:C3Dserver','End frame number lower than start frame number.');
elseif ((startFrame < btkGetFirstFrame(h)) || (startFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid start frame number.');
elseif ((endFrame < btkGetFirstFrame(h)) || (endFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid end frame number.');
end
start_ = startFrame - btkGetFirstFrame(h) + 1;
end_ = endFrame - btkGetFirstFrame(h) + 1;
frames = start_:end_;
[values, data_] = btkGetPoint(h, channel+1);
data = num2cell(data_(frames));


function data = btkC3DserverGetPointResidual(id, channel, frame)
h = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetPointNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((frame < btkGetFirstFrame(h)) || (frame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid frame number.');
end
frame = frame - btkGetFirstFrame(h) + 1;
[values, data] = btkGetPoint(h, channel+1);
data = data(frame);


function data = btkC3DserverGetPointMask(id, channel, frame)
h = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetPointNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((frame < btkGetFirstFrame(h)) || (frame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid frame number.');
end
warning('btk:C3Dserver:FakeMask', 'The library BTK doesn''t support anymore the camera mask. Nothing is set. If you access to the mask, all the returned values are set to ''0000000''.');
data = '0000000';

function data = btkC3DserverGetPointMaskEx(id, channel, startFrame, endFrame)
h = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetPointNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif (endFrame < startFrame)
    error('btk:C3Dserver','End frame number lower than start frame number.');
elseif ((startFrame < btkGetFirstFrame(h)) || (startFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid start frame number.');
elseif ((endFrame < btkGetFirstFrame(h)) || (endFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid end frame number.');
end
warning('btk:C3Dserver:FakeMask', 'The library BTK doesn''t support anymore the camera mask. Nothing is set. If you access to the mask, all the returned values are set to ''0000000''.');
data = repmat({'0000000'},endFrame-startFrame+1,1);


function res = btkC3DserverSetPointData(id, channel, cord, frame, data)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetPointNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((cord < 0) || (cord > 4))
    error('btk:C3Dserver','Invalid has been used.');
elseif ((frame < btkGetFirstFrame(h)) || (frame > btkGetLastFrame(h)))
	error('btk:C3Dserver','Invalid frame number.');
end
frame_ = frame - btkGetFirstFrame(h) + 1;
scale = 1;
md = btkFindMetaData(h, 'POINT', 'SCALE');
if (isstruct(md) && isstruct(md.info))
    scale = abs(md.info.values(1));
end
if (cord <= 2)
	data_ = btkGetPoint(h, channel+1);
    data = double(data);
    if (btkC3DserverHandles(idx).dataType == 1) % Integer
        data = data * scale;
    end
    data_(frame_,cord+1) = data;
    btkSetPointValues(h, channel+1, data_);
elseif (cord == 3)
    [values, data_] = btkGetPoint(h, channel+1);
    data_(frame_) = floor(double(data) / scale) * scale;
    btkSetPointResiduals(h, channel+1, data_);
elseif (cord == 4)
    warning('btk:C3Dserver:FakeMask', 'The library BTK doesn''t support anymore the camera mask. Nothing is set. If you access to the mask, all the returned values are set to ''0000000''.');
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function res = btkC3DserverSetPointDataEx(id, channel, cord, frame, data)
global btkC3DserverHandles;
[h, idx] = btkC3DserverExtractHandle_p(id);
if ((channel < 0) || (channel >= btkGetPointNumber(h)))
    error('btk:C3Dserver','Invalid channel number.');
elseif ((frame < btkGetFirstFrame(h)) || (frame > btkGetLastFrame(h)))
    error('btk:C3Dserver','Invalid start frame number.');
elseif ((cord < 0) || (cord > 4))
    error('btk:C3Dserver','Invalid has been used.');
elseif (size(data,1) ~= 1)
    error('btk:C3Dserver','Invalid data size. Must contain only one row.');
end
sF = frame - btkGetFirstFrame(h) + 1;
eF = btkGetPointFrameNumber(h);
if ((sF + length(data)) <= eF)
    frames = sF:sF+length(data)-1;
else
    frames = sF:eF;
    data = data(1:length(frames));
end
scale = 1;
md = btkFindMetaData(h, 'POINT', 'SCALE');
if (isstruct(md) && isstruct(md.info))
    scale = abs(md.info.values(1));
end
if (cord <= 2)
    data_ = btkGetPoint(h, channel+1);
    data = double(data);
    if (btkC3DserverHandles(idx).dataType == 1) % Integer
        data = data * scale;
    end
    data_(frames,cord+1) = data;
    btkSetPointValues(h, channel+1, data_);
elseif (cord == 3)
    [values, data_] = btkGetPoint(h, channel+1);
    data_(frames) = floor(double(data) / scale) * scale;
    btkSetPointResiduals(h, channel+1, data_);
elseif (cord == 4)
    warning('btk:C3Dserver:FakeMask', 'The library BTK doesn''t support anymore the camera mask. Nothing is set. If you access to the mask, all the returned values are set to ''0000000''.');
end
btkC3DserverSetModified_p(idx,1);
res = 1;


function index = btkC3DserverAddMarker(id)
[h, idx] = btkC3DserverExtractHandle_p(id);
% index = -1; % Not modified
data = zeros(btkGetPointFrameNumber(h),3);
num = btkGetPointNumber(h) + 1;
label = ['uname*', num2str(num)];
btkAppendPoint(h, 'Marker', label, data);
btkAppendMetaData(h, 'POINT', 'USED', btkMetaDataInfo('Integer', num));
btkC3DserverSetModified_p(idx,1);
index = num-1;


function res = btkC3DserverDeleteMarker(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
% res = 0; % Not removed
if (index >= btkGetPointNumber(h))
    error('btk:C3Dserver','Invalid point number.');
end
btkRemovePoint(h, index+1);
btkAppendMetaData(h, 'POINT', 'USED', btkMetaDataInfo('Integer', btkGetPointNumber(h)));
res = 1;
btkC3DserverSetModified_p(idx,1);

function res = btkC3DserverDeleteMarkerWithParameter(id, index)
[h, idx] = btkC3DserverExtractHandle_p(id);
% res = 0; % Not removed
if (index >= btkGetPointNumber(h))
    error('btk:C3Dserver','Invalid channel number.');
end
btkRemovePoint(h, index+1);
btkAppendMetaData(h, 'POINT', 'USED', btkMetaDataInfo('Integer', btkGetPointNumber(h)));
md = btkFindMetaData(h, 'POINT', 'LABELS');
if (isstruct(md))
    labels = md.info.values;
    labels = labels(setdiff(1:length(labels),index+1));
    btkAppendMetaData(h, 'POINT', 'LABELS', btkMetaDataInfo('Char', labels));
end
md = btkFindMetaData(h, 'POINT', 'DESCRIPTIONS');
if (isstruct(md))
    descs = md.info.values;
    descs = descs(setdiff(1:length(descs),index+1));
%     btkAppendMetaData(h, 'POINT', 'DESCRIPTIONS', btkMetaDataInfo('Char', descs));
end
res = 1;
btkC3DserverSetModified_p(idx,1);


function num = btkC3DserverAddFrames(id, frames, insertAt)
[h, idx] = btkC3DserverExtractHandle_p(id);
ff = btkGetFirstFrame(h);
lf = btkGetLastFrame(h);
snpf = btkGetAnalogSampleNumberPerFrame(h);
% num = -1; % Not modified
if (frames < 0)
    error('btk:C3Dserver','Negative number of frames.');
elseif (insertAt == -1)
    insertAt = ff;
elseif (insertAt == -2)
    insertAt = lf+1;
% elseif ((insertAt < ff) || insertAt > lf)
%     error('btk:C3Dserver', 'Invalid insert index.');
else
    error('btk:C3Dserver','As C3dServer under Matlab seems to not give the possibilty to insert frame other than using the index -1 and -2, we decide to do the same. If it is not the case, please contact the developers.');
end
num = btkGetPointFrameNumber(h) + frames;
btkSetFrameNumber(h, num);
% Modifying points
pidx = insertAt:insertAt+frames-1;
newDataIdx = insertAt+frames:num;
oldDataIdx = insertAt:num-frames;
% - values
pv = btkGetPointsValues(h);
pv(newDataIdx,:) = pv(oldDataIdx,:);
pv(pidx,:) = zeros(length(pidx), 3 * btkGetPointNumber(h));
btkSetPointsValues(h,pv);
% - residuals
rv = btkGetPointsResiduals(h);
rv(newDataIdx,:) = rv(oldDataIdx,:);
rv(pidx,:) = zeros(length(pidx), btkGetPointNumber(h));
btkSetPointsResiduals(h,rv);
% Modifying analog channels
aidx = (insertAt-1)*snpf+1:(insertAt+frames-1)*snpf;
av = btkGetAnalogsValues(h);
av(aidx(end)+1:end,:) = av(aidx(1):end-frames*snpf,:);
av(aidx,:) = zeros(length(aidx), btkGetAnalogNumber(h));
btkSetAnalogsValues(h, av);
% Update the parameter POINT:FRAMES
btkAppendMetaData(h, 'POINT', 'FRAMES', btkMetaDataInfo('Integer', num));
btkC3DserverSetModified_p(idx,1);


function num = btkC3DserverDeleteFrames(id, startAt, frames)
[h, idx] = btkC3DserverExtractHandle_p(id);
ff = btkGetFirstFrame(h);
lf = btkGetLastFrame(h);
snpf = btkGetAnalogSampleNumberPerFrame(h);
% num = -1; % Not modified
if ((startAt < ff) || startAt > lf)
    error('btk:C3Dserver','Invalid index.');
elseif (frames > lf - startAt + 1)
    error('btk:C3Dserver','Incorrect number of frames specified.')
elseif (frames == lf - ff + 1) % Clear all
    error('btk:C3Dserver','Due to the mechanism used in BTK, it is not possible to remove all the frames. Contact the developers if you really need it.')
end
% Data to keep
% - Point
pidx = setdiff(ff:lf, startAt:startAt+frames-1)-ff+1;
pv = btkGetPointsValues(h);
pv = pv(pidx,:);
rv = btkGetPointsResiduals(h);
rv = rv(pidx,:);
% - Analog
aidx = setdiff(ff:lf*snpf, ((startAt-1)*snpf)+ff:(startAt+frames-1)*snpf)-ff+1;
av = btkGetAnalogsValues(h);
av = av(aidx,:);
% Resizing
num = btkGetPointFrameNumber(h) - frames;
btkSetFrameNumber(h, num);
% Storing modifications
btkSetPointsValues(h,pv);
btkSetPointsResiduals(h,rv);
btkSetAnalogsValues(h, av);
% Update the parameter POINT:FRAMES
btkAppendMetaData(h, 'POINT', 'FRAMES', btkMetaDataInfo('Integer', num));
btkC3DserverSetModified_p(idx,1);


% -------------------------------------------------------------------------
% Private functions
% -------------------------------------------------------------------------

% Create a new handle and return its ID.
function id = btkC3DserverRequestNewHandle_p()
global btkC3DserverHandles;
id = size(btkC3DserverHandles,2);
idx = id + 1;
btkC3DserverHandles(idx).id = id;
btkC3DserverHandles(idx).handle = NaN;
btkC3DserverHandles(idx).file = '';
btkC3DserverHandles(idx).fileType = 0;
btkC3DserverHandles(idx).dataType = 0;
btkC3DserverHandles(idx).scalingFactor = 1;
btkC3DserverHandles(idx).modified = 0;
btkC3DserverHandles(idx).strictChecking = 1;
btkC3DserverHandles(idx).parameterIndexMapping = {};
btkC3DserverHandles(idx).copiedParameter = 0;
btkC3DserverHandles(idx).hiddenParameterEvents = [0,0];

% Check if the given ID is valid and then return the index of the associated index.
function idx = btkC3DserverCheckHandleID_p(id)
global btkC3DserverHandles;
idx = NaN;
for i=1:size(btkC3DserverHandles,2)
    if (btkC3DserverHandles(i).id == id)
        idx = i;
        break;
    end
end
if isnan(idx)
    error('Unknown C3Dserver handle ID.');
end

% Check if the handle at the index 'idx' is valid (not NaN).
function res = btkC3DserverCheckHandleValue_p(idx)
global btkC3DserverHandles;
res = 0;
if (isnan(btkC3DserverHandles(idx).handle))
    res = 1;
end


% Check the validity of the ID, the value of the handle and extract it.
function [h, idx] = btkC3DserverExtractHandle_p(id)
global btkC3DserverHandles;
idx = btkC3DserverCheckHandleID_p(id);
if (btkC3DserverCheckHandleValue_p(idx) ~= 0)
    error('File is not open.');
end
h = btkC3DserverHandles(idx).handle;


% Set the handle as modified
function btkC3DserverSetModified_p(idx, s)
global btkC3DserverHandles;
btkC3DserverHandles(idx).modified = s;


% Return the parameter associated with the given index
function [parameter, indices, parent] = btkC3DserverExtractParameter_p(h, idx, index)
global btkC3DserverHandles;
index_ = find([btkC3DserverHandles(idx).parameterIndexMapping{:,2}] == index);
if isempty(index_)
    error('btk:C3Dserver', 'Invalid index has been used.');
elseif (length(index_) ~= 1)
    error('btk:C3Dserver', 'Duplicated internal parameter index.');
else
    indices = btkC3DserverHandles(idx).parameterIndexMapping{index_,1};
    try
        md = btkGetMetaData(h);
        if (isstruct(md.children))
            labels = fieldnames(md.children);
            if (size(labels,1) < indices(1))
                error('btk:C3Dserver', 'Invalid internal group index.');
            end
            children = md.children.(labels{indices(1)}).children;
            if (isstruct(children))
                labels_ = fieldnames(children);
                if (size(labels_,1) < indices(2))
                    error('btk:C3Dserver', 'Invalid internal parameter index.');
                else
                    label = labels_{indices(2)};
                    parameter = children.(label);
                    parameter.label = label;
                    parameter.id = indices(1);
                    parent = md.children.(labels{indices(1)});
                    return;
                end
            else
                error('btk:C3Dserver', 'Invalid internal parameter index (no parameter).');
            end
        else
            error('btk:C3Dserver', 'No group and parameter.');
        end
    catch
        err = lasterror();
        error('btk:C3Dserver', ['Internal error: ', err.message]);
    end
end

function parameter = btkC3DserverGetCopiedParameter_p(idx)
global btkC3DserverHandles;
if ~isstruct(btkC3DserverHandles(idx).copiedParameter)
    error('btk:C3Dserver', 'Invalid index has been used.');
end
parameter = btkC3DserverHandles(idx).copiedParameter;

function btkC3DserverModifyCopiedParameter_p(idx, field, value, item)
global btkC3DserverHandles;
if ~isstruct(btkC3DserverHandles(idx).copiedParameter)
    error('btk:C3Dserver', 'Invalid index has been used.');
end
if (nargin == 4) % info.values
    values = btkC3DserverHandles(idx).copiedParameter.(field{1}).(field{2});
    if (item > numel(values))
        error('btk:C3Dserver', 'Invalid index has been used.')
    end
    values{item+1} = value;
    btkC3DserverHandles(idx).copiedParameter.(field{1}).(field{2}) = values;
elseif (iscell(field)) % info.*
    btkC3DserverHandles(idx).copiedParameter.(field{1}).(field{2}) = value;
else
    btkC3DserverHandles(idx).copiedParameter.(field) = value;
end

function [scale, offset, label, description, gain] = btkC3DserverExtractAnalogInfos_p(h,index)
scale = 1;
gen_scale = 1;
offset = 0;
label = ['uname_', num2str(index)];
gain = 0;
description = '';
md = btkGetMetaData(h);
if (isstruct(md.children) && isfield(md.children, 'ANALOG'))
    md = md.children.ANALOG;
    if (isstruct(md.children))
        % SCALE
        if (isfield(md.children, 'SCALE') && isstruct(md.children.SCALE.info) && (numel(md.children.SCALE.info.values) >= index))
            scale = md.children.SCALE.info.values(index);
        end
        % GEN_SCALE
        if (isfield(md.children, 'GEN_SCALE') && isstruct(md.children.GEN_SCALE.info))
            gen_scale = md.children.GEN_SCALE.info.values(1);
        end
        scale = scale * gen_scale;
        % OFFSET
        if (isfield(md.children, 'OFFSET') && isstruct(md.children.OFFSET.info) && (numel(md.children.OFFSET.info.values) >= index))
            format = 'Signed';
            offsets = md.children.OFFSET.info.values;
%             bits = 12;
%             format = 'Signed';
%             if (isfield(md.children, 'BITS') && isstruct(md.children.BITS.info))
%                 bits = md.children.BITS.info.values(1);
%             end
            if (isfield(md.children, 'FORMAT') && isstruct(md.children.FORMAT.info))
                format = md.children.FORMAT.info.values{1};
            end
%             % Check
%             inc = 1;
%             while (inc <= numel(offsets))
%                 if (offsets(inc) > 2^bits)
%                     bits = bits * 2;
%                 else
%                     inc = inc + 1;
%                 end
%             end
%             if (bits >= 16)
%                 format = 'Unsigned';
%             end
            offset = offsets(index);
            if (strcmpi(format,'Unsigned'))
                offset = double(typecast(int16(offset), 'uint16'));
            end
        end
        % LABELS
        if (isfield(md.children, 'LABELS') && isstruct(md.children.LABELS.info) && (numel(md.children.LABELS.info.values) >= index))
            label = md.children.LABELS.info.values{index};
        end
        % DESCRIPTIONS
        if (isfield(md.children, 'DESCRIPTIONS') && isstruct(md.children.DESCRIPTIONS.info) && (numel(md.children.DESCRIPTIONS.info.values) >= index))
            description = md.children.DESCRIPTIONS.info.values{index};
        end
        % GAIN
        if (isfield(md.children, 'GAIN') && isstruct(md.children.GAIN.info) && (numel(md.children.GAIN.info.values) >= index))
            gain = md.children.GAIN.info.values(index);
        end
    end
end

function [h, frames] = btkC3DserverCheckFPInputs_p(id, component, cord, fp, startFrame, endFrame)
h = btkC3DserverExtractHandle_p(id);
switch component
    case 'F'
        cpt = 'force';
    case 'M'
        cpt = 'moment';
end
if ((startFrame == -1) && (endFrame == -1))
    startFrame = btkGetFirstFrame(h);
    endFrame = btkGetLastFrame(h);
end
if ((cord < 0) || (cord > 2))
    error('btk:C3Dserver', ['Incorrect ', cpt, ' component.']);
elseif ((startFrame < btkGetFirstFrame(h)) || (startFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver', 'Incorrect start index.');
elseif ((endFrame < btkGetFirstFrame(h)) || (endFrame > btkGetLastFrame(h)))
    error('btk:C3Dserver', 'Invalid end index.');
elseif (endFrame < startFrame)
    error('btk:C3Dserver', 'End index lower then start index.');
end
sF = (startFrame - btkGetFirstFrame(h)) * btkGetAnalogSampleNumberPerFrame(h) + 1;
eF = (endFrame - btkGetFirstFrame(h) + 1) * btkGetAnalogSampleNumberPerFrame(h);
frames = sF:eF;

% Last input is the description
function btkC3DserverAddMetaData_p(h, varargin)
btkAppendMetaData(h, varargin{1:end-1});
if (isstruct(varargin{end-1}))
    btkSetMetaDataDescription(h, varargin{1:end-2}, varargin{end});
else
    btkSetMetaDataDescription(h, varargin{1:end-1}, varargin{end});
end

function num = btkC3DserverGetEventNumber_p(h,idx)
global btkC3DserverHandles;
num = btkGetEventNumber(h) - btkC3DserverHandles(idx).hiddenParameterEvents(2);

function index_ = btkC3DserverGetEventIndex_p(index,idx)
global btkC3DserverHandles;
index_ = index + 1;
if (index_ >= btkC3DserverHandles(idx).hiddenParameterEvents(1))
    index_ = index_ + btkC3DserverHandles(idx).hiddenParameterEvents(2);
end