// Copyright (C) 2011  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_BORDER_EnUMERATOR_H_
#define DLIB_BORDER_EnUMERATOR_H_

#include "border_enumerator_abstract.h"
#include "rectangle.h"

namespace dlib
{

// ----------------------------------------------------------------------------------------

    class border_enumerator
    {
    public:
        border_enumerator(
        ) 
        {
            reset();
        }

        border_enumerator(
            const rectangle& rect_,
            unsigned long border_size
        ) : 
            rect(rect_),
            inner_rect(shrink_rect(rect_, border_size))
        {
            reset();
        }

        border_enumerator(
            const rectangle& rect_,
            const rectangle& non_border_region
        ) : 
            rect(rect_),
            inner_rect(non_border_region.intersect(rect))
        {
            reset();
        }

        void reset (
        ) 
        {
            // make the four rectangles that surround inner_rect and intersect them
            // with rect.
            bleft   = rect.intersect(rectangle(std::numeric_limits<long>::min(),
                                               std::numeric_limits<long>::min(),
                                               inner_rect.left()-1,
                                               std::numeric_limits<long>::max()));

            bright  = rect.intersect(rectangle(inner_rect.right()+1,
                                               std::numeric_limits<long>::min(),
                                               std::numeric_limits<long>::max(),
                                               std::numeric_limits<long>::max()));

            btop    = rect.intersect(rectangle(inner_rect.left(),
                                               std::numeric_limits<long>::min(),
                                               inner_rect.right(),
                                               inner_rect.top()-1));

            bbottom = rect.intersect(rectangle(inner_rect.left(),
                                               inner_rect.bottom()+1,
                                               inner_rect.right(),
                                               std::numeric_limits<long>::max()));

            p = bleft.tl_corner();
            p.x() -= 1;

            mode = atleft;
        }

        bool at_start (
        ) const
        {
            point temp = bleft.tl_corner();
            temp.x() -=1;
            return temp == p;
        }

        bool current_element_valid(
        ) const
        {
            return rect.contains(p);
        }

        bool move_next()
        {
            if (mode == atleft)
            {
                if (advance_point(bleft, p))
                    return true;
                    
                mode = attop;
                p = btop.tl_corner();
                p.x() -= 1;
            }
            if (mode == attop)
            {
                if (advance_point(btop, p))
                    return true;
                    
                mode = atright;
                p = bright.tl_corner();
                p.x() -= 1;
            }
            if (mode == atright)
            {
                if (advance_point(bright, p))
                    return true;
                    
                mode = atbottom;
                p = bbottom.tl_corner();
                p.x() -= 1;
            }

            if (advance_point(bbottom, p))
                return true;

            // put p outside rect since there are no more points to enumerate
            p = rect.br_corner();
            p.x() += 1;
                    
            return false;
        }

        size_t size (
        ) const
        {
            return rect.area() - inner_rect.area();
        }

        const point& element (
        ) const
        {
            // make sure requires clause is not broken
            DLIB_ASSERT(current_element_valid(),
                "\t point border_enumerator::element()"
                << "\n\t This function can't be called unless the element is valid."
                << "\n\t this: " << this
                );

            return p;
        }

    private:

        bool advance_point (
            const rectangle& r,
            point& p
        ) const
        {
            p.x() += 1;
            if (p.x() > r.right())
            {
                p.x() = r.left();
                p.y() += 1;
            }

            return r.contains(p);
        }

        point p;
        rectangle rect;
        rectangle inner_rect;  // the non-border regions of rect

        enum emode
        {
            atleft,
            atright,
            atbottom,
            attop
        };

        emode mode;

        rectangle btop, bleft, bright, bbottom;
    };

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_BORDER_EnUMERATOR_H_

