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

#ifndef _DECAF_UTIL_HASHCODE_H_
#define _DECAF_UTIL_HASHCODE_H_

#include <decaf/util/Config.h>

#include <decaf/lang/Pointer.h>
#include <decaf/lang/Float.h>
#include <decaf/lang/Double.h>

#include <string>
#include <functional>

namespace decaf {
namespace util {

    template<typename T>
    struct HashCodeUnaryBase {
    public:

        typedef T argument_type;
        typedef int result_type;

        virtual ~HashCodeUnaryBase() {}
    };

    /**
     * Base HashCode template, specializations are created from this to account for
     * the various native types.
     *
     * @since 1.0
     */
    template<typename T>
    struct HashCode : HashCodeUnaryBase<const T&> {
    public:
        int operator()(const T& arg) const {
            return arg.getHashCode();
        }
    };

    template<typename T>
    struct HashCode<const T> : HashCodeUnaryBase<const T&> {
        int operator()(const T& arg) const {
            return arg.getHashCode();
        }
    };

    template<typename T>
    struct HashCode<T*> : public HashCodeUnaryBase<const T*> {
        int operator()(const T* arg) const {
            if (arg != NULL) {
                return arg->getHashCode();
            }
            return 0;
        }
    };

    template<typename T>
    struct HashCode<const T*> : public HashCodeUnaryBase<const T*> {
        int operator()(const T* arg) const {
            if (arg != NULL) {
                return arg->getHashCode();
            }
            return 0;
        }
    };

    template<>
    struct HashCode<bool> : public HashCodeUnaryBase<bool> {
        int operator()(bool arg) const {
            return arg ? 1231 : 1237;
        }
    };

    #if defined(HAVE_WCHAR_T)
    template<>
    struct HashCode<unsigned char> : public HashCodeUnaryBase<unsigned char> {
        int operator()(unsigned char arg) const {
            return (int) arg;
        }
    };
    #endif

    template<>
    struct HashCode<char> : public HashCodeUnaryBase<char> {
        int operator()(char arg) const {
            return (int) arg;
        }
    };

    template<>
    struct HashCode<wchar_t> : public HashCodeUnaryBase<wchar_t> {
        int operator()(wchar_t arg) const {
            return (int) arg;
        }
    };

    template<>
    struct HashCode<unsigned short> : public HashCodeUnaryBase<unsigned short> {
        int operator()(unsigned short arg) const {
            return (int) arg;
        }
    };

    template<>
    struct HashCode<short> : public HashCodeUnaryBase<short> {
        int operator()(short arg) const {
            return (int) arg;
        }
    };

    template<>
    struct HashCode<unsigned int> : public HashCodeUnaryBase<unsigned int> {
        int operator()(unsigned int arg) const {
            return (int) arg;
        }
    };

    template<>
    struct HashCode<int> : public HashCodeUnaryBase<int> {
        int operator()(int arg) const {
            return arg;
        }
    };

    template<>
    struct HashCode<unsigned long long> : public HashCodeUnaryBase<unsigned long long> {
        int operator()(unsigned long long arg) const {
            return (int) (arg ^ (arg >> 32));
        }
    };

    template<>
    struct HashCode<long long> : public HashCodeUnaryBase<long long> {
        int operator()(long long arg) const {
            return (int) ((unsigned long long) arg ^ ((unsigned long long) arg >> 32));
        }
    };

    template<>
    struct HashCode<float> : public HashCodeUnaryBase<float> {
        int operator()(float arg) const {
            return decaf::lang::Float::floatToIntBits(arg);
        }
    };

    template<>
    struct HashCode<double> : public HashCodeUnaryBase<double> {
        int operator()(double arg) const {
            long long value = decaf::lang::Double::doubleToLongBits(arg);
            return (int) ((unsigned long long) value ^ ((unsigned long long) value >> 32));
        }
    };

    template<>
    struct HashCode<std::string> : public HashCodeUnaryBase<const std::string&> {
        int operator()(const std::string& arg) const {
            int h = 0;
            if (h == 0 && arg.length() > 0) {
                std::string::const_iterator iter = arg.begin();
                for (; iter != arg.end(); ++iter) {
                    h = 31 * h + (*iter);
                }
            }
            return h;
        }
    };

    template<>
    struct HashCode<const std::string> : public HashCodeUnaryBase<const std::string&> {
        int operator()(const std::string& arg) const {
            int h = 0;
            if (h == 0 && arg.length() > 0) {
                std::string::const_iterator iter = arg.begin();
                for (; iter != arg.end(); ++iter) {
                    h = 31 * h + (*iter);
                }
            }
            return h;
        }
    };

    template<typename T>
    struct HashCode< decaf::lang::Pointer<T> > : public HashCodeUnaryBase<decaf::lang::Pointer<T> > {
        int operator()(decaf::lang::Pointer<T> arg) const {
            if (arg != NULL) {
                return HashCode<const T>()(*arg);
            }
            return 0;
        }
    };

}}

#endif /* _DECAF_UTIL_HASHCODE_H_ */
