{
    This file is part of Chentrah,
    Copyright (C) 2004-2014 Anton Rzheshevski (chebmaster@mail.ru).

    This program 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 of the License, or
    (at your option) any later version.

    This program 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 program.  If not, see http://www.gnu.org/licenses/

 **********************************************************************}


unit mo_quass;

{$include mo_globaldefs.h}

interface

uses
  SysUtils, Classes, math, chepersy, mo_hub, typinfo, mo_classes;

type
  TQualityAssurer = class //NOT managed, it is useless to store it, gets recreated at each run
  private
    suspended,
    suspend_frames,
    avframes: integer;
    avoqf,
    oqf: float;
    procedure CheckBounds;
  public
    constructor Create;
    destructor Destroy;
    procedure TextureIsNotInTextureMemory(megabytes: float);
    function Enabled: boolean;
    procedure Pulse;
    procedure Suspend; //ignore this frame and the next one;
    procedure EmergencyDrop; // halves qf
  end;

implementation

//  QF_MIN = 10;
//QF_MAX = 999;
//  FPS_CULL_DEFAULT = 30;

  procedure TQualityAssurer.CheckBounds;
  begin
    if MotherState^.BatteryRemaining <= 100
      then MotherState^.QF:= min(MotherState^.QF, min(100, MotherState^.BatteryRemaining * 2.0));
    MotherState^.QF:= math.max(QF_MIN, math.min(QF_MAX, MotherState^.QF));
  end;

  procedure TQualityAssurer.Suspend;
  begin
    suspend_frames:= math.max (2, round(10 + 2 - (MotherState^.QF / 10)));
    MotherState^.SuspendSwbMeasurement:= true;
  end;

  procedure TQualityAssurer.EmergencyDrop;
  begin
    MotherState^.QF:= math.min(100, MotherState^.QF/2);
    CheckBounds;
  end;

  procedure TQualityAssurer.Pulse;
  var
    OptimalQf,
    delta, f, aniso : float;

  begin
    if not Enabled then Exit;
    if suspend_frames > 0 then
      if suspended > 30
        then suspend_frames:= 0
        else begin
          Dec(suspend_frames); Exit;
        end;
    MotherState^.SuspendSwbMeasurement:= false;
    {if (MotherState^.FPS < (FPS_CULL_DEFAULT / 1.5)) then begin
      //assuming the worst case: the videocard couldn't even manage 20 fps
      //Drop the quality like a rock.
      OptimalQf:= MotherState^.QF * MotherState^.FPS / FPS_CULL_DEFAULT;
    end
    else} begin
      // at 16000 nanoseconds and 60 FPS, the call to SwapBuffers would take almost entire frame time.
      // at 64000, the videocard couldn't even keep 30 FPS (*cough* mesa software implementation in Debian *cough*)
      // 6-8 thousands us is meh (your average videocard with Aero transparent windows overlapping our own)
      //OptimalQf:=  (640000.0 / MotherState^.AverageSwapBuffersDuration);
      f:= MotherState^.AverageSwapBuffersDuration / 10000.0; // 1.0 => qf = 100
      if f > 1 then OptimalQf:= 100 / sqrt(f);
      if f < 1 then OptimalQf:= 100 + ((1/f) - 1) * 1498;
    end;
    // 64.000 => 10  //15 fps pure
    // 10.000 => 100 //100 fps pure
    //  6.000 => 999 //333 fps pure

    avoqf+= OptimalQf;
    inc (avframes);
    if avframes > 15 then begin
      oqf:= avoqf / avframes;
      avoqf:= 0.0;
      avframes:= 0;
    end;

    if oqf < 1 then Exit; //not initizlized yet

    if oqf > MotherState^.QF then begin
      //it's *per frame*, folks. So delta should be tiny.
      delta:= math.min(0.099, 0.002 * sqrt(oqf - MotherState^.QF));
    end
    else begin
      //there is NO limit to how fast it could drop
      delta:= - 0.0001 * sqr(MotherState^.QF - oqf);
    end;


    {
      decrease slowly if window doesnt have focus. Otherwise it could rise
      to insane levels if window ins not visible, upt to cause overall system slowdown

    }
    if not MotherState^.WindowHasFocus and (MotherState^.QF > 101)
      then delta:= Math.min(delta, - 1 / Math.max(10, MotherState^.FPS));

    MotherState^.QF += delta;
    CheckBounds;

    //4 at QF = 100, 16 at QF = 1600
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
      16
      //Math.max(1.0, Math.min(MotherState^.OGLMaxAnisotropy, 4.0 * sqrt(MotherState^.QF / 100)))
    );
  end;


  constructor TQualityAssurer.Create;
  begin
    inherited;
  end;

  destructor TQualityAssurer.Destroy;
  begin
    inherited;
  end;

  procedure TQualityAssurer.TextureIsNotInTextureMemory(megabytes: float);
  begin
    MotherState^.QF-= megabytes;
    CheckBounds;
  end;

  function TQualityAssurer.Enabled: boolean;
  begin
    Result:= not MotherState^.QF_ManualOverride;
  end;


end.

