#pragma once

#include <string>

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "xlcall.h"

//////////////////////////////////////////////////////////////////////////////
//
// Some constants defining the Excel maximums for various things.
//
// https://msdn.microsoft.com/en-us/library/office/bb687884.aspx
//
unsigned int const MAX_XL12_STR_LEN = 32767;
int const MAX_XL12_ROWS = 1048576;
int const MAX_XL12_COLS = 16384;

//////////////////////////////////////////////////////////////////////////////
//
// excel_string_to_utf8
//
//    Converts an Excel string, which is a UTF16-encoded string with
//    the string length in the first character, to a UT8-encoded
//    NULL-terminated string stored in a std::string
//
std::string excel_string_to_utf8(XCHAR const* excel_string);

//////////////////////////////////////////////////////////////////////////////
//
// utf8_string_to_excel
//
//    Converts  a UT8-encoded NULL-terminated string stored in a
//    std::string to a UTF16-encoded string with length stored
//    in the first character.
//
//    NOTE: the caller is responsible for calling delete [] ptr on
//          the pointer returned by the function when it is no
//          longer required.
//
XCHAR* utf8_string_to_excel(char const* utf8_string_ptr);

//////////////////////////////////////////////////////////////////////////////
//
// caller_location
//
//    Returns a string created from the book name, sheet name, row and
//    column of the cell from which the current Excel function is being
//    being called.
//
std::string caller_location();

//////////////////////////////////////////////////////////////////////////////
//
// xloper_scope_guard
//
//    Utility class to help provide basic exception safety when working
//    with XLOPERs that need to be free by Excel.
//
class xloper_scope_guard
{
    XLOPER12* xloper_ptr_;

public:
    explicit xloper_scope_guard(XLOPER12* xloper_ptr)
        : xloper_ptr_(xloper_ptr)
    {}

    ~xloper_scope_guard()
    {
        if (xloper_ptr_->xltype == (xltypeStr | xlbitDLLFree))
        {
            // This is a string we allocated so we delete it.
            delete[] xloper_ptr_->val.str;
        }
        else
        {
            Excel12(xlFree, 0, 1, xloper_ptr_);
        }
    }
};

//////////////////////////////////////////////////////////////////////////////
//
// xloper_string
//
//    Wraps the creation of an XPOPER12 string from a UTF8 string
//    and performs the necessary clean-up on deallocation.
//
class xloper_string
{
    XLOPER12 xloper_;

public:
    xloper_string()
    {
        xloper_.xltype = xltypeNil;
        xloper_.val.str = 0;
    }

    explicit xloper_string(std::string const& utf8_string)
    {
        xloper_.xltype = xltypeStr;
        xloper_.val.str = utf8_string_to_excel(utf8_string.c_str());
    }

    ~xloper_string()
    {
        delete[] xloper_.val.str;
    }

    void set_string(std::string const& utf8_string)
    {
        xloper_.xltype = xltypeStr;
        xloper_.val.str = utf8_string_to_excel(utf8_string.c_str());
    }

    XLOPER12* operator&()
    {
        return &xloper_;
    }
};

void free_xloper_memory(XLOPER12* xl_oper);
void create_error_message(XLOPER12* xl_result, char const* error_message);
DWORD xloper_type(XLOPER12* xl_oper);

//////////////////////////////////////////////////////////////////////////////
//
// registration_info
//
//    Encapsulates the data required to register a function with Excel.
//
unsigned int const MAX_XL_PARAMS = 255;
struct registration_info
{
    char* dll_export;
    char* return_and_parameter_types;
    char* function_name_in_excel;
    char* parameter_names;
    char* function_wizard_category;
    char* function_description;
    unsigned int num_parameters;
    char* parameter_description[MAX_XL_PARAMS];
};


unsigned int register_functions(
    unsigned int num_functions_to_register,
    registration_info reg_info[]
);
