#pragma once

/*
 * Compatibility definitions used while compiling the original Opus C source
 * with a modern AMD64 compiler. This header deliberately contains platform
 * adaptation only; document algorithms stay in the original source files.
 */

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>

#include <stddef.h>
#include <stdint.h>
#include <malloc.h>
#include <string.h>

/* A null Win16 module handle made GetProcAddress fail.  On current Windows,
 * passing that invalid handle can instead search the process image.  Opus
 * probes retired modules such as KERNEL, GDI, and USER by ordinal, so an
 * unsuccessful probe must not be allowed to resolve an unrelated WORD1
 * export with the same ordinal. */
static __inline FARPROC OpusGetProcAddress(HMODULE module, LPCSTR name) {
    return module != NULL ? GetProcAddress(module, name) : NULL;
}
#define GetProcAddress OpusGetProcAddress

/* The SDK retains ChangeMenu only for source compatibility, with its Win16
 * UINT submenu parameter.  That truncates HMENU on Win64. */
#ifdef ChangeMenu
#undef ChangeMenu
#endif
#ifdef __cplusplus
extern "C" {
#endif
BOOL OpusChangeMenu(HMENU menu, UINT item, const void *new_text,
		UINT_PTR new_item, UINT flags);
#ifdef __cplusplus
}
#endif
#define ChangeMenu(menu, item, text, new_item, flags) \
	OpusChangeMenu((menu), (UINT)(item), (const void *)(text), \
			(UINT_PTR)(new_item), (UINT)(flags))

/* BSD/16-bit C exposed index() as the strchr() spelling.  Modern MSVC still
 * links an implementation but does not declare it, which would truncate its
 * pointer result under an implicit K&R declaration on AMD64. */
#if !defined(__cplusplus) && !defined(index)
#define index strchr
#endif

/* Win16 Word owns a small wrapper named DeleteAtom.  The modern SDK added a
 * system function with that same name and a different return type.  Keep the
 * original wrapper semantics while giving it a collision-free native name. */
#ifdef OPUS_X64
#define DeleteAtom OpusDeleteAtom
void OpusDeleteAtom(ATOM atom);
#endif

/* Several original translation units define NOGDI to trim the Win16 header,
 * then include resource.h, whose serialized bitmap records still need these
 * two layout types.  Modern windows.h honors NOGDI more broadly, so provide
 * the SDK-compatible data structures when that switch hid them. */
#ifdef NOGDI
typedef struct tagBITMAPCOREHEADER
    {
    DWORD bcSize;
    WORD bcWidth;
    WORD bcHeight;
    WORD bcPlanes;
    WORD bcBitCount;
    } BITMAPCOREHEADER;

typedef struct tagBITMAP
    {
    LONG bmType;
    LONG bmWidth;
    LONG bmHeight;
    LONG bmWidthBytes;
    WORD bmPlanes;
    WORD bmBitsPixel;
    LPVOID bmBits;
    } BITMAP;
#endif

/* Retired Win16 notification retained by Word's message filters.  It is not
 * generated by Win32/Win64 USER, but keeping its historical numeric value
 * preserves the original switch tables. */
#ifndef WM_SYSTEMERROR
#define WM_SYSTEMERROR 0x0017
#endif
#ifndef WM_SETVISIBLE
#define WM_SETVISIBLE 0x0009
#endif

/* Win16's DEVICEDEFAULT_FONT stock-object index was removed from the modern
 * SDK.  Word retains it only for its low-memory emergency font fallback. */
#ifndef DEVICEDEFAULT_FONT
#define DEVICEDEFAULT_FONT 14
#endif

/* Bitmap-file signature retained by the reduced Win16 qwindows header. */
#ifndef BFT_BMAP
#define BFT_BMAP 0x4d42
#endif

/* Microsoft C/CS segmented-memory and p-code keywords. */
#ifndef far
#define far
#endif
#ifndef near
#define near
#endif
#ifndef huge
#define huge
#endif
#ifndef native
#define native
#endif
#ifndef __cplusplus
#ifndef export
#define export
#endif
#endif
#ifndef pascal
#define pascal
#endif
#ifndef EXPORT
#define EXPORT
#endif
#ifndef NATIVE
#define NATIVE
#endif
#ifndef HUGE
#define HUGE
#endif
#ifndef csconst
#define csconst const
#endif
#ifndef __cplusplus
#ifndef string
#define string
#endif
#endif
#ifndef sys
#define sys
#endif
#ifndef uop
#define uop
#endif
#ifndef __FNATIVE__
#define __FNATIVE__ 1
#endif

/* NOMINMAX keeps the Windows SDK from leaking these macros into C++, while
 * original Opus C sources intentionally use their historical spellings. */
#ifndef __cplusplus
#ifndef max
#define max(first, second) (((first) > (second)) ? (first) : (second))
#endif
#ifndef min
#define min(first, second) (((first) < (second)) ? (first) : (second))
#endif
#endif

/* The old segment manager represented these as 16-bit selector/offset pairs.
 * Native pointers are used on AMD64; no selector arithmetic is permitted. */
typedef uintptr_t SB;
typedef uintptr_t IB;
/* The original non-CMerge compiler defined HP as ``int huge *``.  A large
 * amount of the EL interpreter consequently dereferences an HP to recover a
 * stored segment offset.  In the flat port those stored values are native
 * addresses, so retain the dereferenceable contract with a pointer-sized
 * element instead of using void *. */
typedef uintptr_t* HP;
typedef unsigned uns;
typedef IB* HQ;

/* The segmented-memory table is unnecessary in a flat address space.  Keep
 * its startup contract so the original WinMain can progress unchanged. */
#ifndef sbMax
#define sbMax ((SB)~(SB)0)
#endif
#ifndef sbDds
#define sbDds ((SB)1)
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern SB sbMac;
int FInitSegTable(SB requested_max);
void OpusX64ReportSz(const char *message, const char *file, int line);
void OpusX64ReportWin32(const char *message, DWORD error, const char *file,
		int line);
const char *OpusX64LastReport(void);
#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
extern "C" {
#endif
HP OpusHpOfSbIbImpl(SB segment, uintptr_t offset);
SB OpusSbOfHp(const void* pointer);
IB OpusIbOfHp(const void* pointer);
#ifdef __cplusplus
}
#endif
#ifndef OpusHpOfSbIb
#define OpusHpOfSbIb(segment, offset) \
    OpusHpOfSbIbImpl((SB)(segment), (uintptr_t)(offset))
#endif
#ifndef HpOfSbIb
#define HpOfSbIb(segment, offset) OpusHpOfSbIb((segment), (offset))
#endif
#ifndef HpOfSbIbConst
#define HpOfSbIbConst(segment, offset) ((HP)(uintptr_t)(offset))
#endif

#ifndef LOWORDX
#define LOWORDX(value) ((WORD)((uintptr_t)(value) & 0xffffu))
#endif
#ifndef HIWORDX
#define HIWORDX(value) ((WORD)(((uintptr_t)(value) >> 16u) & 0xffffu))
#endif
#ifndef MAKELONGX
#define MAKELONGX(low, high) \
    ((LONG)(((WORD)(low)) | ((DWORD)((WORD)(high))) << 16u))
#endif

#ifndef MAKEPOINT
static __inline POINT OpusMakePoint(LPARAM packed_point) {
    POINT point = {
        (LONG)(SHORT)LOWORD((DWORD_PTR)packed_point),
        (LONG)(SHORT)HIWORD((DWORD_PTR)packed_point)
    };
    return point;
}
#define MAKEPOINT(packed_point) OpusMakePoint((LPARAM)(packed_point))
#endif

/* Opus also has its own coordinate-compatible struct PT.  C allowed the
 * Win16 headers to treat these interchangeably; modern C keeps the aggregate
 * types distinct even though their fields have the same native widths. */
#define OpusSetPtFromLParam(destination, packed_point) \
    do { \
        (destination).xp = (int)(SHORT)LOWORD((DWORD_PTR)(packed_point)); \
        (destination).yp = (int)(SHORT)HIWORD((DWORD_PTR)(packed_point)); \
    } while (0)

/* Flat-memory equivalents of the CS copy/fill intrinsics. Argument order is
 * intentionally source-first/destination-second to match Opus. */
static __inline unsigned char* OpusMoveBytesEnd(const void* source,
                                                void* destination,
                                                size_t count) {
    memmove(destination, source, count);
    return (unsigned char*)destination + count;
}
#define bltbyteNat(source, destination, count) \
    OpusMoveBytesEnd((source), (destination), (size_t)(count))
#define bltbxNat(source, destination, count) \
    OpusMoveBytesEnd((source), (destination), (size_t)(count))
#define bltbcxNat(value, destination, count) \
    memset((destination), (unsigned char)(value), (size_t)(count))
#define BLTBH(source, destination, count) \
    OpusMoveBytesEnd((source), (destination), (size_t)(count))
#define LpOfHp(pointer) ((void*)(pointer))

static __inline void* OpusFillWords(int value, int* destination, size_t count) {
    size_t index;
    for (index = 0; index < count; ++index) {
        destination[index] = value;
    }
    return destination;
}
#define bltcxNat(value, destination, count) \
    OpusFillWords((value), (int*)(destination), (size_t)(count))

/* Win16 thunk helpers collapse to direct function pointers in a flat address
 * space. They are macros so original call sites remain unchanged. */
#ifndef MakeProcInstance
#define MakeProcInstance(proc, instance) (proc)
#endif
#ifndef FreeProcInstance
#define FreeProcInstance(proc) ((void)0)
#endif

/* Win16's GetInstanceData copied another instance's data segment. Opus only
 * used it for multi-instance startup, which does not exist in Win64. */
static __inline int OpusGetInstanceData(HINSTANCE instance, void* destination,
                                        int byte_count) {
    (void)instance;
    (void)destination;
    (void)byte_count;
    return 0;
}
#ifndef GetInstanceData
#define GetInstanceData OpusGetInstanceData
#endif

/* Win16 GetTempFileName accepted a drive byte (optionally ORed with
 * TF_FORCEDRIVE).  The Win32 API replaced that argument with a directory
 * path.  Keep the source-level Win16 contract and translate it here so the
 * original file-management code can remain intact. */
#ifndef TF_FORCEDRIVE
#define TF_FORCEDRIVE 0x80
#endif
static __inline UINT OpusGetTempFileName(BYTE drive_and_flags,
                                         LPCSTR prefix,
                                         UINT unique,
                                         LPSTR output) {
    CHAR directory[MAX_PATH];
    const BYTE drive = (BYTE)(drive_and_flags & (BYTE)~TF_FORCEDRIVE);

    if ((drive_and_flags & TF_FORCEDRIVE) != 0 && drive != 0) {
        directory[0] = (CHAR)drive;
        directory[1] = ':';
        directory[2] = '\\';
        directory[3] = '\0';
    } else {
        const DWORD length = GetTempPathA((DWORD)sizeof(directory), directory);
        if (length == 0 || length >= sizeof(directory)) {
            return 0;
        }
    }

    return GetTempFileNameA(directory, prefix, unique, output);
}
#ifdef GetTempFileName
#undef GetTempFileName
#endif
#define GetTempFileName(drive, prefix, unique, output) \
    OpusGetTempFileName((BYTE)(drive), (LPCSTR)(prefix), (UINT)(unique), \
                        (LPSTR)(output))

/* Opus supplies a parameterless user-notification Beep routine.  The Win32
 * API reused the name for a frequency/duration function, so give the Opus
 * implementation its own native symbol while preserving every original
 * parameterless call site. */
int OpusBeep(void);
#define Beep() OpusBeep()

/* Native entry points originally supplied by resn2.asm. Declaring them here
 * prevents K&R implicit-int calls from truncating pointers on AMD64. */
#ifdef __cplusplus
extern "C" {
#endif
int CchSz(const CHAR* text);
int CchCopySz(const CHAR* source, CHAR* destination);
CHAR* PchInSt(CHAR* counted_string, int character);
int FAlphaNum(int character);
int FDigit(int character);
int FAlpha(int character);
int FUpper(int character);
int FLower(int character);
int FNeNcSz(const CHAR* first, const CHAR* second);
int FNeNcSt(const CHAR* first, const CHAR* second);
int FNeNcRgch(const CHAR* first, const CHAR* second, int count);
CHAR ChUpper(int character);
int ChUpperLookup(int character);
void QszUpper(CHAR* text);
void* N_PxsInit(void* replace_states, int state_count, void* transaction);
int N_PostTn(void* transaction, int intention, void** handle, int count);
int N_FDoTns(void* transaction);
int N_CloseTns(void* transaction);
int N_XDelFndSedPgdPad(int plan, void* transaction, void* replace_state);
int N_CbGenPapxFromPap(CHAR* papx, void* pap, void* base_pap);
LONG N_CpFormatFrom(int ww, int doc, LONG cp);
void N_GetCpFirstCpLimDisplayPara(int ww, int doc, LONG cp, LONG* first,
                                  LONG* limit);
int N_FAssignLr(void* layout_state, int dyl_fill, int empty_ok);
int N_FAbortLayout(int outline, void* layout_state);
int N_FWidowControl(void* layout_state, void* new_layout_state, int dyl_fill,
                     int empty_ok);
int N_XpFromDcp(LONG dcp_first, LONG dcp_limit, unsigned* xp_first,
                int* character_index);
int N_FFillRgwWithSeqLevs(int doc, LONG cp, int outline_index,
                           int field_index, void** outline_plc,
                           void** field_plc, int* levels);
int N_FGetParaState(int all, int abort_ok);
int N_FMarkLine(void* selection, void** display_regions, int region_index,
                void* display_line, LONG selection_first,
                LONG selection_limit, LONG line_first);
int N_IfrdGatherFtnRef(void* layout_state, void* new_layout_state,
                       int reference_index, int normal, int reject_y,
                       int accept_y);
int N_FGetFtnBreak(void* layout_state, int line_index, void* footnote_line,
                   int placement);
CHAR* N_PchSzRtfMove(int symbol_index, CHAR* destination);
int N_FSearchRgrsym(CHAR* symbol, int* symbol_index);
void N_RtfIn(void** input_block, CHAR* text, int text_count);
CHAR N_ChMapSpecChar(CHAR character, int character_set);
int N_DxaFromDxp(int pixels);
int N_DxpFromDxa(int twips);
void N_MiscPlcLoops(void** plc, int first, int limit, CHAR* result,
                    int operation);
int N_WCompSzSrt(CHAR* first, CHAR* second, int case_sensitive);
int N_WCompChCh(CHAR first, CHAR second);
#ifdef __cplusplus
}
#endif
