{
********************************************************************************

YaPeTaVi - Yet another Periodic Table Viewer
Copyright (C) 2009-2011 Geoffray Levasseur <geoffray.levasseurbrandin@numericable.fr>.
All rights reserved.
http://www.geoffray-levasseur.org/

  This source 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 code 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.

  A copy of the GNU General Public License is available on the World Wide Web
  at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
  to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  MA 02111-1307, USA.


********************************************************************************

Description:
  Element list class

}
unit uElemListClass;

{$mode objfpc}{$H+}

interface

uses
  ComCtrls, Classes, SysUtils, uStrings, uCommon;

type
  TElemFamily = (atAlkaline, atRareEarth, atNonMetal, atAlkalieMetal,
                 atOtherMetal, atHalogen, atTransMetal, atNobleGas,
                 atMetalloid);

const
  AFamilyStr: array[TElemFamily] of string = (rsAlcaline, rsRareEarth,
    rsNonMetal, rsAlkalieMetal, rsOtherMetal, rsHalogen, rsTransMetal,
    rsNobleGas, rsMetalloid);

type
  TIonisationEnergy = record
    First: Double;
    Second: Double;
    Third: Double;
  end;

  St2 = String[2];

  TElement = record
    Name: string;
    ShortName: St2;
    Mass: Double;
    CAS: string;
    DiscoYear: Integer;
    Discoverer: string;
    Etymology: String;
    Family: TElemFamily;
    Block: Char;
    MeltingPt: Double;
    BoilingPt: Double;
    ElecAffinity: Double;
    ElecConf: string;
    CovalentRd: integer;    //in picometer so no real needed
    VanDerWaalsRd: integer; //in picometer so no real needed
    IonisationEnergy: TIonisationEnergy;
    Electroneg: Double;
    Radioactivity: string;
    Number: byte;
    Aspect: string;
    Use: string;
    WikiLink: string;
  end;

  TElemArray = array[0..ElemNumber] of TElement;

  TElemSortType = (estNumber, estName, estSymbol, estDiscoveryDate, estMass,
    estMeltingPoint, estBoilingPoint, estElectronicAffinity, estCovalentRadius,
    estVanDerWaalsRadius, estFirstIonisationEnergy, estSecondIonisationEnergy,
    estThirdIonisationEnergy, estElectronegativity);

  TFilterKind = (fkDate, fkMass, fkMeltingPoint, fkBoilingPoint,
    fkElectronicAffinity, fkCovalentRadius, fkVanDerWaalsRadius,
    fkFirstIonisationEnergy, fkSecondIonisationEnergy, fkThirdIonisationEnergy,
    fkElectronegativity);

  TFilter = record
    Min, Max: Double;
    Kind: TFilterKind;
    DisplayUnknow: Boolean;
  end;

  PFilterNode = ^TFilterNode;
  TFilterNode = record
    Item: TFilter;
    Next, Prev: PFilterNode;
  end;

  TElemList = class(TPersistent)
  private
    FFilterTable: Boolean;
    FFilterList: Boolean;
    FFilterGraph: Boolean;
    FElements: TElemArray;
    FSortType: TElemSortType;
    FSortUpToDown: Boolean;
    FSortUnknowFirst: Boolean;
    FSortedElements: TElemArray;
    FMinMass, FMaxMass: Double;
    FMinDiscoveryDate, FMaxDiscoveryDate: Integer;
    FMinMeltingPt, FMaxMeltingPt: Double;
    FMinBoilingPt, FMaxBoilingPt: Double;
    FMinElecAffinity, FMaxElecAffinity: Double;
    FMinCovalentRd, FMaxCovalentRd: Integer;
    FMinVanDerWaalsRd, FMaxVanDerWaalsRd: Integer;
    FMinFirstIonisationEnergy, FMaxFirstIonisationEnergy: Double;
    FMinSecondIonisationEnergy, FMaxSecondIonisationEnergy: Double;
    FMinThirdIonisationEnergy, FMaxThirdIonisationEnergy: Double;
    FMinElectroneg, FMaxElectroneg: Double;
    FMinFilteredMass, FMaxFilteredMass: Double;
    FMinFilteredDiscoveryDate, FMaxFilteredDiscoveryDate: Integer;
    FMinFilteredMeltingPt, FMaxFilteredMeltingPt: Double;
    FMinFilteredBoilingPt, FMaxFilteredBoilingPt: Double;
    FMinFilteredElecAffinity, FMaxFilteredElecAffinity: Double;
    FMinFilteredCovalentRd, FMaxFilteredCovalentRd: Integer;
    FMinFilteredVanDerWaalsRd, FMaxFilteredVanDerWaalsRd: Integer;
    FMinFilteredFirstIonisationEnergy, FMaxFilteredFirstIonisationEnergy: Double;
    FMinFilteredSecondIonisationEnergy, FMaxFilteredSecondIonisationEnergy: Double;
    FMinFilteredThirdIonisationEnergy, FMaxFilteredThirdIonisationEnergy: Double;
    FMinFilteredElectroneg, FMaxFilteredElectroneg: Double;
    FUpdating: Boolean;
    FFltCount: Integer;
    FFltFirst: PFilterNode;
    procedure SetSortType(const AType: TElemSortType);
    procedure SetSortUpToDown(const AUpToDown: Boolean);
    procedure SetSortUnknowFirst(const AUnknowFirst: Boolean);
    procedure UpdateMinMax;
    procedure UpdateFilteredMinMax;
    procedure Sort;
  protected
    function FltGet(Index: Integer): TFilter; virtual;
    function FltGetCount: Integer; virtual;
    procedure FltPut(Index: Integer; const Flt: TFilter); virtual;
  public
    constructor Create(AOwner: TComponent); virtual;
    destructor Destroy; override;
    function Filtered(const I: Integer): Boolean;
    function InActiveRange(const I: Integer): Boolean;
    function Add(const FElem: TElement): integer;
    function AddFilter(Flt: TFilter): integer;
    procedure RemoveFilter(Index: Integer);
    procedure ClearFilters;
    procedure UpdateList(var AListView: TListView);
    procedure UpdateList(var AListItems: TStrings);
    property FilterTable: Boolean read FFilterTable write FFilterTable
      default True;
    property FilterList: Boolean read FFilterList write FFilterList
      default True;
    property FilterGraph: Boolean read FFilterGraph write FFilterGraph
      default True;
    property Elements: TElemArray read FElements;
    property SortedElements: TElemArray read FSortedElements;
    property Filters[Index: integer]: TFilter read FltGet write FltPut;
    property FiltersCount: integer read FltGetCount;
    property MinDiscoveryDate: integer read FMinDiscoveryDate default 0;
    property MaxDiscoveryDate: integer read FMaxDiscoveryDate default 0;
    property MinMass: Double read FMinMass;
    property MaxMass: Double read FMaxMass;
    property MinMeltingPoint: Double read FMinMeltingPt;
    property MaxMeltingPoint: Double read FMaxMeltingPt;
    property MinBoilingPoint: Double read FMinBoilingPt;
    property MaxBoilingPoint: Double read FMaxBoilingPt;
    property MinElectronicAffinity: Double read FMinElecAffinity;
    property MaxElectronicAffinity: Double read FMaxElecAffinity;
    property MinCovalentRadius: integer read FMinCovalentRd default 0;
    property MaxCovalentRadius: integer read FMaxCovalentRd default 0;
    property MinVanDerWaalsRadius: integer read FMinVanDerWaalsRd default 0;
    property MaxVanDerWaalsRadius: integer read FMaxVanDerWaalsRd default 0;
    property MinFirstIonisationEnergy: Double read FMinFirstIonisationEnergy;
    property MaxFirstIonisationEnergy: Double read FMaxFirstIonisationEnergy;
    property MinSecondIonisationEnergy: Double read FMinSecondIonisationEnergy;
    property MaxSecondIonisationEnergy: Double read FMaxSecondIonisationEnergy;
    property MinThirdIonisationEnergy: Double read FMinThirdIonisationEnergy;
    property MaxThirdIonisationEnergy: Double read FMaxThirdIonisationEnergy;
    property MinElectronegativity: Double read FMinElectroneg;
    property MaxElectronegativity: Double read FMaxElectroneg;
    property MinFilteredMass: Double read FMinFilteredMass;
    property MaxFilteredMass: Double read FMaxFilteredMass;
    property MinFilteredMeltingPoint: Double read FMinFilteredMeltingPt;
    property MaxFilteredMeltingPoint: Double read FMaxFilteredMeltingPt;
    property MinFilteredBoilingPoint: Double read FMinFilteredBoilingPt;
    property MaxFilteredBoilingPoint: Double read FMaxFilteredBoilingPt;
    property MinFilteredElectronicAffinity: Double read FMinFilteredElecAffinity;
    property MaxFilteredElectronicAffinity: Double read FMaxFilteredElecAffinity;
    property MinFilteredCovalentRadius: integer read FMinFilteredCovalentRd
      default 0;
    property MaxFilteredCovalentRadius: integer read FMaxFilteredCovalentRd
      default 0;
    property MinFilteredVanDerWaalsRadius: integer
      read FMinFilteredVanDerWaalsRd default 0;
    property MaxFilteredVanDerWaalsRadius: integer
      read FMaxFilteredVanDerWaalsRd default 0;
    property MinFilteredFirstIonisationEnergy: Double
      read FMinFilteredFirstIonisationEnergy;
    property MaxFilteredFirstIonisationEnergy: Double
      read FMaxFilteredFirstIonisationEnergy;
    property MinFilteredSecondIonisationEnergy: Double
      read FMinFilteredSecondIonisationEnergy;
    property MaxFilteredSecondIonisationEnergy: Double
      read FMaxFilteredSecondIonisationEnergy;
    property MinFilteredThirdIonisationEnergy: Double
      read FMinFilteredThirdIonisationEnergy;
    property MaxFilteredThirdIonisationEnergy: Double
      read FMaxFilteredThirdIonisationEnergy;
    property MinFilteredElectronegativity: Double read FMinFilteredElectroneg;
    property MaxFilteredElectronegativity: Double read FMaxFilteredElectroneg;
    property SortType: TElemSortType read FSortType write SetSortType
      default estNumber;
    property SortUpToDown: Boolean read FSortUpToDown write SetSortUpToDown
      default False;
    property SortUnknowFirst: Boolean read FSortUnknowFirst
      write SetSortUnknowFirst default True;
    procedure BeginUpdate;
    procedure EndUpdate;
    function GetDiscovererList: TStrings;
    procedure DbgShowFilters;
  end;

implementation

uses
  uSort, uUtils, uDebug, Forms, Dialogs;


procedure TElemList.ClearFilters;
var
  Node: PFilterNode;
begin
  if FFltFirst <> nil then
  begin
    repeat
      Node := FFltFirst;
      FFltFirst := FFltFirst^.Next;
      Dispose(Node);
    until FFltFirst = nil;
  end;
end;

function TElemList.AddFilter(Flt: TFilter): integer;
var
  I: integer;
  Node, Node2: PFilterNode;
begin
  Node := FFltFirst;
  I := 0;
  if Node = nil then
  begin
    New(Node);
    Node^.Item := Flt;
    Node^.Prev := nil;
    Node^.Next := nil;
    FFltFirst := Node;
    Result := 0;
    Exit;
  end;
  while Node^.Next <> nil do
  begin
    Node := Node^.Next;
    Inc(I);
  end;
  New(Node2);
  Node2^.Item := Flt;
  Node2^.Next := nil;
  Node2^.Prev := Node;
  Node^.Next := Node2;
  Result := I;
  Application.ProcessMessages;
end;

function TElemList.FltGet(Index: integer): TFilter;
var
  I: integer;
  Node: PFilterNode;
begin
  Node := FFltFirst;
  if Index = 0 then
  begin
    Result := FFltFirst^.Item;
    Exit;
  end;
  for I := 0 to Index - 1 do
    if Node <> nil then
      Node := Node^.Next
    else Exit; //this is an error (leave an exception)
  if Node <> nil then
    Result := Node^.Item;
end;

procedure TElemList.FltPut(Index: integer; const Flt: TFilter);
var
  I: integer;
  Node: PFilterNode;
begin
  Node := FFltFirst;
  if Index = 0 then
  begin
    FFltFirst^.Item := Flt;
    Exit;
  end;
  for I := 0 to Index do
    if Node <> nil then
      Node := Node^.Next
    else Exit; //this is an error as above
  if Node <> nil then
    Node^.Item := Flt;
end;

function TElemList.FltGetCount: integer;
var
  I: integer;
  Node: PFilterNode;
begin
  Node := FFltFirst;
  I := 0;
  if Node <> nil then
    while Node <> nil do
    begin
      Node := Node^.Next;
      Inc(I);
    end;
  Result := I;
end;

procedure TElemList.RemoveFilter(Index: integer);
var
  Node: PFilterNode;
  I: integer;
begin
  Node := FFltFirst;
  if Index = 0 then
  begin
    if Node^.Next <> nil then
      Node^.Next^.Prev := nil;
    FFltFirst := Node^.Next;
    Dispose(Node);
    Exit;
  end;
  for I := 1 to Index do
    if Node <> nil then
      Node := Node^.Next
    else Exit; //show error here
  if Node <> nil then
  begin
    Node^.Prev^.Next := Node^.Next;
    if Node^.Next <> nil then
      Node^.Next^.Prev := Node^.Prev;
    Dispose(Node);
  end; //add an else statement showing an error
end;

constructor TElemList.Create(AOwner: TComponent);
begin
  FUpdating := False;
  FFltCount := 0;
  FFltFirst := nil;
end;

destructor TElemList.Destroy;
begin
  ClearFilters;
  inherited;
end;

function TElemList.Filtered(const I: Integer): Boolean;
var
  bDate, bMass, bMelting, bBoiling, bCovalent, bVanDerWaals, bElecAff,
    bElectroneg, bFIE, bSIE, bTIE: Boolean;
begin
  if FUpdating then
  begin
    UpdateMinMax;
    UpdateFilteredMinMax;
    Sort;
  end;
  {$warning TElemList.Filtered: To be done}
end;

function TELemList.InActiveRange(const I: Integer): Boolean;
begin
  Result := not Filtered(I);
end;

function TElemList.Add(const FElem: TElement): integer;
begin
  FElements[FElem.Number] := FElem;
  Result := FElem.Number;
  PrintLnDbg('Element #' + IntToStr(FElem.Number) + ' added successfully',
    vlHigh);
  if not FUpdating then
  begin
    UpdateMinMax;
    UpdateFilteredMinMax;
    Sort;
  end;
end;

procedure TElemList.UpdateList(var AListView: TListView);
begin
  if FUpdating then
  begin
    UpdateMinMax;
    UpdateFilteredMinMax;
    Sort;
  end;
  {$warning TElemList.UpdtateList(ListView): To be done}
end;

procedure TElemList.UpdateList(var AListItems: TStrings);
begin
  if FUpdating then
  begin
    UpdateMinMax;
    UpdateFilteredMinMax;
    Sort;
  end;
  {$warning TElemList.UpdtateList(Strings): To be done}
end;

procedure TElemList.SetSortType(const AType: TElemSortType);
begin
  FSortType := AType;
  if not FUpdating then Sort;
end;

procedure TElemList.SetSortUpToDown(const AUpToDown: Boolean);
begin
  FSortUpToDown := AUpToDown;
  if not FUpdating then Sort;
end;

procedure TElemList.SetSortUnknowFirst(const AUnknowFirst: Boolean);
begin
  FSortUnknowFirst := AUnknowFirst;
  if not FUpdating then Sort;
end;

procedure TElemList.UpdateMinMax;
var
  I: integer;
begin
  PrintLnDbg('Updating min and max values...', vlLow);
  for I := 1 to ElemNumber do
  begin
    if FElements[I].Mass > FMaxMass then
      FMaxMass := FElements[I].Mass;
    if FElements[I].DiscoYear > FMaxDiscoveryDate then
      FMaxDiscoveryDate := FElements[I].DiscoYear;
    if FElements[I].MeltingPt > FMaxMeltingPt then
      FMaxMeltingPt := FElements[I].MeltingPt;
    if FElements[I].BoilingPt > FMaxBoilingPt then
      FMaxBoilingPt := FElements[I].BoilingPt;
    if FElements[I].ElecAffinity > FMaxElecAffinity then
      FMaxElecAffinity := FElements[I].ElecAffinity;
    if FElements[I].CovalentRd > FMaxCovalentRd then
      FMaxCovalentRd := FElements[I].CovalentRd;
    if FElements[I].VanDerWaalsRd > FMaxVanDerWaalsRd then
      FMaxVanDerWaalsRd := FElements[I].VanDerWaalsRd;
    if FElements[I].IonisationEnergy.First > FMaxFirstIonisationEnergy then
      FMaxFirstIonisationEnergy := FElements[I].IonisationEnergy.First;
    if FElements[I].IonisationEnergy.Second > FMaxSecondIonisationEnergy then
      FMaxSecondIonisationEnergy := FElements[I].IonisationEnergy.Second;
    if FElements[I].IonisationEnergy.Third > FMaxThirdIonisationEnergy then
      FMaxThirdIonisationEnergy := FElements[I].IonisationEnergy.Third;
    if FElements[I].Electroneg > FMaxElectroneg then
      FMaxElectroneg := FElements[I].Electroneg;
  end;
  FMinMass := FMaxMass;
  FMinDiscoveryDate :=  FMaxDiscoveryDate;
  FMinMeltingPt := FMaxMeltingPt;
  FMinBoilingPt := FMaxBoilingPt;
  FMinElecAffinity := FMaxElecAffinity;
  FMinCovalentRd := FMaxCovalentRd;
  FMinVanDerWaalsRd := FMaxVanDerWaalsRd;
  FMinFirstIonisationEnergy := FMaxFirstIonisationEnergy;
  FMinSecondIonisationEnergy := FMaxSecondIonisationEnergy;
  FMinThirdIonisationEnergy := FMaxThirdIonisationEnergy;
  FMinElectroneg := FMaxElectroneg;
  for I := 1 to ElemNumber do
  begin
    if (FElements[I].Mass < FMinMass) and (FElements[I].Mass <> 0) then
      FMinMass := FElements[I].Mass;
    if (FElements[I].DiscoYear < FMinDiscoveryDate) and
      (FElements[I].DiscoYear <> 0) then
        FMinDiscoveryDate := FElements[I].DiscoYear;
    if (FElements[I].MeltingPt < FMinMeltingPt) and
      (FElements[I].MeltingPt <> 0) then
        FMinMeltingPt := FElements[I].MeltingPt;
    if (FElements[I].BoilingPt < FMinBoilingPt) and
      (FElements[I].BoilingPt <> 0) then
        FMinBoilingPt := FElements[I].BoilingPt;
    if (FElements[I].ElecAffinity < FMinElecAffinity) then
      FMinElecAffinity := FElements[I].ElecAffinity;
    if (FElements[I].CovalentRd < FMinCovalentRd) and
      (FElements[I].CovalentRd <> 0) then
        FMinCovalentRd := FElements[I].CovalentRd;
    if (FElements[I].VanDerWaalsRd < FMinVanDerWaalsRd) and
      (FElements[I].VanDerWaalsRd <> 0) then
        FMinVanDerWaalsRd := FElements[I].VanDerWaalsRd;
    if (FElements[I].IonisationEnergy.First < FMinFirstIonisationEnergy) and
      (FElements[I].IonisationEnergy.First <> 0) then
        FMinFirstIonisationEnergy := FElements[I].IonisationEnergy.First;
    if (FElements[I].IonisationEnergy.Second < FMinSecondIonisationEnergy) and
      (FElements[I].IonisationEnergy.Second <> 0) then
        FMinSecondIonisationEnergy := FElements[I].IonisationEnergy.Second;
    if (FElements[I].IonisationEnergy.Third < FMinThirdIonisationEnergy) and
      (FElements[I].IonisationEnergy.Third <> 0) then
        FMinThirdIonisationEnergy := FElements[I].IonisationEnergy.Third;
    if (FElements[I].Electroneg < FMinElectroneg) and
      (FElements[I].Electroneg <> -1) then
        FMinElectroneg := FElements[I].Electroneg;
  end;
end;

procedure TElemList.UpdateFilteredMinMax;
var
  I: integer;
begin
  PrintLnDbg('Updating min and max filtered values...', vlLow);
  for I := 1 to ElemNumber do
  begin
    if (FElements[I].Mass > FMaxFilteredMass) and InActiveRange(I) then
      FMaxFilteredMass := FElements[I].Mass;
    if (FElements[I].DiscoYear > FMaxFilteredDiscoveryDate) and InActiveRange(I) then
      FMaxFilteredDiscoveryDate := FElements[I].DiscoYear;
    if (FElements[I].MeltingPt > FMaxFilteredMeltingPt) and InActiveRange(I) then
      FMaxFilteredMeltingPt := FElements[I].MeltingPt;
    if (FElements[I].BoilingPt > FMaxFilteredBoilingPt) and InActiveRange(I) then
      FMaxFilteredBoilingPt := FElements[I].BoilingPt;
    if (FElements[I].ElecAffinity > FMaxFilteredElecAffinity) and InActiveRange(I) then
      FMaxFilteredElecAffinity := FElements[I].ElecAffinity;
    if (FElements[I].CovalentRd > FMaxFilteredCovalentRd) and InActiveRange(I) then
      FMaxFilteredCovalentRd := FElements[I].CovalentRd;
    if (FElements[I].VanDerWaalsRd > FMaxFilteredVanDerWaalsRd) and InActiveRange(I) then
      FMaxFilteredVanDerWaalsRd := FElements[I].VanDerWaalsRd;
    if (FElements[I].IonisationEnergy.First > FMaxFilteredFirstIonisationEnergy) and
      InActiveRange(I) then
        FMaxFilteredFirstIonisationEnergy := FElements[I].IonisationEnergy.First;
    if (FElements[I].IonisationEnergy.Second > FMaxFilteredSecondIonisationEnergy) and
      InActiveRange(I) then
        FMaxFilteredSecondIonisationEnergy := FElements[I].IonisationEnergy.Second;
    if (FElements[I].IonisationEnergy.Third > FMaxFilteredThirdIonisationEnergy) and
      InActiveRange(I) then
        FMaxFilteredThirdIonisationEnergy := FElements[I].IonisationEnergy.Third;
    if (FElements[I].Electroneg > FMaxFilteredElectroneg) and InActiveRange(I) then
      FMaxFilteredElectroneg := FElements[I].Electroneg;
  end;
  FMinFilteredMass := FMaxFilteredMass;
  FMinFilteredDiscoveryDate :=  FMaxFilteredDiscoveryDate;
  FMinFilteredMeltingPt := FMaxFilteredMeltingPt;
  FMinFilteredBoilingPt := FMaxFilteredBoilingPt;
  FMinFilteredElecAffinity := FMaxFilteredElecAffinity;
  FMinFilteredCovalentRd := FMaxFilteredCovalentRd;
  FMinFilteredVanDerWaalsRd := FMaxFilteredVanDerWaalsRd;
  FMinFilteredFirstIonisationEnergy := FMaxFilteredFirstIonisationEnergy;
  FMinFilteredSecondIonisationEnergy := FMaxFilteredSecondIonisationEnergy;
  FMinFilteredThirdIonisationEnergy := FMaxFilteredThirdIonisationEnergy;
  FMinFilteredElectroneg := FMaxFilteredElectroneg;
  for I := 1 to ElemNumber do
  begin
    if (FElements[I].Mass < FMinFilteredMass) and (FElements[I].Mass <> 0) and
      InActiveRange(I) then
        FMinFilteredMass := FElements[I].Mass;
    if (FElements[I].DiscoYear < FMinFilteredDiscoveryDate) and
      (FElements[I].DiscoYear <> 0) and InActiveRange(I) then
        FMinFilteredDiscoveryDate := FElements[I].DiscoYear;
    if (FElements[I].MeltingPt < FMinFilteredMeltingPt) and
      (FElements[I].MeltingPt <> 0) and InActiveRange(I) then
        FMinFilteredMeltingPt := FElements[I].MeltingPt;
    if (FElements[I].BoilingPt < FMinFilteredBoilingPt) and
      (FElements[I].BoilingPt <> 0) and InActiveRange(I) then
        FMinFilteredBoilingPt := FElements[I].BoilingPt;
    if (FElements[I].ElecAffinity < FMinFilteredElecAffinity) and InActiveRange(I) then
      FMinFilteredElecAffinity := FElements[I].ElecAffinity;
    if (FElements[I].CovalentRd < FMinFilteredCovalentRd) and
      (FElements[I].CovalentRd <> 0) and InActiveRange(I) then
        FMinFilteredCovalentRd := FElements[I].CovalentRd;
    if (FElements[I].VanDerWaalsRd < FMinFilteredVanDerWaalsRd) and
      (FElements[I].VanDerWaalsRd <> 0) and InActiveRange(I) then
        FMinFilteredVanDerWaalsRd := FElements[I].VanDerWaalsRd;
    if (FElements[I].IonisationEnergy.First < FMinFilteredFirstIonisationEnergy) and
      (FElements[I].IonisationEnergy.First <> 0) and InActiveRange(I) then
        FMinFilteredFirstIonisationEnergy := FElements[I].IonisationEnergy.First;
    if (FElements[I].IonisationEnergy.Second < FMinFilteredSecondIonisationEnergy) and
      (FElements[I].IonisationEnergy.Second <> 0) and InActiveRange(I) then
        FMinFilteredSecondIonisationEnergy := FElements[I].IonisationEnergy.Second;
    if (FElements[I].IonisationEnergy.Third < FMinFilteredThirdIonisationEnergy) and
      (FElements[I].IonisationEnergy.Third <> 0) and InActiveRange(I) then
        FMinFilteredThirdIonisationEnergy := FElements[I].IonisationEnergy.Third;
    if (FElements[I].Electroneg < FMinFilteredElectroneg) and
      (FElements[I].Electroneg <> -1) and InActiveRange(I) then
        FMinFilteredElectroneg := FElements[I].Electroneg;
  end;
end;

//there's only arround 110 elements to sort so a "dirty" sort is quitte quick
//enough
procedure TElemList.Sort;
begin
  PrintLnDbg('-- Sorting elements...', vlHigh);
  Inc(DebugLevel);
  case FSortType of
    estName: SortByName(FElements, FSortedElements, FSortUpToDown);
    estSymbol: SortByShortName(FElements, FSortedElements, FSortUpToDown);
    estDiscoveryDate : SortByDate(FElements, FSortedElements, FSortUpToDown,
      FSortUnknowFirst);
    estMass: SortByMass(FElements, FSortedElements, FSortUpToDown);
    else SortByAtNum(FElements, FSortedElements, FSortUpToDown);
  end;
  Dec(DebugLevel);
  PrintLnDbg('-- Done', vlHigh);
end;

procedure TElemList.BeginUpdate;
begin
  FUpdating := True;
end;

procedure TElemList.EndUpdate;
begin
  FUpdating := False;
  UpdateMinMax;
  UpdateFilteredMinMax;
  Sort;
end;

function TElemList.GetDiscovererList: TStrings;
var
  I, J: integer;
  SDisco: string;
  Strs: TStringList;
begin
  PrintLnDbg('-- Building discoverer list...', vlHigh);
  Inc(DebugLevel);
  Strs := TStringList.Create;
  for I := 1 to ElemNumber do
    if FElements[I].Discoverer <> '' then
      for J := 1 to CountItemsInStr(FElements[I].Discoverer, ',') do
      begin
        SDisco := Trim(GetItemInStr(FElements[I].Discoverer, ',', J));
        if StrExistsInList(Strs, SDisco) = -1 then
        begin
          Strs.Add(SDisco);
          PrintLnDbg('Discoverer found: ' + SDisco, vlHigh);
        end;
      end;
  Result := Strs;
  Dec(VerboseLevel);
  PrintLnDbg('-- Done', vlHigh);
end;

procedure TElemList.DbgShowFilters;
var
  I: integer;
  S, Tmp: String;
begin
  S := IntToStr(FltGetCount) + ' counted filters:' + LineEnding;
  for I := 0 to FiltersCount - 1 do
  begin
    case Filters[I].Kind of
      fkDate: Tmp := 'Date';
      fkBoilingPoint: Tmp := 'Boiling point';
      fkCovalentRadius: Tmp := 'Covalent Radius';
      fkElectronegativity: Tmp := 'Electronegativity';
      fkElectronicAffinity: Tmp := 'Electronic Affinity';
      fkFirstIonisationEnergy: Tmp := 'First Ionisation Energy';
      fkMass: Tmp := 'Mass';
      fkMeltingPoint: Tmp := 'Melting Point';
      fkSecondIonisationEnergy: Tmp := 'Second Ionisation Energy';
      fkThirdIonisationEnergy: Tmp := 'Third Ionisation Energy';
      fkVanDerWaalsRadius: Tmp := 'Van Der Walls Radius';
    end;
    S := S + Tmp + ': '#9' (min: ' + FloatToStr(Filters[I].Min) + ' / max: ' +
      FloatToStr(Filters[I].Max) + ')' + LineEnding;
  end;
  ShowMessage(S);
end;

end.

