unit unMain;

interface

uses
  SysUtils, Types, Classes, Variants, QGraphics, QControls, QForms, QDialogs,
  QStdCtrls, QExtCtrls, QComCtrls, StrUtils, QGrids;

type
  TfmMain = class(TForm)
    StatusBar1: TStatusBar;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    Label3: TLabel;
    lbDictFindPath: TLabel;
    Label2: TLabel;
    lbWordsCount: TLabel;
    Label4: TLabel;
    lbIndexItemsCount: TLabel;
    edSearchWord: TEdit;
    mmTranslation: TMemo;
    Label1: TLabel;
    rgListOfDictionaries: TRadioGroup;
    ListOfWords: TListBox;
    ScrollBar1: TScrollBar;
    ListBox1: TListBox;
    procedure FormActivate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure rgListOfDictionariesClick(Sender: TObject);
    procedure AddWordsToList(iBegin, iEnd, iPosition: integer);
    procedure LoadDictionary(stDictionaryName: string);
    procedure ListOfWordsClick(Sender: TObject);
    procedure edSearchWordChange(Sender: TObject);
    procedure ListOfWordsKeyPress(Sender: TObject; var Key: Char);
    procedure ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode;
      var ScrollPos: Integer);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;





type TRusAlphabet = array[1..33] of char;


const

aRus2: array [1..33] of Char =
('Á','Â','×','Ç','Ä','Å','£','Ö','Ú','É','Ê','Ë','Ì',
          'Í','Î','Ï','Ð','Ò','Ó','Ô','Õ','Æ','È',
          'Ã','Þ','Û','Ý','ß','Ù','Ø','Ü','À','Ñ');

aRusCap=['á','â','÷','ç','ä','å','³','ö','ú','é','ê','ë','ì',
          'í','î','ï','ð','ò','ó','ô','õ','æ','è',
          'ã','þ','û','ý','ÿ','ù','ø','ü','à','ñ'];

aRus=['Á','Â','×','Ç','Ä','Å','£','Ö','Ú','É','Ê','Ë','Ì',
          'Í','Î','Ï','Ð','Ò','Ó','Ô','Õ','Æ','È',
          'Ã','Þ','Û','Ý','ß','Ù','Ø','Ü','À','Ñ'];
 

type TDictionaryIndex = record
  stValue: string;
  iPosition: integer;
  end;

var
  fmMain: TfmMain;
  stMyPath, stMyDictPath: string;
  aWords, aTranslations: array of string;
  aDictionaryIndex: array of TDictionaryIndex;
  iDifferenceOfSelWord,
    iShowingWordsCount: integer;

implementation

{$R *.xfm}



function DictionaryNotLoaded: boolean;
begin
if High(aWords)<1 then result:=true else result:=false;
end;



function CompareLetters(a,b: char): integer;
var
 i, iA, iB: integer;
begin
if (a in aRus) and (b in aRus) then
 begin
 iA:=0;
 iB:=0;
 while a<>aRus2[iA] do inc(iA);
 while b<>aRus2[iB] do inc(iB);
 result:=iB-iA;
 end
 else
 if (a in aRus) then
 begin
 result:=-1000;
 end
 else
 if (b in aRus) then
 begin
 result:=1000;
 end
 else
 begin
 result:=ord(b)-ord(a);
 end;
end;





function CompareWords(a,b: string): integer;
var
 i: integer;
begin
i:=1;
if (a='') and (b='') then
 begin
 result:=0;
 end
 else
 if (a='') then
 begin
 result:=1;
 end
 else
 if (b='') then
 begin
 result:=-1;
 end
 else
 begin
  while (i<=length(a)) and (i<=length(b)) and (a[i]=b[i]) do inc(i);
  if (i>length(a)) or (i>length(b)) then
   begin //Outrange
   result:=length(b)-length(a);
   end
   else result:=CompareLetters(a[i],b[i]);
 end;
end;







function GetPosOfWord(stWord: string): integer;
var
 i, iIndex: integer;
 stTruncSearch: string;


begin
result:=0;
if TrimLeft(stWord)='' then exit;

stTruncSearch:=AnsiLowerCase(LeftStr(stWord,3));

iIndex:=0;
i:=0;
while (iIndex<=High(aDictionaryIndex)) and
     //(aDictionaryIndex[iIndex].stValue<stTruncSearch)
     (CompareWords(aDictionaryIndex[iIndex].stValue,stTruncSearch)>0)

     {and
 (aDictionaryIndex[i+1].stValue>stTruncSearch))} do
  inc(iIndex);

//while (i<High(aDictionaryIndex)) and not (AnsiCompareText(aDictionaryIndex[i].stValue, stTruncSearch)=0) do
//  inc(i);


//Or equal or not find
if iIndex>High(aDictionaryIndex) then
 begin
 iIndex:=High(aDictionaryIndex){0};
 //fmMain.ListBox2.Items.Add('Word in index not found');
 end;
// else
  //begin
  i:=aDictionaryIndex[iIndex].iPosition;


  while (i<{ListOfWords.Items.Count}High(aWords)) and
  //(AnsiLowerCase({ListOfWords.Items.Strings}aWords[i])<AnsiLowerCase(stWord))
  (CompareWords(AnsiLowerCase({ListOfWords.Items.Strings}aWords[i]),AnsiLowerCase(stWord))>0)
  do inc(i);

  {
  iMax:=ListOfWords.Items.Count;
  iMin:=1;
  i:=iMin+(iMax-iMin+1) div 2;
  if st<ListOfWords.Items.Strings[i] then i:=i+(iMax
  }


  {
  if i<ListOfWords.Items.Count then
   begin
   ListOfWords.ItemIndex:=i;
   if i>iDifferenceOfSelWord then ListOfWords.TopIndex:=i-iDifferenceOfSelWord else ListOfWords.TopIndex:=0;
   end;
  }
  //end;

//fmMain.ListBox2.Items.Add('search='+stWord+
//    ', ndx='+aDictionaryIndex[iIndex].stValue+', trunctxt='+stTruncSearch+', iIndexPos='+inttostr(aDictionaryIndex[iIndex].iPosition)+', i='+inttostr(i));

result:=i;
end;









procedure TfmMain.FormActivate(Sender: TObject);
var
  sr: TSearchRec;
  i: integer;
begin
  rgListOfDictionaries.Items.Clear;
  with rgListOfDictionaries do
  begin
    if FindFirst(stMyDictPath+'/*', 35556, sr) = 0 then
    begin
      repeat
        Items.Add(sr.Name); {sr.Size}
      until FindNext(sr) <> 0;
      FindClose(sr);
    end;
  end;
end;




procedure TfmMain.FormCreate(Sender: TObject);
begin
stMyPath:=ExtractFilePath(paramstr(0));
if (stMyPath='') or (stMyPath='./') then stMyPath:=GetCurrentDir;
stMyDictPath:=IncludeTrailingPathDelimiter(stMyPath)+'dict/';
lbDictFindPath.Caption:=stMyDictPath;
//iShowingWordsCount:=20;
iDifferenceOfSelWord:=5;
FormResize(Sender);
end;


procedure TfmMain.rgListOfDictionariesClick(Sender: TObject);
begin
with rgListOfDictionaries do LoadDictionary(Items.Strings[ItemIndex]);
edSearchWord.SetFocus;
end;






procedure TfmMain.AddWordsToList(iBegin, iEnd, iPosition: integer);
var
 i, iNewBeg, iNewEnd: integer;
begin
ScrollBar1.Tag:=1;
ScrollBar1.Min:=1;
ScrollBar1.Max:=High(aWords)+1;
//ScrollBar1.Position:=iPosition+1;
ScrollBar1.Tag:=0;

ListOfWords.Items.Clear;
iNewEnd:=iEnd;
iNewBeg:=iBegin;
if iNewBeg<0 then
begin
iNewEnd:=iEnd-iNewBeg;
iNewBeg:=0;
end;

i:=iNewBeg;

while (i<=High(aWords)) and (i<=iNewEnd) do
 begin
 ListOfWords.Items.Add(aWords[i]);
 //showmessage(aWords[i]);
 inc(i);
 end;

//mmTranslation.Text:=aTranslations[iPosition];
ListOfWords.ItemIndex:=iPosition-iNewBeg;
ListOfWords.Tag:=iNewBeg;
ListBox1.Items.Add(' Show: iNewBeg='+inttostr(iNewBeg)+', iNewEnd='+inttostr(iNewEnd)+', iPos='+inttostr( iPosition));
end;








procedure TfmMain.LoadDictionary(stDictionaryName: string);
var
 F: textfile;
 st, stWord, stTranslation, stTruncWord: string;
 i, iIndexOfCurWord: integer;
 bParseDone: boolean;
begin
StatusBar1.SimpleText:=' Loading of dictionary. Please, wait..';
StatusBar1.Update;
Update;

//showmessage(stDictionaryName);
ListOfWords.Items.Clear;
SetLength(aTranslations,0);
SetLength(aWords,0);
mmTranslation.Clear;
//edSearchWord.Clear;
lbWordsCount.Caption:='(loaded 0 words)';
SetLength(aDictionaryIndex,0);
lbIndexItemsCount.Caption:='0';
//ListBox1.Items.Clear;

if not FileExists(stMyDictPath+stDictionaryName) then
 begin
 showmessage('File not found: '+stMyDictPath+stDictionaryName);
 exit;
 end;

AssignFile(F, stMyDictPath+stDictionaryName);
Reset(F);

while not eof(F) do
 begin
 readln(F,st);
 i:=1;
 bParseDone:=false;
 if (st<>'') and (st<>' ') then
 while i<=length(st) do
   begin
   if st[i]='=' then
    begin
    stWord:=TrimLeft(LeftStr(st, i-1));
//    iIndexOfCurWord:=ListOfWords.Items.Add(stWord);

    SetLength(aWords,High(aWords)+2);
    aWords[High(aWords)]:=stWord;
    iIndexOfCurWord:=High(aWords);

    stTruncWord:=AnsiLowerCase(LeftStr(stWord,3));

    if (High(aDictionaryIndex)<0) or
      (aDictionaryIndex[High(aDictionaryIndex)].stValue<>stTruncWord) then
    begin
    SetLength(aDictionaryIndex,High(aDictionaryIndex)+2);
    aDictionaryIndex[High(aDictionaryIndex)].stValue:=stTruncWord;
    aDictionaryIndex[High(aDictionaryIndex)].iPosition:=iIndexOfCurWord;
    //ListBox1.Items.Add(stTruncWord+'  '+inttostr(iIndexOfCurWord));
    end;


    stTranslation:=RightStr(st, length(st)-i);

    SetLength(aTranslations,High(aTranslations)+2);
    aTranslations[High(aTranslations)]:=stTranslation;
    bParseDone:=true;
    break;
    end;
    inc(i);
   end;
   if not bParseDone then
    begin
    // This dictionary is from Mueller and have another format

    i:=1;
    if (st<>'') and (st[1]<>' ') then
    while i<length(st) do
      begin
      if (st[i]=' ') and (st[i+1]=' ') then
       begin
       ListOfWords.Items.Add(TrimLeft(LeftStr(st, i-1)));
       //showmessage(LeftStr(st, i-1));
 {
     SetLength(aWords,High(aWords)+2);
     aTranslations[High(aWords)]:=LeftStr(st, i-1);}

       SetLength(aTranslations,High(aTranslations)+2);
       aTranslations[High(aTranslations)]:=RightStr(st, length(st)-i-1);
       bParseDone:=true;
       break;
       end;
       inc(i);
      end;

    end;
 end;

CloseFile(F);

{
i:=1;
while (i<High(aWords)) and (i<iShowingWordsCount) do
 begin
 ListOfWords.Items.Add(aWords[i]);
 end;
}

lbWordsCount.Caption:='(loaded '+inttostr(High(aWords)+1)+' words)';
lbIndexItemsCount.Caption:=IntToStr(High(aDictionaryIndex)+1);
StatusBar1.SimpleText:=' Done';
edSearchWord.OnChange(TObject(nil));
end;






procedure TfmMain.ListOfWordsClick(Sender: TObject);
begin
ScrollBar1.Tag:=1;
ScrollBar1.Position:=ListOfWords.Tag+ListOfWords.ItemIndex+1;
ScrollBar1.Tag:=0;

mmTranslation.Text:=aTranslations[ListOfWords.Tag+ListOfWords.ItemIndex];
//mmTranslation.Text:=aTranslations[GetPosOfWord(ListOfWords.Items.Strings[ListOfWords.ItemIndex])];
//edSearchWord.Text:=ListOfWords.Items.Strings[ListOfWords.ItemIndex];
edSearchWord.SetFocus;
end;










procedure TfmMain.edSearchWordChange(Sender: TObject);
var
 i: integer;
begin
if DictionaryNotLoaded then
 begin
 showmessage('Dictionary is not loaded.');
 exit;
 end;
StatusBar1.SimpleText:=' Finding of word. Please, wait..';
StatusBar1.Update;
Update;

i:=GetPosOfWord(edSearchWord.Text);

StatusBar1.SimpleText:=' Done';
AddWordsToList(i-iDifferenceOfSelWord, i+iShowingWordsCount, i);
end;








procedure TfmMain.ListOfWordsKeyPress(Sender: TObject; var Key: Char);
begin
edSearchWord.Text:=edSearchWord.Text+Key;
edSearchWord.SetFocus;
end;




procedure TfmMain.ScrollBar1Scroll(Sender: TObject;
  ScrollCode: TScrollCode; var ScrollPos: Integer);
var
 i: integer;
begin
if DictionaryNotLoaded then
 begin
 //showmessage('Dictionary is not loaded.');
 ScrollBar1.Position:=0;
 exit;
 end;

if ScrollBar1.Tag=1 then exit;
i:={ScrollPos;}ScrollBar1.Position;
//ScrollPos-1;
//AddWordsToList(i-iDifferenceOfSelWord, i, i+iShowingWordsCount);

//ListBox1.Items.Add('ScrollPos='+inttostr(i));

(*
if ScrollCode=scTrack then
 begin
 ListBox1.Items.Add('Track='+inttostr(i));
 if (ListOfWords.Tag<=i) and (i<ListOfWords.Tag+iShowingWordsCount+iDifferenceOfSelWord) then
  begin // Needed word already showed in list
  ListOfWords.ItemIndex:=i-ListOfWords.Tag;
  end
  else
{  if (ListOfWords.Tag-1=i) then
  begin // LineUp
  end
  else
  if (i=ListOfWords.Tag+iShowingWordsCount+iDifferenceOfSelWord) then
  begin // LineDown
  end;}
  begin
  AddWordsToList(i-iDifferenceOfSelWord, i, i+iShowingWordsCount);
  end;
 end;
*)
 case ScrollCode of


 scLineUp:
  begin
{  if i>1 then
   begin
   dec(i);
   ScrollPos:=i;
   ListBox1.Items.Add('LineUp, new i='+inttostr(i)+', old tag='+inttostr(ListOfWords.Tag));
   if ListOfWords.Tag<=i-1 then
    begin
    ListOfWords.ItemIndex:=ListOfWords.ItemIndex-1;
    end
    else
    begin
    AddWordsToList(ListOfWords.Tag-1, i+iShowingWordsCount+iDifferenceOfSelWord-1, i-1);
    end;
   end
   else
   begin
   //ListBox1.Items.Add('LineUp, i<=1');
   //ScrollPos:=0;
   exit;
   end;
}  end;

 scLineDown:
  begin
{  inc(i);
  ScrollPos:=i;
  ListBox1.Items.Add('LineDown, i='+inttostr(i)+', tag='+inttostr(ListOfWords.Tag));
  if ListOfWords.Tag+iShowingWordsCount+iDifferenceOfSelWord+2>i then
   begin
   ListOfWords.ItemIndex:=ListOfWords.ItemIndex+1;
   end
   else
   begin
   AddWordsToList(ListOfWords.Tag+1, i-1, i-1);
   end;
}  end;

 scPageUp:
  begin
  i:=ListOfWords.Tag-iShowingWordsCount-iDifferenceOfSelWord;
  if i<1 then i:=1;
  ScrollPos:=i;
  ListBox1.Items.Add('PageUp, new i='+inttostr(i)+', old tag='+inttostr(ListOfWords.Tag));
  AddWordsToList(i-1, i+iShowingWordsCount+iDifferenceOfSelWord-1, i-1);
  end;

 scPageDown:
  begin
  i:=ListOfWords.Tag+iShowingWordsCount+iDifferenceOfSelWord+2;
  if i>High(aWords)+1 then i:=High(aWords)+1;
  ScrollPos:=i;
  ListBox1.Items.Add('PageDown, new i='+inttostr(i)+', old tag='+inttostr(ListOfWords.Tag));
  AddWordsToList(i-1, i+iShowingWordsCount+iDifferenceOfSelWord-1, i-1);
  end;

 scPosition:
  begin
   //ListBox1.Items.Add('Position, i='+inttostr(i));
  end;

 scTrack:
  begin
   ListBox1.Items.Add('Track, tag='+inttostr(ListOfWords.Tag)+', item='+inttostr(ListOfWords.ItemIndex)+', i(+1)='+inttostr(i)+', max='+inttostr(ListOfWords.Tag+iShowingWordsCount+iDifferenceOfSelWord));


  if (i-1>=ListOfWords.Tag) and (i-1<=ListOfWords.Tag+iShowingWordsCount+iDifferenceOfSelWord) then
   begin // Word already is showed
   ListBox1.Items.Add(' Already shown');
   ListOfWords.ItemIndex:=i-ListOfWords.Tag-1;
   end
   else
   if i=ListOfWords.Tag then
   begin // LineUp
   ListBox1.Items.Add(' Line up');
   dec(i);
   ScrollPos:=i;
   AddWordsToList(ListOfWords.Tag-1, i+iShowingWordsCount+iDifferenceOfSelWord, i);
   end
   else
   if i=ListOfWords.Tag+iShowingWordsCount+iDifferenceOfSelWord+2 then
   begin  // LineDown
   ListBox1.Items.Add(' Line down');
   inc(i);
   ScrollPos:=i;
   AddWordsToList(ListOfWords.Tag+1, i-2, i-2);
   end
   else // Free Scroll
   begin
   ListBox1.Items.Add(' Free scroll');
   AddWordsToList(i-iDifferenceOfSelWord-1, i+iShowingWordsCount-1, i-1);
   end;

{
  if (i-1<ListOfWords.Tag) and (i-1>ListOfWords.Tag+iShowingWordsCount+iDifferenceOfSelWord) then
   begin
   ListBox1.Items.Add(' Free scroll');
   AddWordsToList(i-iDifferenceOfSelWord-1, i+iShowingWordsCount-1, i-1);
   end;
 }

  end;
{
 scTop:
  begin
   ListBox1.Items.Add('Top, i='+inttostr(i));
  end;

 scBottom:
  begin
   ListBox1.Items.Add('Bottom, i='+inttostr(i));
  end;
}
 scEndScroll:
  begin
  // ListBox1.Items.Add('EndScroll, i='+inttostr(i));
  end;
 end;

{fmMain.ListBox2.Items.Add('ScrollCode='
);}

end;


procedure TfmMain.FormResize(Sender: TObject);
begin
iShowingWordsCount:=ListOfWords.Height div (ListOfWords.ItemHeight+5)-1;
end;

end.
