;***************************************************
; The file manager is a part of Fresh IDE
; that control all files Fresh working with.
; It supersedes most of the functionality
; of project manager and source editor that is
; related to managing any type of files.
;***************************************************

iglobal

FileTypes:
  ; Project file type.
  ftProject         TFileType CreateProjectManager,     \
                              ProjFilter,               \
                              -1,                       \
                              NULL

  ; Usual text source file.
  ftCommonSource    TFileType CreateSourceEditor,       \
                              FileFilter,               \
                              0,                        \
                              fasm_syntax

  ; Main source file.
  ftMainSource      TFileType CreateSourceEditor,       \
                              FileFilter,               \
                              1,                        \
                              fasm_syntax

  ; File containing Form template and form handling source file.
  ftFormSource      TFileType CreateFormEditor,         \
                              FormFilter,               \
                              3,                        \
                              fasm_syntax

  ; Text document without syntax highlighter.
  ftTextDocument    TFileType CreateSourceEditor,       \
                              FileFilter,               \
                              2,                        \
                              NULL

; graphic file
;  ftGraphic         TFileType CreateGraphicEditor,      \
;                              GraphicFilter,            \
;                              4,                        \
;                              NULL

;  ftWatch           TFileType CreateMemWatch,           \
;                              WatchFilter,              \
;                              5,                        \
;                              NULL

  FileTypeNames:
        StrList                               \
          'Project',                          \
          'Source file',                      \
          'Main file',                        \
          'Form',                             \
          'Text document';,                    \
;          'Image',                            \
;          'Memory watch table'

  FileTypeExt  dd '.fpr', '.asm', '.asm', '.frm', '.txt';, '.bmp';, '.mwt'

endg


uglobal
  ptrCurrentFile dd ?   ; TOpenFile descendent that have the focus in that moment.
  listFiles      dd ?   ; TArray with all open files.
endg


;-------------------------------------------------
; Prepares file manager for work.
; Call this procedure only once at the begining.
;-------------------------------------------------
proc InitFileManager
begin
        stdcall CreateArray, 4
        mov     [listFiles], eax
        return
endp


;-------------------------------------------------
; Closes all still open files and finalizes the
; file manager. Call it once on close of Fresh
;-------------------------------------------------
proc CloseFileManager
begin
; Close and free all open files.
        stdcall ListFree, [listFiles], _FreeFileProc
        mov     [listFiles], 0
        return
endp


    proc _FreeFileProc, .ptrOpenFile
    begin
            push    esi
            mov     esi, [.ptrOpenFile]
            mov     [esi+TOpenFile.projectID], 0
            stdcall CloseFile, esi
            pop     esi
            return
    endp


proc OpenFileByNameEdit, .hFileName, .dummy
begin
        stdcall OpenFileByName, [.hFileName], NULL, TRUE
        return
endp


proc OpenFileByName, .hFileName, .hTemplate, .fLoadInEditor
begin
        push    ebx

; log the active file state before changing focus...
        cmp     [fLogFileLock], 0
        jne     @f
        cmp     [.fLoadInEditor], 0
        je      @f
        stdcall LogInHistory, ptrBackHistory
@@:

        stdcall IsFileOpen, [.hFileName]
        mov     ebx, eax
        test    eax, eax
        jnz     .focus

        stdcall DetectFileType, [.hFileName]
        mov     ebx, eax
        test    eax, eax
        jz      .unknowntype

.doopen:
        stdcall CreateFStruct, [.hFileName], ebx
        mov     ebx, eax

.focus:
        cmp     [.fLoadInEditor], FALSE
        je      .finish

        stdcall LoadFile, ebx, [.hTemplate]
        test    eax, eax
        jz      .error

.finish:
        mov     eax, ebx
        pop     ebx
        return

.unknowntype:
        mov     ebx, ftTextDocument

        stdcall AskQuestion, <"Unknown file type.", 13, 10, "Do you want to open it as text file?">, [hMainForm], answerYesNo
        cmp     eax, IDYES
        je      .doopen

        xor     eax, eax
        pop     ebx
        return

.error:
        stdcall FreeFStruct, ebx
        xor     eax, eax
        pop     ebx
        return
endp




;--------------------------------------------------------------------
; Check whether the file is already open in the coresponding editor.
; Returns:
; eax:
;   NULL - the file is not open.
;   ptrOpenFile - the file is open.
;--------------------------------------------------------------------
proc IsFileOpen, .hFileName
begin
        push    esi ebx edx

        stdcall GetFullPathNameUtf8, [.hFileName]
        mov     ebx, eax

        stdcall HashFilename, [.hFileName]
        mov     edx, eax

        mov     esi, [listFiles]
        mov     ecx, [esi+TArray.count]
        lea     esi, [esi+TArray.array]

.while:
        dec     ecx
        js      .notfound
        lodsd
        cmp     edx, [eax+TOpenFile.hashFileName]
        jne     .while

        stdcall StrCompNoCase, [eax+TOpenFile.hFileName], ebx
        jnc     .while

; .Found:

.finish:
        stdcall StrDel, ebx
        pop     edx ebx esi
        return

.notfound:
        xor     eax, eax
        jmp     .finish
endp



;----------------------------------------------------------------------
; Detects the proper type of the file from it's extension.
; Returns valid pointer to TFileType structure or NULL for error.
;----------------------------------------------------------------------
proc DetectFileType, .hFileName
begin
        stdcall StrLen, [.hFileName]
        mov     ecx, eax
        stdcall StrPtr, [.hFileName]

.extloop:
        dec     ecx
        js      .error

        cmp     byte [eax+ecx], '.'
        jne     .extloop

        mov     ecx, dword [eax+ecx]    ; get the extension
        or      ecx, $20202000          ; case insensitive

        mov     eax, ftCommonSource
        cmp     ecx, '.asm'
        je      .typeok

        cmp     ecx, '.inc'
        je      .typeok

        mov     eax, ftFormSource
        cmp     ecx, '.frm'
        je      .typeok

        mov     eax, ftProject
        cmp     ecx, '.fpr'
        je      .typeok

        mov     eax, ftTextDocument
        cmp     ecx, '.txt'
        je      .typeok

;        mov     eax, ftGraphic
;        cmp     ecx, '.bmp'
;        je      .typeok

.error:
        xor     eax, eax
.typeok:
        return
endp




;----------------------------------------------------------------------
; Creates the file structure for given file type.
; If in some of the editors, the file with the same name is
; already open, returns the pointer to the existing file.
;
; In the case if new file is created:
; Set the values for following fields:
;   .type         = needed type value.
;   .hFileName    = full filename of the file.
;   .hashFileName = hash value of the filename.
;   .fNeverSaved  = FALSE if the file with given name exists
;                   TRUE otherwize.
;
;  Returns the pointer to the TOpenFile in the eax register.
;----------------------------------------------------------------------
proc CreateFStruct, .hFileName, .type
begin
        push    ebx esi

        mov     ebx, [.type]
        test    ebx, ebx
        jnz     .typeok

        mov     ebx, ftTextDocument

.typeok:
        stdcall IsFileOpen, [.hFileName]
        test    eax, eax
        jnz     .finish

        stdcall GetMem, sizeof.TOpenFile
        mov     esi, eax

        stdcall AddArrayItems, [listFiles], 1
        mov     [listFiles], edx
        mov     [eax], esi

        mov     [esi+TOpenFile.ptrType], ebx


        stdcall GetFullPathNameUtf8, [.hFileName]
        mov     [esi+TOpenFile.hFileName], eax

        stdcall HashFilename, eax
        mov     [esi+TOpenFile.hashFileName], eax

        stdcall StrPtr, [esi+TOpenFile.hFileName]
        stdcall FileExists, eax
        rcl     [esi+TOpenFile.fNeverSaved], 1

        mov     eax, esi

.finish:
        pop     esi ebx
        return
endp


;--------------------------------------------------
; Frees the TOpenFile descendent structure.
;--------------------------------------------------
proc FreeFStruct, .ptrFile
begin
        push    esi
        mov     esi, [.ptrFile]

        cmp     [esi+TOpenFile.hEditor], 0
        je      .editorclosed
        invoke  DestroyWindow, [esi+TOpenFile.hEditor]
        mov     [esi+TOpenFile.hEditor], 0

.editorclosed:
        cmp     [esi+TOpenFile.projectID], 0
        je      .notinproject

        invoke  SendMessageW, [hProjManager], PMM_REMOVEFILE, esi, 0

.notinproject:
        stdcall ListIndexOf, [listFiles], esi
        jc      .removedfromlist

        stdcall DeleteArrayItems, [listFiles], eax, 1
        mov     [listFiles], edx

.removedfromlist:
        stdcall StrDel, [esi+TOpenFile.hFileName]
        stdcall FreeMem, esi

        pop     esi
        return
endp


;------------------------------------------------------------------------
; Loads the given file in the apropriate editor window.
; If the file is loaded - do nothing.
; Returns NULL if error or eax = [ptrFile] if OK.
;------------------------------------------------------------------------
proc LoadFile, .ptrFile, .hTemplate
begin
        push    esi

        mov     esi, [.ptrFile]
        cmp     [esi+TOpenFile.hEditor], 0
        jne     .fileisloaded

        mov     eax, [esi+TOpenFile.ptrType]

        stdcall [eax+TFileType.CreateProc], esi, [.hTemplate]
        test    eax, eax
        jnz     .error

.fileisloaded:
        stdcall FocusFile, esi

        mov     eax, esi
        pop     esi
        return

.error:
        stdcall ErrorMessage, eax
        xor     eax, eax
        pop     esi
        return
endp


;-------------------------------------------------
; Renames given file and notifies the editor that
; the file was renamed.
;-------------------------------------------------
proc RenameFile, .ptrFile, .hNewName
begin
        push    esi
        mov     esi, [.ptrFile]

        stdcall StrCopy, [esi+TOpenFile.hFileName], [.hNewName]
        stdcall HashFilename, [esi+TOpenFile.hFileName]
        mov     [esi+TOpenFile.hashFileName], eax

        cmp     [esi+TOpenFile.hEditor], 0
        je      .checkproject

        ; notify the editor.
        invoke  SendMessageW, [esi+TOpenFile.hEditor], _CEM_CHANGENOTIFY, 0, 0

.checkproject:
        cmp     [esi+TOpenFile.projectID], 0
        je      .finish

        ; notify project manager.
        invoke  SendMessageW, [hProjManager], PMM_FILECHANGED, esi, 0

.finish:
        pop     esi
        return
endp




;------------------------------------------------
; Saves the file to the disk.
; Also checks if the flag .fNeverSaved is TRUE
; and opens the SaveAs dialog.
; The easy way to make function SaveAs is to
; set .fNeverSaved and to call this function.
; Return: TRUE if the file is saved
;         FALSE if the user canceled operation or error ocures.
;---------------------------------------------------------------
proc SaveFile, .ptrFile
.inmem TInMemoryFile
.res   dd ?
begin
        push    esi

        mov     [.res], TRUE
        mov     [.inmem.ptrBuffer], 0

        mov     esi, [.ptrFile]
        test    esi, esi
        jz      .finish

        cmp     [esi+TOpenFile.hEditor], 0
        je      .finish                         ; the file is not loaded in editor.

; Is the file need SaveAs?
        cmp     [esi+TOpenFile.fNeverSaved], FALSE
        je      .dosave

        mov     eax, [esi+TOpenFile.ptrType]
        stdcall SaveFileDialog, [hMainForm], cSaveFileTitle, [eax+TFileType.ptrFilter], [esi+TOpenFile.hFileName], 0
        test    eax, eax
        jnz     .rename

        mov     [.res], eax
        jmp     .finish

.rename:
        push    eax
        stdcall RenameFile, esi, eax
        stdcall StrDel ; from the stack.

.dosave:
        lea     eax, [.inmem]
        invoke  SendMessageW, [esi+TOpenFile.hEditor], _CEM_GETTEXT, 0, eax
        test    eax, eax
        jnz     .errorsave

        stdcall StrPtr, [esi+TOpenFile.hFileName]

        stdcall SaveBinaryFile, eax, [.inmem.ptrBuffer], [.inmem.fileSize]
        jc      .errorsave

        mov     [esi+TOpenFile.fNeverSaved], 0   ; Mark it as saved at least once.

        invoke  SendMessageW, [esi+TOpenFile.hEditor], EM_EMPTYUNDOBUFFER, 0, 0
        invoke  SendMessageW, [esi+TOpenFile.hEditor], _CEM_SETMODIFIED, 0, 0
        jmp     .free

.errorsave:
        mov     [.res], 0
        stdcall ErrorMessage, eax

.free:
        cmp     [.inmem.ptrBuffer], 0
        je      .finish

        stdcall FreeMem, [.inmem.ptrBuffer]

.finish:
        mov     eax, [.res]
        pop     esi
        return
endp









;--------------------------------------------------
; Close the file and removes it from its editor.
; Returns:
;   0: file is closed successfully
;   1: file is closed and TOpenFile structure was destroyed.
;   2: some error
;   3: CANCEL button was pressed on the save dialog.
;--------------------------------------------------
proc CloseFile, .ptrFile
begin
        push    esi

        mov     esi, [.ptrFile]
        test    esi, esi
        jz      .fault

        cmp     [esi+TOpenFile.hEditor], 0
        je      .checkproj

        stdcall GetModified, esi
        test    eax, eax
        je      .saveok

.loop_not_saved:
        stdcall StrDup, cNotSaved1
        push    eax
        stdcall StrCat, eax, [esi+TOpenFile.hFileName]
        stdcall StrCat, eax, cNotSaved2

        stdcall AskQuestion, eax, [hMainForm], answerYesNoCancel
        stdcall StrDel ; from the stack
        cmp     eax, IDNO
        je      .saveok

        cmp     eax, IDCANCEL
        je      .cancel

        stdcall SaveFile, esi
        test    eax, eax
        jz      .loop_not_saved

.saveok:
        invoke  DestroyWindow, [esi+TOpenFile.hEditor]
        mov     [esi+TOpenFile.hEditor], 0

.checkproj:
        cmp     [esi+TOpenFile.projectID], 0
        jne     .finishok

        stdcall FreeFStruct, esi
        mov     eax, cfrDestroyed
        pop     esi
        return

.finishok:
        xor     eax, eax
        mov     al, cfrClosed
        pop     esi
        return

.fault:
        xor     eax, eax
        mov     al, cfrFault
        pop     esi
        return

.cancel:
        xor     eax, eax
        mov     al, cfrCanceled
        pop     esi
        return
endp

iglobal
  cTitleWarning text 'Warning!'
  cNotSaved1  text 'The file "'
  cNotSaved2  text '" is not saved.', 13, 10,         \
                     'Do you want to save it now?'
endg



proc FocusFile, .ptrFile
begin
        push    esi

        mov     esi, [.ptrFile]
        test    esi, esi
        jz      .focusok

        cmp     [esi+TOpenFile.hEditor], 0
        jz      .finish

        invoke  SendMessageW, [esi+TOpenFile.hEditor], _CEM_FOCUSFILE, 0, 0

.focusok:

        stdcall SetCurrentFile, esi

.finish:
        pop     esi
        return
endp


proc SetCurrentFile, .ptrFile
begin
        push    [.ptrFile]
        pop     [ptrCurrentFile]
        return
endp




;----------------------------------------------------------
; Returns TRUE if the file allows some edit action,
; FALSE otherwise.
;----------------------------------------------------------
proc CanAction, .ptrFile, .action
begin
        invoke  GetFocus
        cmp     eax, [hLister]
        je      .check_action

        mov     eax, [.ptrFile]
        test    eax, eax
        jz      .finish
        mov     eax, [eax+TOpenFile.hEditor]
        test    eax, eax
        jz      .finish

.check_action:
        invoke  SendMessageW, eax, _CEM_CANACTION, 0, [.action]

.finish:
        return
endp



proc CommonAction, .ptrFile, .action
begin
        push    esi

        mov     esi, [.ptrFile]
        test    esi, esi
        jz      .finish
        cmp     [esi+TOpenFile.hEditor], 0
        je      .finish

        invoke  SendMessageW, [esi+TOpenFile.hEditor], _CEM_EDITACTION, 0, [.action]

.finish:
        pop     esi
        return
endp

;*******************************************************
; Returns TRUE if the file is modified after loading
;*******************************************************
proc GetModified, .ptrFile
begin
        mov     eax, [.ptrFile]
        test    eax, eax
        jz      .exit

        cmp     [eax+TOpenFile.fNeverSaved], 0  ; if the file is never saved.
        jne     .finish

        mov     eax, [eax+TOpenFile.hEditor]
        test    eax, eax
        jz      .exit

        invoke  SendMessageW, eax, _CEM_GETMODIFIED, 0, 0
        return

.finish:
        xor     eax, eax
        inc     eax
.exit:
        return
endp



;*****************************************
; Returns visual properties of the file.
; eax - string handle with screen title
; edx - index of the icon in the files
;       image list.
;*****************************************
proc GetScreenProperties, .ptrFile
begin
        push    edi
        mov     edi, [.ptrFile]
        test    edi,edi
        jz      .error

        stdcall ExtractFileName, [edi+TOpenFile.hFileName]
        stdcall StrExtract, [edi+TOpenFile.hFileName], eax, -1

        mov     edx, [edi+TOpenFile.ptrType]
        mov     edx, [edx+TFileType.iFileIcon]
        cmp     [edi+TOpenFile.projectID], 0
        jne     @f
        add     edx, 8
@@:
        pop     edi
        return

.error:
        mov     eax, edi
        pop     edi
        return
endp



;-------------------------------------
; Returns ptr to TOpenFile of given
; editor. IF there is no such editor
; registered, returns NULL
;-------------------------------------
proc GetEditorFile, .hEditor
begin
        push    ecx edx
        invoke  SendMessageW, [.hEditor], _CEM_GETFILE, 0, 0
        pop     edx ecx
        return
endp


;-----------------------------------
; Returns handle of AsmEdit window
; of the some editor. (if any)
; If editor does not contains
; AsmEdit control, returns NULL
;-----------------------------------
proc GetAsmEdit, .hEditor
begin
        invoke  SendMessageW, [.hEditor], _CEM_GETASMEDIT, 0, 0
        return
endp



proc GetFileAsmEdit, .ptrOpenFile
begin
        mov     eax, [.ptrOpenFile]
        test    eax, eax
        jz      .finish
        mov     eax, [eax+TOpenFile.hEditor]
        test    eax, eax
        jz      .finish

        invoke  SendMessageW, eax, _CEM_GETASMEDIT, 0, 0
.finish:
        return
endp




;-------------------------------------------------------
; Notifies editor, that file properties were changed
; ptrFile - TOpenFile structute of the file.
; fWho - fcEditor, fcProject, fcAll
;-------------------------------------------------------
proc FileChanged, .ptrFile, .fWho
begin
        push    ebx
        mov     ebx, [.ptrFile]
        test    ebx, ebx
        jz      .finish

        test    [.fWho], fcDontFree
        jnz     .dont_free

        stdcall CheckFileForFree, ebx
        test    eax, eax
        jnz     .finish

.dont_free:

        test    [.fWho], fcEditor
        jz      .notifyproject

        cmp     [ebx+TOpenFile.hEditor], 0
        je      .notifyproject

        invoke  SendMessageW, [ebx+TOpenFile.hEditor], _CEM_CHANGENOTIFY, 0, 0

.notifyproject:
        test    [.fWho], fcProject
        jz      .finish

        cmp     [eax+TOpenFile.projectID], NULL
        je      .finish

        invoke  SendMessageW, [hProjManager], PMM_FILECHANGED, eax, 0

.finish:
        pop     ebx
        return
endp

;--------------------------------------------
; Returns TRUE if the file was destroyed.
;--------------------------------------------
proc CheckFileForFree, .ptrFile
begin
        xor     eax, eax

        mov     ecx, [.ptrFile]
        jecxz   .finish

        mov     edx, [ecx+TOpenFile.hEditor]
        or      edx, [ecx+TOpenFile.projectID]
        jnz     .finish

        stdcall FreeFStruct, ecx
        xor     eax, eax
        inc     eax

.finish:
        return
endp



;---------------------------------------------------
; Enumerates all open files and calls [ptrCallBack]
; Format for callback is:
; proc MyCallBack, ptrFile, lParam
; if callback returns C=1, the enumeration stops
; and the functions returns value in eax.
;---------------------------------------------------
proc EnumOpenFiles, .ptrCallBack, .lParam
begin
        push    esi ecx

        mov     esi, [listFiles]
        mov     ecx, [esi+TArray.count]
        xor     eax, eax
        jecxz   .finish

        lea     esi, [esi+4*ecx-4+TArray.array]

.filesloop:
        push    esi ecx
        stdcall [.ptrCallBack], [esi], [.lParam]
        pop     ecx esi
        jc      .finish

        lea     esi, [esi-4]
        loop    .filesloop

.finish:
        pop     ecx esi
        return
endp

;------------------------------------------
; Returns the number of the files that are
; members of the currently open project.
;------------------------------------------
proc GetProjectFilesCount
begin
        xor     edx, edx
        stdcall EnumOpenFiles, _docountproject, 0
        mov     eax, edx
        return
endp

proc _docountproject, .ptrFile, .lParam
begin
        mov     eax, [.ptrFile]
        cmp     [eax+TOpenFile.projectID], NULL
        je      @f
        inc     edx
@@:
        clc
        return
endp



;------------------------------------------
; Returns the number of the files that are
; currently open for editing.
;------------------------------------------
proc GetEditFilesCount
begin
        xor     edx, edx
        stdcall EnumOpenFiles, _docountedit, 0
        mov     eax, edx
        return
endp


proc _docountedit, .ptrFile, .lParam
begin
        mov     eax, [.ptrFile]
        cmp     [eax+TOpenFile.hEditor], NULL
        je      @f
        inc     edx
@@:
        clc
        return
endp
