/////////////////////////////////////////////////////////////////////////////
// Name:      src/dfb/region.cpp
// Purpose:   Region handling for wxWidgets/DFB
// Author:    Vaclav Slavik
// Created:   2006-08-08
// Copyright: (c) 2006 REA Elektronik GmbH
// Licence:   wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"


#include "wx/region.h"

wxIMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject);
wxIMPLEMENT_DYNAMIC_CLASS(wxRegionIterator, wxObject);

//-----------------------------------------------------------------------------
// wxRegionRefData
//-----------------------------------------------------------------------------

class WXDLLEXPORT wxRegionRefData : public wxGDIRefData
{
public:
    wxRegionRefData() {}
    wxRegionRefData(const wxRect& rect) : m_rect(rect) {}
    wxRegionRefData(const wxRegionRefData& data) : m_rect(data.m_rect) {}

    ~wxRegionRefData() {}

    // default assignment and comparison operators are OK

    wxRect m_rect;
};

#define M_REGION_OF(r) ((wxRegionRefData*)((r).m_refData))
#define M_REGION       M_REGION_OF(*this)

//-----------------------------------------------------------------------------
// wxRegion
//-----------------------------------------------------------------------------

wxGDIRefData *wxRegion::CreateGDIRefData() const
{
    return new wxRegionRefData;
}

wxGDIRefData *wxRegion::CloneGDIRefData(const wxGDIRefData *data) const
{
    return new wxRegionRefData(*(wxRegionRefData *)data);
}

wxRegion::wxRegion()
{
    m_refData = nullptr;
}

wxRegion::wxRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
{
    m_refData = new wxRegionRefData(wxRect(x, y, w, h));
}

wxRegion::wxRegion(const wxPoint& topLeft, const wxPoint& bottomRight)
{
    m_refData = new wxRegionRefData(wxRect(topLeft, bottomRight));
}

wxRegion::wxRegion(const wxRect& r)
{
    m_refData = new wxRegionRefData(r);
}

wxRegion::wxRegion(size_t n, const wxPoint *points, wxPolygonFillMode fillStyle)
{
#warning "implement this"
    m_refData = nullptr;
}

wxRegion::~wxRegion()
{
    // m_refData unrefed in ~wxObject
}

//-----------------------------------------------------------------------------
// Information about the region
//-----------------------------------------------------------------------------

bool wxRegion::DoIsEqual(const wxRegion& region) const
{
    return M_REGION->m_rect == M_REGION_OF(region)->m_rect;
}

bool wxRegion::DoGetBox(wxCoord& x, wxCoord& y, wxCoord&w, wxCoord &h) const
{
    if ( !m_refData )
        return false;

    const wxRect& r = M_REGION->m_rect;
    x = r.GetX();
    y = r.GetY();
    w = r.GetWidth();
    h = r.GetHeight();

    return true;
}

bool wxRegion::IsEmpty() const
{
    if (!m_refData)
        return true;

    return M_REGION->m_rect.IsEmpty();
}

//-----------------------------------------------------------------------------
// Modifications
//-----------------------------------------------------------------------------

void wxRegion::Clear()
{
    UnRef();
}

bool wxRegion::DoOffset(wxCoord x, wxCoord y)
{
    AllocExclusive();
    M_REGION->m_rect.Offset(x, y);
    return true;
}

bool wxRegion::DoUnionWithRect(const wxRect& rect)
{
    AllocExclusive();

    if ( M_REGION->m_rect.Contains(rect) )
    {
        return true;
    }
    else if ( rect.Contains(M_REGION->m_rect) )
    {
        M_REGION->m_rect = rect;
        return true;
    }
    else
    {
        wxFAIL_MSG( "only rectangular regions are supported" );
        return false;
    }
}

bool wxRegion::DoUnionWithRegion(const wxRegion& region)
{
    wxCHECK_MSG( region.IsOk(), false, "invalid region" );
    return DoUnionWithRect(M_REGION_OF(region)->m_rect);
}

bool wxRegion::DoIntersect(const wxRegion& region)
{
    wxCHECK_MSG( region.IsOk(), false, "invalid region" );

    AllocExclusive();
    M_REGION->m_rect.Intersect(M_REGION_OF(region)->m_rect);
    return true;
}

bool wxRegion::DoSubtract(const wxRegion& region)
{
    wxCHECK_MSG( region.IsOk(), false, "invalid region" );
    wxCHECK_MSG( IsOk(), false, "invalid region" );

    const wxRect& rect = M_REGION_OF(region)->m_rect;

    if ( rect.Contains(M_REGION->m_rect) )
    {
        // subtracted rectangle contains this one, so the result is empty
        // rectangle
        M_REGION->m_rect = wxRect();
        return true;
    }
    else if ( !M_REGION->m_rect.Intersects(rect) )
    {
        // the rectangles are disjoint, so subtracting has no effect
        return true;
    }
    else
    {
        wxFAIL_MSG( "only rectangular regions implemented" );
        return false;
    }
}

bool wxRegion::DoXor(const wxRegion& region)
{
    wxCHECK_MSG( region.IsOk(), false, "invalid region" );
    wxFAIL_MSG( "Xor not implemented" );
    return false;
}


//-----------------------------------------------------------------------------
// Tests
//-----------------------------------------------------------------------------

wxRegionContain wxRegion::DoContainsPoint(wxCoord x, wxCoord y) const
{
    wxCHECK_MSG( IsOk(), wxOutRegion, "invalid region" );

    if (M_REGION->m_rect.Contains(x, y))
        return wxInRegion;
    else
        return wxOutRegion;
}

wxRegionContain wxRegion::DoContainsRect(const wxRect& rect) const
{
    wxCHECK_MSG( IsOk(), wxOutRegion, "invalid region" );

    // 1) is the rectangle entirely covered by the region?
    if (M_REGION->m_rect.Contains(rect))
        return wxInRegion;

    // 2) is the rectangle completely outside the region?
    if (!M_REGION->m_rect.Intersects(rect))
        return wxOutRegion;

    // 3) neither case happened => it is partially covered:
    return wxPartRegion;
}

//-----------------------------------------------------------------------------
// wxRegionIterator
//-----------------------------------------------------------------------------

void wxRegionIterator::Reset(const wxRegion& region)
{
    wxRegionRefData *d = M_REGION_OF(region);
    m_rect = d ? d->m_rect : wxRect();
}

wxRegionIterator& wxRegionIterator::operator++()
{
    // there's only one rectangle in the iterator, so iterating always
    // reaches the end:
    Reset();
    return *this;
}

wxRegionIterator wxRegionIterator::operator++(int)
{
    wxRegionIterator tmp = *this;

    // there's only one rectangle in the iterator, so iterating always
    // reaches the end:
    Reset();

    return tmp;
}
