// =====================================================
//
//   miniLA_win - Data Routines
//
//   (c) miniLA Team
//
// =====================================================
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this package; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

unit uDataHold;
interface

uses StdCtrls, SysUtils, Classes, uLATypes;

type
   TSamplePtr = record
      position 	: int64;		// position
      data_idx	: integer;		// sample number
      offset	: integer;		// sample offset
   end;

type
   TDataHold = class(TObject)
    protected
       FDataLength 	: integer;	// datalength (# of samples)
       FMaxPosition	: int64;	// max. position
       FSample		: cardinal;
       FSamplePtr	: TSamplePtr;
       FStateAnalysis	: boolean;
    public
       DataArray : TDataBuffer;      	// data

       constructor Create; overload;
       procedure InitData(datalenght: integer; stateanalysis: boolean);
       function GetSampleValue(pos: int64): cardinal;
       function GetPosition(data_idx: integer): int64;
       function FindChange(mask: cardinal; lastpos: int64; go_forward: boolean): boolean;
       property Sample: cardinal	read FSample;
       property Position: int64   	read FSamplePtr.position;
       property MaxPosition: int64 	read FMaxPosition;
       property DataLength: integer     read FDataLength;
       property StateAnalysis: boolean  read FStateAnalysis;
    end;

implementation



{ TDataHold }

//=================================================================
// Create dataholder
//=================================================================
constructor TDataHold.Create;
begin
   inherited;
   FDataLength := 1;
   FStateAnalysis := false;
   FMaxPosition := 0;
   FillChar(DataArray,SizeOf(DataArray),0);
end;

//=================================================================
// Initialize dataholder
// - sets up internal variables and calculates max. position
//=================================================================
procedure TDataHold.InitData(datalenght: integer; stateanalysis: boolean);
var
   i	: integer;
begin
   FDataLength := datalenght;
   FStateAnalysis := stateanalysis;
   FMaxPosition := 0;
   for i := 0 to FDataLength-1 do
    begin
       if FStateAnalysis then
	  inc(FMaxPosition, ((DataArray[i] and $FFFF0000) shr 16));
       inc(FMaxPosition);
    end;
   if FMaxPosition>2 then
      dec(FMaxPosition);
   FSamplePtr.position := 0;
   FSamplePtr.data_idx := 0;
   FSamplePtr.offset := 0;
   if not(FStateAnalysis) then
      FSample := DataArray[0]
   else
      FSample := DataArray[0] and $0000FFFF;
end;

//=================================================================
// Return position for given sample position
//=================================================================
function TDataHold.GetPosition(data_idx: integer): int64;
var
   i	: integer;
   pos	: int64;
begin
   if data_idx > FDataLength-1 then
    begin
      result := -1;
      exit;
    end;

   pos := 0;
   for i := 0 to data_idx-1 do
    begin
       if FStateAnalysis then
	  inc(pos, ((DataArray[i] and $FFFF0000) shr 16));
       inc(pos);
    end;
   result := pos;
end;

//=================================================================
// Provide sample value for given position
//=================================================================
function TDataHold.GetSampleValue(pos: int64): cardinal;
var
   next_sam_pos	: int64;
   diff		: integer;
begin
   if pos>FMaxPosition then
      pos := FMaxPosition;
   if pos<0 then
      pos := 0;

   // going forward
   if pos>=FSamplePtr.position then
    begin
      while (pos>FSamplePtr.position) do
       begin
         if not(FStateAnalysis) then
          begin
            inc(FSamplePtr.position);
            inc(FSamplePtr.data_idx);
          end
         else
          begin
            diff := (DataArray[FSamplePtr.data_idx] and $FFFF0000) shr 16;
            next_sam_pos := FSamplePtr.position - FSamplePtr.offset + diff + 1; // position of next sample
            if next_sam_pos <= pos then      	// position not reached, go to next sample
             begin
               FSamplePtr.position := next_sam_pos;
               inc(FSamplePtr.data_idx);
               FSamplePtr.offset := 0;
             end
            else
             begin
               diff := pos - FSamplePtr.position;	// position within sample range
               FSamplePtr.position := pos;
	       FSamplePtr.offset := FSamplePtr.offset + diff;
             end;
          end;
       end;
    end

   // going backward
   else
    begin
      while (pos<FSamplePtr.position) do
       begin
         if not(FStateAnalysis) then
          begin
            dec(FSamplePtr.position);
            dec(FSamplePtr.data_idx);
          end
         else
          begin
	    if pos < FSamplePtr.position - FSamplePtr.offset then
             begin
               dec(FSamplePtr.data_idx);
               dec(FSamplePtr.position, FSamplePtr.offset + 1);
               FSamplePtr.offset := (DataArray[FSamplePtr.data_idx] and $FFFF0000) shr 16;
	     end
            else
             begin
               diff := FSamplePtr.position - pos;
               FSamplePtr.position := pos;
               dec(FSamplePtr.offset, diff);
             end;
          end;
       end;
    end;

   FSample := DataArray[FSamplePtr.data_idx];
   if FStateAnalysis then
      FSample := FSample and $0000FFFF;

   Result := FSample;
end;

//=================================================================
// Seek to sample change
// returns true when change found
//=================================================================
function TDataHold.FindChange(mask: cardinal; lastpos: int64; go_forward: boolean): boolean;
var
   next_sam_pos	: int64;
   diff		: integer;
   val_orig	: cardinal;
   val_new	: cardinal;

begin
   if lastpos > FMaxPosition then
      lastpos := FMaxPosition;
   if lastpos < 0 then
      lastpos := 0;

   if (FStateAnalysis) then
      mask := mask and $0000FFFF;

   val_orig := DataArray[FSamplePtr.data_idx] and mask;
   val_new := not(val_orig);				// initialize val_new to some different value

   // going forward
   if go_forward then
    begin
      repeat
	 if (FSamplePtr.position < lastpos) then
	  begin
	    if not(FStateAnalysis) then
	     begin
	       inc(FSamplePtr.position);
	       inc(FSamplePtr.data_idx);
	     end
	    else
	     begin
	       diff := (DataArray[FSamplePtr.data_idx] and $FFFF0000) shr 16;
	       next_sam_pos := FSamplePtr.position - FSamplePtr.offset + diff + 1; // position of next sample
	       if next_sam_pos <= lastpos then      	// max. position not reached, go to next sample
		begin
		  FSamplePtr.position := next_sam_pos;
		  inc(FSamplePtr.data_idx);
		  FSamplePtr.offset := 0;
		end
	       else
		begin
		  diff := lastpos - FSamplePtr.position;	// max. position within sample range
		  FSamplePtr.position := lastpos;
		  FSamplePtr.offset := FSamplePtr.offset + diff;
		end;
	     end;
	  end;
	 val_new := DataArray[FSamplePtr.data_idx] and mask;
      until not((val_orig = val_new) and (FSamplePtr.position < lastpos))
    end
   else

    // going backward
    begin
      repeat
	 if (lastpos<=FSamplePtr.position) then
	  begin
	    if not(FStateAnalysis) then
	     begin
	       dec(FSamplePtr.position);
	       dec(FSamplePtr.data_idx);
	     end
	    else
	     begin
	       if lastpos < FSamplePtr.position - FSamplePtr.offset then
		begin
		  dec(FSamplePtr.data_idx);
		  dec(FSamplePtr.position, FSamplePtr.offset + 1);
		  FSamplePtr.offset := (DataArray[FSamplePtr.data_idx] and $FFFF0000) shr 16;
		end
	       else
		begin
		  diff := FSamplePtr.position - lastpos;
		  FSamplePtr.position := lastpos;
		  dec(FSamplePtr.offset, diff);
		end;
	     end;
	  end;
	 val_new := DataArray[FSamplePtr.data_idx] and mask;
      until not((val_orig = val_new) and (lastpos < FSamplePtr.position))
    end;

   FSample := val_new;
   Result := val_new <> val_orig;

end;


end.
