/*
 *  directory scanner
 *
 *  Copyright (c) 2008, Veacheslav Patkov.
 *  aLL rigths reserved.
 *
 *  Licensed under GNU GPL v2
 *
 */

#include <windows.h>
#include <commctrl.h>
#include <shlobj.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "../../../pdk/include/plugin.h"
#include "rsrc.h"

#define DIRSCAN_VERSION "0.01"

#define BUFSIZE 1024

typedef struct {
    char *dname;
    unsigned int opt;
    int op_skip;
    char *filter;
    unsigned int level;
} fsearch_args;

HINSTANCE hinst;
HANDLE hthread;
HWND hDLG, hIDC_LIST, hIDC_FILTER_BOX, hIDC_STOP;
HWND hIDC_BROWSE, hIDC_SAVE, hIDC_CLEAR;
plug_interface *pif;
unsigned int item = 0;
char buf[BUFSIZE], dirname[MAX_PATH+8];

void addinfo(char *fname, char *name, char *ext, char *copyright)
{
    LV_ITEM lvi = {0};

    lvi.mask = LVIF_TEXT;
    lvi.iItem = item++;

    lvi.iSubItem = 0;
    lvi.pszText = fname;
    SendMessage(hIDC_LIST,LVM_INSERTITEM,0,(LPARAM)&lvi);
    lvi.iSubItem = 1;
    lvi.pszText = name;
    SendMessage(hIDC_LIST,LVM_SETITEM,0,(LPARAM)&lvi);
    lvi.iSubItem = 2;
    lvi.pszText = ext;
    SendMessage(hIDC_LIST,LVM_SETITEM,0,(LPARAM)&lvi);
    lvi.iSubItem = 3;
    lvi.pszText = copyright;
    SendMessage(hIDC_LIST,LVM_SETITEM,0,(LPARAM)&lvi);
}

// fixme: check it
int maskcmp(char *s, char *m)
{
    int cs, cm, i, k;

    for (i = k = 0; s[i] != 0 && m[k] != 0;)
        switch (cm = m[k++]) {
            case '?':
                i++;
                break;
            case '*':
                if (!(cm = m[k++]))
                    return 1;
                while ((cs = s[i++]) != cm)
                    if (!cs)
                        return 0;
                break;
            default:
                if (cm != s[i++])
                    return 0;
        }

    return !(s[i] || m[k]);
}

void fsearch(char *dname, unsigned int opt, int op_skip,
             char *filter, unsigned int level)
{
    HANDLE hfind = 0;
    WIN32_FIND_DATA fd = {0};
    char buf[MAX_PATH];

    if (!level) return;

    strcpy(buf,dname);
    if (buf[strlen(buf)-1] != '\\') strcat(buf,"\\");
    strcat(buf,"*.*");

    while (1) {
        if (!hfind) {
            if ((hfind = FindFirstFile(buf,&fd)) == INVALID_HANDLE_VALUE)
                return;
        } else if (!FindNextFile(hfind,&fd))
            return;

	if (fd.cFileName[0] == '.') {
	    if (!fd.cFileName[1])
	        continue;
	    if (fd.cFileName[1] == '.' && !fd.cFileName[2])
	        continue;
	}

        strcpy(buf,dname);
        if (buf[strlen(buf)-1] != '\\') strcat(buf,"\\");
        strcat(buf,(char *)&fd.cFileName);

        if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            fsearch(buf,opt,op_skip,filter,level-1);
        else {
            arinfo info;
            SetDlgItemText(hDLG,IDC_STATUS,buf);
            if (maskcmp((char *)&fd.cFileName,filter) &&
                !pif->processfile(buf,&info,opt))
                if (!(op_skip && !strcmp(info.name,"-- unknown --")))
                    addinfo(buf,info.name,info.extension,info.copyright);
        }
    }

    if (hfind)
        FindClose(hfind);
}

DWORD WINAPI fsearch_thread(fsearch_args *fa)
{
    fsearch(fa->dname,fa->opt,fa->op_skip,fa->filter,fa->level);
    EnableWindow(hIDC_STOP,0);
    EnableWindow(hIDC_BROWSE,1);
    EnableWindow(hIDC_SAVE,1);
    EnableWindow(hIDC_CLEAR,1);
    EnableWindow(hIDC_CLEAR,item);
    EnableWindow(hIDC_SAVE,item);
    SetDlgItemText(hDLG,IDC_STATUS,"Done.");
    return 0;
}

void dirscan(char *dname)
{
    int i, c, op_skip;
    unsigned int level = 1, opt = 0;
    static char buf[MAX_PATH], reclevel[4], filter[MAX_PATH];
    static fsearch_args fa;
    static DWORD thread_id;

    op_skip = IsDlgButtonChecked(hDLG,IDC_ONLYDETECTED) == BST_CHECKED;
    if (IsDlgButtonChecked(hDLG,IDC_FORCESFX) == BST_CHECKED)
        opt |= ARID_OP_FORCESFX;

    strcpy(buf,dname);
    strcat(buf,"\\");

    if (IsDlgButtonChecked(hDLG,IDC_FILTER) == BST_CHECKED)
        GetDlgItemText(hDLG,IDC_FILTER_BOX,filter,MAX_PATH-strlen(buf));
    else
        strcpy(filter,"*");

    if (IsDlgButtonChecked(hDLG,IDC_RECURSE) == BST_CHECKED) {
        if (GetDlgItemText(hDLG,IDC_RECLEVEL,reclevel,4)) {
            if (!strcmp(reclevel,"*"))
                level = (unsigned int)-1;
            else {
                for (i = 0; i < 4 && (c = reclevel[i]) != 0; i++)
                    if (c < '0' || c > '9')
                        goto error_level;
                level = strtoul(reclevel,0,10);
            }
        } else {
          error_level:
            MessageBox(0,"Incorrect recurse level","Error !",MB_ICONERROR);
            return;
        }
    }

    fa.dname = buf;
    fa.opt = opt;
    fa.op_skip = op_skip;
    fa.filter = filter;
    fa.level = level;

    hthread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)&fsearch_thread,&fa,0,&thread_id);
    EnableWindow(hIDC_STOP,1);
}

void savelist(char *fname)
{
    FILE *f;
    unsigned int i, k;
    LV_ITEM lvi = {0};

    if (!(f = fopen(fname,"w"))) {
        sprintf(buf,"Cannot create file `%s' !",fname);
        MessageBox(0,buf,"Error !",0);
        return;
    }

    lvi.pszText = buf;
    lvi.cchTextMax = MAX_PATH;

    for (i = 0; i < item; i++) {
        for (k = 0; k < 4; k++) {
            lvi.iSubItem = k;
            SendMessage(hIDC_LIST,LVM_GETITEMTEXT,i,(LPARAM)&lvi);
            if (*buf)
                fprintf(f,k ? " : %s" : "%s",buf);
        }
        fprintf(f,"\n");
    }

    fclose(f);
}

BOOL dialog_main(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam)
{
    static LV_COLUMN lvc;
    static BROWSEINFO bi;
    static OPENFILENAME ofn;
    static char oname[MAX_PATH];

    switch (umsg)
    {
        case WM_INITDIALOG:
            hDLG = hdlg;
            hIDC_STOP = GetDlgItem(hdlg,IDC_STOP);
            hIDC_LIST = GetDlgItem(hdlg,IDC_LIST);
            hIDC_BROWSE = GetDlgItem(hdlg,IDC_BROWSE);
            hIDC_SAVE = GetDlgItem(hdlg,IDC_SAVE);
            hIDC_CLEAR = GetDlgItem(hdlg,IDC_CLEAR);
            hIDC_FILTER_BOX = GetDlgItem(hdlg,IDC_FILTER_BOX);

            SendMessage(hdlg,WM_SETTEXT,0,(LPARAM)" [ Directory Scanner ]");
            CheckDlgButton(hdlg,IDC_FILTER,0);
            CheckDlgButton(hdlg,IDC_RECURSE,1);
            CheckDlgButton(hdlg,IDC_FORCESFX,0);
            CheckDlgButton(hdlg,IDC_ONLYDETECTED,0);

            SendMessage(hIDC_LIST,LVM_FIRST+54,0,0x120);
            memset(&lvc,0,sizeof(lvc));
            lvc.mask = LVCF_TEXT | LVCF_WIDTH;
            lvc.pszText = "File";
            lvc.cx = 200;
            SendMessage(hIDC_LIST,LVM_INSERTCOLUMN,0,(LPARAM)&lvc);
            lvc.pszText = "Archive Type";
            lvc.cx = 120;
            SendMessage(hIDC_LIST,LVM_INSERTCOLUMN,1,(LPARAM)&lvc);
            lvc.pszText = "Extension";
            lvc.cx = 70;
            SendMessage(hIDC_LIST,LVM_INSERTCOLUMN,2,(LPARAM)&lvc);
            lvc.pszText = "Copyright";
            lvc.cx = 120;
            SendMessage(hIDC_LIST,LVM_INSERTCOLUMN,3,(LPARAM)&lvc);

            SendMessage(hIDC_FILTER_BOX,CB_ADDSTRING,0,(LPARAM)"*.exe");
            SendMessage(hIDC_FILTER_BOX,CB_ADDSTRING,0,(LPARAM)"*.com");
            EnableWindow(hIDC_FILTER_BOX,0);

            break;

        case WM_COMMAND:
            switch (LOWORD(wparam)) {
                case IDC_BROWSE:
                    bi.hwndOwner = hdlg;
                    bi.pidlRoot = 0;
                    bi.pszDisplayName = buf;
                    bi.lpszTitle = "Choose directory for scan...";
                    bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN;
                    bi.lpfn = 0;
                    if(SHGetPathFromIDList(SHBrowseForFolder(&bi),dirname)) {
                        EnableWindow(hIDC_BROWSE,0);
                        EnableWindow(hIDC_SAVE,0);
                        EnableWindow(hIDC_CLEAR,0);
                        dirscan(dirname);
                    }
                    break;
                case IDC_STOP:
                    TerminateThread(hthread,0);
                    EnableWindow(hIDC_STOP,0);
                    EnableWindow(hIDC_BROWSE,1);
                    EnableWindow(hIDC_SAVE,1);
                    EnableWindow(hIDC_CLEAR,1);
                    SetDlgItemText(hDLG,IDC_STATUS,"Stopped.");
                    break;
                case IDC_SAVE:
                    ofn.lStructSize = sizeof(OPENFILENAME);
                    ofn.hwndOwner = hdlg;
                    ofn.lpstrFile = oname;
                    ofn.nMaxFile = MAX_PATH;
                    ofn.lpstrInitialDir = ".";
                    ofn.Flags = OFN_PATHMUSTEXIST | OFN_LONGNAMES | OFN_HIDEREADONLY;
                    ofn.lpstrTitle = " [ Directory Scanner ] - set output file...";
                    if (GetSaveFileName(&ofn)) {
                        savelist(oname);
                        sprintf(buf,"Log saved to: %s",oname);
                        SetDlgItemText(hDLG,IDC_STATUS,buf);
                    }
                    break;
                case IDC_CLEAR:
                    SendMessage(hIDC_LIST,LVM_DELETEALLITEMS,0,0);
                    EnableWindow(hIDC_CLEAR,0);
                    EnableWindow(hIDC_SAVE,0);
                    item = 0;
                    break;
                case IDC_ABOUT:
                    MessageBox(hdlg,
                               "Directory Scanner version " DIRSCAN_VERSION "\n"
                               "Copyright \xa9 2008 Veacheslav Patkov\n"
                               "All rights reserved.",
                               " [ Directory Scanner ] - about...",
                               MB_ICONINFORMATION);
                    break;
                case IDC_CLOSE:
                    goto end_dialog;
                case IDC_FILTER:
                    EnableWindow(hIDC_FILTER_BOX,IsDlgButtonChecked(hDLG,
                                 IDC_FILTER) == BST_CHECKED);
                    break;
                case IDC_RECURSE:
                    EnableWindow(GetDlgItem(hdlg,IDC_RECLEVEL),
                                 IsDlgButtonChecked(hDLG,IDC_RECURSE) == BST_CHECKED);
                    break;
            }
            break;

        case WM_CLOSE:
          end_dialog:
            EndDialog(hdlg,0);
            break;
    }
    return 0;
}

char *pname(void)
{
    return "Directory Scanner";
}

void pmain(plug_interface *pi)
{
    pif = pi;
    DialogBoxParam(hinst,MAKEINTRESOURCE(IDD_DIALOG),pi->hwnd,(DLGPROC)&dialog_main,0);
    return;
}

int __stdcall dllentry(HINSTANCE hinstance, DWORD reason, LPVOID reserved)
{
    hinst = hinstance;
    return 1;
}
