%
%  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
%
%  Use of this source code is governed by a BSD-style license
%  that can be found in the LICENSE file in the root of the source
%  tree. An additional intellectual property rights grant can be found
%  in the file PATENTS.  All contributing project authors may
%  be found in the AUTHORS file in the root of the source tree.
%

function apmtest(task, testname, filepath, casenumber, legacy)
%APMTEST is a tool to process APM file sets and easily display the output.
%   APMTEST(TASK, TESTNAME, CASENUMBER) performs one of several TASKs:
%     'test'  Processes the files to produce test output.
%     'list'  Prints a list of cases in the test set, preceded by their
%             CASENUMBERs.
%     'show'  Uses spclab to show the test case specified by the
%             CASENUMBER parameter.
%
%   using a set of test files determined by TESTNAME:
%     'all'   All tests.
%     'apm'   The standard APM test set (default).
%     'apmm'  The mobile APM test set.
%     'aec'   The AEC test set.
%     'aecm'  The AECM test set.
%     'agc'   The AGC test set.
%     'ns'    The NS test set.
%     'vad'   The VAD test set.
%
%   FILEPATH specifies the path to the test data files.
%
%   CASENUMBER can be used to select a single test case. Omit CASENUMBER,
%   or set to zero, to use all test cases.
%

if nargin < 5 || isempty(legacy)
  % Set to true to run old VQE recordings.
  legacy = false;
end

if nargin < 4 || isempty(casenumber)
  casenumber = 0;
end

if nargin < 3 || isempty(filepath)
  filepath = 'data/';
end

if nargin < 2 || isempty(testname)
  testname = 'all';
end

if nargin < 1 || isempty(task)
  task = 'test';
end

if ~strcmp(task, 'test') && ~strcmp(task, 'list') && ~strcmp(task, 'show')
  error(['TASK ' task ' is not recognized']);
end

if casenumber == 0 && strcmp(task, 'show')
  error(['CASENUMBER must be specified for TASK ' task]);
end

inpath = [filepath 'input/'];
outpath = [filepath 'output/'];
refpath = [filepath 'reference/'];

if strcmp(testname, 'all')
  tests = {'apm','apmm','aec','aecm','agc','ns','vad'};
else
  tests = {testname};
end

if legacy
  progname = './test';
else
  progname = './process_test';
end

global farFile;
global nearFile;
global eventFile;
global delayFile;
global driftFile;

if legacy
  farFile = 'vqeFar.pcm';
  nearFile = 'vqeNear.pcm';
  eventFile = 'vqeEvent.dat';
  delayFile = 'vqeBuf.dat';
  driftFile = 'vqeDrift.dat';
else
  farFile = 'apm_far.pcm';
  nearFile = 'apm_near.pcm';
  eventFile = 'apm_event.dat';
  delayFile = 'apm_delay.dat';
  driftFile = 'apm_drift.dat';
end

simulateMode = false;
nErr = 0;
nCases = 0;
for i=1:length(tests)
  simulateMode = false;

  if strcmp(tests{i}, 'apm')
    testdir = ['apm/'];
    outfile = ['out'];
    if legacy
      opt = ['-ec 1 -agc 2 -nc 2 -vad 3'];
    else
      opt = ['--no_progress -hpf' ...
          ' -aec --drift_compensation -agc --fixed_digital' ...
          ' -ns --ns_moderate -vad'];
    end

  elseif strcmp(tests{i}, 'apm-swb')
    simulateMode = true;
    testdir = ['apm-swb/'];
    outfile = ['out'];
    if legacy
      opt = ['-fs 32000 -ec 1 -agc 2 -nc 2'];
    else
      opt = ['--no_progress -fs 32000 -hpf' ...
          ' -aec --drift_compensation -agc --adaptive_digital' ...
          ' -ns --ns_moderate -vad'];
    end
  elseif strcmp(tests{i}, 'apmm')
    testdir = ['apmm/'];
    outfile = ['out'];
    opt = ['-aec --drift_compensation -agc --fixed_digital -hpf -ns ' ...
        '--ns_moderate'];

  else
    error(['TESTNAME ' tests{i} ' is not recognized']);
  end

  inpathtest = [inpath testdir];
  outpathtest = [outpath testdir];
  refpathtest = [refpath testdir];

  if ~exist(inpathtest,'dir')
    error(['Input directory ' inpathtest ' does not exist']);
  end

  if ~exist(refpathtest,'dir')
    warning(['Reference directory ' refpathtest ' does not exist']);
  end

  [status, errMsg] = mkdir(outpathtest);
  if (status == 0)
    error(errMsg);
  end

  [nErr, nCases] = recurseDir(inpathtest, outpathtest, refpathtest, outfile, ...
      progname, opt, simulateMode, nErr, nCases, task, casenumber, legacy);

  if strcmp(task, 'test') || strcmp(task, 'show')
    system(['rm ' farFile]);
    system(['rm ' nearFile]);
    if simulateMode == false
      system(['rm ' eventFile]);
      system(['rm ' delayFile]);
      system(['rm ' driftFile]);
    end
  end
end

if ~strcmp(task, 'list')
  if nErr == 0
    fprintf(1, '\nAll files are bit-exact to reference\n', nErr);
  else
    fprintf(1, '\n%d files are NOT bit-exact to reference\n', nErr);
  end
end


function [nErrOut, nCases] = recurseDir(inpath, outpath, refpath, ...
    outfile, progname, opt, simulateMode, nErr, nCases, task, casenumber, ...
    legacy)

global farFile;
global nearFile;
global eventFile;
global delayFile;
global driftFile;

dirs = dir(inpath);
nDirs = 0;
nErrOut = nErr;
for i=3:length(dirs) % skip . and ..
  nDirs = nDirs + dirs(i).isdir;
end


if nDirs == 0
  nCases = nCases + 1;

  if casenumber == nCases || casenumber == 0

    if strcmp(task, 'list')
      fprintf([num2str(nCases) '. ' outfile '\n'])
    else
      vadoutfile = ['vad_' outfile '.dat'];
      outfile = [outfile '.pcm'];

      % Check for VAD test
      vadTest = 0;
      if ~isempty(findstr(opt, '-vad'))
        vadTest = 1;
        if legacy
          opt = [opt ' ' outpath vadoutfile];
        else
          opt = [opt ' --vad_out_file ' outpath vadoutfile];
        end
      end

      if exist([inpath 'vqeFar.pcm'])
        system(['ln -s -f ' inpath 'vqeFar.pcm ' farFile]);
      elseif exist([inpath 'apm_far.pcm'])
        system(['ln -s -f ' inpath 'apm_far.pcm ' farFile]);
      end

      if exist([inpath 'vqeNear.pcm'])
        system(['ln -s -f ' inpath 'vqeNear.pcm ' nearFile]);
      elseif exist([inpath 'apm_near.pcm'])
        system(['ln -s -f ' inpath 'apm_near.pcm ' nearFile]);
      end

      if exist([inpath 'vqeEvent.dat'])
        system(['ln -s -f ' inpath 'vqeEvent.dat ' eventFile]);
      elseif exist([inpath 'apm_event.dat'])
        system(['ln -s -f ' inpath 'apm_event.dat ' eventFile]);
      end

      if exist([inpath 'vqeBuf.dat'])
        system(['ln -s -f ' inpath 'vqeBuf.dat ' delayFile]);
      elseif exist([inpath 'apm_delay.dat'])
        system(['ln -s -f ' inpath 'apm_delay.dat ' delayFile]);
      end

      if exist([inpath 'vqeSkew.dat'])
        system(['ln -s -f ' inpath 'vqeSkew.dat ' driftFile]);
      elseif exist([inpath 'vqeDrift.dat'])
        system(['ln -s -f ' inpath 'vqeDrift.dat ' driftFile]);
      elseif exist([inpath 'apm_drift.dat'])
        system(['ln -s -f ' inpath 'apm_drift.dat ' driftFile]);
      end

      if simulateMode == false
        command = [progname ' -o ' outpath outfile ' ' opt];
      else
        if legacy
          inputCmd = [' -in ' nearFile];
        else
          inputCmd = [' -i ' nearFile];
        end

        if exist([farFile])
          if legacy
            inputCmd = [' -if ' farFile inputCmd];
          else
            inputCmd = [' -ir ' farFile inputCmd];
          end
        end
        command = [progname inputCmd ' -o ' outpath outfile ' ' opt];
      end
      % This prevents MATLAB from using its own C libraries.
      shellcmd = ['bash -c "unset LD_LIBRARY_PATH;'];
      fprintf([command '\n']);
      [status, result] = system([shellcmd command '"']);
      fprintf(result);

      fprintf(['Reference file: ' refpath outfile '\n']);

      if vadTest == 1
        equal_to_ref = are_files_equal([outpath vadoutfile], ...
                                       [refpath vadoutfile], ...
                                       'int8');
        if ~equal_to_ref
          nErr = nErr + 1;
        end
      end

      [equal_to_ref, diffvector] = are_files_equal([outpath outfile], ...
                                                   [refpath outfile], ...
                                                   'int16');
      if ~equal_to_ref
        nErr = nErr + 1;
      end

      if strcmp(task, 'show')
        % Assume the last init gives the sample rate of interest.
        str_idx = strfind(result, 'Sample rate:');
        fs = str2num(result(str_idx(end) + 13:str_idx(end) + 17));
        fprintf('Using %d Hz\n', fs);

        if exist([farFile])
          spclab(fs, farFile, nearFile, [refpath outfile], ...
              [outpath outfile], diffvector);
          %spclab(fs, diffvector);
        else
          spclab(fs, nearFile, [refpath outfile], [outpath outfile], ...
              diffvector);
          %spclab(fs, diffvector);
        end
      end
    end
  end
else

  for i=3:length(dirs)
    if dirs(i).isdir
      [nErr, nCases] = recurseDir([inpath dirs(i).name '/'], outpath, ...
          refpath,[outfile '_' dirs(i).name], progname, opt, ...
          simulateMode, nErr, nCases, task, casenumber, legacy);
    end
  end
end
nErrOut = nErr;

function [are_equal, diffvector] = ...
    are_files_equal(newfile, reffile, precision, diffvector)

are_equal = false;
diffvector = 0;
if ~exist(newfile,'file')
  warning(['Output file ' newfile ' does not exist']);  
  return
end

if ~exist(reffile,'file')
  warning(['Reference file ' reffile ' does not exist']);  
  return
end

fid = fopen(newfile,'rb');
new = fread(fid,inf,precision);
fclose(fid);

fid = fopen(reffile,'rb');
ref = fread(fid,inf,precision);
fclose(fid);

if length(new) ~= length(ref)
  warning('Reference is not the same length as output');
  minlength = min(length(new), length(ref));
  new = new(1:minlength);
  ref = ref(1:minlength);
end
diffvector = new - ref;

if isequal(new, ref)
  fprintf([newfile ' is bit-exact to reference\n']);
  are_equal = true;
else
  if isempty(new)
    warning([newfile ' is empty']);
    return
  end
  snr = snrseg(new,ref,80);
  fprintf('\n');
  are_equal = false;
end
