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

#pragma once

#include <registry/typereg_reader.hxx>
#include <registry/refltype.hxx>
#include <registry/types.hxx>
#include <registry/version.h>

#include <rtl/ustring.hxx>
#include <sal/types.h>

#include <new>

namespace typereg {

/**
   A type reader working on a binary blob that represents a UNOIDL type.

   <p>Instances of this class are not multi-thread&ndash;safe.</p>

   @since UDK 3.2.0
 */
class Reader {
public:
    /**
       Creates a type reader.

       <p>If the given binary blob is malformed, or of a version larger than
       <code>maxVersion</code>, the created type reader is flagged as
       invalid.</p>

       @param buffer the binary blob representing the type; must point to at
       least <code>length</code> bytes, and need only be byte-aligned

       @param length the size in bytes of the binary blob representing the type

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    Reader(void const * buffer, sal_uInt32 length)
    {
        if (!typereg_reader_create(buffer, length, &m_handle))
        {
            throw std::bad_alloc();
        }
    }

    /**
       Shares a type reader between two <code>Reader</code> instances.

       @param other another <code>Reader</code> instance
     */
    Reader(Reader const & other): m_handle(other.m_handle) {
        typereg_reader_acquire(m_handle);
    }

    /**
       Destroys this <code>Reader</code> instance.

       <p>The underlying type reader is only destroyed if this instance was its
       last user.</p>
     */
    ~Reader() {
        typereg_reader_release(m_handle);
    }

    /**
       Replaces the underlying type reader.

       @param other any <code>Reader</code> instance

       @return this <code>Reader</code> instance
     */
    Reader & operator =(Reader const & other) {
        Reader temp(other);
        std::swap(m_handle, temp.m_handle);
        return *this;
    }

    /**
       Returns whether this type reader is valid.

       @return true iff this type reader is valid
     */
    bool isValid() const {
        return m_handle != nullptr;
    }

    /**
       Returns the binary blob version of this type reader.

       @return the version of the binary blob from which this type reader was
       constructed; if this type reader is invalid,
       <code>TYPEREG_VERSION_0</code> is returned
     */
    typereg_Version getVersion() const {
        return typereg_reader_getVersion(m_handle);
    }

    /**
       Returns the documentation of this type reader.

       @return the documentation of this type reader; if this type reader is
       invalid, an empty string is returned

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getDocumentation() const {
        rtl_uString * s = nullptr;
        typereg_reader_getDocumentation(m_handle, &s);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the file name of this type reader.

       @return the file name of this type reader; if this type reader is
       invalid, an empty string is returned

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
       @deprecated
     */
    OUString getFileName() const {
        rtl_uString * s = nullptr;
        typereg_reader_getFileName(m_handle, &s);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the type class of this type reader.

       <p>This function will always return the type class without the internal
       <code>RT_TYPE_PUBLISHED</code> flag set.  Use <code>isPublished</code> to
       determine whether this type reader is published.</p>

       @return the type class of this type reader; if this type reader is
       invalid, <code>RT_TYPE_INVALID</code> is returned
     */
    RTTypeClass getTypeClass() const {
        return typereg_reader_getTypeClass(m_handle);
    }

    /**
       Returns whether this type reader is published.

       @return whether this type reader is published; if this type reader is
       invalid, <code>false</code> is returned
     */
    bool isPublished() const {
        return typereg_reader_isPublished(m_handle);
    }

    /**
       Returns the type name of this type reader.

       @return the type name of this type reader; if this type reader is
       invalid, an empty string is returned

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getTypeName() const {
        rtl_uString * s = nullptr;
        typereg_reader_getTypeName(m_handle, &s);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the number of super types of this type reader.

       @return the number of super types of this type reader; if this type
       reader is invalid, zero is returned
     */
    sal_uInt16 getSuperTypeCount() const {
        return typereg_reader_getSuperTypeCount(m_handle);
    }

    /**
       Returns the type name of a super type of this type reader.

       @param index a valid index into the range of super types of this type
       reader

       @return the type name of the given super type

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getSuperTypeName(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getSuperTypeName(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the number of fields of this type reader.

       @return the number of fields of this type reader; if this type reader is
       invalid, zero is returned
     */
    sal_uInt16 getFieldCount() const {
        return typereg_reader_getFieldCount(m_handle);
    }

    /**
       Returns the documentation of a field of this type reader.

       @param index a valid index into the range of fields of this type reader

       @return the documentation of the given field

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getFieldDocumentation(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getFieldDocumentation(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the file name of a field of this type reader.

       @param index a valid index into the range of fields of this type reader

       @return the file name of the given field

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
       @deprecated
     */
    OUString getFieldFileName(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getFieldFileName(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the flags of a field of this type reader.

       @param index a valid index into the range of fields of this type reader

       @return the flags of the given field
     */
    RTFieldAccess getFieldFlags(sal_uInt16 index) const {
        return typereg_reader_getFieldFlags(m_handle, index);
    }

    /**
       Returns the name of a field of this type reader.

       @param index a valid index into the range of fields of this type reader

       @return the name of the given field

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getFieldName(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getFieldName(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the type name of a field of this type reader.

       @param index a valid index into the range of fields of this type reader

       @return the type name of the given field

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getFieldTypeName(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getFieldTypeName(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the value of a field of this type reader.

       @param index a valid index into the range of fields of this type reader

       @return the value of the given field

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    RTConstValue getFieldValue(sal_uInt16 index) const {
        RTConstValue v;
        if (!typereg_reader_getFieldValue(
                m_handle, index, &v.m_type, &v.m_value))
        {
            throw std::bad_alloc();
        }
        return v;
    }

    /**
       Returns the number of methods of this type reader.

       @return the number of methods of this type reader; if this type reader is
       invalid, zero is returned
     */
    sal_uInt16 getMethodCount() const {
        return typereg_reader_getMethodCount(m_handle);
    }

    /**
       Returns the documentation of a method of this type reader.

       @param index a valid index into the range of methods of this type reader

       @return the documentation of the given method

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getMethodDocumentation(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getMethodDocumentation(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the flags of a method of this type reader.

       @param index a valid index into the range of methods of this type reader

       @return the flags of the given method
     */
    RTMethodMode getMethodFlags(sal_uInt16 index) const {
        return typereg_reader_getMethodFlags(m_handle, index);
    }

    /**
       Returns the name of a method of this type reader.

       @param index a valid index into the range of methods of this type reader

       @return the name of the given method

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getMethodName(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getMethodName(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the return type name of a method of this type reader.

       @param index a valid index into the range of methods of this type reader

       @return the return type name of the given method

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getMethodReturnTypeName(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getMethodReturnTypeName(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the number of parameters of a method of this type reader.

       @param index a valid index into the range of methods of this type reader

       @return the number of parameters of the given method
     */
    sal_uInt16 getMethodParameterCount(sal_uInt16 index) const {
        return typereg_reader_getMethodParameterCount(m_handle, index);
    }

    /**
       Returns the flags of a parameter of a method of this type reader.

       @param methodIndex a valid index into the range of methods of this type
       reader

       @param parameterIndex a valid index into the range of parameters of the
       given method

       @return the flags of the given method parameter
     */
    RTParamMode getMethodParameterFlags(
        sal_uInt16 methodIndex, sal_uInt16 parameterIndex) const
    {
        return typereg_reader_getMethodParameterFlags(
            m_handle, methodIndex, parameterIndex);
    }

    /**
       Returns the name of a parameter of a method of this type reader.

       @param methodIndex a valid index into the range of methods of this type
       reader

       @param parameterIndex a valid index into the range of parameters of the
       given method

       @return the name of the given method parameter

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getMethodParameterName(
        sal_uInt16 methodIndex, sal_uInt16 parameterIndex) const
    {
        rtl_uString * s = nullptr;
        typereg_reader_getMethodParameterName(
            m_handle, &s, methodIndex, parameterIndex);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the type name of a parameter of a method of this type reader.

       @param methodIndex a valid index into the range of methods of this type
       reader

       @param parameterIndex a valid index into the range of parameters of the
       given method

       @return the type name of the given method parameter

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getMethodParameterTypeName(
        sal_uInt16 methodIndex, sal_uInt16 parameterIndex) const
    {
        rtl_uString * s = nullptr;
        typereg_reader_getMethodParameterTypeName(
            m_handle, &s, methodIndex, parameterIndex);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the number of exceptions of a method of this type reader.

       @param index a valid index into the range of methods of this type reader

       @return the number of exceptions of the given method
     */
    sal_uInt16 getMethodExceptionCount(sal_uInt16 index) const {
        return typereg_reader_getMethodExceptionCount(m_handle, index);
    }

    /**
       Returns the type name of an exception of a method of this type reader.

       @param methodIndex a valid index into the range of methods of this type
       reader

       @param exceptionIndex a valid index into the range of exceptions of the
       given method

       @return the type name of the given method exception

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getMethodExceptionTypeName(
        sal_uInt16 methodIndex, sal_uInt16 exceptionIndex) const
    {
        rtl_uString * s = nullptr;
        typereg_reader_getMethodExceptionTypeName(
            m_handle, &s, methodIndex, exceptionIndex);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the number of references of this type reader.

       @return the number of references of this type reader; if this type reader
       is invalid, zero is returned
     */
    sal_uInt16 getReferenceCount() const {
        return typereg_reader_getReferenceCount(m_handle);
    }

    /**
       Returns the documentation of a reference of this type reader.

       @param index a valid index into the range of references of this type
       reader

       @return the documentation of the given reference

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getReferenceDocumentation(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getReferenceDocumentation(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

    /**
       Returns the flags of a reference of this type reader.

       @param index a valid index into the range of references of this type
       reader

       @return the flags of the given reference
     */
    RTFieldAccess getReferenceFlags(sal_uInt16 index) const {
        return typereg_reader_getReferenceFlags(m_handle, index);
    }

    /**
       Returns the sort of a reference of this type reader.

       @param index a valid index into the range of references of this type
       reader

       @return the sort of the given reference
     */
    RTReferenceType getReferenceSort(sal_uInt16 index) const {
        return typereg_reader_getReferenceSort(m_handle, index);
    }

    /**
       Returns the type name of a reference of this type reader.

       @param index a valid index into the range of references of this type
       reader

       @return the type name of the given reference

       @exception std::bad_alloc is raised if an out-of-memory condition occurs
     */
    OUString getReferenceTypeName(sal_uInt16 index) const {
        rtl_uString * s = nullptr;
        typereg_reader_getReferenceTypeName(m_handle, &s, index);
        if (s == nullptr) {
            throw std::bad_alloc();
        }
        return OUString(s, SAL_NO_ACQUIRE);
    }

private:
    void * m_handle;
};

}

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