unit RpVisualUtils;

interface
uses
  sysutils,
  windows,
  Registry,
  scadabase,
  DBGridEh,
  pFIBDataSet,
  controls,
  Forms,
  Classes,
  Dialogs,
  stdctrls,
  DBChart,
  pFIBDatabase;


Type
  TScadaObjectProc = procedure(Device: TCustomScadaObject);

  // rpVisualGlobal
  procedure InitDeviceProc(Target: TCustomScadaObject);
  procedure UpdateDeviceProc(Target: TCustomScadaObject);
  procedure CustomScanScadaObjects(ctrl: TWinControl; Proc: TScadaObjectProc);
  procedure SetPathRootScadaObjects(ctrl: TWinControl; ATagPath: string; ATagRoot: string);
  procedure SetPathScadaObjects(ctrl: TWinControl; ATagPath: string);
  procedure SetPathRootScadaObject(customScadaObject: TCustomScadaObject; TagPath: string; TagRoot: string);

  procedure SortMarkingChanged(Sender: TObject);

  function repl(Source, Target, Replace: string): string;


  // Globals
  procedure ResetSecurityVars;

  procedure ScanScadaObjects(Mode: integer; Proc: TScadaObjectProc);

//  procedure HideControlPanel;
//  procedure ShowControlPanel(Pnl: TForm);

  procedure ProcessComboBox(Cmb: TComboBox; TagIdx: integer);
  procedure ChangeComboBox(Cmb: TComboBox; TagIdx: integer; Mask: integer = $FFFF);

  procedure CheckExtMode;
  procedure CheckAdminMode;

  procedure ShowAdminPanel;

  function GetStringChannelName(i: integer): string;

  procedure LoadTrendProperties(id: string; Chart: TDBChart);
  procedure SaveTrendProperties(id: string; Ymin, ymax: double);

  function GetpFIBDatabaseByName(name: string): TpFIBDatabase;

  procedure DelayMs(ms: Cardinal; procmesflag: boolean);

  function StartProgram(app_name, ini_name, path_exe_name: string; UseParamVars: boolean=false;
      CommandLineParamStr: string=''): boolean;
  function get_clear_hint(s: string): string;

  function ReplaceStr(Source:AnsiString;const Target,Replace:AnsiString): AnsiString;
  function tailBackSlash(src: string): string;

  procedure setupMainFormColor;

  function isManualAccessIgnore(name: string): boolean;

  function CleanStrToIntDef(s: string; defaultValue: integer): integer;
  function RestoreNumberText(ATagname, ALabel: string): string;

  procedure ShowWesClientButton;
  procedure ShowMotohrButton;

  function findByTagRoot(tagroot: string; cls: TClass; pageIndex: integer = -1): boolean;




implementation


uses
  infodlg,
  InputDialog,
  RpVisualGlobal,
  RpVisualIni,
  hyperstr,
  RpVisualParams,
  ControlPanel,
  AdminMain,
  main,
  TagStorage,
  numbers,
  passwords,
  users,
  inifiles;


var
  tmpTagPath, tmpTagRoot: string;


function repl(Source, Target, Replace: string): string;
begin
  ReplaceS(Source, Target, Replace);
  result := Source;
end;



//============================================================================
// Перебор устройств                                                    SCAN
//============================================================================
procedure InitDeviceProc(Target: TCustomScadaObject);
begin
  Target.Init;
end;

procedure UpdateDeviceProc(Target: TCustomScadaObject);
begin
  Target.Process;
end;

procedure PathRootDeviceProc(Target: TCustomScadaObject);
begin
  Target.TagPath := tmpTagPath;
  Target.TagRoot := tmpTagRoot;
  Target.Init;
  Target.Process;
end;

procedure PathDeviceProc(Target: TCustomScadaObject);
begin
  Target.TagPath := tmpTagPath;
  Target.Init;
  Target.Process;
end;

procedure CustomScanScadaObjects(ctrl: TWinControl; Proc: TScadaObjectProc);
var
  i: integer;
begin
  with ctrl do
    for i:=0 to ControlCount-1 do
      if Controls[i] is TCustomScadaObject then
        Proc(Controls[i] as TCustomScadaObject)
      else
        if Controls[i] is TWinControl then begin
          CustomScanScadaObjects(Controls[i] as TWinControl, Proc);
        end;

end;

procedure SetPathRootScadaObjects(ctrl: TWinControl; ATagPath: string; ATagRoot: string);
begin
  tmpTagPath := ATagPath;
  tmpTagRoot := ATagRoot;
  CustomScanScadaObjects(ctrl, PathRootDeviceProc);
end;

procedure SetPathScadaObjects(ctrl: TWinControl; ATagPath: string);
begin
  tmpTagPath := ATagPath;
  CustomScanScadaObjects(ctrl, PathDeviceProc);
end;

procedure SetPathRootScadaObject(customScadaObject: TCustomScadaObject; TagPath: string; TagRoot: string);
begin
  customScadaObject.TagPath := TagPath;
  customScadaObject.TagRoot := TagRoot;
  customScadaObject.Init;
  customScadaObject.Process;
end;

procedure SortMarkingChanged(Sender: TObject);
var
  i: integer;
  s, fn: string;
begin
  if not rvgLogIn then exit;
  
  with sender as TDBGridEh do begin

    s := '';

    for i := 0 to SortMarkedColumns.Count-1 do begin
      fn:= inttostr(
            (DataSource.DataSet as TpFIBDataSet).FieldByName(
                  SortMarkedColumns[i].FieldName ).Index+1 );

      if Ord(SortMarkedColumns[i].Title.SortMarker) = 1 //smDownEh
        then s:= s + fn + ' DESC, '
        else s:= s + fn + ', ';
    end;

    if s <> '' then s := 'ORDER BY ' + Copy(s,1,Length(s)-2);

    with DataSource.DataSet as TpFIBDataSet do begin
      Close;

      i := pos('ORDER BY', SelectSQL.Text);
      if i=0 then
        SelectSQL.Text := SelectSQL.Text + ' ' + s
      else begin
        SelectSQL.Text := copy(SelectSQL.Text, 1, i-1) + ' ' + s
      end;

      Open;
    end;
  end;
end;




procedure ResetSecurityVars;
begin
  AdminMode_On := false;
  ExtMode_On := false;
  AdminMode_Channel := false;
end;


procedure ScanScadaObjects(Mode: integer; Proc: TScadaObjectProc);
begin
  case Mode of
    dsmOnlyVisible:
      CustomScanScadaObjects(Form1.PageControl.ActivePage, Proc);

    dsmAll:
      CustomScanScadaObjects(Form1, Proc);
  end;
end;



//============================================================================
// Вкл/выкл панели управления                                   CONTROL PANEL
//============================================================================
{
procedure HideControlPanel;
var
  i: integer;
begin
  for i:=0 to Application.ComponentCount-1 do
    if copy((Application.Components[i] as TComponent).ClassName, 1, 10) = 'TCtrlPanel' then
      (Application.Components[i] as TForm).Close;

   Form1.PageControl.Align := alClient;
end;

procedure ShowControlPanel(Pnl: TForm);
var
  i: integer;
begin
//  CloseControlPanel;
  for i:=0 to Application.ComponentCount-1 do
    if copy((Application.Components[i] as TComponent).ClassName, 1, 10) = 'TCtrlPanel' then begin
      if pointer(Application.Components[i]) = pointer(Pnl) then
        with Application.Components[i] as TForm do begin
          Left := Form1.Width - Width;
          Top := Form1.PageControl.Top;
          Height := Form1.PageControl.Height;
          Parent := Form1;
          Anchors := [akRight, akTop, akBottom];
          Show;
          if Assigned(OnActivate) then OnActivate(nil); //
          Form1.PageControl.Align := alNone;

          if Mouse.CursorPos.x > ((Form1.Width div 13)*9) then
            Form1.PageControl.Left := -Width+5;

          if Mouse.CursorPos.x < (Form1.Width div 4) then
            Form1.PageControl.Left := 0;
        end
      else
        (Application.Components[i] as TForm).Close;
    end;

end;
}



//============================================================================
// Процедуры для динамических ComboBox'ов
//============================================================================
procedure ProcessComboBox(Cmb: TComboBox; TagIdx: integer);
var
  value: integer;
begin
  value := GetTagValue( TagIdx );
  if TagIdx < 0 then
    cmb.Visible := not cmb.Visible
  else
    if (value <> cmb.ItemIndex) and (not cmb.DroppedDown) then
      cmb.ItemIndex := iif( value < cmb.Items.Count, value, 0);
end;

procedure ChangeComboBox(Cmb: TComboBox; TagIdx: integer; Mask: integer = $FFFF);
var
  value: integer;
begin
  value := (GetTagValue( TagIdx )) and (Mask);
  if (value <> cmb.ItemIndex) then begin
    SetTagValue(TagIdx, ((GetTagValue( TagIdx )) and ($FFFF-Mask)) + cmb.ItemIndex);

    ProcessComboBox(cmb, TagIdx);
  end;
end;


procedure CheckExtMode;
var
  s: string;
begin
  if not ExtMode_On then
    while InputPassQuery('Пароль', 'Вход в расширенный режим:', s) do
      if (UpperCase(s) = ExtMode_password) or (UpperCase(s) = ExtMode_password1) then begin
        ExtMode_On := true;
        break;
      end else begin
        showInfoDlg('Введен неправильный пароль!', mtError, [mbOk], 0);
      end;
end;


procedure CheckAdminMode;
var
  s: string;
begin
  if rvgLogIn then begin
    AdminMode_On := CheckAccess(PREVILEG_ADMIN, true);
    exit;
  end;

  if not AdminMode_On then
    while InputPassQuery('Пароль', 'Вход в режим администрирования:', s) do
      if UpperCase(s) = AdminMode_password then begin
        AdminMode_On := true;
        break;
      end else begin
        showInfoDlg('Введен неправильный пароль!', mtError, [mbOk], 0);
      end;
end;


procedure ShowAdminPanel;
begin
//  if UsersControlEnable then begin
    if CheckAccess(PREVILEG_ADMIN, true) then AdminMainForm.ShowModal;
    exit;
//  end;
  
  CheckAdminMode;
  if AdminMode_On then AdminMainForm.ShowModal;
end;


function GetStringChannelName(i: integer): string;
begin
  result := IntToStr(i shr 8) + '-' + IntToStr(i and $7F) + iif((i and $80)>0, ' a','');
end;


procedure SaveTrendProperties(id: string; Ymin, ymax: double);
begin
  saveTrendPropIni(id, Ymin, Ymax);
end;

procedure LoadTrendProperties(id: string; Chart: TDBChart);
var
  Ymin, Ymax: double;
begin
  loadTrendPropIni(id, Ymin, Ymax);

  if Ymax - Ymin < 1 then Ymax := Ymin + 10;
  repeat
    if Ymax > Chart.LeftAxis.Minimum then Chart.LeftAxis.Maximum := Ymax;
    if Ymin < Chart.LeftAxis.Maximum then Chart.LeftAxis.Minimum := Ymin;
  until (Chart.LeftAxis.Maximum = Ymax) and (Chart.LeftAxis.Minimum = Ymin);
end;

function GetpFIBDatabaseByName(name: string): TpFIBDatabase;
var
  i,j: integer;
begin
  result := nil;
  for i:=0 to Application.ComponentCount-1 do
    for j:=0 to Application.Components[i].ComponentCount-1 do
      if Application.Components[i].Components[j].ClassName = 'TpFIBDatabase' then
        if Application.Components[i].Components[j].Name = name then begin
          result := Application.Components[i].Components[j] as TpFIBDatabase;
          exit;
        end;
end;


procedure DelayMs(ms: cardinal; procmesflag: boolean);
var
  k: Cardinal;
begin
  k := GetTickCount;
  repeat
    if procmesflag then Application.ProcessMessages;
  until GetTickCount - k > ms;
end;



function EnumProc (Wd: HWnd; Param: LongInt): Boolean; stdcall;
var
  Nm:Array[0..255] of Char;
  Cs: Array[0..255] of Char;
  ProcessID, ThreadID: Cardinal;
  s:string;
begin
    if rezwind<>0 then
      begin
        EnumProc := TRUE;
        exit;
      end;

    GetWindowText(Wd,Nm,255);
    GetClassName(Wd,Cs,255);

//    i:=GetParent(wd);

    ThreadID := GetWindowThreadProcessId(Wd, ProcessID);
    s:='  ';

    if IsWindowEnabled(Wd) then s[1]:='E';
    if IsWindow(Wd) then s[2]:='W';


    if (IsWindowEnabled(Wd) AND IsWindow(Wd)) and
       (ThreadID = Tid) AND (ProcessID=Pid)
       then
         begin
           rezwind := Wd;
         end;

    EnumProc := TRUE;
end;


// Запуск внешнего приложения. Сначала пытается найти окно уже запущенного приложения
// и если находит, то активирует его. Если не находит, запускает новое.
//
// app_name - строка, содержащаяся в начале текста заголовка главного окна приложения
// ini_name - имя параметра для сохранения пути/имени файла приложения
// path_exe_name - пути/имени файла приложения по умолчанию
// UseParamVars - если false, то пути/имени файла приложения ищется в rpvisual.ini
//                если true, в системных переменных rpVisualParams

function StartProgram(app_name, ini_name, path_exe_name: string; UseParamVars: boolean=false;
      CommandLineParamStr: string=''): boolean;
var
  fname,s: string;
  h: HWND;
  hMenuHandle : HMENU;
  si:STARTUPINFO;
  pi:PROCESS_INFORMATION;
  k,n: integer;
  flag: boolean;
  od: TOpenDialog;
begin
  result := false;

  if UseParamVars then begin
    fname := rpVisualParams_Read(ini_name, app_name, path_exe_name);
    if app_name='' then
      app_name := rpVisualParams_getDescr(ini_name);
  end else
    with TIniFile.Create(CurDir + RpVisualIniFile) do begin
      fname := ReadString('ExtPrograms', ini_name, path_exe_name);
      free;
    end;

  if not FileExists(fname) then begin
    k := showInfoDlg('Программа не найдена "'+app_name+'"!'#13#13'Выбрать файл для запуска?', 'Программа не найдена', 'Выбрать', 'Указать в ручную', 'Отмена');
    if k = 3 then
      exit;

    if k = 1 then begin
      s := GetCurrentDir;
      od := TOpenDialog.Create(Form1);
      try
        od.FileName := fname;
        if not od.Execute then
          Exit;
        fname := od.FileName;
      finally
        od.Free;
      end;
      SetCurrentDir(s);
    end else begin
      if not ShowInputDialog('Расположение "'+app_name+'"', 'Введите путь и имя файла:', fname) then
        exit;
    end;

    if UseParamVars then begin
      rpVisualParams_Write(ini_name, app_name, fname);
    end else begin
      with TIniFile.Create(CurDir + RpVisualIniFile) do begin
        WriteString('ExtPrograms', ini_name, fname);
        free;
      end;
    end;
  end;

  // поиск и активация
  flag := true;
  h := findwindow(nil,'');
  if h <> 0 then repeat
    setlength(s, 1024);
    k := getwindowtext(h, @s[1], 1024);
    setlength(s, k);

    n := length(app_name);
    if copy(s, 1, n) = app_name then begin
      if IsWindowEnabled(h) then
        begin
          ShowWindow(h,SW_HIDE);
          ShowWindow(h,SW_RESTORE);
          flag := false;
          break;
        end                 else
        begin

          searchWind := h;
          TID := GetWindowThreadProcessId(searchWind, PID);
          rezwind := 0;
          EnumWindows (@EnumProc, 0);

          if rezwind<>0 then
            begin
              BringWindowToTop(rezwind);
            end;

          flag := false;
          break;

        end;
    end;

    if h = 0 then
      h := findwindow(nil,'*')
    else
      h := GetNextWindow(h, GW_HWNDNEXT);
  until h = 0;

  // запуск
  if flag then begin
    ZeroMemory(@si,sizeof(si));
    si.cb:=SizeOf(si);
    si.dwFlags := STARTF_USESHOWWINDOW;
    si.wShowWindow := SW_SHOW;

    if CommandLineParamStr<>'' then
      fname := fname + ' ' + CommandLineParamStr;

    if CreateProcess(nil, PChar(fname), nil, nil, false, 0, nil, nil, si, pi) then
      result := true
    else
      showInfoDlg('Не могу запустить программу "'+app_name+'"'#13 + fname, mtError, [mbOk], 0);
      
    h := FindWindow(nil, pchar(app_name));
    if (h <> 0) then begin
      hMenuHandle := GetSystemMenu(h, FALSE);
      if (hMenuHandle <> 0) then
        DeleteMenu(hMenuHandle, SC_MINIMIZE, MF_BYCOMMAND);
    end;
  end;
end;



function get_clear_hint(s: string): string;
var
  i: integer;
begin
  i := pos(#13, s);
  if i>0 then
    result := copy(s, 1, i-1)
  else
    result := s;
end;



function ReplaceStr(Source:AnsiString;const Target,Replace:AnsiString): AnsiString;
begin
  if length(Replace)>0 then
    ReplaceS(Source, Target, Replace);
  result := Source;
end;

function tailBackSlash(src: string): string;
begin
  result := src + iif( Copy(src, Length(src), 1)='\', '', '\');
end;



procedure setupMainFormColor;
begin
  form1.color := mainFormColor;
end;


function isManualAccessIgnore(name: string): boolean;
begin
  result := CheckFilter(name, manualAccessIgnoreList);
end;


function CleanStrToIntDef(s: string; defaultValue: integer): integer;
var
  i, j: Integer;
begin
  j := 1;
  for i := 1 to length(s) do
    if s[i] in ['0'..'9', '-'] then
    begin
      if j < i then
        s[j] := s[i];
      Inc(j);
    end;

  Dec(j);  
  if j < Length(s) then
    SetLength(s, j);

  result := StrToIntDef(s, defaultValue);
end;



function RestoreNumberText(ATagname, ALabel: string): string;
var
  i,j: integer;
begin
  result := '';
  j := 1;
  for i:=1 to Length(ATagname) do begin
    if (ATagname[i] = '0') and (j = 1) then
      Continue;

    if j > Length(ALabel) then
      exit;

    if (ATagname[i] = '_') and (ALabel[j] = '.') then
      ATagname[i] := ALabel[j];

    if ATagname[i] <> ALabel[j] then
      exit;

    inc(j);
  end;

  if j <= Length(ALabel) then
    exit;

  result := ATagname;
end;

procedure ShowWesClientButton;
begin
  form1.bvExtra.Visible := true;
  form1.btWesClient.Visible := true;
  Form1.bvTail.Left := Form1.Width;
end;

procedure ShowMotohrButton;
begin
  form1.btMotohr.Visible := true;
  Form1.bvTail.Left := Form1.Width;
end;



procedure findByTagRootProc(Device: TCustomScadaObject);
begin
  if (Device.TagRoot = FindObjectFilter)
        and (Device.Visible)
        and ((FindObjectClass = nil) or (Device.ClassType = FindObjectClass)) then
  begin
    FindObjectList.AddObject(Device.TagRoot, Device);
  end;
end;

function findByTagRoot(tagroot: string; cls: TClass; pageIndex: integer): boolean;
begin
  if pageIndex < 0 then
    pageIndex := Form1.PageControl.ActivePage.TabIndex;

  FindObjectFilter := tagroot;
  FindObjectClass := cls;
  FindObjectList.Clear;
  CustomScanScadaObjects(Form1.PageControl.Pages[pageIndex], findByTagRootProc);
  Result := FindObjectList.Count > 0;
end;

end.
