/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

#include <osl/diagnose.h>
#include "ImplHelper.hxx"
#include <rtl/tencinfo.h>
#include <o3tl/char16_t2wchar_t.hxx>
#include <string.h>
#include <memory>

#if !defined WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

#include <vector>

#define FORMATETC_EXACT_MATCH    1
#define FORMATETC_PARTIAL_MATCH -1
#define FORMATETC_NO_MATCH       0

// returns a windows codepage appropriate to the
// given mime charset parameter value

sal_uInt32 getWinCPFromMimeCharset( const OUString& charset )
{
    sal_uInt32 winCP = GetACP( );

    if ( charset.getLength( ) )
    {
        OString osCharset(
            charset.getStr( ), charset.getLength( ), RTL_TEXTENCODING_ASCII_US );

        rtl_TextEncoding txtEnc =
            rtl_getTextEncodingFromMimeCharset( osCharset.getStr( ) );

        sal_uIntPtr winChrs = rtl_getBestWindowsCharsetFromTextEncoding( txtEnc );

        CHARSETINFO chrsInf;
        bool bRet = TranslateCharsetInfo( reinterpret_cast<DWORD*>(winChrs), &chrsInf, TCI_SRCCHARSET );

        // if one of the above functions fails
        // we will return the current ANSI codepage
        // of this thread
        if ( bRet )
            winCP = chrsInf.ciACP;
    }

    return winCP;
}

// returns a windows codepage appropriate to the
// given locale and locale type

OUString getWinCPFromLocaleId( LCID lcid, LCTYPE lctype )
{
    OSL_ASSERT( IsValidLocale( lcid, LCID_SUPPORTED ) );

    // we set a default value
    OUString winCP;

    // set a default value
    if ( LOCALE_IDEFAULTCODEPAGE == lctype )
    {
        winCP = OUString::number( static_cast<sal_Int32>(GetOEMCP( )) );
    }
    else if ( LOCALE_IDEFAULTANSICODEPAGE == lctype )
    {
        winCP = OUString::number( static_cast<sal_Int32>(GetACP( )) );
    }
    else
        OSL_ASSERT( false );

    // First, get required buffer size, in characters
    int nResult = GetLocaleInfoW(
        lcid, lctype, nullptr, 0 );

    OSL_ASSERT( nResult );

    if ( nResult )
    {
        std::unique_ptr<wchar_t[]> buff( new wchar_t[nResult] );
        // Now get the actual data
        nResult = GetLocaleInfoW( lcid, lctype, buff.get(), nResult );

        OSL_ASSERT(nResult);

        if (nResult)
            winCP = o3tl::toU( buff.get() );

    }

    return winCP;
}

// returns a mime charset parameter value appropriate
// to the given codepage, optional a prefix can be
// given, e.g. "windows-" or "cp"

OUString getMimeCharsetFromWinCP( sal_uInt32 cp, std::u16string_view aPrefix )
{
    return aPrefix + cptostr( cp );
}

// returns a mime charset parameter value appropriate
// to the given locale id and locale type, optional a
// prefix can be given, e.g. "windows-" or "cp"

OUString getMimeCharsetFromLocaleId( LCID lcid, LCTYPE lctype, std::u16string_view aPrefix  )
{
    OUString charset = getWinCPFromLocaleId( lcid, lctype );
    return aPrefix + charset;
}

// IsOEMCP

bool IsOEMCP( sal_uInt32 codepage )
{
    OSL_ASSERT( IsValidCodePage( codepage ) );

    sal_uInt32 arrOEMCP[] = { 437, 708, 709, 710, 720, 737,
                              775, 850, 852, 855, 857, 860,
                              861, 862, 863, 864, 865, 866,
                              869, 874, 932, 936, 949, 950, 1361 };

    for ( size_t i = 0; i < SAL_N_ELEMENTS( arrOEMCP ); ++i )
        if ( arrOEMCP[i] == codepage )
            return true;

    return false;
}

// converts a codepage into its string representation

OUString cptostr( sal_uInt32 codepage )
{
    OSL_ASSERT( IsValidCodePage( codepage ) );

    return OUString::number( static_cast<sal_Int64>( codepage ) );
}

// OleStdDeleteTargetDevice()
//
// Purpose:
//
// Parameters:
//
// Return Value:
//    SCODE  -  S_OK if successful
void DeleteTargetDevice( DVTARGETDEVICE* ptd )
{
    __try
    {
        CoTaskMemFree( ptd );
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
        OSL_FAIL( "Error DeleteTargetDevice" );
    }
}

// OleStdCopyTargetDevice()
//
// Purpose:
//  duplicate a TARGETDEVICE struct. this function allocates memory for
//  the copy. the caller MUST free the allocated copy when done with it
//  using the standard allocator returned from CoGetMalloc.
//  (OleStdFree can be used to free the copy).
//
// Parameters:
//  ptdSrc      pointer to source TARGETDEVICE
//
// Return Value:
//    pointer to allocated copy of ptdSrc
//    if ptdSrc==NULL then returns NULL is returned.
//    if ptdSrc!=NULL and memory allocation fails, then NULL is returned
DVTARGETDEVICE* CopyTargetDevice( DVTARGETDEVICE* ptdSrc )
{
    DVTARGETDEVICE* ptdDest = nullptr;

    __try
    {
        if ( nullptr != ptdSrc )
        {
            ptdDest = static_cast< DVTARGETDEVICE* >( CoTaskMemAlloc( ptdSrc->tdSize ) );
            memcpy( ptdDest, ptdSrc, static_cast< size_t >( ptdSrc->tdSize ) );
        }
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
    }

    return ptdDest;
}

// OleStdCopyFormatEtc()
//
// Purpose:
//  Copies the contents of a FORMATETC structure. this function takes
//  special care to copy correctly copying the pointer to the TARGETDEVICE
//  contained within the source FORMATETC structure.
//  if the source FORMATETC has a non-NULL TARGETDEVICE, then a copy
//  of the TARGETDEVICE will be allocated for the destination of the
//  FORMATETC (petcDest).
//
//  NOTE: the caller MUST free the allocated copy of the TARGETDEVICE
//  within the destination FORMATETC when done with it
//  using the standard allocator returned from CoGetMalloc.
//  (OleStdFree can be used to free the copy).
//
// Parameters:
//  petcDest      pointer to destination FORMATETC
//  petcSrc       pointer to source FORMATETC
//
// Return Value:
//  returns TRUE if copy was successful;
//  returns FALSE if not successful, e.g. one or both of the pointers
//  were invalid or the pointers were equal
bool CopyFormatEtc( LPFORMATETC petcDest, LPFORMATETC petcSrc )
{
    bool bRet = false;

    __try
    {
        if ( petcDest != petcSrc )
        {

            petcDest->cfFormat = petcSrc->cfFormat;

            petcDest->ptd      = nullptr;
            if ( nullptr != petcSrc->ptd )
                petcDest->ptd  = CopyTargetDevice(petcSrc->ptd);

            petcDest->dwAspect = petcSrc->dwAspect;
            petcDest->lindex   = petcSrc->lindex;
            petcDest->tymed    = petcSrc->tymed;

            bRet = true;
        }
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
        OSL_FAIL( "Error CopyFormatEtc" );
    }

    return bRet;
}

// returns:
//  1 for exact match,
//  0 for no match,
// -1 for partial match (which is defined to mean the left is a subset
//    of the right: fewer aspects, null target device, fewer medium).

sal_Int32 CompareFormatEtc( const FORMATETC* pFetcLhs, const FORMATETC* pFetcRhs )
{
    sal_Int32 nMatch = FORMATETC_EXACT_MATCH;

    __try
    {
        if ( pFetcLhs != pFetcRhs )
        {
            if ( ( pFetcLhs->cfFormat != pFetcRhs->cfFormat ) ||
                ( pFetcLhs->lindex   != pFetcRhs->lindex ) ||
                !CompareTargetDevice( pFetcLhs->ptd, pFetcRhs->ptd ) )
            {
                nMatch = FORMATETC_NO_MATCH;
            }

            else if ( pFetcLhs->dwAspect == pFetcRhs->dwAspect )
                // same aspects; equal
                ;
            else if ( ( pFetcLhs->dwAspect & ~pFetcRhs->dwAspect ) != 0 )
            {
                // left not subset of aspects of right; not equal
                nMatch = FORMATETC_NO_MATCH;
            }
            else
                // left subset of right
                nMatch = FORMATETC_PARTIAL_MATCH;

            if ( nMatch == FORMATETC_EXACT_MATCH || nMatch == FORMATETC_PARTIAL_MATCH )
            {
                if ( pFetcLhs->tymed == pFetcRhs->tymed )
                    // same medium flags; equal
                    ;
                else if ( ( pFetcLhs->tymed & ~pFetcRhs->tymed ) != 0 )
                {
                    // left not subset of medium flags of right; not equal
                    nMatch = FORMATETC_NO_MATCH;
                }
                else
                    // left subset of right
                    nMatch = FORMATETC_PARTIAL_MATCH;
            }
        }
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
        OSL_FAIL( "Error CompareFormatEtc" );
        nMatch = FORMATETC_NO_MATCH;
    }

    return nMatch;
}

bool CompareTargetDevice( DVTARGETDEVICE* ptdLeft, DVTARGETDEVICE const * ptdRight )
{
    bool bRet = false;

    __try
    {
        if ( ptdLeft == ptdRight )
        {
            // same address of td; must be same (handles NULL case)
            bRet = true;
        }

        // one of the two is NULL
        else if ( ( nullptr != ptdRight ) && ( nullptr != ptdLeft ) )

        if ( ptdLeft->tdSize == ptdRight->tdSize )

        if ( memcmp( ptdLeft, ptdRight, ptdLeft->tdSize ) == 0 )
            bRet = true;
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
        OSL_FAIL( "Error CompareTargetDevice" );
        bRet = false;
    }

    return bRet;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
