/* -*- 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 <sal/config.h>

#include <dp_misc.h>
#include <dp_ucb.h>
#include <rtl/uri.hxx>
#include <rtl/ustrbuf.hxx>
#include <ucbhelper/content.hxx>
#include <xmlscript/xml_helper.hxx>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/ucb/CommandFailedException.hpp>
#include <com/sun/star/ucb/ContentInfo.hpp>
#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
#include <com/sun/star/ucb/ContentCreationException.hpp>
#include <comphelper/processfactory.hxx>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;

namespace dp_misc
{


bool create_ucb_content(
    ::ucbhelper::Content * ret_ucbContent, OUString const & url,
    Reference<XCommandEnvironment> const & xCmdEnv,
    bool throw_exc )
{
    try {
        // Existence check...
        // content ctor/isFolder() will throw exception in case the resource
        // does not exist.

        // dilemma: no chance to use the given handler here, because it would
        //          raise no such file dialogs, else no interaction for
        //          passwords, ...? xxx todo
        ::ucbhelper::Content ucbContent(
            url, Reference<XCommandEnvironment>(),
            comphelper::getProcessComponentContext() );

        ucbContent.isFolder();

        if (ret_ucbContent != nullptr)
        {
            ucbContent.setCommandEnvironment( xCmdEnv );
            *ret_ucbContent = std::move(ucbContent);
        }
        return true;
    }
    catch (const RuntimeException &) {
        throw;
    }
    catch (const Exception &) {
        if (throw_exc)
            throw;
    }
    return false;
}


bool create_folder(
    ::ucbhelper::Content * ret_ucb_content, OUString const & url_,
    Reference<XCommandEnvironment> const & xCmdEnv, bool throw_exc )
{
    ::ucbhelper::Content ucb_content;
    if (create_ucb_content(
            &ucb_content, url_, xCmdEnv, false /* no throw */ ))
    {
        if (ucb_content.isFolder()) {
            if (ret_ucb_content != nullptr)
                *ret_ucb_content = ucb_content;
            return true;
        }
    }

    OUString url( url_ );
    // xxx todo: find parent
    sal_Int32 slash = url.lastIndexOf( '/' );
    if (slash < 0) {
        // fallback:
        url = expandUnoRcUrl( url );
        slash = url.lastIndexOf( '/' );
    }
    if (slash < 0) {
        // invalid: has to be at least "auth:/..."
        if (throw_exc)
            throw ContentCreationException(
                "Cannot create folder (invalid path): '" + url + "'",
                Reference<XInterface>(), ContentCreationError_UNKNOWN );
        return false;
    }
    ::ucbhelper::Content parentContent;
    if (! create_folder(
            &parentContent, url.copy( 0, slash ), xCmdEnv, throw_exc ))
        return false;
    const Any title( ::rtl::Uri::decode( url.copy( slash + 1 ),
                                         rtl_UriDecodeWithCharset,
                                         RTL_TEXTENCODING_UTF8 ) );
    const Sequence<ContentInfo> infos(
        parentContent.queryCreatableContentsInfo() );
    for ( ContentInfo const & info : infos )
    {
        // look KIND_FOLDER:
        if ((info.Attributes & ContentInfoAttribute::KIND_FOLDER) != 0)
        {
            // make sure the only required bootstrap property is "Title":
            Sequence<beans::Property> const & rProps = info.Properties;
            if ( rProps.getLength() != 1 || rProps[ 0 ].Name != "Title" )
                continue;

            try {
                if (parentContent.insertNewContent(
                        info.Type,
                        { u"Title"_ustr },
                        { title },
                        ucb_content )) {
                    if (ret_ucb_content != nullptr)
                        *ret_ucb_content = ucb_content;
                    return true;
                }
            }
            catch (const RuntimeException &) {
                throw;
            }
            catch (const CommandFailedException &) {
                // Interaction Handler already handled the error
                // that has occurred...
            }
            catch (const Exception &) {
                if (throw_exc)
                    throw;
                return false;
            }
        }
    }
    if (throw_exc)
        throw ContentCreationException(
            "Cannot create folder: '" + url + "'",
            Reference<XInterface>(), ContentCreationError_UNKNOWN );
    return false;
}


bool erase_path( OUString const & url,
                 Reference<XCommandEnvironment> const & xCmdEnv,
                 bool throw_exc )
{
    ::ucbhelper::Content ucb_content;
    if (create_ucb_content( &ucb_content, url, xCmdEnv, false /* no throw */ ))
    {
        try {
            ucb_content.executeCommand(
                u"delete"_ustr, Any( true /* delete physically */ ) );
        }
        catch (const RuntimeException &) {
            throw;
        }
        catch (const Exception &) {
            if (throw_exc)
                throw;
            return false;
        }
    }
    return true;
}


std::vector<sal_Int8> readFile( ::ucbhelper::Content & ucb_content )
{
    std::vector<sal_Int8> bytes;
    Reference<io::XOutputStream> xStream(
        ::xmlscript::createOutputStream( &bytes ) );
    if (! ucb_content.openStream( xStream ))
        throw RuntimeException(
            u"::ucbhelper::Content::openStream( XOutputStream ) failed!"_ustr,
            nullptr );
    return bytes;
}


bool readLine( OUString * res, std::u16string_view startingWith,
               ::ucbhelper::Content & ucb_content, rtl_TextEncoding textenc )
{
    // read whole file:
    std::vector<sal_Int8> bytes( readFile( ucb_content ) );
    OUString file( reinterpret_cast<char const *>(bytes.data()),
                   bytes.size(), textenc );
    sal_Int32 pos = 0;
    for (;;)
    {
        if (file.match( startingWith, pos ))
        {
            OUStringBuffer buf;
            sal_Int32 start = pos;
            pos += startingWith.size();
            for (;;)
            {
                pos = file.indexOf( LF, pos );
                if (pos < 0) { // EOF
                    buf.append( file.subView(start) );
                }
                else
                {
                    if (pos > 0 && file[ pos - 1 ] == CR)
                    {
                        // consume extra CR
                        buf.append( file.subView(start, pos - start - 1) );
                        ++pos;
                    }
                    else
                        buf.append( file.subView(start, pos - start) );
                    ++pos; // consume LF
                    // check next line:
                    if (pos < file.getLength() &&
                        (file[ pos ] == ' ' || file[ pos ] == '\t'))
                    {
                        buf.append( ' ' );
                        ++pos;
                        start = pos;
                        continue;
                    }
                }
                break;
            }
            *res = buf.makeStringAndClear();
            return true;
        }
        // next line:
        sal_Int32 next_lf = file.indexOf( LF, pos );
        if (next_lf < 0) // EOF
            break;
        pos = next_lf + 1;
    }
    return false;
}

bool readProperties( std::vector< std::pair< OUString, OUString> > & out_result,
                     ::ucbhelper::Content & ucb_content )
{
    // read whole file:
    std::vector<sal_Int8> bytes( readFile( ucb_content ) );
    OUString file( reinterpret_cast<char const *>(bytes.data()),
                   bytes.size(), RTL_TEXTENCODING_UTF8);
    sal_Int32 pos = 0;

    for (;;)
    {

        OUStringBuffer buf;
        sal_Int32 start = pos;

        bool bEOF = false;
        pos = file.indexOf( LF, pos );
        if (pos < 0) { // EOF
            buf.append( file.subView(start) );
            bEOF = true;
        }
        else
        {
            if (pos > 0 && file[ pos - 1 ] == CR)
                // consume extra CR
                buf.append( file.subView(start, pos - start - 1) );
            else
                buf.append( file.subView(start, pos - start) );
            pos++;
        }
        OUString aLine = buf.makeStringAndClear();

        sal_Int32 posEqual = aLine.indexOf('=');
        if (posEqual > 0 && (posEqual + 1) <  aLine.getLength())
        {
            OUString name = aLine.copy(0, posEqual);
            OUString value = aLine.copy(posEqual + 1);
            out_result.emplace_back(name, value);
        }

        if (bEOF)
            break;
    }
    return false;
}

}

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