#include <windows.h>
#include <aygshell.h>
#include <string>
#include "stringutils.h"
#include "debug.h"
#include "args.h"

// see c:/Program Files/Windows Mobile 6 SDK/Smartphone/Include/Armv4i/aygshell.h
//     z:/sources/wm500aku/PUBLIC/SHELLW/SDK/INC/aygshell.h
//
// notification update mask
// #define SHNUM_PRIORITY     0x0001
// #define SHNUM_DURATION     0x0002
// #define SHNUM_ICON         0x0004
// #define SHNUM_HTML         0x0008
// #define SHNUM_TITLE        0x0010
// #define SHNUM_SOFTKEYS     0x0020
// #define SHNUM_TODAYKEY     0x0040
// #define SHNUM_TODAYEXEC    0x0080
// #define SHNUM_SOFTKEYCMDS  0x0100
// #define SHNUM_FLAGS        0x0200
//
//
//
//
bool parseguid(CLSID& clsid, const std::string & str)
{
    const std::string hexchars= "0123456789abcdefABCDEF";
    int n= sscanf(str.c_str(), "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 
            &clsid.Data1, &clsid.Data2, &clsid.Data3,
            clsid.Data4+0, clsid.Data4+1, clsid.Data4+2, clsid.Data4+3, clsid.Data4+4, clsid.Data4+5, clsid.Data4+6, clsid.Data4+7);
    return n==11;
}

void init_shn(SHNOTIFICATIONDATA*shn)
{
    memset(shn, 0, sizeof(*shn));
    shn->cbStruct= sizeof(*shn);
}
std::string ShnFlagsString(DWORD f)
{
    StringList l;
    if (f&SHNF_STRAIGHTTOTRAY) l.push_back("STRAIGHTTOTRAY");
    if (f&SHNF_CRITICAL      ) l.push_back("CRITICAL");
    if (f&SHNF_FORCEMESSAGE  ) l.push_back("FORCEMESSAGE");
    if (f&SHNF_DISPLAYON     ) l.push_back("DISPLAYON");
    if (f&SHNF_SILENT        ) l.push_back("SILENT");
    if (f&SHNF_HASMENU       ) l.push_back("HASMENU");
    if (f&SHNF_TITLETIME     ) l.push_back("TITLETIME");
    if (f&SHNF_SPINNERS      ) l.push_back("SPINNERS");
    if (f&SHNF_ALERTONUPDATE ) l.push_back("ALERTONUPDATE");
    if (f&SHNF_WANTVKTTALK   ) l.push_back("WANTVKTTALK");

    DWORD all=SHNF_STRAIGHTTOTRAY|SHNF_CRITICAL|SHNF_FORCEMESSAGE|SHNF_DISPLAYON|SHNF_SILENT|SHNF_HASMENU|SHNF_TITLETIME|SHNF_SPINNERS|SHNF_ALERTONUPDATE|SHNF_WANTVKTTALK;
    if (f&~all) {
        l.push_back(stringformat("SHNFLAG_%08lx", f&~all));
    }
    return JoinStringList(l, ",");
}
void dump_shn(SHNOTIFICATIONDATA* shn)
{
    if (shn->cbStruct!=sizeof(*shn))
        debug("NOTE: structsize = 0x%x ( expected 0x%x )\n", shn->cbStruct, sizeof(*shn));
    debug("id        : %08lx\n", shn->dwID);
    debug("npPriority: %s\n", shn->npPriority==SHNP_INFORM?"INFORM":shn->npPriority==SHNP_ICONIC?"ICONIC":stringformat("%08lx", shn->npPriority).c_str());
    debug("csDuration: %08lx\n", shn->csDuration);
    debug("hicon     : %08lx\n", shn->hicon);
    debug("grfFlags  : %s\n", ShnFlagsString(shn->grfFlags).c_str());
    debug("clsid     : %08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 
            shn->clsid.Data1, shn->clsid.Data2, shn->clsid.Data3,
            shn->clsid.Data4[0], shn->clsid.Data4[1], shn->clsid.Data4[2], shn->clsid.Data4[3], shn->clsid.Data4[4], shn->clsid.Data4[5], shn->clsid.Data4[6], shn->clsid.Data4[7]);
    debug("hwndSink  : %08lx\n", shn->hwndSink);
    debug("pszHTML   : %ls\n", shn->pszHTML);
    debug("pszTitle  : %ls\n", shn->pszTitle);
    debug("lParam    : %08lx\n", shn->lParam);
    if (shn->grfFlags&SHNF_HASMENU) {
        debug("skm       : \n");
        debug("    hMenu    : %08lx\n", shn->skm.hMenu);
        debug("    cskc     : %08lx\n", shn->skm.cskc);
        for (DWORD i=0 ; i<shn->skm.cskc ; i++)
            debug("    prgskc[%2d]   : %08lx %s\n", i, shn->skm.prgskc[i].wpCmd, ShnFlagsString(shn->skm.prgskc[i].grfFlags).c_str());
    }
    else {
        for (int i=0 ; i<2 ; i++) {
            debug("rgskn%d    : \n", i);
            debug("    pszTitle    : %ls\n", shn->rgskn[i].pszTitle);
            debug("    wpCmd       : %08lx\n", shn->rgskn[i].skc.wpCmd);
            debug("    grfFlags    : %s\n", ShnFlagsString(shn->rgskn[i].skc.grfFlags).c_str());
        }
    }
    debug("pszTodaySK: %ls\n",   shn->pszTodaySK);
    debug("pszTodayExec: %ls\n", shn->pszTodayExec);

}

// value for flags:
// SHNF_STRAIGHTTOTRAY  0x0001
// SHNF_CRITICAL        0x0002 // Critical information - highlights the border and title of the bubble.
// SHNF_FORCEMESSAGE    0x0008 // Force the message (bubble) to display even if settings says not to.
// SHNF_DISPLAYON       0x0010 // Force the display to turn on for notification.
// SHNF_SILENT          0x0020 // Force the notification to be silent and not vibrate, regardless of Settings
// SHNF_HASMENU         0x0040 // Softkey bar is created from an HMENU passed in skm structure 
// SHNF_TITLETIME       0x0080 // Draw the current time with the title
// SHNF_SPINNERS        0x0100 // A notification with "stack" support
// SHNF_ALERTONUPDATE   0x0200 // RE-play physical alerts on an update
// SHNF_WANTVKTTALK     0x0400 //Capture the VK_TTALK button and forward it to the notification's sink window
void usage()
{
    debug("options:\n");
    debug("  -a        add\n");
    debug("  -r        remove\n");
    debug("  -g        get info\n");
    debug("  -u MASK   update\n");

    debug("  -c GUID   specify clsid to use\n");
    debug("  -f FLAGS  specify flags\n");
    debug("  -i ID     specify id\n");
    debug("  -t 'title'\n");
    debug("  -h 'html'\n");
    debug("  -d TIME   specify duration\n");
    debug("  -l PARAM  specify lparam\n");
    debug("  -p [1|2]  specify priority\n");
    debug("  -s todaysk\n");
    debug("  -x tdexec\n");
    debug("  -n dll:rsid  specify icon\n");
    debug("  -w HWND   specify hwnd sink\n");
}
int WINAPI WinMain( HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPTSTR    lpCmdLine,
                   int       nCmdShow)
{
    DebugSetLogfile("tstnot.log");

    debug("\n");
    StringList args;
    if (!SplitString(ToString(lpCmdLine), args, false))
    {
        error("Error in commandline");
        return false;
    }

    int action=0;
#define ACT_ADD 1
#define ACT_UPD 2
#define ACT_REM 3
#define ACT_GET 4

    DWORD updmask=0;
    SHNOTIFICATIONDATA shn;
    std::string title;
    std::string html;
    std::string todaysk;
    std::string todayexec;
    DWORD priority=0;

    init_shn(&shn);
// examples:
//   tstnot -a -i 1234 -c 28C86349-5F9E-4c21-A4BB-C18B4251D3BB
//   tstnot -g -c 28C86349-5F9E-4c21-A4BB-C18B4251D3BB
    for (StringList::iterator i= args.begin() ; i!=args.end() ; ++i)
    {
        std::string& arg= *i;
        if (arg[0]=='-') switch(arg[1])
        {
            case 'a': action= ACT_ADD; break;
            case 'r': action= ACT_REM; break;
            case 'g': action= ACT_GET; break;
            case 'u': HANDLESTLULOPTION(updmask, DWORD);
                      action= ACT_UPD;
                      break;

            case 'c': parseguid(shn.clsid, arg[2] ? arg.substr(2) : i+1!=args.end() ? *(++i) : ""); break;
            case 'f': HANDLESTLULOPTION(shn.grfFlags, DWORD); break;
            case 'i': HANDLESTLULOPTION(shn.dwID, DWORD); break;
            case 't': HANDLESTLSTRCOMBIOPTION(title);
                      shn.pszTitle= _tcsdup(ToWString(title).c_str());
                      break;
            case 'h': HANDLESTLSTRCOMBIOPTION(html);
                      shn.pszHTML= _tcsdup(ToWString(html).c_str());
                      break;
            case 'd': HANDLESTLULOPTION(shn.csDuration, DWORD); break;
            case 'l': HANDLESTLULOPTION(shn.lParam, DWORD); break;
            case 'p': HANDLESTLULOPTION(priority, DWORD); break;
            case 's': HANDLESTLSTRCOMBIOPTION(todaysk);
                      shn.pszTodaySK= _tcsdup(ToWString(todaysk).c_str());
                      break;
            case 'x': HANDLESTLSTRCOMBIOPTION(todayexec);
                      shn.pszTodayExec= _tcsdup(ToWString(todayexec).c_str());
                      break;
            case 'w': HANDLESTLULOPTION(shn.hwndSink, HWND); break;
            case 'n': // todo: get icon handle
            default:
                      usage();

        }
    }
    if (priority==1)
        shn.npPriority= SHNP_INFORM;
    else if (priority==2)
        shn.npPriority= SHNP_ICONIC;
    else
        debug("unsupported priority: %d\n", priority);


    LRESULT lr;
    switch(action)
    {
        case ACT_ADD:
            debug("adding notification\n");
            lr= SHNotificationAdd(&shn);
            if (lr) {
                debug("ERROR SHNotificationAdd: %08lx\n", lr);
            }
            break;
        case ACT_UPD:
            debug("updating notification\n");
            lr= SHNotificationUpdate(updmask, &shn);
            if (lr) {
                debug("ERROR SHNotificationUpdate: %08lx\n", lr);
            }
            break;
        case ACT_REM:
            debug("removing notification\n");
            lr= SHNotificationRemove(&shn.clsid, shn.dwID);
            if (lr) {
                debug("ERROR SHNotificationRemove: %08lx\n", lr);
            }
            break;
        case ACT_GET:
            debug("getting notification info\n");
            lr= SHNotificationGetData(&shn.clsid, shn.dwID, &shn);
            if (lr) {
                debug("ERROR SHNotificationGetData: %08lx\n", lr);
            }
            dump_shn(&shn);
            break;
        default:
            debug("unknown action: %d\n", action);
    }
    return 0;
}
