format PE GUI 4.0
entry start
include "win32axp.inc"
macro add_words [word] {
      forward
      db `word
      db 0
      common
      db 0
}
IDD_DIALOG1 = 101
IDI_ICON1 = 102
IDC_EDIT1 = 1000
IDC_BUTTON2 = 1002
IDC_LIST1 = 1003
IDC_STATIC = -1
IDC_STATIC1 = 1004
IDC_CHECK = 1005

section '.data' data readable writeable

filec file 'dictionary.txt'
add_words brussel
done db 'Anagram search complete, in ',0
.dend = $-1
times 10 db 0
err db 'ERROR',0
lok db 0
nostring db 'No string specified',0
running db 'Search in progress',0
numtostr:
db '000000000'
.start db '0 ms.',0

section '.data?' readable writeable

wrd rb 60 ;space for entered word
tmpw rb 60 ;space for rearranged dictionary words
thread2 dd ? ;2nd thread handle
thread2id dd ? ;2nd thread id
hwnd dd ? ;window handle
hndl dd ? ;module handle
stc1 dd ? ;static control
tick dd ? ;tick count

section '.code' code readable executable

start:
	invoke	GetModuleHandle,0
	mov	[hndl],eax
	.blah:
	invoke	DialogBoxParam,[hndl],IDD_DIALOG1,HWND_DESKTOP,DProc,0
	or	eax,eax
	jnz	.blah
	invoke	ExitProcess,0
proc DProc,hwnddlg,msg,wparam,lparam
	push	ebx esi edi
	cmp	[msg],WM_INITDIALOG
	je	init
	cmp	[msg],WM_COMMAND
	je	cmd
	cmp	[msg],WM_CLOSE
	je	close
	xor	eax,eax
	jmp	finish
	init:
		invoke	GetDlgItem,[hwnddlg],IDC_STATIC1
		mov	[stc1],eax
		invoke	SendDlgItemMessage,[hwnddlg],IDC_EDIT1,EM_SETLIMITTEXT,57,0
		invoke	GetDlgItem,[hwnddlg],IDC_EDIT1
		invoke	SetFocus,eax
		jmp	processed
	cmd:
		cmp	[wparam],BN_CLICKED shl 16 + IDOK
		je	close
		cmp	[wparam],BN_CLICKED shl 16 + IDC_CHECK
		je	check
		cmp	[wparam],BN_CLICKED shl 16 + IDC_BUTTON2
		jne	processed
		mov	ebx,[hwnddlg]
		mov	[hwnd],ebx
		cmp	[lok],1
		je	dontRun
		mov	[lok],1     ;prevent from re-running
		invoke	CreateThread,NULL,0,FindAnagram,1,0,thread2id
		or	eax,eax
		jz	close
		mov	[thread2],eax
	dontRun:
		jmp	processed
	check:
		invoke	IsDlgButtonChecked,[hwnddlg],IDC_CHECK
		cmp	eax,BST_CHECKED
		je	.checked
		invoke	SetWindowPos,[hwnddlg],HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE+SWP_NOSIZE
		jmp	processed
		.checked:
		invoke	SetWindowPos,[hwnddlg],HWND_TOPMOST,0,0,0,0,SWP_NOMOVE+SWP_NOSIZE
		jmp	processed
	close:
		invoke	EndDialog,[hwnddlg],0
	processed:

		mov	eax,1
	finish:
		pop	edi esi ebx
		ret
endp
FindAnagram:
	invoke	GetTickCount;number of ticks
	mov	[tick],eax  ;store it
	invoke	SendMessage,[stc1],WM_SETTEXT,0,running ;notify user via static control
.del:	invoke	SendDlgItemMessage,[hwnd],IDC_LIST1,LB_DELETESTRING,0,0 ;delete an entry
	cmp	eax,LB_ERR  ;error? (basically no more entries)
	jne	.del	    ;if none, try again
	invoke	SendDlgItemMessage,[hwnd],IDC_EDIT1,WM_GETTEXT,60,wrd ;get the entered text
	mov	edi,wrd     ;find the length
	call	StrLen	    ;....
	test	eax,eax     ;is it zero?
	jz	.ns	    ;if so, quit
	stdcall LetterSort,wrd,eax ;and sort it
	mov	esi,filec   ;dictionary
.loop:	cmp	[esi],byte 0;is it the end
	je	.done	    ;if so, we're done
	invoke	lstrcpy,tmpw,esi;copy
	mov	edi,esi     ;find the length of the word
	call	StrLen	    ;...
	push	esi	    ;save the original word
	add	esi,eax     ;increment by some
	add	esi,1	    ;and some more
	stdcall LetterSort,tmpw,eax;rearrange it
	stdcall cmpStr,tmpw,wrd;compare
	pop	ebx	    ;restore the original word
	test	eax,eax     ;are they equal?
	jnz	.loop	    ;if not, loop
	invoke	SendDlgItemMessage,[hwnd],IDC_LIST1,LB_ADDSTRING,0,ebx ;guess they are; add to list
	jmp	.loop	    ;loop anyway
.ns:	invoke	SendMessage,[stc1],WM_SETTEXT,0,nostring;complain
	mov	[lok],0     ;unlock
	invoke	CloseHandle,[thread2];free the handle to the thread
	invoke	ExitThread,1;byebye
.done:	mov	[lok],0     ;unlock
	invoke	CloseHandle,[thread2];free the handle to the thread
	invoke	GetTickCount;get the number of ticks
	sub	eax,[tick]  ;subtract the original number
	stdcall int2str,eax ;convert it to a string
	invoke	lstrcat,done,eax ;add it to the done message
	invoke	SendMessage,[stc1],WM_SETTEXT,0,done;notify that we're done
	mov	[done.dend],dword 0 ;set the string up for the next time
	invoke	ExitThread,0;we're done; quit

;Find string length of str and return it in eax
StrLen:
	push	ecx
	mov	ecx,-1
	and	al,0
	cld
repne	scasb
	not	ecx
	sub	ecx,1
	mov	eax,ecx
	pop	ecx
	ret


;Compare str1 and str2 and set eax 0 if equal 1 if not
proc cmpStr,str1,str2
	mov	ecx, [str1]
	mov	edx, [str2]
cmst:	mov	al, [ecx]
	cmp	al, [edx]
	jne	no_match
	add	ecx, 1
	add	edx, 1
	test	al, al
	jne	cmst
	xor	eax,eax
	ret
no_match:mov	eax,1
	ret
endp

;Bytesort a sequence of bytes; ed=pointer ec=length
proc LetterSort,ed,ec
	push	edi ebx ecx
	mov	edi,[ed]
	mov	ecx,[ec]
.bsort: lea	ebx,[edi+ecx]
	mov	al,[edi]
.cploop:dec	ebx
	cmp	al,[ebx]
	jle	.again
	xchg	al,[ebx]
.again: cmp	ebx,edi
	jnz	.cploop
	stosb
	loop	.bsort
	pop	ecx ebx edi
	ret
endp

proc int2str,int
push	edx ebx esi edx
mov	esi,numtostr.start
mov	eax,[int]
mov	ebx,10
mov	[numtostr],dword '0000'
mov	[numtostr+4],dword '0000'
mov	[numtostr+8],word '00'
.loop:
xor	edx,edx
div	ebx
mov	[esi],byte '0'
add	[esi],dl
sub	esi,1
cmp	eax,0
jne	.loop
mov	eax,esi
add	eax,1
pop	edx esi ebx edx
ret
endp

section '.idata' import data readable writeable
library kernel32,'kernel32.dll',\
	user32,'user32.dll'

	include '%fasminc%\apia\kernel32.inc'
	include '%fasminc%\apia\user32.inc'

section '.rsrc' resource from 'anagramv2.res' data readable