/* Copyright 2018 kevin Lau (http://github.com/stormycatcat)

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

#ifndef _STDLIB_MEMORY_
#define _STDLIB_MEMORY_

#include <type_traits>

namespace std
{

template<class T>
struct default_delete
{
    constexpr default_delete() noexcept = default;

    template<class U,enable_if_t<is_convertible_v<U*,T*>,int> =0>
    default_delete(const default_delete<U>& d) noexcept
    {
    }

    void operator()(T* ptr)const noexcept
    {
        static_assert(0<sizeof(T),"can't delete an imcomplete type");
        delete ptr;
    }

    template<class U,enable_if_t<is_convertible_v<U*,T*>,int> =0>
    void operator()(U* ptr)const noexcept
    {
        static_assert(0<sizeof(U),"can't delete an imcomplete type");
        delete ptr;
    }
};

template<class T>
struct default_delete<T[]>
{
    constexpr default_delete() noexcept = default;

    template<class U,enable_if_t<is_convertible_v<U(*)[],T(*)[]>,int> =0>
    default_delete(const default_delete<U[]>& d) noexcept
    {
    }

    void operator()(T* ptr)const noexcept
    {
        static_assert(0<sizeof(T),"can't delete an imcomplete type");
        delete[] ptr;
    }

    template<class U,enable_if_t<is_convertible_v<U(*)[],T(*)[]>,int> =0>
    void operator()(U* ptr)const noexcept
    {
        static_assert(0<sizeof(U),"can't delete an imcomplete type");
        delete[] ptr;
    }
};


template<class T,class Deleter=default_delete<T>>
class unique_ptr
{
public:
    using pointer=T*;
    using element_type=T;
    using deleter_type=Deleter;

    constexpr unique_ptr()noexcept :p(nullptr),d()
    {
    }

    constexpr unique_ptr(nullptr_t np)noexcept :p(np),d()
    {
    }

    explicit unique_ptr(pointer _p)noexcept :p(_p),d()
    {
    }

    ~unique_ptr()noexcept
    {
        reset();
    }

    unique_ptr& operator=(unique_ptr& rhs) noexcept
    {
        reset(rhs.p);
        rhs.reset();
        return *this;
    }

    void release() noexcept
    {
        reset();
    }

    template<class U>
    void reset(U new_ptr) noexcept
    {
        if(p)
            get_deleter()(p);
        p=new_ptr;
    }

    void reset(nullptr_t np=nullptr)noexcept
    {
        if(p)
            get_deleter()(p);
        p=np;
    }

    void swap(unique_ptr& other)noexcept
    {
        std::swap(p,other.p);
        std::swap(d,other.d);
    }

    Deleter& get_deleter()noexcept
    {
        return d;
    }

    const Deleter& get_deleter()const noexcept
    {
        return (const Deleter&)d;
    }

    explicit operator bool()const noexcept
    {
        if(p)
            return true;
        return false;
    }

    T& operator*()
    {
        return *p;
    }

    const T& operator*()const noexcept
    {
        return (const T&)*p;
    }

    pointer operator->() noexcept
    {
        return p;
    }

    const T* operator->()const noexcept
    {
        return (const T*)p;
    }

protected:
    pointer p;
    Deleter d;
};

template<class T,class Deleter>
class unique_ptr<T[],Deleter>
{
public:
    
};

}

#endif
