// This file is a part of the xMule Project.
//
// Copyright (c) 2004 Theodore R. Smith (hopeseekr@xmule.ws / http://www.xmule.ws/)
// DSA-1024 Fingerprint: 10A0 6372 9092 85A2 BB7F 907B CB8B 654B E33B F1ED
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#ifdef PRECOMP
#	include "xmule-headers.h"
#endif

#include "BarShader.h"                      // Module's Prototype(s) - audited 5 Nov 2004
#include "wintypes.h"                       // DWORD - audited 4 Nov 2004
#include "xmule.h"                          // theApp - audited 4 Nov 2004

#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences

#include <cmath>                            // sin

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;

#define new DEBUG_NEW
#endif

#ifndef M_PI_2
#define M_PI_2     1.57079632679489661923
#endif

#ifndef PI
#define PI 3.14159265358979323846264338328
#endif

#define HALF(X) (((X) + 1) / 2)

#include <wx/gdicmn.h>
#include <wx/dc.h>
#include <wx/dcmemory.h>

#define GetRValue(rgb) (((rgb)>>16)&0xff)
#define GetGValue(rgb) (((rgb)>>8)&0xff)
#define GetBValue(rgb) ((rgb)&0xff)

#define min(a,b) ((a)<(b)?(a):(b))

CBarShader::CBarShader(uint32_t height, uint32_t width)
{
    m_iWidth = width;
    m_iHeight = height;
    m_uFileSize = 1;
    m_FirstSpan = new BarSpan(0, 1);
    m_Modifiers = NULL;
}

CBarShader::~CBarShader(void)
{
    delete[] m_Modifiers;
    m_FirstSpan->DeleteUpTo(NULL);
    delete m_FirstSpan;
}

void CBarShader::Reset()
{
    Fill(0);
}

void CBarShader::BuildModifiers()
{
    if (m_Modifiers != NULL)
    delete[] m_Modifiers;
    m_used3dlevel = theApp.dynprefs->Get<long>("progress-bar-style");
    // Barry - New property page slider to control depth of gradient
    // Depth must be at least 2
    // 2 gives greatest depth, the higher the value, the flatter the appearance
    // m_Modifiers[count-1] will always be 1, m_Modifiers[0] depends on the value of depth
    int depth = (7 - theApp.dynprefs->Get<long>("progress-bar-style"));
    int count = HALF(m_iHeight);
    double piOverDepth = PI/depth;
    double base = piOverDepth * ((depth / 2.0) - 1);
    double increment = piOverDepth / (count - 1);
    m_Modifiers = new float[count];
    for (int i = 0 ; i < count ; i++)
    m_Modifiers[i] = (float)(sin(base + i *increment));
}

void CBarShader::SetScaling(void)
{
  // Take care to avoid division by zero in corner cases.
  if (m_uFileSize == 0)
    m_dPixelsPerByte = (double) m_iWidth;
  else
    m_dPixelsPerByte = (double) m_iWidth / m_uFileSize;

  if (m_iWidth == 0)
    m_dBytesPerPixel = (double) m_uFileSize;
  else
    m_dBytesPerPixel = (double) m_uFileSize / m_iWidth;

}

void CBarShader::SetFileSize(uint32_t fileSize)
{
    if (m_uFileSize != fileSize)
    {
        m_uFileSize = fileSize;
        SetScaling();
    }
}

void CBarShader::FillRange(uint32_t start, uint32_t end, DWORD color)
{
    if (end > m_uFileSize)
    {
        end = m_uFileSize;
    }
    if (start >= end)
    return;
    BarSpan *bsPrev, *bsStart, *bsEnd;
    bsPrev = bsStart = m_FirstSpan;
    while (bsStart->next != NULL &&bsStart->end < start)
    bsStart = bsStart->next;
    //place new span, unless it's the same color
    BarSpan *bsNew;
    //case 0, same color
    if (bsStart->color == color)
    {
        //same color, ends before end.
        if (bsStart->end > end)
        return;
        //case 1, same color, ends after end.
        bsNew = bsStart;
        bsNew->end = end;
    }
    else if(bsStart->start == start)
    {
        //case 2, begins at start, ends before end.
        if (bsStart->end > end)
        {
            //the 'ol switcheroo
            bsNew = new BarSpan(bsStart, end, bsStart->end, bsStart->color);
            bsStart->end = end;
            bsStart->color = color;
            return;
        }
        //case 3, begins at start, ends after end.
        else
        {
            //hostile takeover
            bsNew = bsStart;
            bsNew->end = end;
            bsNew->color = color;
        }
    }
    else if(bsStart->start < start)
    {
        //case 4, begins after start, ends before end
        if (bsStart->end > end)
        {
            bsNew = new BarSpan(bsStart, start, end, color);
            bsNew = new BarSpan(bsNew, end, bsStart->end, bsStart->color);
            bsStart->end = start;
            return;
        }
        //case 5, begins after start, ends after end
        else
        {
            bsNew = new BarSpan(bsStart, start, end, color);
            bsStart->end = start;
        }
    }
    else
    {
        //should never get here:
        assert(FALSE);
    }
    bsEnd = bsNew->next;
    while (bsEnd != NULL &&bsEnd->end <= end)
    bsEnd = bsEnd->next;
    if (bsEnd != NULL)
    bsEnd->start = end;
    bsNew->DeleteUpTo(bsEnd);
}

void CBarShader::Fill(DWORD color)
{
    m_FirstSpan->DeleteUpTo(NULL);
    m_FirstSpan->start = 0;
    m_FirstSpan->end = m_uFileSize;
    m_FirstSpan->color = color;
}

void CBarShader::Draw(wxDC *dc, int iLeft, int iTop, bool bFlat)
{
    BarSpan *bsCurrent = m_FirstSpan;
    RECT rectSpan;
    rectSpan.top = iTop;
    rectSpan.bottom = iTop + m_iHeight;
    rectSpan.right = iLeft;
    dc->SetPen( *wxTRANSPARENT_PEN);
    int iBytesInOnePixel = (int)(m_dBytesPerPixel + 0.5f);
    //bsCurrent->start;:
    uint32_t start = 0;
    uint32_t drawnItems = 0;
    while (bsCurrent != NULL && rectSpan.right < static_cast<unsigned int>(iLeft + m_iWidth))
    {
        uint32_t uSpan = bsCurrent->end - start;
        int iPixels = (int)(uSpan *m_dPixelsPerByte + 0.5f);
        if (iPixels > 0)
        {
            rectSpan.left = rectSpan.right;
            rectSpan.right += iPixels;
            FillRect(dc, &rectSpan, bsCurrent->color, bFlat);
            drawnItems++;
            start += (int)(iPixels *m_dBytesPerPixel + 0.5f);
        }
        else
        {
            float fRed = 0;
            float fGreen = 0;
            float fBlue = 0;
            uint32_t iEnd = start + iBytesInOnePixel;
            int iLast = start;
            do
            {
                float fWeight = (min(bsCurrent->end, iEnd) - iLast) *m_dPixelsPerByte;
                fRed += GetRValue(bsCurrent->color) *fWeight;
                fGreen += GetGValue(bsCurrent->color) *fWeight;
                fBlue += GetBValue(bsCurrent->color) *fWeight;
                if (bsCurrent->end > iEnd)
                break;
                iLast = bsCurrent->end;
                bsCurrent = bsCurrent->next;
            }
            while (bsCurrent != NULL);
            rectSpan.left = rectSpan.right;
            rectSpan.right++;
            FillRect(dc, &rectSpan, fRed, fGreen, fBlue, bFlat);
            drawnItems++;
            start += iBytesInOnePixel;
        }
        while (bsCurrent != NULL &&bsCurrent->end < start)
        bsCurrent = bsCurrent->next;
    }
    // for debugging:
    ///printf(" piirettiin %d palaa. Kesti %d ms. Leveys %d\n",drawnItems,GetTickCount()-now,m_iWidth);
}

void CBarShader::FillRect(wxDC *dc, RECT *rectSpan, DWORD color, bool bFlat)
{
    if (rectSpan->right - rectSpan->left < 1)
    return;
    if (!color || bFlat)
    {
        //dc->FillRect(rectSpan, &CBrush(color));
        //dc->SetBrush(*(wxTheBrushList->FindOrCreateBrush(wxColour(GetRValue(color),GetGValue(color),GetBValue(color)),wxSOLID)));
        wxBrush suti(wxColour(GetRValue(color), GetGValue(color), GetBValue(color)), wxSOLID);
        dc->SetBrush(suti);
        dc->DrawRectangle(rectSpan->left, rectSpan->top, rectSpan->right, rectSpan->bottom);
        dc->SetBrush(wxNullBrush);
    }
    else
    {
        FillRect(dc, rectSpan, GetRValue(color), GetGValue(color), GetBValue(color), false);
    }
}

void CBarShader::FillRect(wxDC *dc, RECT *rectSpan, float fRed, float fGreen,
float fBlue, bool bFlat)
{
    if (rectSpan->right - rectSpan->left < 1)
    return;
    if (bFlat)
    {
        //dc->SetBrush(*(wxTheBrushList->FindOrCreateBrush(wxColour((int)(fRed+.5f),(int)(fGreen+.5f),(int)(fBlue+.5f)),wxSOLID)));
        wxBrush suti(wxColour((int)(fRed + .5f), (int)(fGreen + .5f), (int)(fBlue + .5f)), wxSOLID);
        dc->SetBrush(suti);
        dc->DrawRectangle(rectSpan->left, rectSpan->top, rectSpan->right, rectSpan->bottom);
        dc->SetBrush(wxNullBrush);
        //dc->FillRect(rectSpan, &CBrush(color));
    }
    else
    {
        if (m_Modifiers == NULL || m_used3dlevel != theApp.dynprefs->Get<long>("progress-bar-style"))
        BuildModifiers();
        RECT rect;
        memcpy( &rect, rectSpan, sizeof(RECT));
        int iTop = rect.top;
        int iBot = rect.bottom;
        int iMax = HALF(m_iHeight);
        for (int i = 0 ; i < iMax ; i++)
        {
            //CBrush cbNew(RGB((int)(fRed * m_Modifiers[i] + .5f), (int)(fGreen * m_Modifiers[i] + .5f), (int)(fBlue * m_Modifiers[i] + .5f)));
            //wxBrush* cbNew=wxTheBrushList->FindOrCreateBrush(wxColour((int)(fRed * m_Modifiers[i] + .5f), (int)(fGreen * m_Modifiers[i] + .5f), (int)(fBlue * m_Modifiers[i] + .5f)),wxSOLID);
            wxBrush suti(wxColour((int)(fRed *m_Modifiers[i] + .5f), (int)(fGreen *m_Modifiers[i] + .5f), (int)(fBlue *m_Modifiers[i] + .5f)), wxSOLID);
            //printf("(väri=%d;%d,%d\n",(int)(fRed * m_Modifiers[i] + .5f), (int)(fGreen * m_Modifiers[i] + .5f), (int)(fBlue * m_Modifiers[i] + .5f));
            rect.top = iTop + i;
            rect.bottom = iTop + i + 1;
            //dc->SetBrush(*cbNew);
            dc->SetBrush(suti);
            //dc->FillRect(&rect, &cbNew);
            //printf("RC: %d,%d-%d,%d\n",rect.left,rect.top,rect.right,rect.bottom);
            int x, y, w, h;
            x = rect.left;
            y = rect.top;
            w = rect.right - rect.left;
            h = rect.bottom - rect.top;
            dc->DrawRectangle(x, y, w, h);
            rect.top = iBot - i - 1;
            rect.bottom = iBot - i;
            //printf("RC2:%d,%d-%d,%d\n",rect.left,rect.top,rect.right,rect.bottom);
            //dc->FillRect(&rect, &cbNew);
            x = rect.left;
            y = rect.top;
            w = rect.right - rect.left;
            h = rect.bottom - rect.top;
            dc->DrawRectangle(x, y, w, h);
            dc->SetBrush(wxNullBrush);
        }
    }
    //printf(" FillRect took %d ms\n",end-start);
}

