// =====================================================
//
//   miniLA_win - Trigger setup
//
//   (c) miniLA Team
//
// =====================================================
//
// This 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, or (at your option)
// any later version.
//
// This software 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 package; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.


unit dlgTrigger;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ExtCtrls, ComCtrls, Spin,
  uUtils,
  uLATypes;

type
  TfrmTrigger = class(TForm)
    btnOK: TBitBtn;
    btnCancel: TBitBtn;
    PageControl1: TPageControl;
    tabChannels: TTabSheet;
    tabGroups: TTabSheet;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Label18: TLabel;
    Label19: TLabel;
    Image1: TImage;
    Label21: TLabel;
    Label22: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    Edit6: TEdit;
    Edit7: TEdit;
    Edit8: TEdit;
    Edit9: TEdit;
    Edit10: TEdit;
    Edit11: TEdit;
    Edit12: TEdit;
    Edit13: TEdit;
    Edit14: TEdit;
    Edit15: TEdit;
    Edit16: TEdit;
    ScrollBox1: TScrollBox;
    Label23: TLabel;
    Label24: TLabel;
    Label25: TLabel;
    Label26: TLabel;
    Label27: TLabel;
    tabAdvanced: TTabSheet;
    cbInvIntTrig: TCheckBox;
    Label20: TLabel;
    cbTriggerSize: TComboBox;
    Label28: TLabel;
    seTriggerLength: TSpinEdit;
    seTriggerDelay: TSpinEdit;
    lbTriggerDelay: TLabel;
    lbTriggerLength: TLabel;
    labTrigLengthHelp: TLabel;
    Image2: TImage;
    Image3: TImage;
    Image4: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure cbEnableClick(Sender: TObject);
    procedure TrigConditionClick(Sender: TObject);
    procedure btnViewGroupClick(Sender: TObject);
    procedure geGroupEditsKeyPress(Sender: TObject; var Key: Char);
    procedure geGroupEditsExit(Sender: TObject);
    procedure btnViewChannelClick(Sender: TObject);
    procedure btnOKClick(Sender: TObject);
  private
    GroupCount  : integer;
    GroupNames  : TGroupNames;
    GroupEdits  : TGroupEdits;
    GroupButtons: TGroupButtons;
    function  HasTrigChannels(n:integer):boolean;
    procedure SetGroupValText(grp_n: integer);
    procedure UpdateGroupVal(grp_n: integer);
    procedure UpdateTrigDelay;
  public
    TrigField   : TTrigArray;
    TrigLen     : byte;			// minimal length of trigger event
    TrigDelay   : byte;			// number of trigger events
    TrigSize    : byte;			// pre/posttrigger buffer size
    TrigMask    : cardinal;		// trigger mask
    TrigValue   : cardinal;		// trigger value
    TrigEdge    : cardinal;		// trigger edge
    ExtTrig_en  : boolean;		// enable external trigger (=disable internal)
    ExtTrig_val : integer;		// external trigger value
    InvIntTrig  : boolean;		// invert internal trigger
  end;

var frmTrigger: TfrmTrigger;

implementation
uses dlgMain, dlgChannel, dlgChannelList;
{$R *.DFM}


procedure TfrmTrigger.FormCreate(Sender: TObject);
var i,j:byte;
    zr: integer;
    P:TPanel;
begin
  for i:=0 to 16 do begin
    with TrigField[i] do begin
      // Enable check box
      cbEnabled        := TCheckBox.Create(tabChannels);
      cbEnabled.Parent := tabChannels;
      cbEnabled.Top    := round(17+i*19*PixelsPerInch/96);
      cbEnabled.Left   := round(210*PixelsPerInch/96);
      cbEnabled.Width  := round(19*PixelsPerInch/96);
      cbEnabled.Height := round(19*PixelsPerInch/96);
      cbEnabled.Caption:= '';

      // TabSheet for level/edge selection
      P:=TPanel.Create(tabChannels);
      P.Parent    := tabChannels;
      P.BevelOuter:= bvLowered;
      P.Top       := round(17+i*19*PixelsPerInch/96);
      P.Left      := round(254*PixelsPerInch/96);
      P.Width     := round(119*PixelsPerInch/96);
      P.Height    := round(19*PixelsPerInch/96);
      P.Caption   := '';

      // checkboxes for level/edge selections
      for j:=0 to 3 do begin
        TrigCondition[j]:=TRadioButton.Create(tabChannels);
        with TrigCondition[j] do begin
          Top    := round(1*PixelsPerInch/96);
          Left   := round(5+j*30*PixelsPerInch/96);
          Width  := round(17*PixelsPerInch/96);
          Height := round(17*PixelsPerInch/96);
          Caption:= '';
          Parent := P;
        end;
        if j=0 then TrigCondition[j].Checked:=true;
      end;
      
      cbEnabled.Checked:= false;
      cbEnabled.Enabled:= true;
      //cbEnableClick(cbEnabled);
    end;
    
    // buttons for viewing the group
    if i<16 then begin
      GroupButtons[i]:=TButton.Create(tabChannels);
      GroupButtons[i].Tag:=i;			// tag will get updated at FormActivate
      GroupButtons[i].Parent:=tabChannels;
      GroupButtons[i].Top:=round(19+i*19*PixelsPerInch/96);
      GroupButtons[i].Height:=round(16*PixelsPerInch/96);
      GroupButtons[i].Width:=round(30*PixelsPerInch/96);
      GroupButtons[i].Left:=round(43*PixelsPerInch/96);
      GroupButtons[i].Caption:='>>';
      GroupButtons[i].OnClick:=btnViewGroupClick;
      GroupButtons[i].ShowHint:=true;
      GroupButtons[i].Hint:='Switch to associated group';
    end;
  end;

  // Pre/Posttrigger size combo box
  cbTriggerSize.Clear;
  for i:=1 to 15 do				// pre/posttrigger
     cbTriggerSize.Items.Add(IntToStr(i*8)+'K / '+IntToStr((16-i)*8)+'K');
  for i:=1 to 16 do				// posttrigger
     cbTriggerSize.Items.Add('0K / '+IntToStr(i*8)+'K');
  cbTriggerSize.ItemIndex:=0;

  // Initialize variables
  TrigLen := 1;
  TrigDelay := 1;
  TrigSize := 0;
  TrigMask := $0000;
  TrigValue := $0000;
  TrigEdge := $0000;
  ExtTrig_en := false;
  ExtTrig_val := 1;
  InvIntTrig := false;
end;


procedure TfrmTrigger.FormActivate(Sender: TObject);
var i,j: integer;
    mask: cardinal;
    L1,L2: TLabel;
    B: TButton;
    s: string;
begin
  // update signal names
  Edit1.Text:=  frmChannel.ChannelInfo[ 0].Edit.Text;
  Edit2.Text:=  frmChannel.ChannelInfo[ 1].Edit.Text;
  Edit3.Text:=  frmChannel.ChannelInfo[ 2].Edit.Text;
  Edit4.Text:=  frmChannel.ChannelInfo[ 3].Edit.Text;
  Edit5.Text:=  frmChannel.ChannelInfo[ 4].Edit.Text;
  Edit6.Text:=  frmChannel.ChannelInfo[ 5].Edit.Text;
  Edit7.Text:=  frmChannel.ChannelInfo[ 6].Edit.Text;
  Edit8.Text:=  frmChannel.ChannelInfo[ 7].Edit.Text;
  Edit9.Text:=  frmChannel.ChannelInfo[ 8].Edit.Text;
  Edit10.Text:= frmChannel.ChannelInfo[ 9].Edit.Text;
  Edit11.Text:= frmChannel.ChannelInfo[10].Edit.Text;
  Edit12.Text:= frmChannel.ChannelInfo[11].Edit.Text;
  Edit13.Text:= frmChannel.ChannelInfo[12].Edit.Text;
  Edit14.Text:= frmChannel.ChannelInfo[13].Edit.Text;
  Edit15.Text:= frmChannel.ChannelInfo[14].Edit.Text;
  Edit16.Text:= frmChannel.ChannelInfo[15].Edit.Text;

  // make list of groups
  ScrollBox1.DestroyComponents;			// delete all components
  GroupCount:=0;
  with frmMain.LogPanel.Display.DataSet do
    for i:=0 to NumberOfLines-1 do
      if Data[i].Group and HasTrigChannels(i) then 	// je-li SKUPINA na obrazovce (tzn. neni prazdna), pridej
       begin
	 // Group name
	 L1:=TLabel.Create(ScrollBox1);
	 L1.Parent:=ScrollBox1;
	 L1.Top:=round(5+GroupCount*19*PixelsPerInch/96);
	 L1.Left:=round(5*PixelsPerInch/96);
	 L1.Caption:=Data[i].Name;
	 L1.AutoSize:=false;
	 L1.Width:=round(144*PixelsPerInch/96);

	 // number base
	 L2:=TLabel.Create(ScrollBox1);
	 L2.Parent:=ScrollBox1;
	 L2.Left:=round(150*PixelsPerInch/96);
	 L2.Top:=round(5+GroupCount*19*PixelsPerInch/96);
	 case Data[i].Base of
	   0: L2.Caption:= 'binary';
	   1: L2.Caption:= 'octal';
	   2: L2.Caption:= 'decimal';
	   3: L2.Caption:= 'hexadecimal';
	 end;

	 GroupNames[GroupCount]:=Data[i].Name;

	 // trigger value
	 GroupEdits[GroupCount]:=TEdit.Create(ScrollBox1);
	 GroupEdits[GroupCount].Parent:=ScrollBox1;
	 GroupEdits[GroupCount].Top:=round(2+GroupCount*19*PixelsPerInch/96);
	 GroupEdits[GroupCount].Left:=round(220*PixelsPerInch/96);
	 GroupEdits[GroupCount].Width:=round(100*PixelsPerInch/96);
	 GroupEdits[GroupCount].Tag:=i;
	 GroupEdits[GroupCount].OnKeyPress :=geGroupEditsKeyPress;
	 GroupEdits[GroupCount].OnExit :=geGroupEditsExit;
	 //GroupEdits[GroupCount].Text:='';

	 // info button - view signals in group
	 B:=TButton.Create(ScrollBox1);
	 B.Parent:=ScrollBox1;
	 B.Left:=round(325*PixelsPerInch/96);
	 B.Width:=round(40*PixelsPerInch/96);
	 B.Top:=round(3+GroupCount*19*PixelsPerInch/96);
	 B.Height:=round(17*PixelsPerInch/96);
	 B.Caption:='View';
	 B.OnClick:=btnViewChannelClick;
	 B.Tag:=GroupCount;

	 inc(GroupCount);
       end;

  // update visibility of buttons "view group"
  for i:=0 to 15 do
   begin
     GroupButtons[i].Visible:=(frmChannel.ChannelInfo[i].Group.ItemIndex>0) and
                              (TrigField[i].cbEnabled.Enabled);
     with frmChannel.ChannelInfo[i].Group do
       s:=Items.Strings[ItemIndex];

     // update tag to point to groupnames index
     j:=0;
     while j<GroupCount do
       if s<>GroupNames[j] then
          inc(j)
       else
          break;

     GroupButtons[i].Tag:=j; // groupnames position
   end;


  // update form values
  for i:=0 to 16 do
   begin
     mask := 1 shl i;
     TrigField[i].cbEnabled.OnClick := nil;				// disable event
     TrigField[i].cbEnabled.Enabled :=    ((i<>16) and not(ExtTrig_en))
     				       or (i=16);
     TrigField[i].cbEnabled.Checked :=    ((i<>16) and ((TrigMask AND mask)<>0))
     				       or ((i=16)  and ExtTrig_En);
     TrigField[i].cbEnabled.OnClick := cbEnableClick;			// re-enable event
     for j:=0 to 3 do
      begin
	TrigField[i].TrigCondition[j].OnClick := nil;			// disable event
	TrigField[i].TrigCondition[j].Enabled := TrigField[i].cbEnabled.Enabled and TrigField[i].cbEnabled.Checked;
        TrigField[i].TrigCondition[j].Checked :=    ((i<>16) and (   (((    TrigValue  AND (NOT(TrigEdge)) AND Mask)<>0) and (j=0))
	                                                          or (((NOT(TrigValue) AND (NOT(TrigEdge)) AND Mask)<>0) and (j=1))
	                                                          or (((    TrigValue  AND (    TrigEdge ) AND Mask)<>0) and (j=2))
	                                                          or (((NOT(TrigValue) AND (    TrigEdge ) AND Mask)<>0) and (j=3))))
					         or ((i=16)  and (   ((ExtTrig_val <> 0) and (j=0))
	                                                          or ((ExtTrig_val = 0)  and (j=1))));
	TrigField[i].TrigCondition[j].OnClick := TrigConditionClick;	// re-enable event
      end;
   end;

  // update group values
  for i:=0 to GroupCount-1 do
     SetGroupValText(i);


  seTriggerDelay.Value := TrigDelay;
  UpdateTrigDelay;
  seTriggerLength.Value := TrigLen;
  cbTriggerSize.ItemIndex := TrigSize;
  cbInvIntTrig.Checked := InvIntTrig;

  PageControl1.ActivePage:=tabChannels;
end;


// return true if group has any signal with trigger functionality
function TfrmTrigger.HasTrigChannels(n:integer):boolean;
var i:integer;
begin
   Result:=false;
   with frmMain.LogPanel.Display.DataSet.Data[n] do
    begin
      i:= 0;
      while (i<NumberOfSignals) and (result=false) do
	if Signals[i]<16 then
	   Result:=true
	else
	   inc(i);
    end;
end;

procedure TfrmTrigger.cbEnableClick(Sender: TObject);
var
   n, i, j	: integer;
begin
  i:=0;
  while i<17 do begin
    if Sender=TrigField[i].cbEnabled then
       break;
    inc(i);
  end;
  if i=17 then exit;

  // if external trigger, disable/enable all internal triggers
  if i = 16 then
     for n:=0 to 15 do
      begin
        TrigField[n].cbEnabled.Enabled := not(TrigField[i].cbEnabled.Checked);
	for j:=0 to 3 do
	  TrigField[n].TrigCondition[j].Enabled := not(TrigField[i].cbEnabled.Checked) and TrigField[n].cbEnabled.Checked;
      end;

  // enable/disable selected trigger
  for j:=0 to 3 do
        TrigField[i].TrigCondition[j].Enabled:=TrigField[i].cbEnabled.Checked
    and ((i<>16) or (j<2));

  // update group trigger values
  for j:=0 to GroupCount-1 do
     SetGroupValText(j);

  UpdateTrigDelay;

end;


procedure TfrmTrigger.TrigConditionClick(Sender: TObject);
var
   i, j		: integer;
   found	: boolean;
begin
   i := 0;
   found := false;
   while (i<17) and not(found) do
    begin
      j := 0;
      while (j<4) and not(found) do
       begin
         if Sender=TrigField[i].TrigCondition[j] then
            found := true
         else
	    inc(j);
       end;
      if not(found) then
         inc(i);
    end;

   if not(found) then exit;

   // update associated group value
   if (frmChannel.ChannelInfo[i].Group.ItemIndex>0) then
      SetGroupValText(GroupButtons[i].Tag);

   UpdateTrigDelay;

end;

procedure TfrmTrigger.btnViewGroupClick(Sender: TObject);
begin
   PageControl1.ActivePage:=tabGroups;
   GroupEdits[(Sender as TButton).Tag].SetFocus;
end;


procedure TFrmTrigger.geGroupEditsKeyPress(Sender: TObject; var Key: Char);
var
   i		: integer;
begin
   if Key=chr(13) then
    begin
     i:=0;
     while i<GroupCount-1 do begin
       if Sender=GroupEdits[i] then
	  break;
       inc(i);
     end;
     if i=GroupCount then exit;

     UpdateGroupVal(i);
    end;
end;

procedure TFrmTrigger.geGroupEditsExit(Sender: TObject);
var
   i		: integer;
begin
  i:=0;
  while i<GroupCount-1 do begin
    if Sender=GroupEdits[i] then
       break;
    inc(i);
  end;
  if i=GroupCount then exit;

  SetGroupValText(i);
end;

procedure TFrmTrigger.UpdateGroupVal(grp_n: integer);
var
  tr_value	: cardinal;
  tr_mask	: cardinal;
  i, j		: integer;
begin
   tr_value := 0;
   with frmMain.LogPanel.Display.DataSet.Data[GroupEdits[grp_n].Tag] do
    begin
      // get group value
      case Base of
        0: tr_value := BinToInt(GroupEdits[grp_n].Text);
        1: tr_value := OctToInt(GroupEdits[grp_n].Text);
        2: try
	      tr_value := StrToInt(GroupEdits[grp_n].Text);
	   except
	   end;
        3: tr_value := HexToInt(GroupEdits[grp_n].Text);
      end;

      // update individual signals
      i:=0;
      while i<NumberOfSignals do
       begin
         if Signals[i]<16 then
          begin
	    tr_mask := (1 shl i);
            TrigField[Signals[i]].cbEnabled.Checked := true;
	    for j:= 0 to 3 do
	       TrigField[Signals[i]].TrigCondition[j].Checked := (   (((    tr_value  and tr_mask)<>0) and (j=0))	// log 1
	       							  or (((not(tr_value) and tr_mask)<>0) and (j=1)));	// log 0
          end;
	 inc(i);
       end;
    end;
   SetGroupValText(grp_n);

   UpdateTrigDelay;
end;

// set group trigger value text
procedure TFrmTrigger.SetGroupValText(grp_n: integer);
var
   tr_value	: cardinal;
   tr_en	: boolean;
   i		: integer;
begin
   tr_value := 0;
   tr_en := true;
   with frmMain.LogPanel.Display.DataSet.Data[GroupEdits[grp_n].Tag] do
    begin
      i:=0;
      // get trigger value
      while i<NumberOfSignals do
       begin
         if Signals[i]<16 then
          begin
            if not(TrigField[Signals[i]].cbEnabled.Enabled) or not(TrigField[Signals[i]].cbEnabled.Checked) then
               tr_en := false;
	    if TrigField[Signals[i]].cbEnabled.Checked and (   TrigField[Signals[i]].TrigCondition[0].Checked
            						    or TrigField[Signals[i]].TrigCondition[2].Checked) then
	       tr_value := tr_value or (1 shl i);
          end;
	 inc(i);
       end;

      // print value
      case Base of
	0: GroupEdits[grp_n].Text := IntToBin(tr_value, NumberOfSignals, false);
	1: GroupEdits[grp_n].Text := IntToOct(tr_value, NumberOfSignals);
	2: GroupEdits[grp_n].Text := IntToStr(tr_value);
	3: GroupEdits[grp_n].Text := IntToHex(tr_value, NumberOfSignals);
      end;

      if tr_en then
         GroupEdits[grp_n].Font.Color := clWindowText
      else
         GroupEdits[grp_n].Font.Color := clGrayText;
   end;
end;

// updates trigger delay combo box
procedure TfrmTrigger.UpdateTrigDelay;
var i : integer;
    edge : boolean;
begin
   edge := false;

   // look for edge
   for i:=0 to 15 do
    begin
      if TrigField[i].cbEnabled.Checked and (   TrigField[i].TrigCondition[2].Checked
                                             or TrigField[i].TrigCondition[3].Checked) then
         edge := true;
    end;

  if edge then
   begin
     lbTriggerLength.Enabled := false;
     seTriggerLength.Value := 1;
     seTriggerLength.Enabled := false;
     labTrigLengthHelp.Visible := true;
   end
  else
   begin
     lbTriggerLength.Enabled := true;
     seTriggerLength.Enabled := true;
     labTrigLengthHelp.Visible := false;
   end;

end;

procedure TfrmTrigger.btnViewChannelClick(Sender: TObject);
var i:integer;
    s:string;
begin
  // view data
  frmChannelList.ListBox1.Clear;
  with frmMain.LogPanel.Display.DataSet.Data[(Sender as TButton).Tag] do begin
    frmChannelList.Caption:='Group "'+Name+'" - channels';
    for i:=0 to NumberOfSignals-1 do begin
      s:=IntToStr(Signals[i]+1)+'. '+frmChannel.ChannelInfo[Signals[i]].Edit.Text;
      if (i=0) and (NumberOfSignals>1) then
        s:=s+' (LSB)';
      if (i+1=NumberOfSignals) and (NumberOfSignals>1) then
        s:=s+' (MSB)';
      if (Signals[i]>15) then
        s:=s+' (unavailable for trigger)'
      else if not(TrigField[Signals[i]].cbEnabled.Checked) or not(TrigField[Signals[i]].cbEnabled.Enabled) then
        s:=s+' (triggering disabled)';

      frmChannelList.ListBox1.Items.Add(s);
    end;
  end;
  frmChannelList.ShowModal;
end;


procedure TfrmTrigger.btnOKClick(Sender: TObject);
var i: byte;
begin
  // update signal names
  frmChannel.ChannelInfo[ 0].Edit.Text:= Edit1.Text;
  frmChannel.ChannelInfo[ 1].Edit.Text:= Edit2.Text;
  frmChannel.ChannelInfo[ 2].Edit.Text:= Edit3.Text;
  frmChannel.ChannelInfo[ 3].Edit.Text:= Edit4.Text;
  frmChannel.ChannelInfo[ 4].Edit.Text:= Edit5.Text;
  frmChannel.ChannelInfo[ 5].Edit.Text:= Edit6.Text;
  frmChannel.ChannelInfo[ 6].Edit.Text:= Edit7.Text;
  frmChannel.ChannelInfo[ 7].Edit.Text:= Edit8.Text;
  frmChannel.ChannelInfo[ 8].Edit.Text:= Edit9.Text;
  frmChannel.ChannelInfo[ 9].Edit.Text:= Edit10.Text;
  frmChannel.ChannelInfo[10].Edit.Text:= Edit11.Text;
  frmChannel.ChannelInfo[11].Edit.Text:= Edit12.Text;
  frmChannel.ChannelInfo[12].Edit.Text:= Edit13.Text;
  frmChannel.ChannelInfo[13].Edit.Text:= Edit14.Text;
  frmChannel.ChannelInfo[14].Edit.Text:= Edit15.Text;
  frmChannel.ChannelInfo[15].Edit.Text:= Edit16.Text;
  frmChannel.btnOKClick(Sender);

  // calculate trigger values
  TrigMask:= 0;
  TrigValue := 0;
  TrigEdge := 0;
  for i:=0 to 15 do begin
    // mask
    if TrigField[i].cbEnabled.Checked then
       TrigMask:= TrigMask or (1 shl i);
    // value
    if    TrigField[i].TrigCondition[0].Checked
       or TrigField[i].TrigCondition[2].Checked then
       TrigValue := TrigValue or (1 shl i);
    // edge
    if    TrigField[i].TrigCondition[2].Checked
       or TrigField[i].TrigCondition[3].Checked then
       TrigEdge := TrigEdge or (1 shl i);
  end;
  TrigDelay := seTriggerDelay.Value;
  TrigLen   := seTriggerLength.Value;
  if cbTriggerSize.ItemIndex<15 then
     TrigSize  := cbTriggerSize.ItemIndex
  else
     TrigSize  := cbTriggerSize.ItemIndex + 1;
  InvIntTrig := cbInvIntTrig.Checked;

  ExtTrig_en := TrigField[16].cbEnabled.Checked;
  if TrigField[16].TrigCondition[0].Checked then
     ExtTrig_val := 1
  else
     ExtTrig_val := 0;

end;

end.
