; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define BINNO_PRODUCTION
#define BINNO_CALL_URL 'http://d3oxtn1x3b8d7i.cloudfront.net'

#ifndef BINNO_PRODUCTION
;#define BINNO_DEBUG            
#define BINNO_USE_LOCAL_CONFIG 'C:\\Users\\mihindu\\Desktop\\installer_data.xml'
#define BINNO_USE_LOCAL_CHECK_XML 'C:\\Users\\mihindu\\Desktop\\pre_checks.xml'

[Setup]
AppName=My Program
AppVersion=1.5
CreateAppDir=no
PrivilegesRequired=admin
#endif

#define macro(ss) GetArrayLength(ss)

[Files]
Source: "{#BaseDir}\Source\Installer\binno\itdownload.dll"; Flags: dontcopy; DestDir: {tmp}
Source: "{#BaseDir}\Source\Installer\binno\InnoCallback.dll"; Flags: dontcopy; DestDir: {tmp}

;#[Run]
;#Filename: {code:GetDownloaderInfo|exe} ; Parameters: {code:GetDownloaderInfo|args} ; Check: biShouldRunDownloader ; Flags: nowait

[Code]
#ifndef UNICODE
  #include "it_download.iss"
#else
  #include "it_download_u.iss"
#endif
#include "utility.iss"

const
#ifndef UNICODE
  BINNO_VERSION = '2.2.0';
#else
  BINNO_VERSION = '2.2.0u';
#endif

OPTION_V_SPACING = 10;
SUB_OPTION_V_SPACING = 7;

// Win API
GWL_USERDATA = -21;
GWL_STYLE = -16;

BM_GETCHECK =  $00F0;
BM_SETCHECK =  $00F1;
BST_UNCHECKED = $0000;
BST_CHECKED = $0001;

WM_SETFONT = $0030;

// option types
RADIO = 0;
CHECK = 1;
TEXT = 2;

ERROR_SUCCESS = 0;

// registry types..
REG_SZ = 1;
REG_EXPAND_SZ = 2;
REG_BINARY = 3;
REG_DWORD  = 4;
REG_MULTI_SZ = 7;

type
#ifndef UNICODE
  TGUID = record
    D1: LongWord;
    D2: Word;
    D3: Word;
    D4: array[0..7] of Byte;
  end;
#endif

  AT = array[0..100] of byte;

  BI_URL = record
        href:string;
        target:string;
        end;
  
  BI_DOWNLOADER = record
            en:boolean;
            exe:string;
            args:string;
            url:string;
            oninit:boolean;
            end;

  SIZE = record 
          cx:Longint;
          cy:Longint;
          end;
  
  HDC = Longint;
  HKEY = Longint;
  HGDIOBJ = longint;
  HBRUSH  = longint;
  COLORREF = dword;
  HHOOK = longint;
  LRESULT = longint;
  LPARAM = longint;
  WPARAM = integer;

  TStrProc =  function(nCode:integer;wParam:WPARAM;lParam:LPARAM):integer;

  BI_RECT = record 
       left:Longint; 
       top:Longint; 
       right:Longint; 
       bottom:Longint; 
    end;

  BI_HWND_ARRAY = array of HWND;

  BI_SUB_OPTION = record
            master_switch:boolean;
            on_value:string;
            off_value:string;
            hwnd:HWND;
            attached_texts: BI_HWND_ARRAY;
            control_type:integer;
            control_ref:TWinControl;
            end;
  
  BI_SUB_OPTION_ARRAY = array of BI_SUB_OPTION;

  BI_MAIN_OPTION = record
              master_switch:boolean;
              on_value:string;
              off_value:string;
              sub_options:BI_SUB_OPTION_ARRAY;
              hwnd:HWND;
              attached_texts: BI_HWND_ARRAY;
              control_type:integer;
              control_ref:TWinControl;
              end;
  
  BI_ALERT_WINDOW = record
    id:string;
    text:string;
    confirm_name:string;
    confirm_behavior:string;
    confirm_style:string;
    confirm_selected:string;
    decline_name:string;
    decline_behavior:string;
    decline_style:string;
    decline_selected:string;
    confirm_next_alert_id:string;
    decline_next_alert_id:string;
  end;

  BI_OFFER = record
      id:integer;
      offer_id:string;
      download_url,execution_args,downloader_args:String;
      //license_path:String;
      control_next_btn:boolean;
      valid,visible:boolean;
      ready:boolean; // used to stop offer in runtime...
      //user_cancel:boolean; // in anycase user cancel this offer...
      main_options:array of BI_MAIN_OPTION;
      link_array: array of BI_URL;
      resource_table: array of BI_RESOURCE;
      xml:variant;
      page:TWizardPage;
      cmd_line:string; // command line for specific offer
      forced_args:string;
      forced_accept:boolean;

      back_btn_text:string;
      back_btn_style:string;

      skip_btn_text:string;
      skip_btn_style:string;
      skip_btn_behavior:string;

      next_btn_text:string;
      next_btn_style:string;

      cancel_btn_style:string;
      cancel_btn_text:string;
      
      master_cfm_text,master_cfm_yes_text,master_cfm_yes_args,master_cfm_yes_style,master_cfm_yes_selected:string;
      master_cfm_no_text,master_cfm_no_args,master_cfm_no_style,master_cfm_no_selected:string;

      x_btn_behavior:string ;
      x_cfm_text,x_cfm_yes_text,x_cfm_yes_args,x_cfm_yes_style,x_cfm_yes_selected:string;
      x_cfm_no_text,x_cfm_no_args,x_cfm_no_style,x_cfm_no_selected:string;

      ask_for_skip:boolean;
      alerts: array of BI_ALERT_WINDOW;
      simulated_next:boolean;
      end;

var
offer_uid,installer_muid,offer_softid,offer_affid,execution_args:String;
installer_v1:String;
old_cancel_text,old_next_text,old_back_text:String;
link_array: array of BI_URL;
downloader:BI_DOWNLOADER;
offers: array of BI_OFFER;
hhk:HHOOK;
yes_text,no_text:String;
msgbox_flags:integer;
option_loop:boolean;
check_resource_table: array of BI_RESOURCE;
binno_quit:boolean;

// Win32 API
function biCoCreateGuid(var Guid:TGuid):integer;
 external 'CoCreateGuid@ole32.dll stdcall';

function biLookupAccountName(a,b:AnsiString;var sid:AT;var cbSid:Longword;d:AnsiString;var x:LongWord;var y:Longword):integer;
 external 'LookupAccountNameA@advapi32.dll stdcall';

function biConvertSidToStringSid(pSid:AT;var ssid:Longint):integer;
 external 'ConvertSidToStringSidA@advapi32.dll stdcall';

function biGetLastError():Longword;
 external 'GetLastError@kernel32.dll stdcall';

function biLocalFree(hMem:Longint):Longint;
 external 'LocalFree@kernel32.dll stdcall';

function biGetWindowLong(hWnd:Longint;nIndex:Longint):Longint;
 external 'GetWindowLongA@user32.dll stdcall';

function biSetWindowLong(hWnd,nIndex,dwNewLong:Longint):Longint;
 external 'SetWindowLongA@user32.dll stdcall';

function biShowWindow(hMem:Longint;nCmdShow:Longint):Longint;
 external 'ShowWindow@user32.dll stdcall';

function biGetDC(hWnd:HWND):Longint;
 external 'GetDC@user32.dll stdcall';

function biCreateCompatibleDC(hdc:HDC):HDC;
 external 'CreateCompatibleDC@gdi32.dll stdcall';

function biCreateCompatibleBitmap(hdc:HDC;nWidth,nHeight:integer):Longint;
 external 'CreateCompatibleBitmap@gdi32.dll stdcall';

function biSelectObject(hdc:HDC;hgdiobj:Longint):Longint;
 external 'SelectObject@gdi32.dll stdcall';

function biGetPixel(hdc:HDC;nXPos,nYPos:integer):COLORREF;
 external 'GetPixel@gdi32.dll stdcall';

function biDrawText(hdc:HDC;lpString:AnsiString;nCount:integer;var lpRect:BI_RECT;uFormat:integer):Longint;
 external 'DrawTextA@user32.dll stdcall';

function biSetBkColor(hdc:HDC;crColor:COLORREF):COLORREF;
 external 'SetBkColor@gdi32.dll stdcall';

function biFillRect(hdc:HDC;var lprc:BI_RECT;hbr:Longint):COLORREF;
 external 'FillRect@user32.dll stdcall';

function biGetStockObject(fnObject:integer):Longint;
 external 'GetStockObject@gdi32.dll stdcall';

function biGetTextExtentPoint32(hdc:Longint;lpString:AnsiString;c:integer;var lpSize:SIZE):Longint;
 external 'GetTextExtentPoint32A@Gdi32.dll stdcall';

function biDeleteObject(hObject:Longint):Longint;
 external 'DeleteObject@gdi32.dll stdcall';

function biDeleteDC(hdc:HDC):Longint;
 external 'DeleteDC@gdi32.dll stdcall';

function biReleaseDC(hWnd:HWND;hdc:HDC):Longint;
 external 'ReleaseDC@user32.dll stdcall';

function biEnableWindow(hWnd:HWND;bEnable:boolean):Longint;
  external 'EnableWindow@user32.dll stdcall';

function biSetWindowsHookEx(idHook:integer;lpfn:longword;hMod:Longint;dwThreadId:DWORD):HHOOK;
  external 'SetWindowsHookExW@user32.dll stdcall';

function biUnhookWindowsHookEx(hhk:HHOOK):Longint;
  external 'UnhookWindowsHookEx@user32.dll stdcall';

function biCallNextHookEx(hhk:HHOOK;nCode:integer;wParam:integer;lParam:longint):Longint;
  external 'CallNextHookEx@user32.dll stdcall';

function biGetCurrentThreadId():DWORD;
  external 'GetCurrentThreadId@kernel32.dll stdcall';

function biGetDlgItem(hDlg:HWND;nIDDlgItem:integer):HWND;
  external 'GetDlgItem@user32.dll stdcall';

function biSetDlgItemText(hDlg:HWND;nIDDlgItem:integer;lpString:AnsiString):integer;
  external 'SetDlgItemTextA@user32.dll stdcall';

function biRegOpenKeyEx(hKey:HKEY;lpSubKey:AnsiString;ulOptions:DWORD;samDesired:DWORD;var phkResult:HKEY):longint;
  external 'RegOpenKeyExA@Advapi32.dll stdcall';

function biRegQueryValueExCustom(hKey:HKEY;lpValueName:AnsiString;lpReserved:longint;var lpType:DWORD;lpData:longint;lpcbData:longint):longint;
  external 'RegQueryValueExA@Advapi32.dll stdcall';

function biRegCloseKey(hKey:HKEY):longint;
  external 'RegCloseKey@Advapi32.dll stdcall';
  
function biWrapCBTProc(callback:TStrProc; paramcount:integer):longword;
  external 'wrapcallback@files:innocallback.dll stdcall';    

function biMessageBox(hWnd:HWND;lpText,lpCaption:AnsiString;uType:integer):integer;
  external 'MessageBoxA@user32.dll stdcall';

function biSendMessage(hWnd:HWND;message:UINT;wParam:WPARAM;lParam:LPARAM):integer;
  external 'SendMessageA@user32.dll stdcall';

function CBTProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):integer;
var
hChildWnd:HWND;    // msgbox is "child"
myfont:TFont;
begin
  // hook function
  
   // notification that a window is about to be activated
   // window handle is wParam
   if nCode = 5 then begin

      // set window handles
      hChildWnd := HWND(wParam);

      myfont := TFont.Create();
      myfont.Name := WizardForm.NextButton.Font.Name;
      myfont.Pitch := WizardForm.NextButton.Font.Pitch;
      myfont.Size := WizardForm.NextButton.Font.Size;
      myfont.PixelsPerInch := WizardForm.NextButton.Font.PixelsPerInch;
      myfont.Height := WizardForm.NextButton.Font.Height;
      myfont.Style := myfont.Style + [fsBold]; 
      
      //to get the text of the Yes button
      if biGetDlgItem(hChildWnd,IDYES) <>  0 then begin
         biSetDlgItemText(hChildWnd,IDYES,yes_text);
         
         if (msgbox_flags and $02) <> 0 then
          biSendMessage(biGetDlgItem(hChildWnd,IDYES),WM_SETFONT,myfont.Handle,0);
      end;
      
      if biGetDlgItem(hChildWnd,IDNO) <> 0 then begin
        biSetDlgItemText(hChildWnd,IDNO,no_text);

        if (msgbox_flags and $10) <> 0 then
          biSendMessage(biGetDlgItem(hChildWnd,IDNO),WM_SETFONT,myfont.Handle,0);
      end;

      // exit CBT hook
      biUnhookWindowsHookEx(hhk);
   end else begin
    // otherwise, continue with any possible chained hooks
      biCallNextHookEx(hhk,nCode,wParam,lParam);
   end;
   Result:=0;
end;
    
function CBTMsgbox(Text,YesText,NoText: String;const default:integer):integer;
var
  callback:longword;
  default_btn:integer;
begin

 default_btn := MB_DEFBUTTON1;
 yes_text:=YesText;
 no_text:=NoText;
 msgbox_flags := default;

 StringChangeEx(Text,'\n', chr(10), True);

 callback:=biWrapCBTProc(@CBTProc,3); //Our proc has 3 arguments
 hhk := biSetWindowsHookEx(5,callback,0,biGetCurrentThreadId());

 if (default and $1) <> 0 then  default_btn := MB_DEFBUTTON2;
 
 Result := biMessageBox(WizardForm.Handle,Text,'Confirmation',MB_YESNO or default_btn);

end;

// Utilities
function biGetResourceById(Offer:integer;Id:String):String;
var 
i:integer;
begin
  result := '';
  // find the correct resouce for this id
  for i := 0 to GetArrayLength(offers[Offer].resource_table)-1 do begin
    if offers[Offer].resource_table[i].id = Id then begin
      result := offers[Offer].resource_table[i].path; 
      exit;
    end
  end;
end;

function biGetXmlStringSafe(var node:variant;path:string):string;
var
  v:variant;
begin
  result := '';

  try  
    if VarIsEmpty(node) then exit;
      
    v := node.selectSingleNode(path);

    if VarIsEmpty(v) then exit;
    //if VarIsNull(v.text) then exit;
    result := v.text;
  except
    // return empty string
  end

end;

function biGetXmlAttributeSafe(var node:variant;name,default:string):string;
var
  v:variant;

begin
  result := default;
try  
  if VarIsEmpty(node) then exit;

  v := node.getAttribute(name);

  if VarIsEmpty(v) or VarIsNull(v) then exit;

  result := v;
except

end;  

end;

// the famous text width calculation function...
Function biGetTextSize(hWnd:HWND;hFont:Longint;text:string;var cx,cy:integer):integer;
var
memdc,hdc:HDC;
r,wbrush,old_bmp,hbmp:Longint;
size:SIZE;
rc:BI_RECT;
c:COLORREF;
begin
    // Get DC
      hdc := biGetDC(hWnd);
      memdc := biCreateCompatibleDC(hdc);
      hbmp := biCreateCompatibleBitmap(memdc,1000,40);
      old_bmp := biSelectObject(memdc,hbmp);

      // Select object to set the font....
      biSelectObject(memdc,hFont);

      biGetTextExtentPoint32(memdc,text,length(text),size);
      size.cx := size.cx + 1;
      rc.left := 0;
      rc.top := 0;
      rc.right:=size.cx;
      rc.bottom:=size.cy;
      
      // get white gbrush
      wbrush := biGetStockObject(0);

      // fill it wish white brush...
      r := biFillRect(memdc,rc,wbrush);

      c := biSetBkColor(memdc,0); // set bk color to black

      r := biDrawText(memdc,text,length(text),rc,$00000820);
      
      repeat
        c := biGetPixel(memdc,size.cx,size.cy/2);
        size.cx := size.cx - 1;  
      until (c = 0);

      biSelectObject(memdc,old_bmp);
      biDeleteObject(hbmp);
      biDeleteDC(memdc);
      biReleaseDC(hWnd,hdc);
      
      cx := size.cx;
      cy := size.cy;

      Result := 1;
end;

procedure biAddWindowStyle(hWnd:HWND;style:Longint);
var tmp:longint;
begin
  tmp := biGetWindowLong(hWnd,GWL_STYLE);
  tmp := tmp or style;
  biSetWindowLong(hWnd,GWL_STYLE,tmp);
end;

procedure biRemoveWindowStyle(hWnd:HWND;style:Longint);
var tmp:longint;
begin
  tmp := biGetWindowLong(hWnd,GWL_STYLE);
  tmp := tmp and not style;
  biSetWindowLong(hWnd,GWL_STYLE,tmp);
end;

function biGetUserSID():string;
var
u:string;
l,x,y:Longword;
ee: AT;
bak:Longint;
begin

    u := StringOfChar( ' ', 64 );
    l:=63;
    x:=63;

    biLookupAccountName('',GetUserNameString(),ee,l,u,x,y);
    
    if biConvertSidToStringSid(ee,bak) = 1 then
      Result := CastIntegerToString(bak);

    biLocalFree(bak);
end;

function  biCreateGUID() : String;
var
Guid:TGUID;
begin
     biCoCreateGuid(Guid);
     Result := Format('%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x', [Guid.D1, Guid.D2, Guid.D3, Guid.D4[0], Guid.D4[1], Guid.D4[2], Guid.D4[3], Guid.D4[4], Guid.D4[5], Guid.D4[6], Guid.D4[7]]);;
end;

function  biCreateMUID() : String;
var
k:String;


begin
  k := biGetMAC();
  if k = '' then begin
     Result := biCreateGuid();
  end else begin
    result :=GetMD5OfString(k)
  end;
end;

procedure biExpandMacros(var S: String;offer_idx:integer);
begin

  StringChangeEx(S, '%MUID%', installer_muid, True);
  StringChangeEx(S, '%UID%', offer_uid, True);
  StringChangeEx(S, '%AffiliateID%', offer_affid, True);
  StringChangeEx(S, '%SoftwareID%', offer_softid, True);

  if GetArrayLength(offers) > 0 then 
    StringChangeEx(S, '%OfferID%', offers[offer_idx].offer_id, True);

  StringChangeEx(S, '%UserSID%', biGetUserSID(), True);
end;

function biGetSilentIndication():string;
begin
    result := '0';
    if WizardSilent() then begin
      result := '1'
    end;
end;

function biCheckExeHint(resource,args,store_key,store_name:string;return_actual:boolean;var Value:string):boolean;
var
  ResultCode,root_key:integer;
  reg_path:string;
begin
  result := false; 
  
  if Exec(biGetResourceByKey(resource,check_resource_table), args, '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin
    // read the regkey to return value
    root_key := biSplitRegistryKeyNoWOW(store_key,reg_path);
    // MsgBox(reg_path, mbInformation, MB_OK);
    if RegQueryStringValue(root_key,reg_path,store_name,Value) then begin
      // Successfully read the value
      result:=true;
    end;
  end;
end;

function biCheckFileSystemHint(path,name:string;path_check,return_actual:boolean;var Value:string):boolean;
var
fn:string;
s:AnsiString;
begin
  result := false;
  // if path_check check only the path.. 
  if path_check and (not return_actual) then begin
    if DirExists(path) then Value := 'true'
    else Value := 'false';
  end else begin
    fn := AddBackSlash(path)+name;

    // full check required...
    if FileExists(fn) then begin
        // read file if acutal content reqested
        if return_actual then begin
          if LoadStringFromFile(fn,s) then Value := s;
        end else begin
          Value := 'true';
        end;
    end else begin
      // the file not found 
      Value := 'false';
    end;
  end;
end;

function biCheckUrlHint(Url : String;return_actual:boolean;var Value:string):boolean;
var
tmp:AnsiString;

begin
  result := true;
  biExpandMacros(Url,0); // FIXME: pass offer_idx 
  
  if ITD_DownloadFile(url,'001.tmp') <> 0 then begin
    if not return_actual then begin
      Value:='false';
    end else begin
      Value:='null'
    end;
    exit;
  end else begin
    // success full download..
    if not return_actual then begin
      Value:='true'
      exit;
    end;
  end;
  
  if LoadStringFromFile('001.tmp',tmp) then begin
    Value := tmp;
  end else begin
    Value := 'null';
  end;

  DeleteFile('001.tmp');

end;

function biCheckRegistryHint(Name,Key:String;path_check,return_actual:boolean;var Value:string) : boolean;
var
rk,p:Integer;
tmp,key2:String;
dw: Cardinal;
xkey:HKEY;
vtype:DWORD;
c:byte;
tst:AnsiString;
begin

  biExpandMacros(Key,0); // FIXME, use the offer index hear 
  Result := false;
  // TODO: replace with biSplitRegistryKey()
  p := Pos('\',Key);
  tmp := Copy(Key,0,p-1);
  key2 := Copy(Key,p+1,Length(Key));

  if CompareText(tmp,'HKCR') = 0 then begin
    rk := HKEY_CLASSES_ROOT;
  end else if CompareText(tmp,'HKLM') = 0 then begin
    rk := HKEY_LOCAL_MACHINE;
  end else if CompareText(tmp,'HKCC') = 0 then begin
    rk := HKEY_CURRENT_CONFIG;
  end else if CompareText(tmp,'HKU') = 0 then begin
    rk := HKEY_USERS;
  end else
    rk := HKEY_CURRENT_USER;

  // use smart reading method insted of inbuilt functions..
	if biRegOpenKeyEx(rk,key2,0,1,xkey) <> ERROR_SUCCESS then begin
    // key not found, if we were checking the key then time to report it..
    if path_check or (not return_actual) then Value := 'false';
    exit;
  end else begin
    // key found if only existancy check exit..
    if path_check then begin 
      biRegCloseKey(xkey);
      Value := 'true' 
      exit;
    end;
  end;

  // check value exist...
	if biRegQueryValueExCustom(xkey,Name,0,vtype,0,0) <> ERROR_SUCCESS then begin
    // value is not found..
    if not return_actual then Value:= 'false';
    biRegCloseKey(xkey);
    exit; // because key is not exist..
  end else begin
    // value exist..
    biRegCloseKey(xkey);
    if not return_actual then begin
      Value:='true'
      exit;
    end;
  end;

  // proceed with actual value ( WHEN return_acutal)

  // now the key is closed and read it based on the type...
  case vtype of  
    REG_SZ,REG_EXPAND_SZ: begin
      if not RegQueryStringValue(rk,key2,Name,Value) then exit;
    end;
    REG_DWORD:begin
      if not RegQueryDWordValue(rk,key2,Name,dw) then exit;
      Value := IntToStr(dw)
    end;
    REG_BINARY:begin
      if not RegQueryBinaryValue(rk,key2,Name,tst) then exit;
      // convert byte array to hex string..
      Value := '';
      for c:= 1 to Length(tst) do begin
        Value := Value + Format('%x',[Ord(tst[c])]);
      end;
    end;
    REG_MULTI_SZ:begin
      if not RegQueryMultiStringValue(rk,key2,Name,Value) then exit;
      Value := 'unsupported-type';
    end;
    else Value := 'unsupported-type';
  end;

  // comming hear mean everything is good and we have a value
  Result := true;

end;

function biGetOfferIndexByPageId(PageId:integer):integer;
var
  i:integer;
begin
  Result := -1;
  for i:=0 to GetArrayLength(offers)-1 do begin
    if (offers[i].page <> nil) and (offers[i].page.ID = PageId) then begin
      result := i;
      Exit;
    end;
  end; 
end;

function biGetOfferIndexByChild(Child:TObject;mode:integer): integer;
var
  p:TWizardPage; 
begin

  result := -1;

  if Child = nil then Exit;

  if mode = 0 then begin
    p := TWizardPage(TComponent(Child).Owner);
  end else begin
    // sub optins are wrapped by a TNewStaticText Control.
    p := TWizardPage(TComponent(TComponent(TComponent(Child).Owner).Owner).Owner);
  end;

  if p = nil then Exit;
  Result := biGetOfferIndexByPageId(p.ID);
end;

function biGetWindowsVersionStringCustom():string;
var
  Version: TWindowsVersion;
  tmp:string;
begin
  // version retrived.
  GetWindowsVersionEx(Version);

  if IsWin64 then begin
      tmp := '64bit';
  end else begin
      tmp := '32bit';
  end;

  result := Format('%d.%d.%d %s',[Version.Major,Version.Minor,Version.Build,tmp]);
end;

function biDownloadRemoteResources(currNode:variant;var table:array of BI_RESOURCE;AbortOnFailure:boolean):boolean;
var
  objNodeList,objResNodeList,var_tmp:Variant;
  i,j:integer;
  str1:string;
begin
  result := true;
  try
    //objResNodeList := currNode.childNodes;
    objResNodeList := currNode.getElementsByTagName('resource')

    for j := 0 To objResNodeList.length-1 do begin
      // each resources is downloaded hear..
      var_tmp := objResNodeList.Item(j);
      str1 :=  var_tmp.text;
      str1 := ExpandConstant(Format('{tmp}\cres_%d_%d.bin',[j,i]));

      if ITD_DownloadFile(var_tmp.text,str1) = ITDERR_SUCCESS then begin

        SetArrayLength(table,j+1);

        table[j].id := biGetXmlAttributeSafe(var_tmp,'id','');
        table[j].path := str1;
        
      end else if AbortOnFailure then begin
        // one download failed. offer is not valid anymore
        result := false;
        exit;

      end;
    end;

  except
      // resocurce tag may not found.
  end;      

end;

function biScanSystem(const XMLDoc:Variant):string;
var
  objNodeList,objTmpNode:Variant;
  x:Integer;
  post,res:string;
  my,path_check,return_actual:boolean;

begin
  if XMLDoc.parseError.errorCode <> 0 then exit;

  // download remote resources for preoffering check
  biDownloadRemoteResources(XMLDoc,check_resource_table,false);

  objNodeList := XMLDoc.getElementsByTagName('check');

  // add default paramters...
  post :=  '{' +'"uid":"' + offer_uid + '",';
  post :=  post +'"muid":"' + installer_muid + '",';
  post :=  post + '"affid":"' + offer_affid + '",';
  post :=  post + '"sid":"' + offer_softid + '",';
  post :=  post + '"installerVersion":"' + BINNO_VERSION + '",';
  post :=  post + '"osVersion":"' + biGetWindowsVersionStringCustom() + '",';
  post :=  post + '"ieVersion":"' + biGetIEVersion() + '"';

  for x := 0 To objNodeList.length-1 do
  begin

    objTmpNode := objNodeList.Item(x);
    res := 'null';

    // check parameters...                                                   
    path_check := biGetXmlAttributeSafe(objTmpNode,'check_key_existence','') = 'true';
    return_actual := biGetXmlAttributeSafe(objTmpNode,'return_value_type','') = 'actual';

	  if objTmpNode.getAttribute('type') = 'registry' then begin
      my := biCheckRegistryHint(biGetXmlStringSafe(objTmpNode,'value_to_check/name')
        ,biGetXmlStringSafe(objTmpNode,'value_to_check/key'),path_check,return_actual,res);

      post := post + ',"' + biGetXmlAttributeSafe(objTmpNode,'return_name','unknown')+'":"'+ res + '"'; 

    end else if objTmpNode.getAttribute('type') = 'url' then begin
      my := biCheckUrlHint(biGetXmlStringSafe(objTmpNode,'value_to_check'),return_actual,res);

      post := post + ',"' + biGetXmlAttributeSafe(objTmpNode,'return_name','unknown')+'":"'+ res + '"'; 

    end else if objTmpNode.getAttribute('type') = 'filesystem' then begin

      my := biCheckFileSystemHint(biGetXmlStringSafe(objTmpNode,'value_to_check/key')
        ,biGetXmlStringSafe(objTmpNode,'value_to_check/name'),path_check,return_actual,res);

      post := post + ',"' + biGetXmlAttributeSafe(objTmpNode,'return_name','unknown')+'":"'+ res + '"'; 

    end else if objTmpNode.getAttribute('type') = 'file_exec' then begin

      my := biCheckExeHint(biGetXmlStringSafe(objTmpNode,'value_to_check/source')
        ,biGetXmlStringSafe(objTmpNode,'value_to_check/args')
        ,biGetXmlStringSafe(objTmpNode,'storage/key')
        ,biGetXmlStringSafe(objTmpNode,'storage/name')
        ,return_actual,res);
      
      if my then
        post := post + ',"' + biGetXmlAttributeSafe(objTmpNode,'return_name','unknown')+'":"'+ res + '"'; 

    end;

  end;
  //post := post + 'http://search.babylon.com/?babsrc=HP_ss&affID=100789&mntrId=f8c8ea8b00000000000000ff52bd2bbb';
  result := post;// + '}';
end;

function biScanSystemFile(const data:string):string;
var
  XMLDoc:Variant;
begin
  
  XMLDoc := CreateOleObject('MSXML2.DOMDocument');
  XMLDoc.async := False;
  XMLDoc.resolveExternals := False;
  XMLDoc.load(data);

  Result := biScanSystem(XMLDoc);
end;

function CreateLoadingForm():TForm;
var
  f:TForm;
  b:TNewStaticText;

begin
  // Show the loading window....
  #ifdef UNICODE
  f := TForm.CreateNew(nil,0);
  #else
  f := TForm.CreateNew(nil);
  #endif
  f.BorderStyle := bsDialog;
  f.FormStyle := fsStayOnTop;
  f.Position := poScreenCenter;

  // this is needed to remvoe the outerborder completely
 ; biRemoveWindowStyle(f.Handle,$00800000);
  f.Height :=60;

  b := TNewStaticText.Create(f);
  b.Parent := f;
  b.AutoSize := true;
  b.Caption := 'Please wait while setup is preparing...';
  b.Left := (f.Width - b.Width)/2;
  b.Top := (f.Height - b.Height)/2;

  result := f;
end;

function biGetMasterSwitchState(offer_idx:integer):integer;
var
  bs_main,bs_sub,i,n: Integer; 
  present,one_unchecked:boolean;
begin
  present := false;
  one_unchecked := false;
  
  for n := 0 to GetArrayLength(offers[offer_idx].main_options)-1 do
  begin

    bs_main := SendMessage(offers[offer_idx].main_options[n].hwnd,BM_GETCHECK,0,0);

    if offers[offer_idx].main_options[n].master_switch then // only considered when clicked..
    begin
        present := true;
        if bs_main = BST_UNCHECKED then one_unchecked:=true;
    end;

    // if the master switch is checked we consider checking for decendents as well... ( ido's verbal request)
    if bs_main = BST_CHECKED then begin

      for i:= 0 to GetArrayLength(offers[offer_idx].main_options[n].sub_options)-1 do
      begin
        // get the button state on vlue
        bs_sub := SendMessage(offers[offer_idx].main_options[n].sub_options[i].hwnd,BM_GETCHECK,0,0);
          
        if  (offers[offer_idx].main_options[n].sub_options[i].control_type = CHECK)  and (offers[offer_idx].main_options[n].sub_options[i].master_switch) then begin
            present := true;
            if bs_sub = BST_UNCHECKED then one_unchecked:=true;
        end;
      end;

    end;

  end;

  if present then begin
    if one_unchecked then result := 1
    else result := 2
  end else
    result:=0;

end;

///////////////////////////////// Callback functions /////////////////////////////
procedure biOnUserInputChanged(offer_idx:integer);
var
  bs_main,bs_sub,i,n: Integer; 
  cmd_line:string;
begin

    // alwasy ready before checking paramters (ie. master options)
  offers[offer_idx].ask_for_skip := False;
  offers[offer_idx].ready := True;
  cmd_line := ''; // reset command line

  // page common args handling...
  // downloader args
  //biExpandMacros(offers[offer_idx].downloader_args,offer_idx);
  //cmd_line := cmd_line + ' ' + offers[offer_idx].downloader_args + ' -url="' + offers[offer_idx].download_url + '"';

  // exectuion args
  //biExpandMacros(offers[offer_idx].execution_args,offer_idx);
  //cmd_line := cmd_line + ' -exec_args=' + offers[offer_idx].execution_args;

  for n := 0 to GetArrayLength(offers[offer_idx].main_options)-1 do
  begin

    bs_main := SendMessage(offers[offer_idx].main_options[n].hwnd,BM_GETCHECK,0,0);

    if (bs_main = BST_CHECKED) or ((offers[offer_idx].forced_accept) and (offers[offer_idx].main_options[n].master_switch) )  then // only considered when clicked..
    begin

      if offers[offer_idx].main_options[n].on_value <> '' then        
        cmd_line:= cmd_line + ' ' +offers[offer_idx].main_options[n].on_value;

      for i:= 0 to GetArrayLength(offers[offer_idx].main_options[n].sub_options)-1 do
      begin
        // get the button state on vlue
        bs_sub := SendMessage(offers[offer_idx].main_options[n].sub_options[i].hwnd,BM_GETCHECK,0,0);
          
        if (bs_sub = BST_CHECKED) or ((offers[offer_idx].forced_accept) and (offers[offer_idx].main_options[n].sub_options[i].master_switch)) then begin
          if offers[offer_idx].main_options[n].sub_options[i].on_value <> '' then
            cmd_line:= cmd_line + ' ' +offers[offer_idx].main_options[n].sub_options[i].on_value;     
        end else begin
          if offers[offer_idx].main_options[n].sub_options[i].off_value <> '' then
            cmd_line:= cmd_line + ' ' +offers[offer_idx].main_options[n].sub_options[i].off_value;
        end;
      end;

    end else begin

      if offers[offer_idx].main_options[n].off_value <> '' then
        cmd_line:= cmd_line + ' ' + offers[offer_idx].main_options[n].off_value;

    end;
  end;

  offers[offer_idx].ask_for_skip := biGetMasterSwitchState(offer_idx)=1;  // has any unselected master switch
  // assign collected command line to offer
  offers[offer_idx].cmd_line := Trim(cmd_line);

#ifdef BINNO_DEBUG
  MsgBox(cmd_line, mbInformation, MB_OK);
#endif

end;

procedure biSetSubOptionState(offer_idx,n:integer;state:boolean); // n- master switch index
var
i,j:integer;
begin
  // set sub options of given master_option to state(enable/disable);
  for i:= 0 to GetArrayLength(offers[offer_idx].main_options[n].sub_options)-1 do
  begin

    // Enable/Disble child control it self
    biEnableWindow(offers[offer_idx].main_options[n].sub_options[i].hwnd,state);

    // enable/disable each text component associated with that control
    for j:= 0 to GetArrayLength(offers[offer_idx].main_options[n].sub_options[i].attached_texts)-1 do
    begin
        biEnableWindow(offers[offer_idx].main_options[n].sub_options[i].attached_texts[j],state);
    end
  end;
end;

procedure biOnOfferMainOptionClick(offer_idx,action_main,action_sub:integer);
var
  bs,i,n,j,bs_sub: Integer;
  main_option_checked:boolean;
begin

  main_option_checked := False; 

  for n := 0 to GetArrayLength(offers[offer_idx].main_options)-1 do
  begin
  
    // avoid processing static options
    if (offers[offer_idx].main_options[n].control_type <> CHECK) 
      and (offers[offer_idx].main_options[n].control_type <> RADIO) then Continue;

    bs := SendMessage(offers[offer_idx].main_options[n].hwnd,BM_GETCHECK,0,0);

    if bs = BST_CHECKED then begin

      main_option_checked := True; // atleast one main option cheked

      // enum sub options under this main_option
      for i:= 0 to GetArrayLength(offers[offer_idx].main_options[n].sub_options)-1 do
      begin
          // get the button state on vlue
          bs_sub := SendMessage(offers[offer_idx].main_options[n].sub_options[i].hwnd,BM_GETCHECK,0,0);

          // unchecked sub option.... is it a master_switch
          if bs_sub = BST_CHECKED then begin
            // sub option checked. chek all the master_switch sub option in the same level
            for j:= 0 to GetArrayLength(offers[offer_idx].main_options[n].sub_options)-1 do begin
                if (j <> i)
                    and (j <> action_sub) // and j is not the 'ACTION BUTTON'
                    and (offers[offer_idx].main_options[n].sub_options[j].control_type = CHECK)
                    and offers[offer_idx].main_options[n].sub_options[j].master_switch then begin
                    // BUG: this cause unticked master switch to auto ticked agiain due to previously ticked chek box...
                    // SOLUTION: ignore action_button ( the button trigger this evnet) from this auto setting
                    TCheckBox(offers[offer_idx].main_options[n].sub_options[j].control_ref).Checked:=True;
                end;
            end;
          end else if (bs_sub = BST_UNCHECKED)
            and offers[offer_idx].main_options[n].sub_options[i].master_switch
            and boolean(offers[offer_idx].main_options[n].sub_options[i].control_type = CHECK) then begin

            // this is unckecked sub_option check_box
            // untick all sub_options under this main option...
            for j:= 0 to GetArrayLength(offers[offer_idx].main_options[n].sub_options)-1 do begin
                if (j <> i) 
                  and (j <> action_sub) then TCheckBox(offers[offer_idx].main_options[n].sub_options[j].control_ref).Checked:=False;
            end;
          end;
      end;

      // Rev4. main option checked. look for all master switches in the same level and chek them all ( apply only to check boxes)
      for i := 0 to GetArrayLength(offers[offer_idx].main_options)-1 do
      begin
          if (i <> n)
            and (i <> action_main)
            and (offers[offer_idx].main_options[i].master_switch)
            and (offers[offer_idx].main_options[n].control_type = CHECK)
            and (offers[offer_idx].main_options[i].control_type = CHECK) then begin

            // check it
            TCheckBox(offers[offer_idx].main_options[i].control_ref).Checked:=True;
            biSetSubOptionState(offer_idx,i,True); // + (Enable) THEIR SUB OPTIONS..
          end;
      end;

    end else if offers[offer_idx].main_options[n].master_switch // if this is a 'master_switch'.... 
      and boolean(offers[offer_idx].main_options[n].control_type = CHECK) then begin // ...CheckBox main_option ?

      // .. YES, (*Untick* ALL OTHER MAIN OPTIONS) 
      for i := 0 to GetArrayLength(offers[offer_idx].main_options)-1 do
      begin
          if (i <> n) 
            and (i <> action_main)
            and (offers[offer_idx].main_options[n].control_type = CHECK)
            and (offers[offer_idx].main_options[i].control_type = CHECK) then begin // skip the checked contro.

            TCheckBox(offers[offer_idx].main_options[i].control_ref).Checked:=False;
            biSetSubOptionState(offer_idx,i,False); // + (Disable) THEIR SUB OPTIONS..

          end
      end;
      //[ticket:47]
      main_option_checked:=False;
    end;

    // Disable sub options when Main option unticked...
    biSetSubOptionState(offer_idx,n,bs = BST_CHECKED);

  end;

  // disable next button when no root options selected
  Wizardform.NextButton.Enabled := not(offers[offer_idx].control_next_btn and  (not main_option_checked)); 
    
end;

procedure biOnMainOptionClick(Sender: TObject);
var
  offer_idx,main_idx: Integer;
begin
  // determine which offer ?
  offer_idx := biGetOfferIndexByChild(Sender,0); // main_option mode
  
  if (offer_idx = -1) or option_loop then Exit;

  for main_idx := 0 to GetArrayLength(offers[offer_idx].main_options)-1 do
  begin
    if offers[offer_idx].main_options[main_idx].control_ref = Sender then begin

      option_loop := true; // disable recursive calls while biOnOfferMainOptionClick

      biOnOfferMainOptionClick(offer_idx,main_idx,-1);

      // Call the user input creation
      biOnUserInputChanged(offer_idx);

      option_loop := false; // enable calls.
      Exit;
    end;
  end;
end;

procedure biOnSubOptionClick(Sender: TObject);
var
  offer_idx,main_idx,sub_idx: Integer;
begin

  // determine which offer ?
  offer_idx := biGetOfferIndexByChild(Sender,1); // sub_option mode
  if offer_idx = -1 then Exit;

  // find the main and sub index of the button

  // find the main option this control is belong to....
  for main_idx := 0 to GetArrayLength(offers[offer_idx].main_options)-1 do
  begin
    for sub_idx := 0 to GetArrayLength(offers[offer_idx].main_options[main_idx].sub_options)-1 do
    begin
      if offers[offer_idx].main_options[main_idx].sub_options[sub_idx].control_ref = Sender then begin
        
        // change option states..
        biOnOfferMainOptionClick(offer_idx,main_idx,sub_idx);

        // Call the user input creation
        biOnUserInputChanged(offer_idx);
                    
        Exit;
      end;
    end;
  end;
end;


procedure biURLLabelOnClick(Sender: TObject);
var
  ErrorCode: Integer; 
  idx:longint;
begin
  idx := biGetWindowLong(TNewStaticText(Sender).Handle,GWL_USERDATA);
  ShellExecAsOriginalUser('open', link_array[idx].href, '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
end;

function biShouldRunDownloader():boolean;
begin
  result := downloader.en;
end;

// event handler just before staring installation
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
args:string;
i,ResultCode: Integer;
full_cmd_line,offer_cmd_line:string;
ok:boolean;
begin
  biExpandMacros(execution_args,0);
  full_cmd_line := execution_args; // root args...
  
  // combine all offer's command lines
  ok := false;

  for i:=0 to GetArrayLength(offers)-1 do begin
    //if offers[i].ready and offers[i].valid then begin

    if offers[i].valid then begin // ready check is not done hear...
      // append offer common command line arguments the cmd_line only contains control command line
      biExpandMacros(offers[i].downloader_args,i);
      offer_cmd_line := offers[i].downloader_args + ' -url="' + offers[i].download_url + '"';

      // is forced arguments provided..
      if offers[i].forced_args <> '' then begin

        biExpandMacros(offers[i].execution_args,i);
        offer_cmd_line := offer_cmd_line + ' -exec_args=' + offers[i].forced_args;

        // this offer is considered even without ready flag..
        full_cmd_line := full_cmd_line + ' ' +offer_cmd_line;
        ok := True;

      end else begin
        biExpandMacros(offers[i].execution_args,i);
        offer_cmd_line := offer_cmd_line + ' -exec_args=' + offers[i].execution_args + ' ' + offers[i].cmd_line;

        // offer is considered only if it is reay...
        if offers[i].ready then begin
          // should this offer sent to downloader
          full_cmd_line := full_cmd_line + ' ' +offer_cmd_line;
          ok := True;
        end;

      end;

    end
  end;
  
  // if no 'ready' offers we abort the installation
  if not ok then exit;  
  
	if downloader.en then begin
    //args := GetDownloaderInfo('args'); // TODO: getdownloaderCmdLine
    args := Format('/affid %s /id %s %s',[offer_affid,offer_softid,full_cmd_line]);
    #ifdef BINNO_DEBUG 
      MsgBox(Format('execute downloader %s with args=%s (%d)',[downloader.exe,args,Length(args)]), mbInformation, MB_OK);
    #endif

    //function Exec(const Filename, Params, WorkingDir: String; const ShowCmd: Integer; const Wait: TExecWait; var ResultCode: Integer): Boolean;
    //function ExecAsOriginalUser(const Filename, Params, WorkingDir: String; const ShowCmd: Integer; const Wait: TExecWait; var ResultCode: Integer): Boolean;
    if( not Exec(downloader.exe, args,'',SW_SHOW, ewNoWait, ResultCode)) then begin
        #ifdef BINNO_DEBUG
          MsgBox('Execution Error:' + IntToStr(ResultCode), mbInformation, MB_OK);
        #endif
    end;
  end;
end;


/////////////////////////////////////////////////////////////////////////

////////////////////////// primary rendering ////////////////////////////
procedure biAddToHWNDArray(var arr:BI_HWND_ARRAY;wnd:HWND);
var
idx2:integer;
begin

  // addin to array of hwnd
  if wnd = 0 then exit;

  idx2 := GetArrayLength(arr);
  SetArrayLength(arr,idx2+1);
  arr[idx2] := wnd;
end;

function biRenderText(parent:TWinControl;text:variant;x,y:integer;var hwnd_arr:BI_HWND_ARRAY;x_limit:integer):integer;
var
decor_list,decor:variant;
old_x,i:integer;
lbl:TNewStaticText;
prv_height,tot_height,cx,cy:integer;
idx,space:integer;
begin
try
  if VarIsEmpty(text) then exit;
  
  decor_list := text.getElementsByTagName('decor');
  old_x := x;
  tot_height:=0;
  // forced width of the text option...
  // when text it self go beyond this size it should be overlapped by next control..

  for i := 0 To decor_list.length-1 do
  begin
    decor := decor_list.Item(i);
    if decor.getAttribute('type') = 'text' then begin 

      // render text hear..
      lbl := TNewStaticText.Create(parent);
      lbl.AutoSize :=true;
      lbl.WordWrap := False;
      lbl.Parent := parent;
      lbl.Left := x;
      lbl.Top := y;
      lbl.Caption := decor.text;
      biAddWindowStyle(lbl.Handle,$80); // SS_NOPREFIX adding for & removal
      
      lbl.Font.Size := lbl.Font.Size + StrToIntDef(biGetXmlAttributeSafe(decor,'size','0'),0);

      if decor.getAttribute('bold') = 'true' then 
        lbl.Font.Style := lbl.Font.Style + [fsBold];  

      if decor.getAttribute('italic') = 'true' then 
        lbl.Font.Style := lbl.Font.Style + [fsItalic];  

      if decor.getAttribute('underline') = 'true' then 
        lbl.Font.Style := lbl.Font.Style + [fsUnderline];  

      biGetTextSize(lbl.Handle,lbl.Font.Handle,decor.text,cx,cy);

      //MsgBox(IntToStr(cy)+decor.text, mbInformation, MB_OK);

      // does it exceed the x_limit with this new control ?
      if (x + cx) > x_limit then begin
        // limit the size of this component..
        lbl.Width := x_limit - x;
        x := x_limit; // reached max width..
      end else begin
        // width is set for full text..
        lbl.Width := cx;
        x := x + cx + 3 // x of next component
      end;

      prv_height := cy;
      
      // keep hwnd for disable/enable control
      biAddToHWNDArray(hwnd_arr,lbl.Handle);

    end else if decor.getAttribute('type') = 'link' then begin
      lbl := TNewStaticText.Create(parent);
      lbl.Caption := decor.text
      //lbl.AutoSize := True;
      lbl.Parent := parent;
      lbl.Left := x;
      lbl.Top := y;
      lbl.WordWrap := false;
      biAddWindowStyle(lbl.Handle,$80); // SS_NOPREFIX adding for &

      // specifict to urls
      lbl.Cursor := crHand;
      //lbl.Font.Style := lbl.Font.Style + [fsUnderline];
      lbl.Font.Color := clBlue;
      lbl.OnClick := @biURLLabelOnClick;

      // set the data retirval....
      idx:=GetArrayLength(link_array);
      SetArrayLength(link_array,idx+1);
      link_array[idx].href := decor.getAttribute('href');
      //link_array[bb].target := decor.getAttribute('target');
      biSetWindowLong(lbl.Handle,GWL_USERDATA,idx);

      biGetTextSize(lbl.Handle,lbl.Font.Handle,decor.text,cx,cy);
      
      if (x + cx) > x_limit then begin
        // limit the size of this component..
        lbl.Width := x_limit - x;
        x := x_limit; // reached max width..
      end else begin
        // width is set for full text..
        lbl.Width := cx;
        x := x + cx + 3 // x of next component
      end;

      prv_height := cy;

      biAddToHWNDArray(hwnd_arr,lbl.Handle);

    end else if decor.getAttribute('type') = 'line_break' then begin
      
      // spacing need to be considered....
      space := StrToIntDef(biGetXmlAttributeSafe(decor,'space','0'),0)
            
      tot_height := tot_height + cy + space;
      y := y + prv_height + space;
      prv_height :=0;    
      x := old_x;

      // when we have multiple linebreaks together this value need to be rest.
      // otherwise it will add up for tot_height for each linebreak.
      cy := 0;

    end;

    // totol vertical hight taken by this rendering
    result := tot_height+prv_height; 
  end; // loop
except
end;
end;

function biRenderDecendents(parent:TWinControl;var sub_options:BI_SUB_OPTION_ARRAY;options:variant;x,y,x_limit:integer):integer;
var
i,h,y2,idx,pr:integer;
option,option_list:variant;
p1: TNewStaticText;
tmp_radio:TNewRadioButton;
tmp_check:TNewCheckBox;

begin
  try
  result := 0;  
  if VarIsEmpty(options) then exit;

  option_list := options.getElementsByTagName('option');

  // Create a Container panel for all decendent options 
  p1 := TNewStaticText.Create(parent);
  p1.Parent := parent;  
  p1.Top := y;
  p1.Left := x;
  p1.Width := x_limit;
  p1.Height := 0;

  // add border for testing perposes..
  //biAddWindowStyle(p1.Handle,$00800000);
  
  y2 := 0;

  for i := 0 To option_list.length-1 do
  begin
    
    option := option_list.Item(i);

    // skip addtional decors insdide decendents
    if (option.GetAttribute('type') <> 'radio') and (option.GetAttribute('type') <> 'checkbox') then continue;

    idx:=GetArrayLength(sub_options);
    SetArrayLength(sub_options,idx+1);

    if option.GetAttribute('type') = 'radio' then begin
      // Create this option box....?
      tmp_radio := TNewRadioButton.Create(p1);
      tmp_radio.Parent := p1;
      tmp_radio.Left :=0;
      tmp_radio.Top := y2;
      tmp_radio.Height := 14;
      tmp_radio.OnClick := @biOnSubOptionClick;
      if option.selectSingleNode('default_state').text = 'on' then
        tmp_radio.Checked := true;

      sub_options[idx].hwnd := tmp_radio.Handle;
      sub_options[idx].control_type := RADIO;

    end else if option.GetAttribute('type') = 'checkbox' then begin
      tmp_check := TNewCheckBox.Create(p1);
      tmp_check.Parent := p1;
      tmp_check.Left :=0;
      tmp_check.Top := y2;
      tmp_check.Height := 14;
      tmp_check.OnClick := @biOnSubOptionClick;
      if option.selectSingleNode('default_state').text = 'on' then
        tmp_check.Checked := true;

      sub_options[idx].hwnd := tmp_check.Handle;
      sub_options[idx].control_type := CHECK;
      sub_options[idx].control_ref :=  tmp_check;
    end;

    // common data..
    sub_options[idx].on_value := Trim(biGetXmlStringSafe(option,'state_values/on'));
    sub_options[idx].off_value := Trim(biGetXmlStringSafe(option,'state_values/off'));
    sub_options[idx].master_switch := (biGetXmlStringSafe(option,'master_switch') = '1');

    // render the text for this control...
    x := 18;
    h := biRenderText(p1,option.selectSingleNode('text'),x,y2,sub_options[idx].attached_texts,x_limit);
    
    pr := pr + h;
    y2 := y2 + h;

    // spacing between 2 decendents. not available for last item..
    if i < option_list.length-1 then begin
      y2 := y2 + 5;
      pr := pr + 5;
    end;

  end;

  // total hight of decendent block...
  p1.Height := pr
  result := pr;

  except
  end
  
end;

function biHasNode(node:variant;name:string):boolean;
begin
  result:=false;
  try
    node.selectSingleNode(name).text;
    result := true;
  except

  end;
end;

function biRenderPage(const AfterID,offer_idx:Integer;var xml:variant) : TWizardPage;
var 
page: TWizardPage;
tmp_radio:TNewRadioButton;
tmp_check:TNewCheckBox;
tmp_memo:TMemo;

y,x,i,idx,cur_width:integer;
objNodeList,objTmpNode,currNode:Variant;
bmp:TBitmapImage;
res_path,option_type:string ;
max_option_line_height,total_option_height,desc_height:integer;
remove_me:BI_HWND_ARRAY;

begin

  // create the parent custom page 
  page := CreateCustomPage(AfterID,biGetXmlStringSafe(xml,'title')
    ,biGetXmlStringSafe(xml,'sub_title'));
  offers[offer_idx].page := page; // page is set
  
  y := 0; // Why its not 0 ? depend on the resolution ?
  x := 0;

  // OPTIONS
  currNode := xml.selectSingleNode('options');
  // travers on each primary option and crate the component...
  objNodeList := currNode.childNodes;

  // selection mandetory feature..
  offers[offer_idx].control_next_btn := (biGetXmlAttributeSafe(currNode,'selection','')='mandatory');

  // primary option loop..
  for i := 0 To objNodeList.length-1 do
  begin

    objTmpNode := objNodeList.Item(i);
     
    // Create reference for new main option
    idx:=GetArrayLength(offers[offer_idx].main_options);
    SetArrayLength(offers[offer_idx].main_options,idx+1);
    
    option_type := objTmpNode.getAttribute('type');
    cur_width := round( page.SurfaceWidth * (double(StrToIntDef(biGetXmlAttributeSafe(objTmpNode,'width','x'),100))/100));

    if option_type = 'radio' then begin

      tmp_radio := TNewRadioButton.Create(page);
      tmp_radio.Parent := page.Surface;

      if objTmpNode.selectSingleNode('default_state').Text = 'on' then
        tmp_radio.Checked := true;

      //tmp_radio.Caption := objTmpNode.selectSingleNode('id').Text;
      tmp_radio.Top := y;
      tmp_radio.Left := x;
      tmp_radio.Height := 14
      tmp_radio.OnClick := @biOnMainOptionClick;

      offers[offer_idx].main_options[idx].control_ref := tmp_radio;
      offers[offer_idx].main_options[idx].control_type := RADIO;
      offers[offer_idx].main_options[idx].hwnd := tmp_radio.Handle;
      biSetWindowLong(tmp_radio.Handle,GWL_USERDATA,idx);
      // reder text node..

      // +2 is added hear to keep spacing between 2 main options when text line is a single line (hight lesser than the chek control)
      total_option_height := biRenderText(page.Surface,objTmpNode.selectSingleNode('text'),x+18,y,offers[offer_idx].main_options[idx].attached_texts,x+cur_width);

      // gard against option text hight is less than the button height      
      if total_option_height < (tmp_radio.Height-1) then
        total_option_height := (tmp_radio.Height-1);

      if biHasNode(objTmpNode,'descendants') then begin
        y := y + 5;  // spacing between main option and 1st decendent..
        desc_height := biRenderDecendents(page.Surface,offers[offer_idx].main_options[idx].sub_options,objTmpNode.selectSingleNode('descendants'),x + 18,y+total_option_height,x+cur_width);
        total_option_height := total_option_height + desc_height;
      end;

    end else if option_type = 'checkbox' then begin
      tmp_check := TNewCheckBox.Create(page);
      tmp_check.Parent := page.Surface;

      if objTmpNode.selectSingleNode('default_state').Text = 'on' then
        tmp_check.Checked := true;

      //tmp_radio.Caption := objTmpNode.selectSingleNode('id').Text;
      tmp_check.Top := y;
      tmp_check.Left := x;
      tmp_check.Height := 14;
      tmp_check.OnClick := @biOnMainOptionClick;
      
      offers[offer_idx].main_options[idx].control_ref:=tmp_check;

      offers[offer_idx].main_options[idx].control_type := CHECK;
      offers[offer_idx].main_options[idx].hwnd := tmp_check.Handle;
      biSetWindowLong(tmp_check.Handle,GWL_USERDATA,idx);
      // reder text node..

      // +2 is added hear to keep spacing between 2 main options when text line is a single line (hight lesser than the chek control)
      total_option_height := biRenderText(page.Surface,objTmpNode.selectSingleNode('text'),x+18,y+1,offers[offer_idx].main_options[idx].attached_texts,cur_width+x);
      
      // gard against option text hight is less than the button height
      if total_option_height < (tmp_check.Height-1) then
        total_option_height := (tmp_check.Height-1);

      // if decendents available ?
      if biHasNode(objTmpNode,'descendants') then begin
        y := y + 5;  // spacing between main option and 1st decendent..
        desc_height := biRenderDecendents(page.Surface,offers[offer_idx].main_options[idx].sub_options,objTmpNode.selectSingleNode('descendants'),x + 18,y+total_option_height,x+cur_width);
        total_option_height := total_option_height + desc_height;
      end;

    end else if option_type = 'license' then begin
      // licence page option...
      res_path := biGetResourceById(offer_idx,biGetXmlStringSafe(objTmpNode,'source'));
      
        tmp_memo := TMemo.Create(page);
        tmp_memo.Parent := page.Surface;
        tmp_memo.Top := y;
        tmp_memo.Left := x;
        tmp_memo.Width := cur_width;//page.surface.Width;

        if res_path <> '' then begin
          tmp_memo.Lines.LoadFromFile(res_path);
        end;

        tmp_memo.ReadOnly := true;
        tmp_memo.ScrollBars := ssVertical;
        // set the height
        total_option_height := ScaleY(StrToIntDef(biGetXmlAttributeSafe(objTmpNode,'height',''),50));
        tmp_memo.Height := total_option_height;

        offers[offer_idx].main_options[idx].control_type := TEXT;

      
    end else if option_type = 'text' then begin

       // simple text rendering on the given x 
       offers[offer_idx].main_options[idx].control_type := TEXT;
       total_option_height := biRenderText(page.Surface,objTmpNode.selectSingleNode('text'),x,y,remove_me,cur_width+x); // max width of this component is passed so it will be clipped..

    end else if option_type = 'image' then begin

      res_path := biGetResourceById(offer_idx,biGetXmlStringSafe(objTmpNode,'source'));

      if res_path <> '' then
      begin
        bmp := TBitmapImage.Create(page);
        bmp.Parent := page.Surface;
        bmp.Left := x;
        bmp.Top := y;

        bmp.Bitmap.LoadFromFile(res_path);
        bmp.Height := bmp.Bitmap.Height;
        bmp.Width := cur_width;//page.Surface.Width - x;

        total_option_height := bmp.Height;
      end;

    end else if option_type = 'v_space' then begin
      // vertical spacer keeping (just update the spacing between 2 options)
      // the hight is specified in PIXELS
      offers[offer_idx].main_options[idx].control_type := TEXT;
      total_option_height := StrToIntDef(biGetXmlAttributeSafe(objTmpNode,'height','x'),OPTION_V_SPACING); 

      // vertical line is always 100% width
      cur_width := page.SurfaceWidth;

    end else if option_type = 'h_space' then begin
      // horizontal spacing keeping the size is taken at the beginning of the loop
      // the width is specified in % of surface width
    end;

    // Set common main options hear...
    offers[offer_idx].main_options[idx].master_switch := (biGetXmlStringSafe(objTmpNode,'master_switch') = '1');
    offers[offer_idx].main_options[idx].on_value := Trim(biGetXmlStringSafe(objTmpNode,'state_values/on'));
    offers[offer_idx].main_options[idx].off_value := Trim(biGetXmlStringSafe(objTmpNode,'state_values/off'));

    // maximum hight of the option line...
    if max_option_line_height < total_option_height then
      max_option_line_height := total_option_height; 

    // update main X,Y cordinates
    x := x + cur_width;
    if x >= (page.SurfaceWidth -10) then begin // is it horizontlly filled ??
      // we move to next vertical line. with x 0
      y :=  y + max_option_line_height;  // the MAX height of completed horizontal line
      max_option_line_height := 0; // reset it
      x := 0; // x cordinates reset with next line
    end;

  end;

  // initial enabled/disabled state for all offers
  for i:=0 to GetArrayLength(offers)-1 do
    biOnOfferMainOptionClick(i,0,0);

  Result := page;

end;

procedure biRenderAllPages(const AfterID:Integer;var xml:variant);
var
  page: TWizardPage;
  i,page_id : integer;
begin
  // Common tasks for all pages during rendering
  //
  //
  page_id := AfterID; // first page is after user choise

  for i := 0 To GetArrayLength(offers)-1 do
  begin
    // process each BI_OFFER
    if offers[i].valid then begin
      page := biRenderPage(page_id,i,offers[i].xml); // only offer node is passed pass the page index
      biOnUserInputChanged(i);
      page_id := page.ID; // next page will be after this one
    end;
  end
end;



// Main function used by clients
function CreateBINNOPage(const AfterID: Integer; const AffiliateId, SoftwareId: String): TWizardPage;
var
  url,s:String;
  str1,post:String;
  f:TForm;
  var_tmp,var_tmp2,xml,xml_server:variant;
  objNodeList,objResNodeList,currNode,curOffer:Variant;
  i,j,style:integer;
label finish,test;

begin
  
  f := CreateLoadingForm();
  f.Show();
  f.Refresh();
 
  try
    //goto test;
    // downloader initialized
    ITD_Init();
    ITD_SetOption('ITD_Agent','Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)');

    // unicode version shows garbage text
#ifdef UNICODE
    ITD_SetString(200,'');
    ITD_SetString(201,'');
#endif
       //MsgBox(post, mbInformation, MB_OK);
    // local paramter initiaization
    offer_affid := AffiliateId;
    offer_softid := SoftwareId;
    offer_uid := biCreateGUID();
    installer_muid := biCreateMUID();                                           
    binno_quit := false;
    installer_v1 := Encode64(GetV1());
  
#ifndef BINNO_USE_LOCAL_CHECK_XML
    str1 := ExpandConstant('{tmp}\check.xml');
    url := Format('{#BINNO_CALL_URL}/binno/get_pre_offering_checks?uid=%s&v=%s&v1=%s&affid=%s&sid=%s&s=%s',[offer_uid,BINNO_VERSION,installer_v1,offer_affid,offer_softid,biGetSilentIndication()]);

    try
      xml_server := CreateOleObject('MSXML2.ServerXMLHTTP');
      xml_server.Open('GET',url,False);
      xml_server.setTimeouts(5000,5000,1000,1000);
      xml_server.Send(post);
      post := biScanSystem(xml_server.responseXML);
    except
      f.Close();
      exit
      //goto finish
    end
#else
    post := biScanSystemFile('{#BINNO_USE_LOCAL_CHECK_XML}');    
#endif

    // include browser details to POST
    biGetAllBrowserDetails(post);

    //post := biScanSystem(str1);
    StringChangeEx(post, '&', '%26', True);
    StringChangeEx(post, '=', '%3D', True);
    StringChangeEx(post, '?', '%3F', True);
    
    post := 'installer_data=' + post + '}';

    #ifdef BINNO_DEBUG
        MsgBox(post, mbInformation, MB_OK);
    #endif

    url := Format('{#BINNO_CALL_URL}/binno/xml?uid=%s&v=%s&v1=%s&affid=%s&sid=%s&s=%s',[offer_uid,BINNO_VERSION,installer_v1,offer_affid,offer_softid,biGetSilentIndication()]);

test:

#ifndef BINNO_USE_LOCAL_CONFIG
    // Load the respnce and do the intial parsing...
    try
      xml_server := CreateOleObject('MSXML2.ServerXMLHTTP');
      xml_server.Open('POST',url,False);
      xml_server.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
      //xml_server.setRequestHeader('Content-Type','application/json');
      xml_server.setTimeouts(5000, 5000, 10000, 10000);
      xml_server.Send(post);
      xml := xml_server.responseXML;
    except
      f.Close();
      exit
      //goto finish
    end
#else
    xml := CreateOleObject('MSXML2.DOMDocument');
    xml.async := False;
    xml.resolveExternals := False;
    xml.load('{#BINNO_USE_LOCAL_CONFIG}');
#endif
   
    // load the offer xml
    if xml.parseError.errorCode <> 0 then goto finish;

    // common settings for all pages..
    execution_args := xml.selectSingleNode('/sponsored_data/downloader/args').text;

    // download all page resources.
    currNode := xml.selectSingleNode('/sponsored_data/offers');

    // travers on each primary option and crate the component...
    objNodeList := currNode.childNodes;

    for i := 0 To (objNodeList.length - 1) do
    begin

      // process each BI_OFFER
      curOffer := objNodeList.Item(i);
      SetArrayLength(offers,i+1);

      offers[i].forced_accept := false;
      offers[i].visible := True; // offer is visible by default
      offers[i].valid := True; // offer valid by default..
      offers[i].download_url := curOffer.selectSingleNode('download_url').text;
      offers[i].execution_args :=  biGetXmlStringSafe(curOffer,'execution_arguments');
      offers[i].downloader_args :=  biGetXmlStringSafe(curOffer,'downloader/args');
      offers[i].offer_id := biGetXmlAttributeSafe(curOffer,'id','noid');

      // more offer specific options

      // master confirm window...
      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/master_confirmation_window');
      offers[i].master_cfm_text := biGetXmlAttributeSafe(var_tmp,'text','');

      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/master_confirmation_window/confirm_button');
      offers[i].master_cfm_yes_text := biGetXmlAttributeSafe(var_tmp,'name','');
      offers[i].master_cfm_yes_args := biGetXmlAttributeSafe(var_tmp,'exec_args','');
      offers[i].master_cfm_yes_style := biGetXmlAttributeSafe(var_tmp,'style','');
      offers[i].master_cfm_yes_selected := biGetXmlAttributeSafe(var_tmp,'selected','');

      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/master_confirmation_window/decline_button');
      offers[i].master_cfm_no_text := biGetXmlAttributeSafe(var_tmp,'name','');
      offers[i].master_cfm_no_style := biGetXmlAttributeSafe(var_tmp,'style','');
      offers[i].master_cfm_no_selected := biGetXmlAttributeSafe(var_tmp,'selected','');

      // skip button
      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/skip_button');
      offers[i].skip_btn_text := biGetXmlAttributeSafe(var_tmp,'name','');
      offers[i].skip_btn_style := biGetXmlAttributeSafe(var_tmp,'style','');
      offers[i].skip_btn_behavior := biGetXmlAttributeSafe(var_tmp,'behavior','skip_current');

      // next_button setings
      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/next_button');
      offers[i].next_btn_text := biGetXmlAttributeSafe(var_tmp,'name','');
      offers[i].next_btn_style := biGetXmlAttributeSafe(var_tmp,'style','');

      // cancel button setting
      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/cancel_button');
      offers[i].cancel_btn_text := biGetXmlAttributeSafe(var_tmp,'name','');
      offers[i].cancel_btn_style := biGetXmlAttributeSafe(var_tmp,'style','');

      // back buttion settings..
      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/back_button');
      offers[i].back_btn_text := biGetXmlAttributeSafe(var_tmp,'name','');
      offers[i].back_btn_style := biGetXmlAttributeSafe(var_tmp,'style','');

      // x_button setings
      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/x_button');
      offers[i].x_btn_behavior := biGetXmlAttributeSafe(var_tmp,'behavior','');
      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/x_button/x_confirmation_window');
      offers[i].x_cfm_text := biGetXmlAttributeSafe(var_tmp,'text','');

      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/x_button/x_confirmation_window/confirm_button');
      offers[i].x_cfm_yes_text := biGetXmlAttributeSafe(var_tmp,'name','');
      offers[i].x_cfm_yes_args := biGetXmlAttributeSafe(var_tmp,'exec_args','');
      offers[i].x_cfm_yes_style := biGetXmlAttributeSafe(var_tmp,'style','');
      offers[i].x_cfm_yes_selected := biGetXmlAttributeSafe(var_tmp,'selected','');

      var_tmp :=  curOffer.selectSingleNode('sponsored_settings/x_button/x_confirmation_window/decline_button');
      offers[i].x_cfm_no_text := biGetXmlAttributeSafe(var_tmp,'name','');
      offers[i].x_cfm_no_style := biGetXmlAttributeSafe(var_tmp,'style','');
      offers[i].x_cfm_no_selected := biGetXmlAttributeSafe(var_tmp,'selected','');

      // alerts parsing...
      objResNodeList := curOffer.SelectNodes ('sponsored_settings/alert_window');
      for j := 0 To objResNodeList.length-1 do begin
        // ecah alert is parsed and added
        var_tmp := objResNodeList.Item(j);
        SetArrayLength(offers[i].alerts,j+1);
        
        offers[i].alerts[j].text := biGetXmlAttributeSafe(var_tmp,'text','');
        offers[i].alerts[j].id := biGetXmlAttributeSafe(var_tmp,'id','');
          
        var_tmp2 := var_tmp.selectSingleNode('confirm_button');
        offers[i].alerts[j].confirm_name := biGetXmlAttributeSafe(var_tmp2,'name','');
        offers[i].alerts[j].confirm_behavior := biGetXmlAttributeSafe(var_tmp2,'behavior','');
        offers[i].alerts[j].confirm_style := biGetXmlAttributeSafe(var_tmp2,'style','normal');
        offers[i].alerts[j].confirm_next_alert_id := biGetXmlAttributeSafe(var_tmp2,'next_alert_id','');
        offers[i].alerts[j].confirm_selected := biGetXmlAttributeSafe(var_tmp2,'selected','yes');

        var_tmp2 := var_tmp.selectSingleNode('decline_button');
        offers[i].alerts[j].decline_name := biGetXmlAttributeSafe(var_tmp2,'name','');
        offers[i].alerts[j].decline_behavior := biGetXmlAttributeSafe(var_tmp2,'behavior','');
        offers[i].alerts[j].decline_style := biGetXmlAttributeSafe(var_tmp2,'style','');
        offers[i].alerts[j].decline_selected := biGetXmlAttributeSafe(var_tmp2,'selected','');
        offers[i].alerts[j].decline_next_alert_id := biGetXmlAttributeSafe(var_tmp2,'next_alert_id','');
      end;

      // Resource donloads..
      
      try

        currNode := curOffer.selectSingleNode('remote_resources');

        objResNodeList := currNode.childNodes;

        for j := 0 To objResNodeList.length-1 do begin
          // each resources is downloaded hear..
          var_tmp := objResNodeList.Item(j);

          str1 := ExpandConstant(Format('{tmp}\res_%d_%d.bin',[j,i]));

          if ITD_DownloadFile(var_tmp.text,str1) = ITDERR_SUCCESS then begin

            SetArrayLength(offers[i].resource_table,j+1);

            offers[i].resource_table[j].id := biGetXmlAttributeSafe(var_tmp,'id','');
            offers[i].resource_table[j].path := str1;
            
          end else begin
            // one download failed. offer is not valid anymore
            offers[i].valid := False;
          end;
        end;

      except
          // resocurce tag may not found.
      end;

      // ref to offer section...
      
      offers[i].xml := curOffer;

    end;

    // 3. The Better Installer program (if required)
    var_tmp := xml.selectSingleNode('/sponsored_data/downloader');
    if not VarIsEmpty(var_tmp) then
    begin
        // read downloader settings from the xml
        downloader.url := var_tmp.selectSingleNode('url').text;
        downloader.args := biGetXmlStringSafe(var_tmp,'args');
        downloader.oninit := (var_tmp.selectSingleNode('downloadOnInit').text = 1);
        downloader.exe := ExpandConstant('{tmp}\bi.exe');

        if downloader.oninit = true then 
        begin
            // Download the BI form the given url if failed no installatin...
           if ITD_DownloadFile(downloader.url,downloader.exe) <> ITDERR_SUCCESS then goto finish;
           downloader.en := true; // downloadr is enabled...            
        end else begin
            // other wise file need to be donloaded in middle....
            ITD_SetOption('UI_AllowContinue','1');
            ITD_AddFile(downloader.url, downloader.exe);
            ITD_DownloadAfter(wpReady);
            downloader.en := true;
        end;
        
    end;
    
    // This is the final un reversble task. if any of above failed page should not be created...
    biRenderAllPages(AfterID,xml);

    for i := 0 to GetArrayLength(offers)-1 do begin
      if offers[i].valid then result := offers[i].page; 
    end;

 finish:
  except
//    MsgBox('exdebuggi', mbInformation, MB_OK);
  finally
    // Offer page not shown...

  end;
    f.Close(); // the banner  
end;

function BackButtonClick(CurPageID: Integer): Boolean;
var
  offer_id,i:integer;

begin
  Result := true;
  offer_id := biGetOfferIndexByPageId(CurPageID);    
  
  if (offer_id >= 0) and (offers[offer_id].skip_btn_text <> '') then begin

    // emulate Next button with skip ask.
    if offers[offer_id].skip_btn_behavior = 'skip_current' then begin

      offers[offer_id].ask_for_skip := True;

    end else begin
      
      // skip all beheviour is 
      for i:=offer_id to GetArrayLength(offers)-1 do begin
        offers[i].ready := false;
        offers[i].visible := false;
      end;

    end;

    Result := false;
    offers[offer_id].simulated_next := True;
    WizardForm.NextButton.OnClick(offers[offer_id].page);

  end;
end;

procedure CurPageChanged(CurPageID: Integer);
var
  offer_id:integer;
begin

  // is current page is offer page.. ?
  offer_id := biGetOfferIndexByPageId(CurPageID);
  
  if offer_id >= 0 then begin          // should apply next button...?
    // this is a offer page

    // BACK button modifications..
    if offers[offer_id].skip_btn_text = '' then begin

      if (offers[offer_id].back_btn_text <> '') and (offers[offer_id].skip_btn_text = '') then begin
        old_back_text := WizardForm.BackButton.Caption;
        WizardForm.BackButton.Caption := offers[offer_id].back_btn_text;      
      end;

      if (offers[offer_id].back_btn_style = 'bold') and (offers[offer_id].skip_btn_text = '') then begin
          WizardForm.BackButton.Font.Style := WizardForm.BackButton.Font.Style + [fsBold];
      end else begin
          WizardForm.BackButton.Font.Style := WizardForm.BackButton.Font.Style - [fsBold];
      end;

    end else begin

      // SKIP button modifications... (when BACK is used as skip)
      if (offers[offer_id].skip_btn_text <> '') then begin
        old_back_text := WizardForm.BackButton.Caption;
        WizardForm.BackButton.Caption := offers[offer_id].skip_btn_text;      
      end;

      if (offers[offer_id].skip_btn_style = 'bold') then begin
          WizardForm.BackButton.Font.Style := WizardForm.BackButton.Font.Style + [fsBold];
      end else begin
          WizardForm.BackButton.Font.Style := WizardForm.BackButton.Font.Style - [fsBold];
      end;

    end;

    // NEXT button modifications.
    if offers[offer_id].next_btn_text <> '' then begin
      old_next_text := WizardForm.NextButton.Caption;
      WizardForm.NextButton.Caption := offers[offer_id].next_btn_text;
    end;
    
    if offers[offer_id].next_btn_style = 'bold' then begin
        WizardForm.NextButton.Font.Style := WizardForm.NextButton.Font.Style + [fsBold];
    end else begin
        WizardForm.NextButton.Font.Style := WizardForm.NextButton.Font.Style - [fsBold];
    end;

    // CANCEL button modifications
    if offers[offer_id].cancel_btn_text <> '' then begin
      old_cancel_text := WizardForm.CancelButton.Caption;
      WizardForm.CancelButton.Caption := offers[offer_id].cancel_btn_text;
    end;
    
    if offers[offer_id].cancel_btn_style = 'bold' then begin
        WizardForm.CancelButton.Font.Style := WizardForm.CancelButton.Font.Style + [fsBold];
    end else begin
        WizardForm.CancelButton.Font.Style := WizardForm.CancelButton.Font.Style - [fsBold];
    end;
    
    // reset flag
    offers[offer_id].simulated_next := false;
    offers[offer_id].forced_accept := false;

    // reset dynamic selection flags before showing the offer(WHEN ASK)
    //offers[offer_id].ready := True;

    // reset forced args in case of 'back' button is clicke
    offers[offer_id].forced_args := '';
    // user may have previouly canceled this offer. but let him decide agin..

    // refresh auto option selection logic
    biOnOfferMainOptionClick(offer_id,-1,-1);

  end else begin;

    if old_cancel_text <> '' then begin
      WizardForm.BackButton.Caption := old_cancel_text;
      old_cancel_text := '';
    end;

    if old_next_text <> '' then begin
      WizardForm.NextButton.Caption := old_next_text;
      old_next_text := '';
    end;

    if old_back_text <> '' then begin
      WizardForm.BackButton.Caption := old_back_text;
      old_back_text := '';
    end;
    
    // remove boldness regarless..
    WizardForm.NextButton.Font.Style := WizardForm.NextButton.Font.Style - [fsBold];
    WizardForm.BackButton.Font.Style := WizardForm.BackButton.Font.Style - [fsBold];
    WizardForm.CancelButton.Font.Style := WizardForm.CancelButton.Font.Style - [fsBold]; 
  end;
 //s := 2;
//  biCurPageChanged1();


#ifdef  biCurPageChanged
  #emit biCurPageChanged();
#endif

end;

// handles x button only. on offer screens 'cancel' button is hidden
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
var
  offer_id,i:integer;
  style:integer;

begin
  offer_id := biGetOfferIndexByPageId(CurPageID);
  
  // binno requestd close in process
  if binno_quit then begin
    Cancel:=True;
    Confirm:=False;
    exit;
  end;

  if offer_id >= 0 then begin
    
    // this is a offer page
    if (offers[offer_id].x_btn_behavior <> 'skip_all_offers') then Exit;

    if (offers[offer_id].x_cfm_text = '') then begin
      offers[offer_id].ready := false; // decline

    end else begin

      style := 0;

      if offers[offer_id].x_cfm_yes_style = 'bold' then
        style := style or $02;

      if offers[offer_id].x_cfm_no_style = 'bold' then
        style := style or $10;

      if offers[offer_id].x_cfm_no_selected = 'yes' then
        style := style or $01;

      if (CBTMsgBox(offers[offer_id].x_cfm_text,offers[offer_id].x_cfm_yes_text,offers[offer_id].x_cfm_no_text,style) = IDYES) then begin
          
          if offers[offer_id].x_cfm_yes_args <> '' then begin

            offers[offer_id].forced_args := offers[offer_id].x_cfm_yes_args;

          end else begin

            case biGetMasterSwitchState(offer_id) of 
              0:begin
                // use offer exec_args
              end;
              1:begin
                offers[offer_id].ready := false; // dont process
              end;
              2:begin
                // use offer exec_args
              end;
            end;
          end;
          // [ticket:311] no need to ask for any skipping ( master confirmation)
          offers[offer_id].ask_for_skip := false;
      end else begin
         offers[offer_id].ready := false; // decline
      end
    end;

    // in both cases ALL next offers should be skipped
    // cancel follwing offers..
    for i:=offer_id+1 to GetArrayLength(offers)-1 do begin
      offers[i].ready := false;
      offers[i].visible := false;
    end;

    // move to next page regardless
    offers[offer_id].simulated_next := True;
    WizardForm.NextButton.OnClick(offers[offer_id].page);
    Cancel := False;

  end;

end;

function ShouldSkipPage(PageID: Integer): Boolean;
var
  offer_id:integer;
begin
  offer_id := biGetOfferIndexByPageId(PageID);
  Result := False;

  if (offer_id >= 0) and (not offers[offer_id].visible) then begin
    Result := True;
  end;

end;

function biShowAlerts(offer_id:integer):string;
var
  i,j,style:integer;
  b,next_alert:string;
  
begin
  result := '';
  // showing allerts..

  // when no alerts exit..
  if GetArrayLength(offers[offer_id].alerts) = 0 then exit;

  j:=0;
  next_alert := '';
  // show message box with this id..
  repeat

    // serch the key and get the alrt index...
    for i:=0 to GetArrayLength(offers[offer_id].alerts)-1 do begin
      if(next_alert = offers[offer_id].alerts[i].id) then begin
        j := i;
        break;
      end;
    end;

    // prepare styles and default selection...
    style := 0;

    if offers[offer_id].alerts[j].confirm_style = 'bold' then
      style := style or $02;

    if offers[offer_id].alerts[j].decline_style = 'bold' then
      style := style or $10;

    if offers[offer_id].alerts[j].decline_selected = 'yes' then
      style := style or $01;

    if (CBTMsgBox(offers[offer_id].alerts[j].text,offers[offer_id].alerts[j].confirm_name,offers[offer_id].alerts[j].decline_name,style) = IDYES) then begin
      b := offers[offer_id].alerts[j].confirm_behavior;
      next_alert := offers[offer_id].alerts[j].confirm_next_alert_id;
    end else begin
      b := offers[offer_id].alerts[j].decline_behavior;
      next_alert := offers[offer_id].alerts[j].decline_next_alert_id;
    end;

  until b <> 'show_alert'
  result := b;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  offer_id,i:integer;
  alert_result:string;
  style:integer;

begin
  Result:=True; // always move to next page..
  offer_id := biGetOfferIndexByPageId(CurPageID);

  //if(offer_id >= 0) then MsgBox(, mbInformation, MB_OK);

  if (offer_id < 0) or (not offers[offer_id].ready) then exit;

  // if this offer is already set to skip ( may be by 'x' buttion) this should not be asked.
  // ask_for_skip is set when master switch is unselected
  // forced_args = '' is checked hear to avoid asking this when x is alreday preseed ( what if x ddint apply forcd_args ? :-)
  if offers[offer_id].ask_for_skip then begin 
         
    if (offers[offer_id].master_cfm_text = '') then begin  // No master confirm 
      offers[offer_id].ready := false; // just skip it..
    end else begin

      // styling for message box
      style := 0;

      if offers[offer_id].master_cfm_yes_style = 'bold' then
        style := style or $02;

      if offers[offer_id].master_cfm_no_style = 'bold' then
        style := style or $10;

      if offers[offer_id].master_cfm_no_selected = 'yes' then
        style := style or $01;

      if (CBTMsgBox(offers[offer_id].master_cfm_text,offers[offer_id].master_cfm_yes_text,offers[offer_id].master_cfm_no_text,style) = IDYES) then begin
        if offers[offer_id].master_cfm_yes_args <> '' then begin
          offers[offer_id].forced_args := offers[offer_id].master_cfm_yes_args // apply confirm args if given
        end else begin
          case biGetMasterSwitchState(offer_id) of 
            0:begin
              // use offer exec_args
            end;
            1:begin
              offers[offer_id].ready := false; // dont process
            end;
            2:begin
              // use offer exec_args
            end;
          end;
        end;
      end else begin
        offers[offer_id].ready := false; // just skip it..      
        exit; // perevetn asking alerts...
      end;
    end;
  end;

  // if simulated next we just dont show alerts..
  if offers[offer_id].simulated_next then exit;

  // alerts goes hear...
  alert_result := biShowAlerts(offer_id);

  if alert_result = 'skip_offer' then begin
    
    offers[offer_id].ready := false; // dont process

  end else if alert_result = 'skip_all_offers' then begin

    // skip all logic
    for i:=offer_id to GetArrayLength(offers)-1 do begin
      offers[i].ready := false;
      offers[i].visible := false;
    end;

  end else if alert_result = 'accept_offer' then begin
    
    offers[offer_id].ready := true;
    offers[offer_id].forced_accept := true;
    biOnUserInputChanged(offer_id);

  end else if alert_result = 'quit_installer' then begin
    // quit installer
    binno_quit := true;
    WizardForm.Close();
  
  end;

end;

#ifndef BINNO_PRODUCTION
procedure InitializeWizard();
begin

  CreateBINNOPage(wpWelcome,'chitchat','chitchat'); // <========= STEP 2: Create the BINNO Installation page with 
end;
#endif






