/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <wchar.h>
#include <windows.h>
#include <winnls.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "StringUtils.h"



const char *  JVM_NOT_FOUND_PROP           = "nlw.jvm.notfoundmessage";
const char *  JVM_USER_DEFINED_ERROR_PROP  = "nlw.jvm.usererror";
    
const char * JVM_UNSUPPORTED_VERSION_PROP = "nlw.jvm.unsupportedversion";
const char * NOT_ENOUGH_FREE_SPACE_PROP   = "nlw.freespace";
const char * CANT_CREATE_TEMP_DIR_PROP    = "nlw.tmpdir";
const char * INTEGRITY_ERROR_PROP         = "nlw.integrity";
const char * OUTPUT_ERROR_PROP            = "nlw.output.error";
const char * JAVA_PROCESS_ERROR_PROP      = "nlw.java.process.error";
const char * EXTERNAL_RESOURE_LACK_PROP   = "nlw.missing.external.resource";
const char * BUNDLED_JVM_EXTRACT_ERROR_PROP = "nlw.bundled.jvm.extract.error";
const char * BUNDLED_JVM_VERIFY_ERROR_PROP  = "nlw.bundled.jvm.verify.error";

const char * ARG_OUTPUT_PROPERTY          = "nlw.arg.output";
const char * ARG_JAVA_PROP                = "nlw.arg.javahome";
const char * ARG_DEBUG_PROP               = "nlw.arg.verbose";
const char * ARG_TMP_PROP                 = "nlw.arg.tempdir";
const char * ARG_CPA_PROP                 = "nlw.arg.classpatha";
const char * ARG_CPP_PROP                 = "nlw.arg.classpathp";
const char * ARG_EXTRACT_PROP             = "nlw.arg.extract";
const char * ARG_DISABLE_SPACE_CHECK      = "nlw.arg.disable.space.check";
const char * ARG_LOCALE_PROP              = "nlw.arg.locale";
const char * ARG_SILENT_PROP              = "nlw.arg.silent";
const char * ARG_HELP_PROP                = "nlw.arg.help";

const char * MSG_CREATE_TMPDIR     = "nlw.msg.create.tmpdir";
const char * MSG_EXTRACT_DATA      = "nlw.msg.extract";
const char * MSG_JVM_SEARCH        = "nlw.msg.jvmsearch";
const char * MSG_SET_OPTIONS       = "nlw.msg.setoptions";
const char * MSG_RUNNING           = "nlw.msg.running";
const char * MSG_TITLE             = "nlw.msg.title";
const char * MSG_MESSAGEBOX_TITLE  = "nlw.msg.messagebox.title";
const char * MSG_PROGRESS_TITLE    = "nlw.msg.progress.title";

const char * EXIT_BUTTON_PROP     = "nlw.msg.button.error" ;   
const char * MAIN_WINDOW_TITLE     = "nlw.msg.main.title" ;




//adds string the the initial string and modifies totalWCHARs and capacity
// initial - the beginning of the string
// size - pointer to the value that contains the length of the initial string
//        It is modified to store returning string length
// addString - additional string
//


void freeI18NMessages(LauncherProperties * props) {
    if(props->i18nMessages!=NULL) {
        DWORD i=0;
        
        for(i=0;i<props->I18N_PROPERTIES_NUMBER;i++) {
            FREE(props->i18nMessages->properties[i]);
            FREE(props->i18nMessages->strings[i]);
        }        
        FREE(props->i18nMessages->properties);        
        FREE(props->i18nMessages->strings);        
        FREE(props->i18nMessages);
    }
}

char * searchA(const char * wcs1, const char * wcs2) {
    char *cp = (char *) wcs1;
    char *s1, *s2;
    
    if ( !*wcs2) {
        return (char *)wcs1;
    }
    
    while (*cp) {
        s1 = cp;
        s2 = (char *) wcs2;
        
        while ( *s1 && *s2 && !(*s1-*s2) ) {
            s1++, s2++;
        }
        if (!*s2) {
            return(cp);
        }
        cp++;
    }
    return(NULL);
}

WCHAR * searchW(const WCHAR * wcs1, const WCHAR * wcs2) {
    WCHAR *cp = (WCHAR *) wcs1;
    WCHAR *s1, *s2;
    
    if ( !*wcs2) {
        return (WCHAR *)wcs1;
    }
    
    while (*cp) {
        s1 = cp;
        s2 = (WCHAR *) wcs2;
        
        while ( *s1 && *s2 && !(*s1-*s2) ) {
            s1++, s2++;
        }
        if (!*s2) {
            return(cp);
        }
        cp++;
    }
    return(NULL);
}

void getI18nPropertyTitleDetail(LauncherProperties * props, const char * name, WCHAR ** title, WCHAR ** detail) {
    const WCHAR * prop = getI18nProperty(props,name);    
    WCHAR * detailStringSep = searchW(prop, L"\n");
    
    if(detailStringSep == NULL) {
        *title = appendStringW(NULL, prop);
        *detail = NULL;
    } else {
        DWORD dif = getLengthW(prop) - getLengthW(detailStringSep);
        *title = appendStringNW(NULL, 0, prop, dif);
        *detail = appendStringW(NULL, prop + (dif + 1));
    }    
}
const WCHAR * getI18nProperty(LauncherProperties * props, const char * name) {
    if(name==NULL) return NULL;
    if(name!=NULL && props->i18nMessages!=NULL) {
        DWORD i;
        for(i=0;i<props->I18N_PROPERTIES_NUMBER;i++) {
            char * pr = props->i18nMessages->properties[i];
            if(pr!=NULL) { // hope so it`s true
                if(lstrcmpA(name, pr)==0) {
                    return props->i18nMessages->strings[i];
                }
            }
        }
    }
    return getDefaultString(name);
}

WCHAR * getDefaultString(const char *name) {
    if(lstrcmpA(name, JVM_NOT_FOUND_PROP)==0) {
        return L"Can`t find suitable JVM. Specify it with %s argument";
    } else if(lstrcmpA(name, NOT_ENOUGH_FREE_SPACE_PROP)==0) {
        return L"Not enought free space at %s";
    } else if(lstrcmpA(name, CANT_CREATE_TEMP_DIR_PROP)==0) {
        return L"Can`t create temp directory %s";
    } else if(lstrcmpA(name, INTEGRITY_ERROR_PROP)==0) {
        return L"Integrity error. File %s is corrupted";
    } else if(lstrcmpA(name, JVM_USER_DEFINED_ERROR_PROP)==0) {
        return L"Can`t find JVM at %s";
    } else if(lstrcmpA(name, JVM_UNSUPPORTED_VERSION_PROP)==0) {
        return L"Unsupported JVM at %s";
    } else if(lstrcmpA(name, OUTPUT_ERROR_PROP)==0) {
        return L"Can`t create file %s.\nError: %s";
    } else if(lstrcmpA(name, JAVA_PROCESS_ERROR_PROP)==0) {
        return L"Java error:\n%s";
    } else if(lstrcmpA(name, ARG_JAVA_PROP)==0) {
        return L"%s Using specified JVM";
    }  else if(lstrcmpA(name, ARG_OUTPUT_PROPERTY)==0) {
        return L"%s Output all stdout/stderr to the file";
    } else if(lstrcmpA(name, ARG_DEBUG_PROP)==0) {
        return L"%s Use verbose output";
    } else if(lstrcmpA(name, ARG_TMP_PROP)==0) {
        return L"%s Use specified temporary dir for extracting data";
    } else if(lstrcmpA(name, ARG_CPA_PROP)==0) {
        return L"%s Append classpath";
    }  else if(lstrcmpA(name, ARG_CPP_PROP)==0) {
        return L"%s Prepend classpath";
    } else if(lstrcmpA(name, ARG_EXTRACT_PROP)==0) {
        return L"%s Extract all data";
    }  else if(lstrcmpA(name, ARG_HELP_PROP)==0) {
        return L"%s Using this help";
    } else if(lstrcmpA(name, ARG_DISABLE_SPACE_CHECK)==0) {
        return L"%s Disable free space check";
    } else if(lstrcmpA(name, ARG_LOCALE_PROP )==0) {
        return L"%s Use specified locale for messagess";       
    } else if(lstrcmpA(name, ARG_SILENT_PROP )==0) {
        return L"%s Run silently";       
    } else if(lstrcmpA(name, MSG_CREATE_TMPDIR)==0) {
        return L"Creating tmp directory...";
    } else if(lstrcmpA(name, MSG_EXTRACT_DATA)==0) {
        return L"Extracting data...";
    } else if(lstrcmpA(name, MSG_JVM_SEARCH)==0) {
        return L"Finding JVM...";
    } else if(lstrcmpA(name, MSG_RUNNING)==0) {
        return L"Running JVM...";
    } else if(lstrcmpA(name, MSG_SET_OPTIONS)==0) {
        return L"Setting command options...";
    } else if(lstrcmpA(name, MSG_MESSAGEBOX_TITLE)==0) {
        return L"Message";
    } else if(lstrcmpA(name, MSG_PROGRESS_TITLE)==0) {
        return L"Running";
    } else if(lstrcmpA(name, EXIT_BUTTON_PROP)==0) {
        return L"Exit";
    } else if(lstrcmpA(name, MAIN_WINDOW_TITLE)==0) {
        return L"NBI Launcher";
    } else if(lstrcmpA(name, EXTERNAL_RESOURE_LACK_PROP)==0) {
        return L"Can`t run launcher\nThe following file is missing : %s";
    } else if(lstrcmpA(name, BUNDLED_JVM_EXTRACT_ERROR_PROP)==0) {
        return L"Can`t run prepare bundled JVM";
    } else if(lstrcmpA(name, BUNDLED_JVM_VERIFY_ERROR_PROP)==0) {
        return L"Can`t run verify bundled JVM";
    }
    return NULL;   
}

DWORD getLengthA(const char * message) {
    return (message!=NULL) ? lstrlenA(message) : 0;
}

DWORD getLengthW(const WCHAR * message) {
    return (message!=NULL) ? lstrlenW(message) : 0;
}

//adds string the the initial string
char *  appendStringN(char *  initial, DWORD initialLength, const char * addString, DWORD addStringLength) {
    DWORD length = initialLength + addStringLength + 1;
    if (length > 1) {
        char * tmp = newpChar(length+1);
        DWORD i=0;
        if(initialLength!=0) {
            for(i=0;i<initialLength;i++) {
                tmp[i]=initial[i];
            }
            FREE(initial);
        }
        for(i=0;i<addStringLength;i++) {
            tmp[i+initialLength] = addString[i];
        }
        
        return tmp;
    } else {
        return NULL;
    }
}

char * appendString(char *  initial, const char * addString) {
    return appendStringN(initial, getLengthA(initial), addString, getLengthA(addString));
}


//adds string the the initial string
WCHAR *  appendStringNW(WCHAR *  initial, DWORD initialLength, const WCHAR * addString, DWORD addStringLength) {
    
    DWORD length = initialLength + addStringLength + 1;
    if(length>1) {
        
        WCHAR * tmp = newpWCHAR(length+1);
        DWORD i=0;
        
        if(initialLength!=0) {
            for(i=0;i<initialLength;i++) {
                tmp[i]=initial[i];
            }
            FREE(initial);
        }
        
        for(i=0;i<addStringLength;i++) {
            tmp[i+initialLength] = addString[i];
        }
        tmp[length] = 0;
        
        return tmp;
    } else {
        
        return NULL;
    }
}

WCHAR * appendStringW(WCHAR *  initial, const WCHAR * addString) {
    return appendStringNW(initial, getLengthW(initial), addString, getLengthW(addString));
}

WCHAR * escapeString(const WCHAR * string) {
    DWORD length = getLengthW(string);
    WCHAR *result = newpWCHAR(length * 2 + 4);
    DWORD i=0;
    DWORD r=0;
    DWORD bsCounter = 0;    
    int quoting = searchW(string, L" ") || searchW(string, L"\t");
    if(quoting) {
        result[r++] = '\"';
    }
    for(i=0;i<length;i++) {
        const WCHAR c = string[i];
        switch(c) {
            case '\\' :
                bsCounter++;
                break;
            case '"':
                bsCounter++;
                do {
                    result[r++] = '\\';
                    bsCounter--;
                } while(bsCounter>0);
            default:
                bsCounter = 0;                
                break;
        }
        result[r++] = c;
    }
    if(quoting) {
        while(bsCounter>0) {
            result[r++] = '\\';
            bsCounter--;
        }
        result[r++] = '\"';
    }
    result[r] = '\0';    
    return result;     
}

char * DWORDtoCHARN(DWORD value, int fillZeros) {
    int digits = 0;
    DWORD tmpValue = value;
    int i = 0;
    char * str;

    do {
        digits++;
        tmpValue = tmpValue / 10;
    } while(tmpValue!=0);
    tmpValue = value;
    if(digits < fillZeros) {
        digits = fillZeros;
    }
    str = (char*) LocalAlloc(LPTR, sizeof(char)*(digits +1));
    str[digits] = '\0';
    for(i=0;i<digits;i++) {
        str [digits - i - 1] = '0' + (char) (tmpValue - ((tmpValue / 10) * 10));
        tmpValue = tmpValue / 10;
    }
    return str;
}

WCHAR * DWORDtoWCHARN(DWORD value, int fillZeros) {
    int digits = 0;
    DWORD tmpValue = value;
    int i = 0;
    WCHAR * str;

    do {
        digits++;
        tmpValue = tmpValue / 10;
    } while(tmpValue!=0);
    tmpValue = value;
    if(digits < fillZeros) {
        digits = fillZeros;
    }
    
    str = (WCHAR*) LocalAlloc(LPTR, sizeof(WCHAR)*(digits +1));
    str[digits] = L'\0';
    for(i=0;i<digits;i++) {
        str [digits - i - 1] = L'0' + (WCHAR) (tmpValue - ((tmpValue / 10) * 10));
        tmpValue = tmpValue / 10;
    }
    return str;
}
WCHAR * DWORDtoWCHAR(DWORD value) {
    return DWORDtoWCHARN(value, 0);
}

char * DWORDtoCHAR(DWORD value) {
    return DWORDtoCHARN(value,0);
}

char * long2charN(long value, int fillZeros) {
    int digits = 0;
    long tmpValue = value;
    int i = 0;
    char * str;
    
    do {
        digits++;
        tmpValue = tmpValue / 10;
    } while(tmpValue!=0);
    tmpValue = value;
    if(digits < fillZeros) {
        digits = fillZeros;
    }
    str = (char*) LocalAlloc(LPTR, sizeof(char)*(digits +1));
    str[digits] = '\0';
    for(i=0;i<digits;i++) {
        str [digits - i - 1] = '0' + (char) (tmpValue - ((tmpValue / 10) * 10));
        tmpValue = tmpValue / 10;
    }
    return str;
}

char * long2char(long value) {
    return long2charN(value,0);
}

char * word2charN(WORD value, int fillZeros) {
    int digits = 0;
    WORD tmpValue = value;
    int i = 0;
    char * str;

    do {
        digits++;
        tmpValue = tmpValue / 10;
    } while(tmpValue!=0);
    tmpValue = value;
    if(digits < fillZeros) {
        digits = fillZeros;
    }
    str = (char*) LocalAlloc(LPTR, sizeof(char)*(digits +1));
    str[digits] = '\0';
    for(i=0;i<digits;i++) {
        str [digits - i - 1] = '0' + (char) (tmpValue - ((tmpValue / 10) * 10));
        tmpValue = tmpValue / 10;
    }
    return str;
}

char * word2char(WORD value) {
    return word2charN(value,0);
}

char * int64ttoCHAR(int64t* value) {
    if(value->High==0) {
        return DWORDtoCHAR(value->Low);
    } else {
        char * high = DWORDtoCHAR(value->High);
        char * low  = DWORDtoCHAR(value->Low);
        DWORD highLength = getLengthA(high);
        DWORD lowLength  = getLengthA(low);

        char * str = newpChar(highLength + lowLength + 9);
        DWORD i = 0;
        str[0] = '[';
        str[1] = 'H';
        str[2] = ']';
        str[3] = '[';
        str[4] = 'L';
        str[5] = ']';
        str[6] = '=';

        for(i = 0; i < highLength; i++) {
            str [7 + i] = high[i];
        }
        str [7 + highLength] =L',';
        for(i = 0; i < lowLength;i++) {
            str [8 + highLength + i] = low[i];
        }
        str [8 + highLength + lowLength] = '\0';
        FREE(high);
        FREE(low);
        return str;
    }
}
WCHAR * int64ttoWCHAR(int64t*value) {
    if(value->High==0) {
        return DWORDtoWCHAR(value->Low);
    } else {
        WCHAR * high = DWORDtoWCHAR(value->High);
        WCHAR * low  = DWORDtoWCHAR(value->Low);
        DWORD highLength = getLengthW(high);
        DWORD lowLength  = getLengthW(low);

        WCHAR * str = newpWCHAR(highLength + lowLength + 9);
        DWORD i = 0;
        str[0] = L'[';
        str[1] = L'H';
        str[2] = L']';
        str[3] = L'[';
        str[4] = L'L';
        str[5] = L']';
        str[6] = L'=';

        for(i = 0; i < highLength;i++) {
            str [7 + i] = high[i];
        }
        str [7 + highLength] = L',';
        for(i = 0; i < lowLength;i++) {
            str [8 + highLength + i] = low[i];
        }
        str [8 + highLength + lowLength] = L'\0';
        FREE(high);
        FREE(low);
        return str;
    }
}

void freeStringList(StringListEntry **ss) {
    while ( (*ss) !=NULL) {
        StringListEntry * tmp = (*ss)->next;
        FREE((*ss)->string);
        FREE((*ss));
        * ss = tmp;
    }
}

DWORD inList(StringListEntry * top, WCHAR * str) {    
    StringListEntry * tmp = top;
    while(tmp!=NULL) {
        if(lstrcmpW(tmp->string, str)==0) {
            return 1;            
        }
        tmp = tmp->next;
    }
    return 0;
}

StringListEntry * addStringToList(StringListEntry * top, WCHAR * str) {
    StringListEntry * ss = (StringListEntry*) LocalAlloc(LPTR,sizeof(StringListEntry));    
    ss->string = appendStringW(NULL, str);
    ss->next   = top;
    return ss;
}


DWORD getLineSeparatorNumber(char *str) {
    DWORD result = 0;
    char *ptr = str;
    if(ptr!=NULL) {
        while((ptr = searchA(ptr, "\n"))!=NULL) {
            ptr++;
            result++;
            if(ptr==NULL)  break;
        }
    }
    return result;
}

char *toCharN(const WCHAR * string, DWORD n) {    
    DWORD len = 0;
    DWORD length = 0;
    char * str = NULL;
    if(string==NULL) return NULL;
    //static DWORD excludeCodepages [] = { 50220, 50221, 50222, 50225, 50227, 50229, 52936, 54936, 57002,  57003, 57004, 57005, 57006, 57007, 57008, 57009, 57010, 57011, 65000, 42};
    //int symbols = 0;
    len = getLengthW(string);    
    if(n<len) len = n;
    length = WideCharToMultiByte(CP_ACP, 0, string, len, NULL, 0, 0, NULL);
    str = newpChar(length+1);
    WideCharToMultiByte(CP_ACP, 0, string, len, str, length, 0, NULL);
    return str;
}

char * toChar(const WCHAR * string) {
    return toCharN(string, getLengthW(string));
}

WCHAR *createWCHAR(SizedString * sz) {
    char * str = sz->bytes;
    DWORD len = sz->length;
    int unicodeFlags;    
    DWORD i;
    char * string = NULL;
    char * ptr = NULL;
    WCHAR * wstr = NULL;
    if(str==NULL) return NULL;
    //static DWORD excludeCodepages [] = { 50220, 50221, 50222, 50225, 50227, 50229, 52936, 54936, 57002,  57003, 57004, 57005, 57006, 57007, 57008, 57009, 57010, 57011, 65000, 42};
    
    string = appendStringN(NULL, 0 , str, len);    
    ptr = string;
    unicodeFlags = -1 ;
    if(len>=2) {
        BOOL hasBOM        = (*ptr == '\xFF' && *(ptr+1) == '\xFE');
        BOOL hasReverseBOM = (*ptr == '\xFE' && *(ptr+1) == '\xFF');
        
        if (IsTextUnicode(string, len, &unicodeFlags) || hasBOM || hasReverseBOM) {
            //text is unicode
            len-= 2;
            ptr+= 2;
            if (unicodeFlags & IS_TEXT_UNICODE_REVERSE_SIGNATURE || hasReverseBOM) {
                //we need to change bytes order
                char c;
                for (i = 0 ; i < len/2 ; i++) {
                    c = ptr [2 * i] ;
                    ptr [2 * i] = ptr [2 * i + 1] ;
                    ptr [2 * i + 1] = c;
                }
            }
        }
        
    }
    wstr = newpWCHAR(len/2+1);
    
    for(i=0;i<len/2;i++) {
        ptr[2*i] = (ptr[2*i]) & 0xFF;
        ptr[2*i+1] = (ptr[2*i+1])& 0xFF;
        wstr[i] = ((unsigned char) ptr[2*i]) + (((unsigned char)ptr[2*i+1])  << 8);
    }
    
    FREE(string);
    return wstr;
}
WCHAR *toWCHARn(char * str, DWORD n) {
    DWORD len = 0;
    DWORD length = 0;
    WCHAR * wstr = NULL;
    if(str==NULL) return NULL;
    //static DWORD excludeCodepages [] = { 50220, 50221, 50222, 50225, 50227, 50229, 52936, 54936, 57002,  57003, 57004, 57005, 57006, 57007, 57008, 57009, 57010, 57011, 65000, 42};
    len = getLengthA(str);
    if(n<len) len = n;
    length = MultiByteToWideChar(CP_ACP, 0, str, len, NULL, 0);
    wstr = newpWCHAR(length+1);
    MultiByteToWideChar(CP_ACP, 0, str, len, wstr, length);
    return wstr;
}

WCHAR * toWCHAR( char *string) {
    return toWCHARn(string, getLengthA(string));
}

SizedString * createSizedString() {
    SizedString * s = (SizedString*)LocalAlloc(LPTR,sizeof(SizedString));
    s->bytes = NULL;
    s->length = 0;
    return s;
}

void freeSizedString(SizedString ** s) {
    if(*s!=NULL) {
        FREE((*s)->bytes);
        FREE((*s));
        *s = NULL;
    }
}


WCHAR * getLocaleName() {
    LANGID langID = LANGIDFROMLCID(GetUserDefaultLCID());
    LCID localeID = MAKELCID(langID, SORT_DEFAULT);
    const   DWORD MAX_LENGTH = 512;
    WCHAR * lang = newpWCHAR(MAX_LENGTH);
    WCHAR * country = newpWCHAR(MAX_LENGTH);
    WCHAR * locale = NULL;
    GetLocaleInfoW(localeID, LOCALE_SISO639LANGNAME, lang, MAX_LENGTH);
    GetLocaleInfoW(localeID, LOCALE_SISO3166CTRYNAME, country, MAX_LENGTH);    
    locale = appendStringW(appendStringW(appendStringW(NULL, lang), L"_"), country);
    FREE(country);
    FREE(lang);
    return locale;
}

WCHAR * newpWCHAR(DWORD length) {
    WCHAR * res = (WCHAR*) LocalAlloc(LPTR,sizeof(WCHAR) * length);
    ZERO(res, length * sizeof(WCHAR));
    return res;
}
WCHAR ** newppWCHAR(DWORD length) {
    return (WCHAR**) LocalAlloc(LPTR,sizeof(WCHAR *) * length);
}


char * newpChar(DWORD length) {
    char * res = (char*) LocalAlloc(LPTR,sizeof(char) * length);
    ZERO(res, length * sizeof(char));
    return res;
}

char ** newppChar(DWORD length) {
    return (char**) LocalAlloc(LPTR,sizeof(char*) * length);
}

int compare(int64t * size, DWORD value) {
    if (size->High > 0) return 1;
    
    if (size->Low > value)  
        return 1;
    else if(size->Low == value) 
        return 0;
    else //if(size->Low < value)  
        return -1;    
}
int compareInt64t(int64t * a1, int64t * a2) {
    if (a1->High > a2->High) {
        return 1;
    } else if(a1->High == a2->High) {
        if (a1->Low > a2->Low)  {
            return 1;
        } else if(a1->Low == a2->Low)  {
            return 0;
        }
    }
    return -1;
}

void plus(int64t * size, DWORD value) {
    if(value!=0) {
        if((MAXDWORD - size->Low) >= (value - 1)) {
            size->Low = size->Low + value;
        } else {
            size->High = size->High + 1;
            size->Low  = value - (MAXDWORD - size->Low) - 1;
        }
    }
}
void multiply(int64t * size, DWORD value) {
    if(value==0) {
        size->Low = 0;
        size->High = 0;
    } else {
        DWORD i = 0;
        DWORD low = size->Low;
        DWORD high = size->High;
        size->High = 0;
        for(; i < value - 1 ; i++) {
            plus(size, low);
        }
        size->High += high * value;
    }
}

void minus(int64t * size, DWORD value) {
    if(value!=0) {
        if(size->Low < value) {
            size->High = size->High -1;
            size->Low = size->Low + (MAXDWORD - value) + 1;
        } else {
            size->Low = size->Low - value;
        }}
}
int64t * newint64_t(DWORD low, DWORD high) {
    int64t * res = (int64t *) LocalAlloc(LPTR,sizeof(int64t));
    res->Low = low;
    res->High = high;
    return res;
}
WCHAR * getErrorDescription(DWORD dw) {
    WCHAR * lpMsgBuf;
    WCHAR * lpDisplayBuf = NULL;
    WCHAR * res = DWORDtoWCHAR(dw);
    
    FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) &lpMsgBuf, 0, NULL );
    
    lpDisplayBuf = appendStringW(lpDisplayBuf, L"Error code (");
    lpDisplayBuf = appendStringW(lpDisplayBuf, res);
    lpDisplayBuf = appendStringW(lpDisplayBuf, L"): ");
    lpDisplayBuf = appendStringW(lpDisplayBuf, lpMsgBuf);

    FREE(lpMsgBuf);
    FREE(res);
    
    return lpDisplayBuf;
    
}

WCHAR * formatMessageW(const WCHAR* message, const DWORD varArgsNumber, ...) {
    DWORD totalLength=getLengthW(message);
    DWORD counter=0;
    WCHAR * result = NULL;
    
    va_list ap;
    va_start(ap, varArgsNumber);
    
    while((counter++)<varArgsNumber) {
        WCHAR * arg = va_arg( ap, WCHAR * );
        totalLength+=getLengthW(arg);
    }
    va_end(ap);
    
    result = newpWCHAR(totalLength + 1);
    va_start(ap, varArgsNumber);
    wvsprintfW(result, message, ap);
    va_end(ap);
    return result;
}

DWORD isOK(LauncherProperties * props) {
    return (props->status == ERROR_OK);
}
