/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 *
 * Copyright (c) 2009, Dave Chapman
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 ****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <wchar.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <tchar.h>

#include "mtp_common.h"

#include "../MTP_DLL/MTP_DLL.h"


static int filesize(const char* filename);


int mtp_init(struct mtp_info_t* mtp_info)
{
   /* Fill the info struct with zeros - mainly for the strings */
    memset(mtp_info, 0, sizeof(struct mtp_info_t));

    return 0;

}

int mtp_finished(struct mtp_info_t* mtp_info)
{
    (void)mtp_info;

    return 0;
}

int mtp_scan(struct mtp_info_t* mtp_info)
{
    wchar_t name[256];
    wchar_t manufacturer[256];
    DWORD version;
    int num = 0;

    num = mtp_description(name, manufacturer, &version);

    wcstombs(mtp_info->manufacturer, manufacturer, 200);
    wcstombs(mtp_info->modelname, name, 200);

    sprintf(mtp_info->version, "%x", (unsigned int)version);
    return (num > 0) ? num : -1;

}

static void callback(unsigned int progress, unsigned int max)
{
    int percent = (progress * 100) / max;

    printf("[INFO] Progress: %u of %u (%d%%)\r", progress, max, percent);
    fflush(stdout);
}


int mtp_send_firmware(struct mtp_info_t* mtp_info, unsigned char* fwbuf,
                      int fwsize)
{
    HANDLE hTempFile;
    DWORD dwRetVal;
    DWORD dwBytesWritten;
    UINT uRetVal;
    TCHAR szTempName[1024];
    TCHAR lpPathBuffer[1024];
    BOOL fSuccess;
    wchar_t *tmp;
    int ret;

    (void)mtp_info;

    /* Get the path for temporary files */
    dwRetVal = GetTempPath(sizeof(lpPathBuffer), lpPathBuffer);
    if (dwRetVal > sizeof(lpPathBuffer) || (dwRetVal == 0))
    {
        fprintf(stderr, "[ERR]  GetTempPath failed (%d)\n", (int)GetLastError());
        return -1;
    }

    /* Create the temporary file */
    uRetVal = GetTempFileName(lpPathBuffer, TEXT("NKBIN"), 0, szTempName);
    if (uRetVal == 0)
    {
        fprintf(stderr, "[ERR]  GetTempFileName failed (%d)\n", (int)GetLastError());
        return -1;
    }

    /* Now create the file */
    hTempFile = CreateFile((LPTSTR) szTempName, // file name
                           GENERIC_READ | GENERIC_WRITE, // open r-w
                           0,                    // do not share
                           NULL,                 // default security
                           CREATE_ALWAYS,        // overwrite existing
                           FILE_ATTRIBUTE_NORMAL,// normal file
                           NULL);                // no template
    if (hTempFile == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "[ERR]  Could not create %s\n", szTempName);
        return -1;
    }

    fSuccess = WriteFile(hTempFile, fwbuf, fwsize, &dwBytesWritten, NULL);
    if (!fSuccess)
    {
        fprintf(stderr, "[ERR]  WriteFile failed (%d)\n", (int)GetLastError());
        return -1;
    }

    fSuccess = CloseHandle (hTempFile);
    if (!fSuccess)
    {
       fprintf(stderr, "[ERR]  CloseHandle failed (%d)\n", (int)GetLastError());
       return -1;
    }

    tmp = (LPWSTR)malloc(_tcslen(szTempName)*2+1);
    mbstowcs(tmp, (char*)szTempName, _tcslen(szTempName)*2+1);

    fprintf(stderr, "[INFO] Sending firmware...\n");
    if (mtp_sendnk(tmp, fwsize, &callback))
    {
        fprintf(stderr, "\n");
        fprintf(stderr, "[INFO] Firmware sent successfully\n");
        ret = 0;
    }
    else
    {
        fprintf(stderr, "\n");
        fprintf(stderr, "[ERR]  Error occured during sending.\n");
        ret = -1;
    }
    free(tmp);

    if (!DeleteFile(szTempName))
        fprintf(stderr,"[WARN] Could not remove temporary file %s\n",szTempName);

    return ret;
}


int mtp_send_file(struct mtp_info_t* mtp_info, const char* filename)
{
    wchar_t *fn;

    fn = (LPWSTR)malloc(strlen(filename)*2+1);
    mbstowcs(fn, filename, strlen(filename)*2+1);

    if (mtp_init(mtp_info) < 0) {
        fprintf(stderr,"[ERR]  Can not init MTP\n");
        return 1;
    }
    /* Scan for attached MTP devices. */
    if (mtp_scan(mtp_info) < 0)
    {
        fprintf(stderr,"[ERR]  No devices found\n");
        return 1;
    }

    fprintf(stderr, "[INFO] Sending firmware...\n");
    if (mtp_sendnk(fn, filesize(filename), &callback))
    {
        /* keep progress on screen */
        printf("\n");
        fprintf(stderr, "[INFO] Firmware sent successfully\n");
        return 0;
    }
    else
    {
        fprintf(stderr, "[ERR]  Error occured during sending.\n");
        return -1;
    }
    mtp_finished(mtp_info);
}


static int filesize(const char* filename)
{
    struct _stat sb;
    int res;

    res = _stat(filename, &sb);
    if(res == -1) {
        fprintf(stderr, "Error getting filesize!\n");
        return -1;
    }
    return sb.st_size;
}

/* Retrieve version of WMP as described in
 * http://msdn.microsoft.com/en-us/library/dd562731%28VS.85%29.aspx
 * Since we're after MTP support checking for the WMP6 key is not necessary.
 */
int mtp_wmp_version(void)
{
    DWORD ret;
    HKEY hk;
    DWORD type;
    DWORD enable = -1;
    DWORD enablelen = sizeof(DWORD);
    char buf[32];
    DWORD buflen = sizeof(buf);
    int version = 0;

    ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
        "SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{6BF52A52-394A-11d3-B153-00C04F79FAA6}",
        0, KEY_QUERY_VALUE, &hk);

    if(ret != ERROR_SUCCESS) {
        return 0;
    }
    type = REG_DWORD;
    ret = RegQueryValueExA(hk, "IsInstalled", NULL, &type, (LPBYTE)&enable, &enablelen);
    if(ret != ERROR_SUCCESS) {
        RegCloseKey(hk);
        return 0;
    }
    if(enable) {
        type = REG_SZ;
        ret = RegQueryValueExA(hk, "Version", NULL, &type, (LPBYTE)buf, &buflen);
    }
    if(ret == ERROR_SUCCESS) {
        /* get major version from registry value */
        buf[31] = '\0';
        version = atoi(buf);
    }
    RegCloseKey(hk);

    return version;
}

