const
  cFormWidth  = 450;
  cFormHeight = 230;

var
  FMacro: WideString;
  FContains, FBetween: WideString;
  FCondition: Integer;

  AForm: TvxSceneForm;
  AFormCaption: String;
  AMacroEdit: TvxTextBox; AContainsEdit: TvxTextBox; ABetweenEdit: TvxTextBox;
  ACondition: TvxPopupBox;
  ASample: TvxLabel;


procedure UpdateSampleText;
var
  AItem: TImageItem;
begin
  ASample.Text := '';
  AItem := nil;
  if ActiveCollection <> nil then
  begin
    if ActiveCollection.Items.Count > 0 then
      AItem := ActiveCollection.Items.Items[0];
  end;

  if AItem = nil then exit;

  ASample.Text := AItem.ParseMacros(AMacroEdit.Text, nil);
end;

procedure UpdateContainsEditState;
begin
  AContainsEdit.Visible := (ACondition.ItemIndex <> 7) and   // is blank
                           (ACondition.ItemIndex <> 8);      // is not blank

  ABetweenEdit.Visible := (ACondition.ItemIndex = 6);        // between
end;

procedure _MacroChangeTracking(Sender: TObject);
begin
  ASample.Text := '';
  //UpdateSampleText;
end;

procedure _SampleButtonClick(Sender: TObject);
begin
  UpdateSampleText;
end;

procedure _MacroButtonClick(Sender: TObject);
begin
  MacroValuesPopup.Component := AMacroEdit;
  MacroValuesPopup.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y);
end;

procedure _SaveClick (Sender: Tobject);
var
  AElem: TidElement;
begin
  SaveDialog.Filter := 'Macro Save Files|*.msf';
  SaveDialog.DefaultExt := '.msf';
  SaveDialog.InitialDir := DefaultAppDataDir + WideIncludeTrailingBackslash('Filters');
  if not SaveDialog.Execute then
    exit;

  AForm.Caption := AFormCaption + ' - ' + UTF8Encode(WideExtractFileName(SaveDialog.FileName));

  AElem := TidElement.Create(nil);
  AElem.Prop['FilterMacro']          := AMacroEdit.Text;
  AElem.Prop['FilterMacroContains']  := AContainsEdit.Text;
  AElem.Prop['FilterMacroBetween']   := ABetweenEdit.Text;
  AElem.Prop['FilterMacroCondition'] := ACondition.ItemIndex;
  AElem.SaveAsXMLFile(SaveDialog.FileName);
  AElem.Free;
end;

procedure _LoadClick (Sender: TObject);
var
  AElem: TidElement;
begin
  OpenDialog.Filter     := 'Macro Save Files|*.msf';
  OpenDialog.DefaultExt := '.msf';
  OpenDialog.InitialDir := DefaultAppDataDir + WideIncludeTrailingBackslash('Filters');
  if not OpenDialog.Execute then
    exit;
  AElem := TidElement.Create(nil);
  AElem.LoadFromXMLFile(OpenDialog.FileName);

  AForm.Caption := AFormCaption + ' - ' + UTF8Encode(WideExtractFileName(OpenDialog.FileName));

  FMacro    := '';
  FMacro    := Nvl(AElem.Prop['FilterMacro'], FMacro);
  FContains := '';
  FContains := Nvl(AElem.Prop['FilterMacroContains'], FContains);
  FBetween  := '';
  FBetween  := Nvl(AElem.Prop['FilterMacroBetween'], FBetween);
  FCondition:= 0;
  FCondition:= Nvl(AElem.Prop['FilterMacroCondition'], FCondition);
  AElem.Free;

  AMacroEdit.Text := FMacro;
  AContainsEdit.Text := FContains;
  ABetweenEdit.Text := FBetween;
  ACondition.ItemIndex := FCondition;
  UpdateContainsEditState;
end;

procedure _CondtionChange(Sender: TObject);
begin
  UpdateContainsEditState;
end;

function BuildForm: TvxSceneForm;
var
  ALabel: TvxLabel;
  AEdit: TvxTextBox;
  ACombo: TvxPopupBox;
  AOk: TvxButton;
  ASave: TvxButton;
  ALoad: TvxButton;
  AMacroButton: TvxButton;
  ASampleButton: TvxButton;
begin
  result := TvxSceneForm.Create(nil);
  result.Caption  := AFormCaption;
  result.Position := poScreenCenter;
  result.Width := Round(cFormWidth * result.DPIFactor);
  result.Height := Round(cFormHeight * result.DPIFactor);

  ALabel := TvxLabel.Create(result.Root);
  result.Root.AddObject(ALabel);
  ALabel.Text := 'Macro Command';
  ALabel.TextAlign := vgTextAlignNear;
  ALabel.Position.Point := vgPoint(20, 20);

  AEdit := TvxTextBox.Create(result.Root);
  result.Root.AddObject(AEdit);
  AEdit.Text := FMacro;
  AEdit.Width := 200;
  AEdit.Position.Point := vgPoint(ALabel.Position.X, ALabel.Position.Y + ALabel.Height + 0);
  AEdit.OnChangeTracking := '_MacroChangeTracking';
  AMacroEdit := AEdit;

  AMacroButton := TvxButton.Create(result.Root);
  result.Root.AddObject(AMacroButton);
  AMacroButton.Position.Point := vgPoint(AEdit.Position.X + AEdit.Width, AEdit.Position.Y);
  AMacroButton.Width  := AEdit.Height;
  AMacroButton.Text   := '...';
  AMacroButton.OnClick:= '_MacroButtonClick';

  ASampleButton := TvxButton.Create(result.Root);
  result.Root.AddObject(ASampleButton);
  ASampleButton.Position.Point := vgPoint(AMacroButton.Position.X + AMacroButton.Width, AMacroButton.Position.Y);
  ASampleButton.Width  := 100;
  ASampleButton.Text   := 'show sample';
  ASampleButton.OnClick:= '_SampleButtonClick';
  if ActiveCollection = nil then
    ASampleButton.Visible := False
  else
    ASampleButton.Visible := ActiveCollection.Items.Count > 0;

  ASample := TvxLabel.Create(result.Root);
  result.Root.AddObject(ASample);
  ASample.Text := '';
  ASample.Font.Style := vgFontItalic;
  ASample.Width := cFormWidth - AEdit.Position.X - 10;
  ASample.Height := ASample.Height * 2;
  ASample.TextAlign := vgTextAlignNear;
  ASample.WordWrap := True;
  ASample.Position.Point := vgPoint(AEdit.Position.X, AEdit.Position.Y + AEdit.Height + 0);

  ACombo := TvxPopupBox.Create(result.Root);
  result.Root.AddObject(ACombo);
  //ACombo.Style := csDropDownList;
  ACombo.Items.Clear;
  ACombo.Items.Add('Contains value');
  ACombo.Items.Add('Doesn''t contain value');
  ACombo.Items.Add('Equals');
  ACombo.Items.Add('Doesn''t Equal');
  ACombo.Items.Add('Less Than');
  ACombo.Items.Add('Greater Than');
  ACombo.Items.Add('Between');
  ACombo.Items.Add('Is Blank');
  ACombo.Items.Add('Is Not  Blank');
  ACombo.ItemIndex := FCondition;
  ACombo.Position.Point := vgPoint(20, ASample.Position.Y + ASample.Height + 10);
  ACombo.Width := 150;
  ACombo.TextAlign := vgTextAlignNear;
  ACombo.OnChange := '_CondtionChange'
  ACondition := ACombo;

  AEdit := TvxTextBox.Create(result.Root);
  result.Root.AddObject(AEdit);
  AEdit.Text := FContains;
  AEdit.Width := 200;
  AEdit.Position.Point := vgPoint(ACombo.Position.X, ACombo.Position.Y + ACombo.Height + 10);
  AContainsEdit := AEdit;

  AEdit := TvxTextBox.Create(result.Root);
  result.Root.AddObject(AEdit);
  AEdit.Text := FBetween;
  AEdit.Width := 200;
  AEdit.Position.Point := vgPoint(AContainsEdit.Position.X + AContainsEdit.Width + 10, ACombo.Position.Y + ACombo.Height + 10);
  ABetweenEdit := AEdit;

  AOk := TvxButton.Create(result.Root);
  result.Root.AddObject(AOk);
  AOk.ModalResult := mrOk;
  AOk.Position.Point := vgPoint(ACombo.Position.X, (result.Height / result.DPIFactor) - 10 - AOk.Height);
  AOk.Text := 'OK';
  AOk.Default := True;

  ASave := TvxButton.Create(result.Root);
  result.Root.AddObject(ASave);
  ASave.OnClick := '_SaveClick';
  ASave.Position.Point := vgPoint((result.Width / result.DPIFactor) - 10 - ASave.Width, (result.Height / result.DPIFactor) - 10 - ASave.Height);
  ASave.Text := 'Save';

  ALoad := TvxButton.Create(result.Root);
  result.Root.AddObject(ALoad);
  ALoad.OnClick := '_LoadClick';
  ALoad.Position.Point := vgPoint(ASave.Position.X - 5 - ALoad.Width, (result.Height / result.DPIFactor) - 10 - ALoad.Height);
  ALoad.Text := 'Load';

  UpdateContainsEditState;
end;

function Initialize: Boolean;
begin
  result := True;

  FHit := 0;
  FMacro    := '';
  FMacro    := ReadFromRegistry ('Scripts', 'FilterMacro', FMacro);
  FContains := '';
  FContains := ReadFromRegistry ('Scripts', 'FilterMacroContains', FContains);
  FBetween  := '';
  FBetween  := ReadFromRegistry ('Scripts', 'FilterMacroBetween', FBetween);
  FCondition:= 0;
  FCondition:= ReadFromRegistry ('Scripts', 'FilterMacroCondition', FCondition);

  AFormCaption := 'Filter by Macro Command';
  AForm := BuildForm;
  AForm.ShowModal;
  result := (AForm.ModalResult = mrOk);
  if result then
  begin
    FMacro     := Trim(AMacroEdit.Text);
    FContains  := Trim(AContainsEdit.Text);
    FBetween   := Trim(ABetweenEdit.Text);
    FCondition := ACondition.ItemIndex;
  end;
  AForm.Free;
end;

procedure Finalize;
begin
  MacroValuesPopup.Component := nil;

  WriteToRegistry ('Scripts', 'FilterMacro', FMacro);
  WriteToRegistry ('Scripts', 'FilterMacroContains', FContains);
  WriteToRegistry ('Scripts', 'FilterMacroBetween', FBetween);
  WriteToRegistry ('Scripts', 'FilterMacroCondition', FCondition);
end;

function Filter(AItem: TImageItem): Boolean;
var
  AVal: WideString;
  AContains, ABetween: WideString;
begin
  result := False;
  if FMacro = '' then
    exit;

  AVal      := AItem.ParseMacros (FMacro, nil);
  AContains := AItem.ParseMacros (FContains, nil);
  ABetween  := AItem.ParseMacros (FBetween, nil);

  case FCondition of
    0:  // contains
      result := (WideTextPos (AContains, AVal) > 0);
    1:  // not contains
      result := (WideTextPos (AContains, AVal) = 0);
    2:  // equals
      result := WideSameText (AContains, AVal);
    3:  // not equals
      result := not WideSameText (AContains, AVal);
    4:  // less than
    begin
      if AVal = '' then
        result := False
      else if IsValidNumberString(AContains, False, DecimalSeparator) and
              IsValidNumberString(AVal, False, DecimalSeparator)
      then
        result := (AVal < StrToFloat(AContains))
      else
        result := (WideCompareStr(AVal, AContains) < 0);
    end
    5:  // greater than
    begin
      if AVal = '' then
        result := False
      else if IsValidNumberString(AContains, False, DecimalSeparator) and
              IsValidNumberString(AVal, False, DecimalSeparator)
      then
        result := (AVal > StrToFloat(AContains))
      else
        result := (WideCompareStr(AVal, AContains) > 0);
    end
    6:  // between
    begin
      if (AVal = '') then
        result := False
      else if IsValidNumberString(AContains, False, DecimalSeparator) and
              IsValidNumberString(AVal, False, DecimalSeparator) and
              IsValidNumberString(ABetween, False, DecimalSeparator) and
              IsValidNumberString(AVal, False, DecimalSeparator)
      then
        result := ((AVal >= StrToFloat(AContains)) and (AVal <= StrToFloat(ABetween)))
      else
        result := ((WideCompareStr(AVal, AContains) >= 0) and (WideCompareStr(AVal, ABetween) <= 0));
    end
    7:  // is blank
      result := AVal = '';
    8:  // is not blank
      result := AVal <> '';
  end;
end;
