unit sparam;

interface

uses windows,iac_global;

const
  ACF_PARNUM   = $00000001; // Param is number
  ACF_UNICODE  = $00000002; // Param is Unicode string
  ACF_CURRENT  = $00000004; // Param is ignored, used current user handle
                            // from current message window
  ACF_RESULT   = $00000008; // Param is previous action result
  ACF_PARAM    = $00000010; // Param is Call parameter
  ACF_STRUCT   = $00000020;
  ACF_PARTYPE  = ACF_PARNUM  or ACF_UNICODE or
                 ACF_CURRENT or ACF_RESULT  or
                 ACF_PARAM   or ACF_STRUCT;
  ACF_TEMPLATE     = $00000800;
  ACF_SCRIPT_PARAM = $00001000;
  // dummy
  ACF_STRING = 0;

  ACF_RSTRING  = $00010000; // Service result is string
  ACF_RUNICODE = $00020000; // Service result is Widestring
  ACF_RSTRUCT  = $00040000; // Service result in structure
  ACF_RFREEMEM = $00080000; // Need to free memory

function  CreateParamBlock(parent:HWND;x,y,width:integer):THANDLE;
procedure ClearParamFields(Dialog:HWND);
function  FillParam       (Dialog:HWND;txt:pAnsiChar):integer;
function  SetParamValue   (Dialog:HWND;    flags:dword;    value:pointer):boolean;
function  GetParamValue   (Dialog:HWND;var flags:dword;var value:pointer):boolean;

procedure ClearParam    (flags:dword; var param);
function  DuplicateParam(flags:dword; var sparam,dparam):dword;

procedure PreProcess(flags:dword;var l_param:LPARAM;const WorkData:tWorkData);

function  CreateResultBlock(parent:HWND;x,y,width:integer):THANDLE;
procedure ClearResultFields(Dialog:HWND);
function  SetResultValue(Dialog:HWND;flags:dword):integer;
function  GetResultValue(Dialog:HWND):dword;

implementation

uses
  messages,
  common, editwrapper, wrapper, syswin,
  m_api, sedit, strans, mirutils;

{$include i_sparam_const.inc}
{$resource sparam.res}

const
  ptNumber  = 0;
  ptString  = 1;
  ptUnicode = 2;
  ptCurrent = 3;
  ptResult  = 4;
  ptParam   = 5;
  ptStruct  = 6;

const
  sresInt    = 0;
  sresString = 1;
  sresWide   = 2;
  sresStruct = 3;

//----- Processing functions -----

procedure PreProcess(flags:dword;var l_param:LPARAM;const WorkData:tWorkData);
var
  tmp1:pWideChar;
begin
  with WorkData do
  begin
    if (flags and ACF_STRUCT)<>0 then
    begin
      l_param:=uint_ptr(MakeStructure(pAnsiChar(l_param),Parameter,LastResult,ResultType))
    end
    else if (flags and ACF_PARAM)<>0 then
    begin
      l_param:=Parameter;
    end
    else if (flags and ACF_RESULT)<>0 then
    begin
      l_param:=LastResult;
    end
    else if (flags and ACF_CURRENT)<>0 then
    begin
      l_param:=WndToContact(WaitFocusedWndChild(GetForegroundwindow){GetFocus});
    end
    else
    begin
      if (flags and ACF_SCRIPT_PARAM)<>0 then
        l_param:=uint_ptr(ParseVarString(pWideChar(l_param),Parameter));

      tmp1:=pWideChar(l_param);
      if (flags and ACF_PARNUM)=0 then
      begin
        if (flags and ACF_UNICODE)=0 then
          WideToAnsi(tmp1,pAnsiChar(l_param),MirandaCP)
        else
          StrDupW(pWideChar(l_param),tmp1);
      end
      else
        l_param:=NumToInt(tmp1);

      if (flags and ACF_SCRIPT_PARAM)<>0 then
        mFreeMem(tmp1);
    end;
  end;
end;

procedure PostProcess(flags:dword;var l_param:LPARAM; var WorkData:tWorkData);
var
  code:integer;
  len:integer;
  pc:pAnsiChar;
begin
  if (flags and ACF_STRUCT)<>0 then
  begin
    with WorkData do
    begin
      LastResult:=GetStructureResult(l_param,@code,@len);
      case code of
{
        SST_LAST: begin
          result:=LastResult;
        end;
}
        SST_PARAM: begin //??
          LastResult:=Parameter;
          ResultType:=rtInt;
        end;
        SST_BYTE,SST_WORD,SST_DWORD,
        SST_QWORD,SST_NATIVE: begin
          ResultType:=rtInt;
        end;
        SST_BARR: begin
          StrDup(pAnsiChar(pc),pAnsiChar(LastResult),len);
          AnsiToWide(pAnsiChar(pc),PWideChar(LastResult),MirandaCP);
          mFreeMem(pAnsiChar(pc));
          ResultType:=rtWide;
        end;
        SST_WARR: begin
          StrDupW(pWideChar(LastResult),pWideChar(LastResult),len);
          ResultType:=rtWide;
        end;
        SST_BPTR: begin
          AnsiToWide(pAnsiChar(LastResult),pWideChar(LastResult),MirandaCP);
          ResultType:=rtWide;
        end;
        SST_WPTR: begin
          StrDupW(pWideChar(LastResult),pWideChar(LastResult));
          ResultType:=rtWide;
        end;
      end;
      FreeStructure(l_param);
      l_param:=0;
    end
  end;
end;

//----- Dialog functions -----

procedure MakeParamTypeList(wnd:HWND);
begin
  SendMessage(wnd,CB_RESETCONTENT,0,0);
  InsertString(wnd,ptNumber ,'number value');
  InsertString(wnd,ptString ,'ANSI string');
  InsertString(wnd,ptUnicode,'Unicode string');
  InsertString(wnd,ptCurrent,'current contact');
  InsertString(wnd,ptResult ,'last result');
  InsertString(wnd,ptParam  ,'parameter');
  InsertString(wnd,ptStruct ,'structure');
  SendMessage(wnd,CB_SETCURSEL,0,0);
end;

function IsParamNumber(txt:pAnsiChar):boolean;
begin
  if (txt[0] in ['0'..'9']) or ((txt[0]='-') and (txt[1] in ['0'..'9'])) or
    ((txt[0]='$') and (txt[1] in sHexNum)) or
    ((txt[0]='0') and (txt[1]='x') and (txt[2] in sHexNum)) then
    result:=true
  else
    result:=false;
end;

// Set parameter type by parameter template
function FixParam(buf:PAnsiChar):integer;
begin
  if      StrCmp(buf,'hContact'    )=0 then result:=ptCurrent
  else if StrCmp(buf,'parameter'   )=0 then result:=ptParam
  else if StrCmp(buf,'result'      )=0 then result:=ptResult
  else if StrCmp(buf,'structure'   )=0 then result:=ptStruct
  else if StrCmp(buf,'Unicode text')=0 then result:=ptUnicode
  else                                      result:=ptString;
end;

// get line from template
function GetParamLine(src:pAnsiChar;dst:pWideChar;var ltype:integer):pAnsiChar;
var
  pp,pc:pAnsiChar;
  savechar:AnsiChar;
  j:integer;
begin
  pc:=StrScan(src,'|');

  if pc<>nil then
  begin
    savechar:=pc^;
    pc^:=#0;
  end;

  if IsParamNumber(src) then
  begin
    j:=0;
    pp:=src;
    repeat
      dst[j]:=WideChar(pp^);
      inc(j); inc(pp);
    until (pp^=#0) or (pp^=' ');
    dst[j]:=WideChar(pp^); // anyway, #0 or " " needs
    if pp^<>#0 then
    begin
      dst[j+1]:='-'; dst[j+2]:=' '; inc(j,3);
      FastAnsitoWideBuf(pp+1,dst+j);
      StrCopyW(dst+j,TranslateW(dst+j));
    end;
    ltype:=ptNumber;
  end
  else
  begin
    ltype:=FixParam(src);
    StrCopyW(dst,TranslateW(FastAnsitoWideBuf(src,dst)));
  end;

  if pc<>nil then
  begin
    pc^:=savechar;
    inc(pc);
  end;

  result:=pc;
end;

// Set parameter value by parameter template
function FillParam(Dialog:HWND;txt:pAnsiChar):integer;
var
  bufw:array [0..2047] of WideChar;
  wnd:HWND;
  p,pc:PAnsiChar;
  ltype:integer;
begin
  wnd:=GetDlgItem(Dialog,IDC_EDIT_PAR);
  SendMessage(wnd,CB_RESETCONTENT,0,0);
  if (txt<>nil) and (txt^<>#0) then
  begin
    result:=-1;
    p:=txt;
    repeat
      pc:=GetParamLine(p,bufw,ltype);
      if result<0 then
        result:=ltype;
      SendMessageW(wnd,CB_ADDSTRING,0,lparam(@bufw));

      if result=ptStruct then
        break
      else
        p:=pc;
    until pc=nil;
  end
  else
    result:=ptNumber;
  SendMessage(wnd,CB_SETCURSEL,0,0);
end;

procedure ClearParamFields(Dialog:HWND);
var
  wnd:HWND;
begin
  ShowWindow(GetDlgItem(Dialog,IDC_STRUCT),SW_HIDE);
  wnd:=GetDlgItem(Dialog,IDC_EDIT_PAR);
  ShowEditField  (wnd,SW_SHOW);
  EnableEditField(wnd,true);
  SendMessage    (wnd,CB_RESETCONTENT,0,0);
  SetEditFlags   (wnd,EF_ALL,0);
  CB_SelectData(Dialog,IDC_FLAG_PAR,ptNumber);
end;

function ParamDlgResizer(Dialog:HWND;lParam:LPARAM;urc:PUTILRESIZECONTROL):int; cdecl;
begin
  case urc^.wId of
    IDC_FLAG_PAR: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_TOP; //RD_ANCHORX_RIGHT
    IDC_EDIT_PAR: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_TOP;
    IDC_STRUCT:   result:=RD_ANCHORX_WIDTH or RD_ANCHORY_TOP;
    IDC_STAT_PAR: result:=RD_ANCHORX_LEFT  or RD_ANCHORY_TOP;
//    IDC_CLOSE:    result:=RD_ANCHORX_WIDTH or RD_ANCHORY_TOP;
  else
    result:=0;
  end;
end;

function DlgParamProc(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
var
  wnd,wnd1:HWND;
  i:integer;
  pcw:pWideChar;
  pc:pAnsiChar;
  urd:TUTILRESIZEDIALOG;
begin
  result:=0;

  case hMessage of
    WM_DESTROY: begin
      pc:=pAnsiChar(GetWindowLongPtrW(GetDlgItem(Dialog,IDC_STRUCT),GWLP_USERDATA));
      mFreeMem(pc);
    end;

    WM_INITDIALOG: begin
      MakeEditField(Dialog,IDC_EDIT_PAR);

      MakeParamTypeList(GetDlgItem(Dialog,IDC_FLAG_PAR));
    end;

    WM_SIZE: begin
      FillChar(urd,SizeOf(TUTILRESIZEDIALOG),0);
      urd.cbSize    :=SizeOf(urd);
      urd.hwndDlg   :=Dialog;
      urd.hInstance :=hInstance;
      urd.lpTemplate:=MAKEINTRESOURCEA('IDD_SPARAM');
      urd.lParam    :=0;
      urd.pfnResizer:=@ParamDlgResizer;
      CallService(MS_UTILS_RESIZEDIALOG,0,tlparam(@urd));
    end;

    WM_SHOWWINDOW: begin
      // hide window by ShowWindow function
      if (lParam=0) and (wParam=0) then
      begin
        pc:=pAnsiChar(SetWindowLongPtrW(GetDlgItem(Dialog,IDC_STRUCT),GWLP_USERDATA,0));
        mFreeMem(pc);
      end;
    end;

    WM_COMMAND: begin
      case wParam shr 16 of
        CBN_EDITCHANGE,
        EN_CHANGE: begin
          SendMessage(GetParent(Dialog),WM_COMMAND,CBN_EDITCHANGE shl 16,Dialog);
        end;

        CBN_SELCHANGE:  begin
          SendMessage(GetParent(Dialog),WM_COMMAND,CBN_EDITCHANGE shl 16,Dialog);
          case loword(wParam) of
            IDC_FLAG_PAR: begin
              wnd :=GetDlgItem(Dialog,IDC_EDIT_PAR);
              wnd1:=GetDlgItem(Dialog,IDC_STRUCT);

              i:=CB_GetData(GetDlgItem(Dialog,loword(wParam)));

              if i=ptStruct then
              begin
                ShowEditField(wnd ,SW_HIDE);
                ShowWindow   (wnd1,SW_SHOW);
              end
              else
              begin
                ShowEditField(wnd ,SW_SHOW);
                ShowWindow   (wnd1,SW_HIDE);

                if i in [ptCurrent,ptResult,ptParam] then
                  EnableEditField(wnd,false)
                else
                begin
                  if i=ptNumber then
                  begin
                    pcw:='0';
                    SendMessageW(wnd,WM_SETTEXT,0,TLParam(pcw));
                  end;
                  EnableEditField(wnd,true);
                end;
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

//----- Common interface functions -----

function CreateParamBlock(parent:HWND;x,y,width:integer):THANDLE;
var
  rc,rc1:TRECT;
begin
  SetRect(rc,x,y,x+width,y+0{dlgheight});
  MapDialogRect(parent,rc);

  result:=CreateDialogW(hInstance,'IDD_SPARAM',parent,@DlgParamProc);
  GetClientRect(result,rc1);
  SetWindowPos(result,0,
      x,y{rc.left,rc.top},rc.right-rc.left,rc1.bottom-rc1.top,
      SWP_NOZORDER);
end;

// if separate
function DestroyBlock(block:pointer):integer;
begin
  result:=0;
end;

function SetParamValue(Dialog:HWND;flags:dword;value:pointer):boolean;
var
  wnd,wnd1:HWND;
  pc:pAnsiChar;
  vtype:integer;
begin
  result:=true;
//?? Check for "Apply" activation

  wnd:=GetDlgItem(Dialog,IDC_EDIT_PAR);
  if (flags and ACF_TEMPLATE)<>0 then
  begin
    vtype:=FillParam(Dialog,value);
  end
  else if (flags and ACF_PARAM)<>0 then
  begin
    SendMessageW(wnd,WM_SETTEXT,0,LPARAM(TranslateW('Parameter')));
    EnableWindow(wnd,false);
    vtype:=ptParam;
  end
  else if (flags and ACF_RESULT)<>0 then
  begin
    SendMessageW(wnd,WM_SETTEXT,0,LPARAM(TranslateW('Last result')));
    EnableWindow(wnd,false);
    vtype:=ptResult;
  end
  else if (flags and ACF_CURRENT)<>0 then
  begin
    SendMessageW(wnd,WM_SETTEXT,0,LPARAM(TranslateW('Current user')));
    EnableWindow(wnd,false);
    vtype:=ptCurrent;
  end
  else if (flags and ACF_STRUCT)<>0 then
  begin
    vtype:=ptStruct;

    ShowEditField(wnd,SW_HIDE);
    wnd1:=GetDlgItem(Dialog,IDC_STRUCT);
    ShowWindow(wnd1,SW_SHOW);
    // delete old value
    pc:=pAnsiChar(GetWindowLongPtrW(wnd1,GWLP_USERDATA));
    mFreeMem(pc);
    // set newly allocated
    SetWindowLongPtrW(wnd1,GWLP_USERDATA,long_ptr(StrDup(pc,pAnsiChar(value))));
//!!!!!!!!
  end
  else if (flags and ACF_PARNUM)<>0 then
  begin
    vtype:=ptNumber;
    SendMessageW(wnd,WM_SETTEXT,0,LPARAM(value));
  end
  else if (flags and ACF_UNICODE)<>0 then
  begin
    vtype:=ptUnicode;
    SendMessageW(wnd,WM_SETTEXT,0,LPARAM(value));
  end
  else
  begin
    vtype:=ptString;
    SendMessageW(wnd,WM_SETTEXT,0,LPARAM(value));
  end;
  SetEditFlags(wnd,EF_SCRIPT,ord((flags and ACF_SCRIPT_PARAM)<>0));
  
  CB_SelectData(GetDlgItem(Dialog,IDC_FLAG_PAR),vtype);
end;

function GetParamValue(Dialog:HWND;var flags:dword;var value:pointer):boolean;
var
  wnd:HWND;
begin
  result:=true;
  flags:=0;
  value:=nil;
  wnd:=GetDlgItem(Dialog,IDC_EDIT_PAR);
  case CB_GetData(GetDlgItem(Dialog,IDC_FLAG_PAR)) of
    ptParam: begin
      flags:=flags or ACF_PARAM
    end;
    ptResult: begin
      flags:=flags or ACF_RESULT
    end;
    ptCurrent: begin
      flags:=flags or ACF_CURRENT
    end;
    ptNumber: begin
      flags:=flags or ACF_PARNUM;
      value:=GetDlgText(wnd);
    end;
    ptStruct: begin
      flags:=flags or ACF_STRUCT;
      StrDup(pAnsiChar(value),
          pAnsiChar(GetWindowLongPtrW(GetDlgItem(Dialog,IDC_STRUCT),GWLP_USERDATA)));
    end;
    ptUnicode: begin
      flags:=flags or ACF_UNICODE;
      value:=GetDlgText(wnd);
    end;
    ptString: value:=GetDlgText(wnd);
  end;
  if (GetEditFlags(wnd) and EF_SCRIPT)<>0 then
     flags:=flags or ACF_SCRIPT_PARAM;
end;

procedure ClearParam(flags:dword; var param);
begin
  if (flags and (ACF_CURRENT or ACF_RESULT or ACF_PARAM))=0 then
    mFreeMem(pointer(param));
end;

function DuplicateParam(flags:dword; var sparam,dparam):dword;
var
  tmpdst:array [0..2047] of WideChar;
  ltype:integer;
begin
  mFreeMem(dparam);

  if (flags and ACF_TEMPLATE)<>0 then
  begin
    flags:=flags and not (ACF_TEMPLATE or ACF_PARTYPE);
    GetParamLine(pAnsiChar(sparam),tmpdst,ltype);
    case ltype of
      ptNumber: begin
        flags:=flags or ACF_PARNUM;
        StrDupW(pWideChar(dparam),pWideChar(@tmpdst));
      end;
      ptString: begin
        flags:=flags or ACF_STRING;
        StrDupW(pWideChar(dparam),pWideChar(@tmpdst));
      end;
      ptUnicode: begin
        flags:=flags or ACF_UNICODE;
        StrDupW(pWideChar(dparam),pWideChar(@tmpdst));
      end;
      ptStruct: begin
        flags:=flags or ACF_STRUCT;
        StrDup(pAnsiChar(dparam),pAnsiChar(sparam)+10); //10=StrLen('structure|')
      end;
      ptCurrent: flags:=flags or ACF_CURRENT;
      ptResult : flags:=flags or ACF_RESULT;
      ptParam  : flags:=flags or ACF_PARAM;
    end;
  end
  else if (flags and (ACF_CURRENT or ACF_RESULT or ACF_PARAM))=0 then
  begin
    if (flags and ACF_PARNUM)<>0 then
      StrDupW(pWideChar(dparam),pWideChar(sparam))
    else if (flags and ACF_STRUCT)<>0 then
      StrDup(pAnsiChar(dparam),pAnsiChar(sparam))
    else if (flags and  ACF_UNICODE)<>0 then
      StrDupW(pWideChar(dparam),pWideChar(sparam))
    else
      StrDupW(pWideChar(dparam),pWideChar(sparam));
  end;
  result:=flags;
end;

//----- result block -----

procedure MakeResultTypeList(wnd:HWND);
begin
  SendMessage(wnd,CB_RESETCONTENT,0,0);
  InsertString(wnd,sresInt   ,'Integer');
  InsertString(wnd,sresString,'String');
  InsertString(wnd,sresWide  ,'Wide String');
  InsertString(wnd,sresStruct,'Structure');
  SendMessage(wnd,CB_SETCURSEL,0,0);
end;

function ResultDlgResizer(Dialog:HWND;lParam:LPARAM;urc:PUTILRESIZECONTROL):int; cdecl;
begin
  case urc^.wId of
    IDC_RES_TYPE   : result:=RD_ANCHORX_WIDTH or RD_ANCHORY_TOP; //RD_ANCHORX_RIGHT
    IDC_RES_FREEMEM: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_TOP;
    IDC_RES_STAT   : result:=RD_ANCHORX_LEFT  or RD_ANCHORY_TOP;
    IDC_RES_GROUP  : result:=RD_ANCHORX_WIDTH or RD_ANCHORY_TOP;
  else
    result:=0;
  end;
end;

procedure ClearResultFields(Dialog:HWND);
begin
  CheckDlgButton(Dialog,IDC_RES_FREEMEM,BST_UNCHECKED);
  ShowWindow(GetDlgItem(Dialog,IDC_RES_FREEMEM),SW_HIDE);
  CB_SelectData(Dialog,IDC_RES_TYPE,sresInt);
end;

function DlgResultProc(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
var
  urd:TUTILRESIZEDIALOG;
  i:integer;
begin
  result:=0;

  case hMessage of
    WM_INITDIALOG: begin
      MakeResultTypeList(GetDlgItem(Dialog,IDC_RES_TYPE));
    end;

    WM_SIZE: begin
      FillChar(urd,SizeOf(TUTILRESIZEDIALOG),0);
      urd.cbSize    :=SizeOf(urd);
      urd.hwndDlg   :=Dialog;
      urd.hInstance :=hInstance;
      urd.lpTemplate:=MAKEINTRESOURCEA('IDD_SRESULT');
      urd.lParam    :=0;
      urd.pfnResizer:=@ResultDlgResizer;
      CallService(MS_UTILS_RESIZEDIALOG,0,tlparam(@urd));
    end;

    WM_COMMAND: begin
      case wParam shr 16 of
        CBN_SELCHANGE:  begin
          case loword(wParam) of
            IDC_RES_TYPE: begin
              case CB_GetData(lParam) of
                sresInt,sresStruct: begin
                  i:=SW_HIDE;
                end;
                sresString,sresWide: begin
                  i:=SW_SHOW;
                end;
              end;
              ShowWindow(GetDlgItem(Dialog,IDC_RES_FREEMEM),i);
            end;
          end;
        end;
      end;
    end;
  end;
end;

function CreateResultBlock(parent:HWND;x,y,width:integer):THANDLE;
var
  rc,rc1:TRECT;
begin
  SetRect(rc,x,y,x+width,y+0{dlgheight});
  MapDialogRect(parent,rc);

  result:=CreateDialogW(hInstance,'IDD_SRESULT',parent,@DlgResultProc);
  GetClientRect(result,rc1);
  SetWindowPos(result,0,
      x,y{rc.left,rc.top},rc.right-rc.left,rc1.bottom-rc1.top,
      SWP_NOZORDER);
end;

function SetResultValue(Dialog:HWND;flags:dword):integer;
var
  btn:integer;
begin
  // RESULT
  if (flags and ACF_RSTRUCT)<>0 then
    result:=sresStruct
  else if (flags and ACF_RSTRING)<>0 then
  begin
    if (flags and ACF_RFREEMEM)<>0 then
      btn:=BST_CHECKED
    else
      btn:=BST_UNCHECKED;
    CheckDlgButton(Dialog,IDC_RES_FREEMEM,btn);

    if (flags and ACF_RUNICODE)<>0 then
      result:=sresWide
    else
      result:=sresString;
  end
  else
  begin
    result:=sresInt;
  end;
  CB_SelectData(Dialog,IDC_RES_TYPE,result);
end;

function GetResultValue(Dialog:HWND):dword;
begin
  case CB_GetData(GetDlgItem(Dialog,IDC_RES_TYPE)) of
    sresString: begin
      result:=ACF_RSTRING;
      if IsDlgButtonChecked(Dialog,IDC_RES_FREEMEM)=BST_CHECKED then
        result:=result or ACF_RFREEMEM;
    end;
    sresWide: begin
      result:=ACF_RSTRING or ACF_RUNICODE;
      if IsDlgButtonChecked(Dialog,IDC_RES_FREEMEM)=BST_CHECKED then
        result:=result or ACF_RFREEMEM;
    end;
    sresStruct: result:=ACF_RSTRUCT;
  else
    result:=0;
  end;

end;

end.