//*****************************************************************************
// Copyright 2017-2018 Intel Corporation
//
// Licensed 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
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************

#pragma once

#include <limits>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>

#include "ngraph/type/element_type.hpp"

namespace ngraph
{
    namespace runtime
    {
        namespace gpu
        {
            class TypeInfo
            {
            public:
                virtual ~TypeInfo() = default;
                // Helper functions to request information about the underlying c-type
                // that is implicitly associated with the registed element::Type
                virtual std::string lowest() const = 0;
                virtual std::string min() const = 0;
                virtual std::string max() const = 0;

                using TypeDispatch = std::unordered_map<std::string, std::shared_ptr<TypeInfo>>;
                static const std::shared_ptr<TypeInfo>& Get(const element::Type& type)
                {
                    return dispatcher.at(type.c_type_string());
                }
                static const std::shared_ptr<TypeInfo>& Get(std::string type)
                {
                    return dispatcher.at(type);
                }

            protected:
                template <typename T>
                std::string to_string(const T& val) const
                {
                    std::stringstream ss;
                    ss.precision(std::numeric_limits<T>::digits10 + 2);
                    ss << val;
                    return ss.str();
                }

            private:
                static const TypeDispatch dispatcher;
            };

            template <typename T>
            class TypeInfo_Impl : public TypeInfo
            {
            public:
                std::string lowest() const override
                {
                    return to_string<T>(std::numeric_limits<T>::lowest());
                }
                std::string min() const override
                {
                    return to_string<T>(std::numeric_limits<T>::min());
                }
                std::string max() const override
                {
                    return to_string<T>(std::numeric_limits<T>::max());
                }
            };
        }
    }
}
