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

YaPeTaVi - Yet another Periodic Table Viewer
Copyright (C) 2018 Geoffray Levasseur <geoffray.levasseurbrandin@numericable.fr>.

All rights reserved.
http://www.geoffray-levasseur.org/

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 3 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, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA  02111-1307, USA.

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

Description:
  Common specific YaPeTaVi code

}
unit uCommon;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Graphics, ComCtrls;

const
  ElemNumber = 112;

type
  TRange = array[1..ElemNumber] of Boolean;

  //Get a color from the given value according min and max levels
  function GetGradientColor(const Min, Max, Value: integer;
    const MinCol, MaxCol: TColor): TColor;
  function GetGradientColor(const Min, Max, Value: Double;
    const MinCol, MaxCol: TColor): TColor;

  //Convert ListView index to an element list index
  function GetListIndexFromView(var LV: TListView; const I: integer): integer;

  //setup a range with the given string
  function SetRange(S: string): Boolean;

  //setup the range to include all possible values
  procedure FullRange;

  //look for picture in different possible place and with different possible format
  function GetPicRessource(Name: string): string;

implementation

uses
  uDebug, uConst, uUtils;

function GetGradientColor(const Min, Max, Value: integer;
  const MinCol, MaxCol: TColor): TColor;
var
  MinR, MinG, MinB, MaxR, MaxG, MaxB: byte;
begin
  MinR := Red(ColorToRGB(MinCol));
  MinG := Green(ColorToRGB(MinCol));
  MinB := Blue(ColorToRGB(MinCol));
  MaxR := Red(ColorToRGB(MaxCol));
  MaxG := Green(ColorToRGB(MaxCol));
  MaxB := Blue(ColorToRGB(MaxCol));
  Result := RGBToColor(
    Round((MaxR - MinR) * ((Value - Min) / (Max - Min)) + MinR),
    Round((MaxG - MinG) * ((Value - Min) / (Max - Min)) + MinG),
    Round((MaxB - MinB) * ((Value - Min) / (Max - Min)) + MinB));
end;

function GetGradientColor(const Min, Max, Value: Double;
  const MinCol, MaxCol: TColor): TColor;
var
  MinR, MinG, MinB, MaxR, MaxG, MaxB: byte;
begin
  MinR := Red(ColorToRGB(MinCol));
  MinG := Green(ColorToRGB(MinCol));
  MinB := Blue(ColorToRGB(MinCol));
  MaxR := Red(ColorToRGB(MaxCol));
  MaxG := Green(ColorToRGB(MaxCol));
  MaxB := Blue(ColorToRGB(MaxCol));
  Result := RGBToColor(
    Round((MaxR - MinR) * ((Value - Min) / (Max - Min))) + MinR,
    Round((MaxG - MinG) * ((Value - Min) / (Max - Min))) + MinG,
    Round((MaxB - MinB) * ((Value - Min) / (Max - Min))) + MinB);
end;

function SetRange(S: string): Boolean;
{$warning Add a Range as var parameter, then change all the calls}
{$warning Improve in order to use the error system}
var
  TmpRange: TRange;

  procedure InitRange(var R: TRange);
  var
    I: integer;
  begin
    for I := 1 to ElemNumber do
       R[I] := False;
  end;

  function ApplyRange(S: string): Boolean;
  var
    I: integer;
    J: integer;
    N1, N2: integer;
    Tmp1, Tmp2: string;
  begin
    PrintLnDbg('-- Applying new range: "' + S + '"...', vlLow);
    Inc(DebugLevel);
    Tmp1 := '';
    Tmp2 := '';
    I := 1;
    while S[I] <> ';' do  //find the first number
    begin
      while S[I] in ['0'..'9'] do
      begin
        Tmp1 := Tmp1 + S[I];
        Inc(I);
      end {while};
      if Length(Tmp1) < 1 then
      begin
        Result := False; //syntax error
        PrintLnDbg('*** uUtils.ApplyRange: ERROR: Range error at pos #' +
          IntToStr(I), vlLow);
        Exit;
      end {if};
      while S[I] = ' ' do Inc(I); //eliminate space
      if S[I] = ';' then //single number
      begin
        TmpRange[StrToInt(Tmp1)] := True;
        if I < Length(S) then
          Result := ApplyRange(Copy(S, I + 1, Length(S) - I));
      end else
      if S[I] = '-' then // full range
      begin
        Inc(I);
        while S[I] in ['0'..'9'] do  //find second number
        begin
          Tmp2 := Tmp2 + S[I];
          Inc(I);
        end {while};
        if Length(Tmp2) < 1 then
        begin
          Result := False; //syntax error
          PrintLnDbg('*** uUtils.ApplyRange: ERROR: Range error at pos #' +
            IntToStr(I), vlLow);
          Exit;
        end {if};
        if StrToInt(Tmp1) >= StrToInt(Tmp2) then
          for J := StrToInt(Tmp2) to StrToInt(Tmp1) do
            TmpRange[J] := True
        else for J := StrToInt(Tmp1) to StrToInt(Tmp2) do
          TmpRange[J] := True;
        if I < Length(S) then
          Result := ApplyRange(Copy(S, I + 1, Length(S) - I));
        if S[I] <> ';' then
        begin
          Result := False; //syntax error
          PrintLnDbg('   *** uUtils.ApplyRange: ERROR: Range error at pos #' +
            IntToStr(I), vlLow);
          Exit;
        end {if};
      end else
      begin
        Result := False; //syntax error
          PrintLnDbg('*** uUtils.ApplyRange: ERROR: Range error at pos #' +
            IntToStr(I), vlLow);
        Exit;
      end {if};
    end;
    Dec(DebugLevel);
    PrintLnDbg('-- Ok', vlLow);
  end;

begin
  InitRange(TmpRange);
  S := Trim(S);
  if Length(S) = 0 then
    S := '1-' + IntToStr(ElemNumber) + ';';
  if S[Length(S)] <> ';' then
    S := S + ';';
  Result := ApplyRange(S);
  if Result then
  begin
    InitRange(RangeArray);
    RangeArray := TmpRange;
  end;
end;

procedure FullRange;
var
  I: integer;
begin
  for I := 1 to ElemNumber do
    RangeArray[I] := True;
end;

function GetListIndexFromView(var LV: TListView; const I: integer): integer;
var
  N: integer;
begin
//  Writeln('Get list index from view... I = ', I, ' for ',
//    LV.Items[I].SubItems[0]);
  for N := 1 to ElemNumber do
    if (ElementsList.Elements[N].ShortName = LV.Items[I].SubItems[0]) then
    begin
      Result := N;
      Exit;
    end;
end;

//return the full path of a picture ressource:
//- check it in users path (for personnal datas), then application prefix,
//  then (on unix-like) on FHS
//- check for .jpg, .jpeg, .png, .gif
//CAREFULL: on unix like this is case sensitive
function GetPicRessource(Name: string): string;
var
  Err, APos: integer;
  S: string;

  function TestExt(Path, Name: string): string;
  begin
    if Path[Length(Path)] <> DirectorySeparator then
      Path := Path + DirectorySeparator;
    if not FileExists(Path + Name + '.jpg') then
      if not FileExists(Path+ Name + '.jpeg') then
        if not FileExists(Path + Name + '.png') then
          if not FileExists(Path + Name + '.gif') then
            Result := ''
          else Result := Path + Name + '.gif'
        else Result := Path + Name + '.png'
      else Result := Path + Name + '.jpeg'
    else Result := Path + Name + '.jpg';
    PrintLnDbg('Path ' + Path + ' checked for pictures with result: ' + Result,
      vlHigh);
  end;

begin
  S := TestExt(sPicPath, Name);
  if S = '' then
  begin
    S := TestExt(GetProgramPrefix + '../pics', Name);
    if S = '' then
    {$ifdef UNIX}
    begin
      S := TestExt('/usr/share/yapetavi/pics/', Name);
      if S = '' then
      begin
        S := TestExt('/usr/local/share/yapetavi/pics/', Name);
        if S = '' then
    {$endif}
          PrintLnDbg('E Can''t find picture for "' + Name + '"!', vlLow);
    {$ifdef UNIX}
      end;
    end;
    {$endif}
  end;
  if S <> '' then
    FormatFileName(S, Result);
end;

end.

