#include "D3D9_Overlay.h"

#ifdef WIN32

namespace videonext { namespace media {

struct VERTEX_2D
{
    float x, y, z;
    float rhw;
    D3DCOLOR    color;      // diffuse color
};

#define D3DFVF_VERTEX_2D (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)


//////////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Overlay /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
D3D9_Overlay::D3D9_Overlay(IDirect3DDevice9* device, D3DCOLOR color)
    : device_(device)
    , color_(color)
{
}

/*virtual*/ D3D9_Overlay::~D3D9_Overlay()
{
}


//////////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Line_Overlay ////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
D3D9_Line_Overlay::D3D9_Line_Overlay(IDirect3DDevice9* device, POINT p1, POINT p2, INT width, D3DCOLOR color)
    : D3D9_Overlay(device, color)
{
    D3DXCreateLine(device_, &line_);
    vectors_[0].x = p1.x;
    vectors_[0].y = p1.y;
    vectors_[1].x = p2.x;
    vectors_[1].y = p2.y;
    ID3DXLine_SetWidth(line_, width);
}

/*virtual*/ D3D9_Line_Overlay::~D3D9_Line_Overlay()
{
    IDirect3D9_SafeRelease(line_);
}

/*virtual*/ HRESULT D3D9_Line_Overlay::draw()
{
    HR(ID3DXLine_Begin(line_));
    HR(ID3DXLine_Draw(line_, vectors_, 2, color_));
    return ID3DXLine_End(line_);
}


///////////////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Rectangle_Overlay ////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
D3D9_Rectangle_Overlay::D3D9_Rectangle_Overlay(IDirect3DDevice9* device, RECT rectangle, INT width, D3DCOLOR color)
    : D3D9_Overlay(device, color)
{
    D3DXCreateLine(device_, &line_);
    
    ID3DXLine_SetWidth(line_, width);

    vectors_[0].x = rectangle.left;
    vectors_[0].y = rectangle.top;
    
    vectors_[1].x = rectangle.right;
    vectors_[1].y = rectangle.top;

    vectors_[2].x = rectangle.right;
    vectors_[2].y = rectangle.bottom;

    vectors_[3].x = rectangle.left;
    vectors_[3].y = rectangle.bottom;
    
    vectors_[4].x = rectangle.left;
    vectors_[4].y = rectangle.top;
}

/*virtual*/ D3D9_Rectangle_Overlay::~D3D9_Rectangle_Overlay()
{
    IDirect3D9_SafeRelease(line_);
}

/*virtual*/ HRESULT D3D9_Rectangle_Overlay::draw()
{
    HR(ID3DXLine_Begin(line_));
    HR(ID3DXLine_Draw(line_, vectors_, 5, color_));
    return ID3DXLine_End(line_);
}


///////////////////////////////////////////////////////////////////////////////////////
////////////////////// D3D9_Fill_Rectangle_Overlay ////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
D3D9_Fill_Rectangle_Overlay::D3D9_Fill_Rectangle_Overlay(IDirect3DDevice9* device, RECT rectangle, D3DCOLOR color)
    : D3D9_Overlay(device, color)
    , rect_(rectangle)
{
}
    
/*virtual*/ D3D9_Fill_Rectangle_Overlay::~D3D9_Fill_Rectangle_Overlay()
{
    IDirect3D9_SafeRelease(vertex_buffer_);
}

/*virtual*/ HRESULT D3D9_Fill_Rectangle_Overlay::draw()
{
    IDirect3DDevice9_CreateVertexBuffer(device_, sizeof(VERTEX_2D) * 4, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_VERTEX_2D, D3DPOOL_DEFAULT, &vertex_buffer_, NULL);

    VERTEX_2D *verticles;

    POINT points[4] = {
        {rect_.left,  rect_.top},
        {rect_.right, rect_.top},
        {rect_.right, rect_.bottom},
        {rect_.left , rect_.bottom},
    };

    HR(IDirect3DVertexBuffer9_Lock(vertex_buffer_, 0, 0, (void**)&verticles, D3DLOCK_DISCARD));
    for (unsigned i = 0; i < 4; i++)
    {
        verticles[i].x     = points[i].x;
        verticles[i].y     = points[i].y;
        verticles[i].z     = 0.0f;
        verticles[i].rhw   = 1.0f;
        verticles[i].color = color_;
    }
    HR(IDirect3DVertexBuffer9_Unlock(vertex_buffer_));
    
    HR(IDirect3DDevice9_SetStreamSource(device_, 0, vertex_buffer_, 0, sizeof(VERTEX_2D)));
    HR(IDirect3DDevice9_SetFVF(device_, D3DFVF_VERTEX_2D));
    HR(IDirect3DDevice9_SetVertexShader(device_, 0));
    HR(IDirect3DDevice9_SetPixelShader(device_, 0));
    HR(IDirect3DDevice9_SetTexture(device_, 0, 0));
    HR(IDirect3DDevice9_DrawPrimitive(device_, D3DPT_TRIANGLEFAN, 0, 2));

    return S_OK;
}


///////////////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Polygon_Overlay //////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
D3D9_Polygon_Overlay::D3D9_Polygon_Overlay(IDirect3DDevice9* device, POINT* points, INT pointsLen, INT width, D3DCOLOR color)
    : D3D9_Overlay(device, color) 
{
    D3DXCreateLine(device_, &line_);
    vectors_ = new D3DXVECTOR2[pointsLen + 1];
    for(int i = 0 ; i < pointsLen; i++)
    {
        vectors_[i].x = points[i].x;
        vectors_[i].y = points[i].y;
    }

    vectors_[pointsLen].x = points[0].x;
    vectors_[pointsLen].y = points[0].y;

    numOfVectors_ = pointsLen + 1;

    ID3DXLine_SetWidth(line_, width);
}

/*virtual*/ D3D9_Polygon_Overlay::~D3D9_Polygon_Overlay()
{
    delete[] vectors_;
    IDirect3D9_SafeRelease(line_);
}

/*virtual*/ HRESULT D3D9_Polygon_Overlay::draw()
{
    HR(ID3DXLine_Begin(line_));
    HR(ID3DXLine_Draw(line_, vectors_, numOfVectors_, color_));
    return ID3DXLine_End(line_);
}


///////////////////////////////////////////////////////////////////////////////////////
////////////////////// D3D9_Fill_Polygon_Overlay //////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
D3D9_Fill_Polygon_Overlay::D3D9_Fill_Polygon_Overlay(IDirect3DDevice9* device, POINT* points, INT pointsLen, D3DCOLOR color)
    : D3D9_Overlay(device, color)
{
    IDirect3DDevice9_CreateVertexBuffer(device, sizeof(VERTEX_2D) * pointsLen, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_VERTEX_2D, D3DPOOL_DEFAULT, &vertex_buffer_, NULL);

    for (int i = 0; i < pointsLen; ++i)
        points_.push_back(points[i]);
}
    
/*virtual*/ D3D9_Fill_Polygon_Overlay::~D3D9_Fill_Polygon_Overlay()
{
    IDirect3D9_SafeRelease(vertex_buffer_);
}

/*virtual*/ HRESULT D3D9_Fill_Polygon_Overlay::draw()
{
    VERTEX_2D *verticles;
    HR(IDirect3DVertexBuffer9_Lock(vertex_buffer_, 0, 0, (void**)&verticles, D3DLOCK_DISCARD));
    for (unsigned i = 0; i < points_.size(); i++)
    {
        verticles[i].x     = points_[i].x;
        verticles[i].y     = points_[i].y;
        verticles[i].z     = 0.0f;
        verticles[i].rhw   = 1.0f;
        verticles[i].color = color_;
    }
    HR(IDirect3DVertexBuffer9_Unlock(vertex_buffer_));
    
    HR(IDirect3DDevice9_SetStreamSource(device_, 0, vertex_buffer_, 0, sizeof(VERTEX_2D)));
    HR(IDirect3DDevice9_SetFVF(device_, D3DFVF_VERTEX_2D));
    HR(IDirect3DDevice9_SetVertexShader(device_, 0));
    HR(IDirect3DDevice9_SetPixelShader(device_, 0));
    HR(IDirect3DDevice9_SetTexture(device_, 0, 0));
    HR(IDirect3DDevice9_DrawPrimitive(device_, D3DPT_TRIANGLEFAN, 0, points_.size() - 2));

    return S_OK;
}


///////////////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Ellipse_Overlay //////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
D3D9_Ellipse_Overlay::D3D9_Ellipse_Overlay(IDirect3DDevice9* device, INT center_x, INT center_y, INT width, INT height, INT line_width, D3DCOLOR color)
    : D3D9_Overlay(device, color)
    , center_x_(center_x), center_y_(center_y), width_(width), height_(height)
{
    D3DXCreateLine(device_, &line_);
    ID3DXLine_SetWidth(line_, line_width);

    float theta = 0.0f;               // angle that will be increased each loop
    float step  = (2*M_PI)/360.0f;    // amount to add to theta each time (degrees)
    
    while (theta <= 2*M_PI)
    {
        float x = center_x_ + (width_/2) * cos(theta);
        float y = center_y_  + (height_/2) * sin(theta);

        D3DXVECTOR2 point(x, y);
        vectors_.push_back(point);

        theta += step;
    }
}
    
/*virtual*/ D3D9_Ellipse_Overlay::~D3D9_Ellipse_Overlay()
{
   IDirect3D9_SafeRelease(line_);
}

/*virtual*/ HRESULT D3D9_Ellipse_Overlay::draw()
{
    // for (int y = -height_; y <= height_; y++)
    // {
    //     for (int x = -width_; x <= width_; x++)
    //     {
    //         if (x*x*height_*height_+y*y*width_*width_ <= height_*height_*width_*width_)
    //         {
    //             D3DXVECTOR2 point(x_+ x, y_ + y);
    //             vectors_.push_back(point);
    //         }
    //     }
    // }


    HR(ID3DXLine_Begin(line_));
    HR(ID3DXLine_Draw(line_, (D3DXVECTOR2*)&vectors_[0], vectors_.size(), color_));
    return ID3DXLine_End(line_);
}


///////////////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Fill_Ellipse_Overlay /////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
D3D9_Fill_Ellipse_Overlay::D3D9_Fill_Ellipse_Overlay(IDirect3DDevice9* device, INT center_x, INT center_y, INT width, INT height, D3DCOLOR color)
    : D3D9_Overlay(device, color)
    , center_x_(center_x), center_y_(center_y), width_(width), height_(height), vertex_buffer_(0)
{
    float theta = 0.0f;               // angle that will be increased each loop
    float step  = (2*M_PI)/180.0f;    // amount to add to theta each time (degrees)

    POINT center = {center_x_, center_y_};
    points_.push_back(center);

    while (theta <= 2*M_PI)
    {
        int x = center_x_ + (width_/2) * cos(theta);
        int y = center_y_  + (height_/2) * sin(theta);

        POINT p = {x, y};
        points_.push_back(p);

        theta += step;
    }
}
    
/*virtual*/ D3D9_Fill_Ellipse_Overlay::~D3D9_Fill_Ellipse_Overlay()
{
    IDirect3D9_SafeRelease(vertex_buffer_);
}

/*virtual*/ HRESULT D3D9_Fill_Ellipse_Overlay::draw()
{
    HR(IDirect3DDevice9_CreateVertexBuffer(device_, sizeof(VERTEX_2D) * points_.size(), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_VERTEX_2D, D3DPOOL_DEFAULT, &vertex_buffer_, NULL));

    VERTEX_2D *verticles;
    HR(IDirect3DVertexBuffer9_Lock(vertex_buffer_, 0, 0, (void**)&verticles, D3DLOCK_DISCARD));
    for (unsigned i = 0; i < points_.size(); i++)
    {
        verticles[i].x     = points_[i].x;
        verticles[i].y     = points_[i].y;
        verticles[i].z     = 0.0f;
        verticles[i].rhw   = 1.0f;
        verticles[i].color = color_;
    }
    HR(IDirect3DVertexBuffer9_Unlock(vertex_buffer_));

    HR(IDirect3DDevice9_SetStreamSource(device_, 0, vertex_buffer_, 0, sizeof(VERTEX_2D)));
    HR(IDirect3DDevice9_SetFVF(device_, D3DFVF_VERTEX_2D));
    HR(IDirect3DDevice9_SetVertexShader(device_, 0));
    HR(IDirect3DDevice9_SetPixelShader(device_, 0));
    HR(IDirect3DDevice9_SetTexture(device_, 0, 0));
    HR(IDirect3DDevice9_DrawPrimitive(device_, D3DPT_TRIANGLEFAN, 0, points_.size() - 2));

    return S_OK;
}


////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Font //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
D3D9_Font::D3D9_Font(IDirect3DDevice9* device, LPCSTR fontName, INT fontSize, UINT weight)
{
    D3DXCreateFont( device, fontSize, 0, weight, 0, FALSE, 0, 0, 0, 0, fontName, &font_ );
}

/*virtual*/ D3D9_Font::~D3D9_Font()
{
    IDirect3D9_SafeRelease(font_);
}

HRESULT D3D9_Font::prepare_text(char *text)
{
    return ID3DXFont_PreloadText(font_, text, -1);
}

HRESULT D3D9_Font::draw_text(char *text, LPRECT rect, D3DCOLOR color)
{
    return ID3DXFont_DrawText(font_, NULL, text, -1, rect, 0, color);
}


////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Font_Factory /////////////////////////////
////////////////////////////////////////////////////////////////////////////
D3D9_Font_Factory::D3D9_Font_Factory()
{
}
	
D3D9_Font_Factory::~D3D9_Font_Factory()
{
    clear();
}

/*static*/ D3D9_Font_Factory *D3D9_Font_Factory::instance() 
{
    static D3D9_Font_Factory instance;
    return &instance; 
}

void D3D9_Font_Factory::clear()
{
    if (factory_map_.empty())
        return;
    
    for (Factory_Map::iterator it = factory_map_.begin(); it != factory_map_.end(); ++it)
    {
        delete it->second;
    }
    
    factory_map_.clear();
}

D3D9_Font* D3D9_Font_Factory::get_font(IDirect3DDevice9* device, LPCSTR fontName, INT fontSize, UINT weight)
{
    D3D9_Font* font;
    std::string fName(fontName);
    
    Factory_Map::iterator it = factory_map_.find(fName);
    if( it == factory_map_.end() )
    {
        font = new D3D9_Font(device, fontName, fontSize, weight);
        factory_map_[fName] = font;
    }
    else
    {
        font = it->second;
    }
    
    return font;
}


////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Text_Overlay //////////////////////////////
////////////////////////////////////////////////////////////////////////////
D3D9_Text_Overlay::D3D9_Text_Overlay(IDirect3DDevice9* device, LPCSTR text, RECT pos, INT size, D3DCOLOR color, LPCSTR font)
    : D3D9_Overlay(device, color)
{
    pos_ = pos;
    text_ = text;
    
    font_ = D3D9_Font_Factory::instance()->get_font(device, font, size, FW_NORMAL);
    font_->prepare_text((char*)text_.c_str());
}

/*virtual*/ D3D9_Text_Overlay::~D3D9_Text_Overlay()
{
}

/*virtual*/ HRESULT D3D9_Text_Overlay::draw()
{
    return font_->draw_text((char*)text_.c_str(), &pos_, color_ );
}


////////////////////////////////////////////////////////////////////////////
/////////////////////////// D3D9_Overlay_Store /////////////////////////////
////////////////////////////////////////////////////////////////////////////
D3D9_Overlay_Store::D3D9_Overlay_Store()
{
}

/*virtual*/ D3D9_Overlay_Store::~D3D9_Overlay_Store()
{
    clear();
}

void D3D9_Overlay_Store::add_overlay(D3D9_Overlay* overlay, uint64_t id)
{
    MutexGuard mg(lock_);
    Overlay_Map::iterator i = overlays_.find(id);
    if (i != overlays_.end())
        delete i->second;
    overlays_[id] = overlay;
}

void D3D9_Overlay_Store::remove_overlay(uint64_t id)
{
    MutexGuard mg(lock_);
    Overlay_Map::iterator i = overlays_.find(id);
    if (i != overlays_.end())
    {
        delete i->second;
        overlays_.erase(i);
    }
}

void D3D9_Overlay_Store::draw()
{
    if (is_empty())
        return;

    MutexGuard mg(lock_);
    for (Overlay_Map::iterator it = overlays_.begin(); it != overlays_.end(); ++it)
    {
        it->second->draw();
    }
}

bool D3D9_Overlay_Store::is_empty()
{
    MutexGuard mg(lock_);
    return overlays_.empty();
}

void D3D9_Overlay_Store::clear()
{
    MutexGuard mg(lock_);

    D3D9_Font_Factory::instance()->clear();
    for (Overlay_Map::iterator it = overlays_.begin(); it != overlays_.end(); ++it)
    {
        delete it->second;
    }
    overlays_.clear();
}

}}

#endif /*WIN32*/
