/* -*- 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 .
 */


/** @#file
 *
 *  export of all variable related text fields (and database display field)
 */
#include <txtvfldi.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/txtimp.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/i18nmap.hxx>
#include <xmloff/xmlimp.hxx>
#include <xmloff/xmluconv.hxx>
#include <xmloff/xmlement.hxx>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/text/SetVariableType.hpp>
#include <com/sun/star/text/XDependentTextField.hpp>
#include <com/sun/star/text/XTextFieldsSupplier.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/style/NumberingType.hpp>
#include <com/sun/star/container/XIndexReplace.hpp>

#include <sax/tools/converter.hxx>

#include <rtl/ustring.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>

#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>


// service names
constexpr char16_t sAPI_fieldmaster_prefix[] = u"com.sun.star.text.FieldMaster.";
constexpr OUString sAPI_get_expression = u"GetExpression"_ustr;
constexpr OUString sAPI_set_expression = u"SetExpression"_ustr;
constexpr OUString sAPI_user      = u"User"_ustr;
constexpr OUString sAPI_database  = u"com.sun.star.text.TextField.Database"_ustr;

// property names
constexpr OUString sAPI_content   = u"Content"_ustr;
constexpr OUString sAPI_sub_type  = u"SubType"_ustr;
constexpr OUString sAPI_number_format = u"NumberFormat"_ustr;
constexpr OUString sAPI_is_visible = u"IsVisible"_ustr;
constexpr OUString sAPI_current_presentation = u"CurrentPresentation"_ustr;


using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::style;
using namespace ::xmloff::token;


// XMLVarFieldImportContext: superclass for all variable related fields


XMLVarFieldImportContext::XMLVarFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp,
    const OUString& pServiceName,
    bool bFormula, bool bFormulaDefault,
    bool bDescription, bool bHelp, bool bHint, bool bVisible,
    bool bIsDisplayFormula,
    bool bType, bool bStyle, bool bValue,
    bool bPresentation) :
        XMLTextFieldImportContext(rImport, rHlp, pServiceName),
        aValueHelper(rImport, rHlp, bType, bStyle, bValue, false),
        bDisplayFormula(false),
        bDisplayNone(false),
        bFormulaOK(false),
        bDescriptionOK(false),
        bHelpOK(false),
        bHintOK(false),
        bDisplayOK(false),
        bSetFormula(bFormula),
        bSetFormulaDefault(bFormulaDefault),
        bSetDescription(bDescription),
        bSetHelp(bHelp),
        bSetHint(bHint),
        bSetVisible(bVisible),
        bSetDisplayFormula(bIsDisplayFormula),
        bSetPresentation(bPresentation)
{
}

void XMLVarFieldImportContext::ProcessAttribute(
    sal_Int32 nAttrToken,
    std::string_view sAttrValue )
{
    switch (nAttrToken)
    {
        case XML_ELEMENT(TEXT, XML_NAME):
            sName = OUString::fromUtf8(sAttrValue);
            bValid = true;      // we assume: field with name is valid!
            break;
        case XML_ELEMENT(TEXT, XML_DESCRIPTION):
            sDescription = OUString::fromUtf8(sAttrValue);
            bDescriptionOK = true;
            break;
        case XML_ELEMENT(TEXT, XML_HELP):
            sHelp = OUString::fromUtf8(sAttrValue);
            bHelpOK = true;
            break;
        case XML_ELEMENT(TEXT, XML_HINT):
            sHint = OUString::fromUtf8(sAttrValue);
            bHintOK = true;
            break;
        case XML_ELEMENT(TEXT, XML_FORMULA):
            {
                OUString sTmp;
                sal_uInt16 nPrefix = GetImport().GetNamespaceMap().
                        GetKeyByAttrValueQName(OUString::fromUtf8(sAttrValue), &sTmp);
                if( XML_NAMESPACE_OOOW == nPrefix )
                {
                    sFormula = sTmp;
                    bFormulaOK = true;
                }
                else
                    sFormula = OUString::fromUtf8(sAttrValue);
            }
            break;
        case XML_ELEMENT(TEXT, XML_DISPLAY):
            if (IsXMLToken(sAttrValue, XML_FORMULA))
            {
                bDisplayFormula = true;
                bDisplayNone = false;
                bDisplayOK = true;
            }
            else if (IsXMLToken(sAttrValue, XML_VALUE))
            {
                bDisplayFormula = false;
                bDisplayNone = false;
                bDisplayOK = true;
            }
            else if (IsXMLToken(sAttrValue, XML_NONE))
            {
                bDisplayFormula = false;
                bDisplayNone = true;
                bDisplayOK = true;
            } // else: no change
            DBG_ASSERT(!(bDisplayFormula && bDisplayNone),
                       "illegal display values");
            break;
        default:
            // delegate all others to value helper
            aValueHelper.ProcessAttribute(nAttrToken, sAttrValue);
            break;
    }
}

void XMLVarFieldImportContext::PrepareField(
    const Reference<XPropertySet> & xPropertySet)
{
    // bSetName: not implemented

    if (bSetFormula)
    {
        if (!bFormulaOK && bSetFormulaDefault)
        {
            sFormula = GetContent();
            bFormulaOK = true;
        }

        if (bFormulaOK)
        {
            xPropertySet->setPropertyValue(sAPI_content, Any(sFormula));
        }
    }

    if (bSetDescription && bDescriptionOK)
    {
        xPropertySet->setPropertyValue(u"Hint"_ustr, Any(sDescription));
    }

    if (bSetHelp && bHelpOK)
    {
        xPropertySet->setPropertyValue(u"Help"_ustr, Any(sHelp));
    }

    if (bSetHint && bHintOK)
    {
        xPropertySet->setPropertyValue(u"Tooltip"_ustr, Any(sHint));
    }

    if (bSetVisible && bDisplayOK)
    {
        bool bTmp = !bDisplayNone;
        xPropertySet->setPropertyValue(sAPI_is_visible, Any(bTmp));
    }

    // workaround for #no-bug#: display formula by default
    if (xPropertySet->getPropertySetInfo()->
                hasPropertyByName(u"IsShowFormula"_ustr) &&
        !bSetDisplayFormula)
    {
        bDisplayFormula = false;
        bSetDisplayFormula = true;
    }


    if (bSetDisplayFormula)
    {
        bool bTmp = bDisplayFormula && bDisplayOK;
        xPropertySet->setPropertyValue(u"IsShowFormula"_ustr, Any(bTmp));
    }

    // delegate to value helper
    aValueHelper.SetDefault(GetContent());
    aValueHelper.PrepareField(xPropertySet);

    // finally, set the current presentation
    if (bSetPresentation)
    {
        Any aAny;
        aAny <<= GetContent();
        xPropertySet->setPropertyValue(sAPI_current_presentation, aAny);
    }
}


// variable set fields


XMLSetVarFieldImportContext::XMLSetVarFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp,
    const OUString& pServiceName, VarType eVarType,
    bool bFormula, bool bFormulaDefault,
    bool bDescription, bool bHelp, bool bHint, bool bVisible, bool bIsDisplayFormula,
    bool bType, bool bStyle, bool bValue, bool bPresentation) :
        XMLVarFieldImportContext(rImport, rHlp, pServiceName,
                                 bFormula, bFormulaDefault,
                                 bDescription, bHelp, bHint, bVisible, bIsDisplayFormula,
                                 bType, bStyle, bValue, bPresentation),
        eFieldType(eVarType)
{
}

void XMLSetVarFieldImportContext::endFastElement(sal_Int32 )
{
    // should we call PrepareField on the field, or rather on it's master?
    // currently: call on field (just like superclass)
    // possible alternatives: call on master
    //                        call field or master depending on variable
    //                        PrepareMaster() in addition to PrepareField()

    DBG_ASSERT(!GetServiceName().isEmpty(), "no service name for element!");

    if (bValid)
    {
        DBG_ASSERT(!GetName().isEmpty(), "variable name needed!");

        // find field master
        Reference<XPropertySet> xMaster;
        if (FindFieldMaster(xMaster))
        {
            // create field/Service
            Reference<XPropertySet> xPropSet;
            if (CreateField(xPropSet, "com.sun.star.text.TextField." + GetServiceName()))
            {
                Reference<XDependentTextField> xDepTextField(xPropSet, UNO_QUERY);
                if (xDepTextField.is())
                {
                    // attach field to field master
                    xDepTextField->attachTextFieldMaster(xMaster);

                    // attach field to document
                    Reference<XTextContent> xTextContent(xPropSet, UNO_QUERY);
                    if (xTextContent.is())
                    {
                        try {
                        // insert, set field properties and exit!
                        GetImportHelper().InsertTextContent(xTextContent);
                        PrepareField(xPropSet);
                        } catch (lang::IllegalArgumentException & /*e*/)
                        {
                            // ignore e: #i54023#
                        };
                        return;
                    }
                }
            }
        }
    }

    // above: exit on success; so for all error cases we end up here!
    // write element content
    GetImportHelper().InsertString(GetContent());
}

bool XMLSetVarFieldImportContext::FindFieldMaster(
    Reference<XPropertySet> & xMaster)
{
    // currently: delegate to XMLVariableDeclImportContext;
    // should eventually go here
    return XMLVariableDeclImportContext::FindFieldMaster(xMaster,
                                                         GetImport(),
                                                         GetImportHelper(),
                                                         GetName(),
                                                         eFieldType);
}


// sequence field


XMLSequenceFieldImportContext::XMLSequenceFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLSetVarFieldImportContext(rImport, rHlp, sAPI_set_expression,
                                    VarTypeSequence,
                                    // formula
                                    true, true,
                                    false, false, false, false,
                                    false,
                                    false, false, false, true),

        sNumFormat(OUString('1')),
        sNumFormatSync(GetXMLToken(XML_FALSE)),
        bRefNameOK(false)
{
}

void XMLSequenceFieldImportContext::ProcessAttribute(
    sal_Int32 nAttrToken, std::string_view sAttrValue )
{
    switch (nAttrToken)
    {
        case XML_ELEMENT(STYLE, XML_NUM_FORMAT):
            sNumFormat = OUString::fromUtf8(sAttrValue);
            break;
        case XML_ELEMENT(STYLE, XML_NUM_LETTER_SYNC):
            sNumFormatSync = OUString::fromUtf8(sAttrValue);
            break;
        case XML_ELEMENT(TEXT, XML_REF_NAME):
            sRefName = OUString::fromUtf8(sAttrValue);
            bRefNameOK = true;
            break;
        default:
            // delegate to super class (name, formula)
            XMLSetVarFieldImportContext::ProcessAttribute(nAttrToken,
                                                          sAttrValue);
            break;
    } // switch
}

void XMLSequenceFieldImportContext::PrepareField(
    const Reference<XPropertySet> & xPropertySet)
{
    // delegate to super class (formula)
    XMLSetVarFieldImportContext::PrepareField(xPropertySet);

    // set format
    sal_Int16 nNumType = NumberingType::ARABIC;
    GetImport().GetMM100UnitConverter().convertNumFormat( nNumType, sNumFormat, sNumFormatSync );
    xPropertySet->setPropertyValue(sAPI_number_format, Any(nNumType));

    // handle reference name
    if (bRefNameOK)
    {
        Any aAny = xPropertySet->getPropertyValue(u"SequenceValue"_ustr);
        sal_Int16 nValue = 0;
        aAny >>= nValue;
        GetImportHelper().InsertSequenceID(sRefName, GetName(), nValue);
    }
}


// variable set field


XMLVariableSetFieldImportContext::XMLVariableSetFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLSetVarFieldImportContext(rImport, rHlp, sAPI_set_expression,
                                    VarTypeSimple,
                                    // formula, value&type, style,
                                    // display none
                                    true, true,
                                    false, false, false,
                                    true, false,
                                    true, true, true,
                                    true)
{
}

void XMLVariableSetFieldImportContext::PrepareField(
        const Reference<XPropertySet> & xPropertySet)
{
    // set type
    Any aAny;
    aAny <<= (IsStringValue()? SetVariableType::STRING : SetVariableType::VAR);
    xPropertySet->setPropertyValue(sAPI_sub_type, aAny);

    // the remainder is handled by super class
    XMLSetVarFieldImportContext::PrepareField(xPropertySet);
}


// variable input field


XMLVariableInputFieldImportContext::XMLVariableInputFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLSetVarFieldImportContext(rImport, rHlp, sAPI_set_expression,
                                    VarTypeSimple,
                                    // description, display none/formula,
                                    // value&type, style, formula
                                    true, true,
                                    true, true, true,
                                    true, false,
                                    true, true, true,
                                    true)
{
}

void XMLVariableInputFieldImportContext::PrepareField(
        const Reference<XPropertySet> & xPropertySet)
{
    // set type (input field)
    Any aAny;
    xPropertySet->setPropertyValue(u"Input"_ustr, Any(true));

    // set type
    aAny <<= (IsStringValue()? SetVariableType::STRING : SetVariableType::VAR);
    xPropertySet->setPropertyValue(sAPI_sub_type, aAny);

    // the remainder is handled by super class
    XMLSetVarFieldImportContext::PrepareField(xPropertySet);
}


// user field


XMLUserFieldImportContext::XMLUserFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLSetVarFieldImportContext(rImport, rHlp, sAPI_user,
                                    VarTypeUserField,
                                    // display none/formula, style
                                    false, false,
                                    false, false, false, true,
                                    true,
                                    false, true, false,
                                    false)
{
}


// user input field


// bug: doesn't work (SO API lacking)
XMLUserFieldInputImportContext::XMLUserFieldInputImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLVarFieldImportContext(rImport, rHlp, u"InputUser"_ustr,
                                 // description, style
                                 false, false,
                                 true, false, false,
                                 false, false,
                                 false /*???*/, true, false,
                                 false)
{
}

void XMLUserFieldInputImportContext::PrepareField(
    const Reference<XPropertySet> & xPropertySet)
{
    xPropertySet->setPropertyValue(sAPI_content, Any(GetName()));

    // delegate to super class
    XMLVarFieldImportContext::PrepareField(xPropertySet);
}


// variable get field


XMLVariableGetFieldImportContext::XMLVariableGetFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLVarFieldImportContext(rImport, rHlp, sAPI_get_expression,
                                 // style, display formula
                                 false, false,
                                 false, false, false,
                                 false, true,
                                 true, true, false,
                                 true)
{
}

void XMLVariableGetFieldImportContext::PrepareField(
        const Reference<XPropertySet> & xPropertySet)
{
    // set name
    xPropertySet->setPropertyValue(sAPI_content, Any(GetName()));

    // the remainder is handled by super class
    XMLVarFieldImportContext::PrepareField(xPropertySet);
}


// expression field


XMLExpressionFieldImportContext::XMLExpressionFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLVarFieldImportContext(rImport, rHlp, sAPI_get_expression,
                                 // formula, type, style, display formula
                                 true, true,
                                 false, false, false,
                                 false, true,
                                 true, true, false,
                                 true)
{
    bValid = true;  // always valid
}


void XMLExpressionFieldImportContext::PrepareField(
    const Reference<XPropertySet> & xPropertySet)
{
    xPropertySet->setPropertyValue(sAPI_sub_type, Any(sal_Int16(SetVariableType::FORMULA)));

    // delegate to super class
    XMLVarFieldImportContext::PrepareField(xPropertySet);
}


// text input field


XMLTextInputFieldImportContext::XMLTextInputFieldImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLVarFieldImportContext(rImport, rHlp, u"Input"_ustr,
                                 // description
                                 false, false,
                                 true, true, true,
                                 false, false,
                                 false, false, false,
                                 false)
{
    bValid = true;  // always valid
}

void XMLTextInputFieldImportContext::PrepareField(
    const Reference<XPropertySet> & xPropertySet)
{
    XMLVarFieldImportContext::PrepareField(xPropertySet);

    xPropertySet->setPropertyValue(sAPI_content, Any(GetContent()));
}


// table formula field


XMLTableFormulaImportContext::XMLTableFormulaImportContext(
    SvXMLImport& rImport,
    XMLTextImportHelper& rHlp) :
        XMLTextFieldImportContext(rImport, rHlp, u"TableFormula"_ustr),
        aValueHelper(rImport, rHlp, false, true, false, true),
        bIsShowFormula(false)
{
}

void XMLTableFormulaImportContext::ProcessAttribute(
    sal_Int32 nAttrToken,
    std::string_view sAttrValue )
{
    switch (nAttrToken)
    {
        case XML_ELEMENT(TEXT, XML_FORMULA):
            aValueHelper.ProcessAttribute( nAttrToken, sAttrValue );
            bValid = true;  // we need a formula!
            break;

        case XML_ELEMENT(STYLE, XML_DATA_STYLE_NAME):
            aValueHelper.ProcessAttribute( nAttrToken, sAttrValue );
            break;
        case XML_ELEMENT(TEXT, XML_DISPLAY):
            if ( sAttrValue == "formula" )
                 bIsShowFormula = true;
            break;
        default:
            // unknown attribute -> ignore
            XMLOFF_WARN_UNKNOWN_ATTR("xmloff", nAttrToken, sAttrValue);
            break;
    }
}

void XMLTableFormulaImportContext::PrepareField(
    const Reference<XPropertySet> & xPropertySet)
{
    // set format and formula
    aValueHelper.PrepareField( xPropertySet );

    Any aAny;

    // set 'show formula' and presentation
    xPropertySet->setPropertyValue( u"IsShowFormula"_ustr, Any(bIsShowFormula) );

    aAny <<= GetContent();
    xPropertySet->setPropertyValue( u"CurrentPresentation"_ustr, aAny );
}


// variable declarations

// Should be adapted to XMLVarField-/XMLSetVarFieldImportContext scheme!


// declaration container import (<variable/user-field/sequence-decls>)


XMLVariableDeclsImportContext::XMLVariableDeclsImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp, enum VarType eVarType) :
        SvXMLImportContext(rImport),
        eVarDeclsContextType(eVarType),
        rImportHelper(rHlp)
{
}

css::uno::Reference< css::xml::sax::XFastContextHandler > XMLVariableDeclsImportContext::createFastChildContext(
        sal_Int32 nElement,
        const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    if( IsTokenInNamespace(nElement, XML_NAMESPACE_TEXT) )
    {
        enum XMLTokenEnum eElementName;
        switch (eVarDeclsContextType)
        {
            case VarTypeSequence:
                eElementName = XML_SEQUENCE_DECL;
                break;
            case VarTypeSimple:
                eElementName = XML_VARIABLE_DECL;
                break;
            case VarTypeUserField:
                eElementName = XML_USER_FIELD_DECL;
                break;
            default:
                OSL_FAIL("unknown field type!");
                eElementName = XML_SEQUENCE_DECL;
                break;
        }

        if( nElement == XML_ELEMENT(TEXT, eElementName) )
        {
            return new XMLVariableDeclImportContext(
                GetImport(), rImportHelper, nElement, xAttrList,
                eVarDeclsContextType);
        }
    }

    // if no context was created, use default context
    return nullptr;
}


// declaration import (<variable/user-field/sequence-decl> elements)


XMLVariableDeclImportContext::XMLVariableDeclImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp,
    sal_Int32 nElement,
    const Reference<xml::sax::XFastAttributeList> & xAttrList,
    enum VarType eVarType) :
        SvXMLImportContext(rImport)
{
    // bug?? which properties for userfield/userfieldmaster
    XMLValueImportHelper aValueHelper(rImport, rHlp, true, false, true, false);
    sal_Unicode cSeparationChar('.');

    sal_Int8 nNumLevel(-1);
    OUString sName;

    if (nElement != XML_ELEMENT(TEXT, XML_SEQUENCE_DECL) &&
        nElement != XML_ELEMENT(TEXT, XML_VARIABLE_DECL) &&
        nElement != XML_ELEMENT(TEXT, XML_USER_FIELD_DECL) )
        return;

    // TODO: check validity (need name!)

    // parse attributes
    for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
    {
        switch (aIter.getToken())
        {
            case XML_ELEMENT(TEXT, XML_NAME):
                sName = aIter.toString();
                break;
            case XML_ELEMENT(TEXT, XML_DISPLAY_OUTLINE_LEVEL):
            {
                sal_Int32 nLevel;
                bool const bRet = ::sax::Converter::convertNumber(
                    nLevel, aIter.toView(), 0,
                    GetImport().GetTextImport()->GetChapterNumbering()->
                                                               getCount());
                if (bRet)
                {
                    nNumLevel = static_cast< sal_Int8 >( nLevel-1 ); // API numbers -1..9
                }
                break;
            }
            case XML_ELEMENT(TEXT, XML_SEPARATION_CHARACTER):
                cSeparationChar =
                    static_cast<char>(aIter.toString().toChar());
                break;

            default:
                // delegate to value helper
                aValueHelper.ProcessAttribute(aIter.getToken(), aIter.toView());
                break;
        }
    }

    Reference<XPropertySet> xFieldMaster;
    if (!FindFieldMaster(xFieldMaster, GetImport(), rHlp,
                        sName, eVarType))
        return;

    // now we have a field master: process attributes!
    Any aAny;

    switch (eVarType)
    {
    case VarTypeSequence:
        xFieldMaster->setPropertyValue(u"ChapterNumberingLevel"_ustr, Any(nNumLevel));

        if (nNumLevel >= 0)
        {
            OUString sStr(&cSeparationChar, 1);
            xFieldMaster->setPropertyValue(
                u"NumberingSeparator"_ustr, Any(sStr));
        }
        break;
    case VarTypeSimple:
        {
            // set string or non-string SubType (#93192#)
            // The SubType was already set in the FindFieldMaster
            // method, but it needs to be adjusted if it's a string.
            aAny <<= aValueHelper.IsStringValue()
                ? SetVariableType::STRING : SetVariableType::VAR;
            xFieldMaster->setPropertyValue(sAPI_sub_type, aAny);
        }
        break;
    case VarTypeUserField:
    {
        bool bTmp = !aValueHelper.IsStringValue();
        xFieldMaster->setPropertyValue(u"IsExpression"_ustr, Any(bTmp));
        aValueHelper.PrepareField(xFieldMaster);
        break;
    }
    default:
        OSL_FAIL("unknown varfield type");
    } // switch
}


bool XMLVariableDeclImportContext::FindFieldMaster(
    Reference<XPropertySet> & xMaster, SvXMLImport& rImport,
    XMLTextImportHelper& rImportHelper,
    const OUString& sVarName, enum VarType eVarType)
{
    static sal_Int32 nCollisionCount = 0;

    // rename field
    // currently: no family in use! Use 0.
    OUString sName = rImportHelper.GetRenameMap().Get(
        sal::static_int_cast< sal_uInt16 >(eVarType), sVarName);

    // get text fields supplier and field masters
    Reference<XTextFieldsSupplier> xTextFieldsSupp(rImport.GetModel(),
                                                   UNO_QUERY);
    Reference<container::XNameAccess> xFieldMasterNameAccess =
        xTextFieldsSupp->getTextFieldMasters();

    OUString sVarServiceName =
        OUString::Concat(sAPI_fieldmaster_prefix) +
        sAPI_set_expression +
        "." +
        sName;

    OUString sUserServiceName =
        OUString::Concat(sAPI_fieldmaster_prefix) +
        sAPI_user +
        "." +
        sName;

    if (xFieldMasterNameAccess->hasByName(sVarServiceName)) {
        // variable field master already in document

        Any aAny = xFieldMasterNameAccess->getByName(sVarServiceName);
        aAny >>= xMaster;

        aAny = xMaster->getPropertyValue(sAPI_sub_type);
        sal_Int16 nType = 0;
        aAny >>= nType;

        enum VarType eFMVarType =
            (SetVariableType::SEQUENCE == nType) ?
                VarTypeSequence : VarTypeSimple;

        if (eFMVarType != eVarType)
        {
            ++nCollisionCount;
            OUString sNew(sName + "_renamed_" + OUString::number(nCollisionCount));

            // FIXME! can't find if name is taken already!!!!

            rImportHelper.GetRenameMap().Add(
                sal::static_int_cast< sal_uInt16 >(eVarType), sName, sNew);

            // call FindFieldMaster recursively to create new master
            return FindFieldMaster(xMaster, rImport, rImportHelper,
                                   sNew, eVarType);
        }
    } else if (xFieldMasterNameAccess->hasByName(sUserServiceName)) {
        // user field: get field master
        Any aAny = xFieldMasterNameAccess->getByName(sUserServiceName);
        aAny >>= xMaster;

        if (VarTypeUserField != eVarType) {
            ++nCollisionCount;
            // find new name that is not taken
            OUString sNew(sName + "_renamed_" + OUString::number(nCollisionCount));

            // FIXME! can't find if name is taken already!!!!

            rImportHelper.GetRenameMap().Add(
                sal::static_int_cast< sal_uInt16 >(eVarType), sName, sNew);

            // call FindFieldMaster recursively to create new master
            return FindFieldMaster(xMaster, rImport, rImportHelper,
                                   sNew, eVarType);
        }
    } else {
        // field name not used: create field master

        // import -> model is MultiServiceFactory -> createInstance
        Reference<lang::XMultiServiceFactory>
            xFactory(rImport.GetModel(),UNO_QUERY);
        if( xFactory.is() ) {

            OUString sService = sAPI_fieldmaster_prefix
                + ((eVarType==VarTypeUserField) ?
                                 sAPI_user : sAPI_set_expression);
            Reference<XInterface> xIfc =
                xFactory->createInstance( sService );
            if (xIfc.is()) {
                xMaster.set(xIfc, UNO_QUERY);

                // set name
                xMaster->setPropertyValue(u"Name"_ustr, Any(sName));

                if (eVarType != VarTypeUserField) {
                    // set subtype for setexp field
                    Any aAny;
                    aAny <<= ((eVarType == VarTypeSimple) ?
                              SetVariableType::VAR :
                              SetVariableType::SEQUENCE);
                    xMaster->setPropertyValue(sAPI_sub_type, aAny);
                } // else : user field: no subtype

            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    DBG_ASSERT(xMaster.is(), "no field master found!?!");
    return true;
}


// Database Display field import


XMLDatabaseDisplayImportContext::XMLDatabaseDisplayImportContext(
    SvXMLImport& rImport, XMLTextImportHelper& rHlp) :
        XMLDatabaseFieldImportContext(rImport, rHlp, sAPI_database, false),
        aValueHelper(rImport, rHlp, false, true, false, false),
        bColumnOK(false),
        bDisplay( true ),
        bDisplayOK( false )
{
}

void XMLDatabaseDisplayImportContext::ProcessAttribute(
    sal_Int32 nAttrToken, std::string_view sAttrValue )
{
    switch (nAttrToken)
    {
        case XML_ELEMENT(TEXT, XML_COLUMN_NAME):
            sColumnName = OUString::fromUtf8(sAttrValue);
            bColumnOK = true;
            break;
        case XML_ELEMENT(TEXT, XML_DISPLAY):
            {
                bool bNone = IsXMLToken( sAttrValue, XML_NONE );
                bool bValue = IsXMLToken( sAttrValue, XML_VALUE );
                bDisplay = bValue;
                bDisplayOK = bNone || bValue;
            }
            break;
        case XML_ELEMENT(TEXT, XML_DATABASE_NAME):
        case XML_ELEMENT(TEXT, XML_TABLE_NAME):
        case XML_ELEMENT(TEXT, XML_TABLE_TYPE):
            // handled by super class
            XMLDatabaseFieldImportContext::ProcessAttribute(nAttrToken,
                                                            sAttrValue);
            break;
        default:
            // remainder handled by value helper
            aValueHelper.ProcessAttribute(nAttrToken, sAttrValue);
            break;
    }

    bValid = m_bTableOK && m_bDatabaseOK && bColumnOK;
}

void XMLDatabaseDisplayImportContext::endFastElement(sal_Int32 )
{
    // we have an EndElement of our own, because database fields need
    // to be attached to a field master before they can be inserted into
    // the document. Database stuff (database, table, column) all goes
    // to the field master, value & style go to the field.

    if (bValid)
    {

        // so here goes: we start with the master
        Reference<XPropertySet> xMaster;

        // create and prepare field master first
        if (CreateField(xMaster,
                        u"com.sun.star.text.FieldMaster.Database"_ustr))
        {
            Any aAny;
            xMaster->setPropertyValue(u"DataColumnName"_ustr, Any(sColumnName));

            // fieldmaster takes database, table and column name
            XMLDatabaseFieldImportContext::PrepareField(xMaster);

            // create field
            Reference<XPropertySet> xField;
            if (CreateField(xField,
                            sAPI_database))
            {
                // attach field master
                Reference<XDependentTextField> xDepField(xField, UNO_QUERY);
                if (xDepField.is())
                {
                    // attach field to field master
                    xDepField->attachTextFieldMaster(xMaster);

                    // attach field to document
                    Reference<XTextContent> xTextContent(xField, UNO_QUERY);
                    if (xTextContent.is())
                    {
                        // insert, set field properties and exit!
                        try
                        {
                            GetImportHelper().InsertTextContent(xTextContent);

                            // prepare field: format from database?
                            bool bTmp = !aValueHelper.IsFormatOK();
                            xField->setPropertyValue(u"DataBaseFormat"_ustr, Any(bTmp));

                            // value, value-type and format done by value helper
                            aValueHelper.PrepareField(xField);

                            // visibility
                            if( bDisplayOK )
                            {
                                xField->setPropertyValue(sAPI_is_visible, Any(bDisplay));
                            }

                            // set presentation
                            aAny <<= GetContent();
                            xField->setPropertyValue(sAPI_current_presentation, aAny);

                            // success!
                            return;
                        }
                        catch (const lang::IllegalArgumentException&)
                        {
                            TOOLS_WARN_EXCEPTION("xmloff.text", "Failed to insert text content");
                        }
                    }
                }
            }
        }
    }

    // above: exit on success; so for all error cases we end up here!
    // write element content
    GetImportHelper().InsertString(GetContent());
}


// value import helper

namespace {

enum ValueType
{
    XML_VALUE_TYPE_STRING,
    XML_VALUE_TYPE_FLOAT,
    XML_VALUE_TYPE_CURRENCY,
    XML_VALUE_TYPE_PERCENTAGE,
    XML_VALUE_TYPE_DATE,
    XML_VALUE_TYPE_TIME,
    XML_VALUE_TYPE_BOOLEAN
};

}

SvXMLEnumMapEntry<ValueType> const aValueTypeMap[] =
{
    { XML_FLOAT,        XML_VALUE_TYPE_FLOAT },
    { XML_CURRENCY,     XML_VALUE_TYPE_CURRENCY },
    { XML_PERCENTAGE,   XML_VALUE_TYPE_PERCENTAGE },
    { XML_DATE,         XML_VALUE_TYPE_DATE },
    { XML_TIME,         XML_VALUE_TYPE_TIME },
    { XML_BOOLEAN,      XML_VALUE_TYPE_BOOLEAN },
    { XML_STRING,       XML_VALUE_TYPE_STRING },
    { XML_TOKEN_INVALID, ValueType(0) }
};

XMLValueImportHelper::XMLValueImportHelper(
    SvXMLImport& rImprt,
    XMLTextImportHelper& rHlp,
    bool bType, bool bStyle, bool bValue, bool bFormula) :

        rImport(rImprt),
        rHelper(rHlp),

        fValue(0.0),
        nFormatKey(0),
        bIsDefaultLanguage(true),

        bStringType(false),
        bFormatOK(false),
        bStringValueOK(false),
        bFormulaOK(false),

        bSetType(bType),
        bSetValue(bValue),
        bSetStyle(bStyle),
        bSetFormula(bFormula)
{
}

void XMLValueImportHelper::ProcessAttribute(
    sal_Int32 nAttrToken, std::string_view sAttrValue )
{
    switch (nAttrToken)
    {
        case XML_ELEMENT(TEXT, XML_VALUE_TYPE): // #i32362#: src680m48++ saves text:value-type
        case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
        {
            // convert enum
            ValueType eValueType = XML_VALUE_TYPE_STRING;
            bool bRet = SvXMLUnitConverter::convertEnum(
                eValueType, sAttrValue, aValueTypeMap);

            if (bRet) {
                switch (eValueType)
                {
                    case XML_VALUE_TYPE_STRING:
                        bStringType = true;
                        break;
                    case XML_VALUE_TYPE_FLOAT:
                    case XML_VALUE_TYPE_CURRENCY:
                    case XML_VALUE_TYPE_PERCENTAGE:
                    case XML_VALUE_TYPE_DATE:
                    case XML_VALUE_TYPE_TIME:
                    case XML_VALUE_TYPE_BOOLEAN:
                        bStringType = false;
                        break;

                    default:
                        OSL_FAIL("unknown value type");
                }
            }
            break;
        }

        case XML_ELEMENT(TEXT, XML_VALUE):
        case XML_ELEMENT(OFFICE, XML_VALUE):
        {
            double fTmp;
            bool const bRet = ::sax::Converter::convertDouble(fTmp,sAttrValue);
            if (bRet) {
                fValue = fTmp;
            }
            break;
        }

        case XML_ELEMENT(TEXT, XML_TIME_VALUE):
        case XML_ELEMENT(OFFICE, XML_TIME_VALUE):
        {
            double fTmp;
            bool const bRet =
                ::sax::Converter::convertDuration(fTmp, sAttrValue);
            if (bRet) {
                fValue = fTmp;
            }
            break;
        }

        case XML_ELEMENT(TEXT, XML_DATE_VALUE):
        case XML_ELEMENT(OFFICE, XML_DATE_VALUE):
        {
            double fTmp;
            bool bRet = rImport.GetMM100UnitConverter().
                convertDateTime(fTmp,sAttrValue);
            if (bRet) {
                fValue = fTmp;
            }
            break;
        }

        case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE):
        {
            bool bTmp(false);
            bool bRet = ::sax::Converter::convertBool(bTmp, sAttrValue);
            if (bRet) {
                fValue = (bTmp ? 1.0 : 0.0);
            }
            else
            {
                double fTmp;
                bRet = ::sax::Converter::convertDouble(fTmp, sAttrValue);
                if (bRet) {
                    fValue = fTmp;
                }
            }
            break;
        }

        case XML_ELEMENT(TEXT, XML_STRING_VALUE):
        case XML_ELEMENT(OFFICE, XML_STRING_VALUE):
            sValue = OUString::fromUtf8(sAttrValue);
            bStringValueOK = true;
            break;

        case  XML_ELEMENT(TEXT, XML_FORMULA):
            {
                OUString sTmp;
                sal_uInt16 nPrefix = rImport.GetNamespaceMap().
                        GetKeyByAttrValueQName(OUString::fromUtf8(sAttrValue), &sTmp);
                if( XML_NAMESPACE_OOOW == nPrefix )
                {
                    sFormula = sTmp;
                    bFormulaOK = true;
                }
                else
                    sFormula = OUString::fromUtf8(sAttrValue);
            }
            break;

        case XML_ELEMENT(STYLE, XML_DATA_STYLE_NAME):
        {
            sal_Int32 nKey = rHelper.GetDataStyleKey(
                                          OUString::fromUtf8(sAttrValue), &bIsDefaultLanguage);
            if (-1 != nKey)
            {
                nFormatKey = nKey;
                bFormatOK = true;
            }
            break;
        }
        default:
            XMLOFF_WARN_UNKNOWN_ATTR("xmloff", nAttrToken, sAttrValue);
    } // switch
}

void XMLValueImportHelper::PrepareField(
    const Reference<XPropertySet> & xPropertySet)
{
    Any aAny;

    if (bSetType)
    {
        // ??? how to set type?
    }

    if (bSetFormula)
    {
        aAny <<= !bFormulaOK ? sDefault : sFormula;
        xPropertySet->setPropertyValue(sAPI_content, aAny);
    }

    // format/style
    if (bSetStyle && bFormatOK)
    {
        xPropertySet->setPropertyValue(sAPI_number_format, Any(nFormatKey));

        if( xPropertySet->getPropertySetInfo()->
                hasPropertyByName( u"IsFixedLanguage"_ustr ) )
        {
            bool bIsFixedLanguage = ! bIsDefaultLanguage;
            xPropertySet->setPropertyValue( u"IsFixedLanguage"_ustr, Any(bIsFixedLanguage) );
        }
    }

    // value: string or float
    if (bSetValue)
    {
        if (bStringType)
        {
            aAny <<= !bStringValueOK ? sDefault : sValue;
            xPropertySet->setPropertyValue(sAPI_content, aAny);
        }
        else
        {
            xPropertySet->setPropertyValue(u"Value"_ustr, Any(fValue));
        }
    }
}

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