/**************************************************************************
*
* Copyright (c) 1998-2000, Microsoft Corp.  All Rights Reserved.
*
* Module Name:
*
*   Gdipscsave.cpp
*
* Abstract:
*
*   Demonstration GDI+ based screen saver with several different
*   fractal patterns
*
* Revision History:
*
*   8/17/2000  peterost - Created it.
*
***************************************************************************/

#include "gdipscsave.h"
#include <math.h>
#include "../gpinit.inc"

extern HINSTANCE hMainInstance; /* screen saver instance handle  */ 
 
/**********************************************************************
*
*  Handle configuration dialog
*
***********************************************************************/

BOOL WINAPI ScreenSaverConfigureDialog (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{ 
    static HWND hNumber; /* handle to number of fractals scroll bar */ 
    static HWND hOK;     /* handle to OK push button */ 
 
    switch(message) 
    { 
        case WM_INITDIALOG: 
 
			/* Retrieve the application name from the .rc file. */
			LoadString(hMainInstance,idsAppName,szAppName,40);
 
            /* Retrieve any redraw speed data from the registry. */ 
            GetFractalConfig (&nFractType, &nNumFracts);
  
            /* Initialize the number of fractals scroll bar control. */ 
            hNumber = GetDlgItem(hDlg, ID_SPEED); 
            SetScrollRange(hNumber, SB_CTL, MINVEL, MAXVEL, FALSE); 
            SetScrollPos(hNumber, SB_CTL, nNumFracts, TRUE); 

            /* Initialize the type of fractals radio buttons */
            CheckRadioButton(hDlg, IDC_RADIOTYPE1, IDC_RADIOTYPE5, IDC_RADIOTYPE1+nFractType);

            /* Retrieve a handle to the OK push button control. */ 
            hOK = GetDlgItem(hDlg, IDOK); 
 
            return TRUE; 
 
        case WM_HSCROLL: 
 
            /* 
             * Process scroll bar input, adjusting the nNumFracts 
             * value as appropriate. 
             */ 
 
            switch (LOWORD(wParam)) 
                { 
                    case SB_PAGEUP: 
                        --nNumFracts; 
                    break; 
 
                    case SB_LINEUP: 
                        --nNumFracts; 
                    break; 
 
                    case SB_PAGEDOWN: 
                        ++nNumFracts; 
                    break; 
 
                    case SB_LINEDOWN: 
                        ++nNumFracts; 
                    break; 
 
                    case SB_THUMBPOSITION: 
                        nNumFracts = HIWORD(wParam); 
                    break; 
 
                    case SB_BOTTOM: 
                        nNumFracts = MINVEL; 
                    break; 
 
                    case SB_TOP: 
                        nNumFracts = MAXVEL; 
                    break; 
 
                    case SB_THUMBTRACK: 
                    case SB_ENDSCROLL: 
                        return TRUE; 
                    break; 
                } 
                if ((int) nNumFracts <= MINVEL) 
                    nNumFracts = MINVEL; 
                if ((int) nNumFracts >= MAXVEL) 
                    nNumFracts = MAXVEL; 
 
                SetScrollPos((HWND) lParam, SB_CTL, nNumFracts, TRUE); 
            break; 
 
        case WM_COMMAND: 
            switch(LOWORD(wParam)) 
            { 
                case ID_OK: 
                    if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE1))
                        nFractType = 0;
                    else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE2))
                        nFractType = 1;
                    else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE3))
                        nFractType = 2;
                    else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE4))
                        nFractType = 3;
                    else
                        nFractType = 4;
                    SetFractalConfig(nFractType, nNumFracts);
 
                case ID_CANCEL: 
                    EndDialog(hDlg, LOWORD(wParam) == IDOK); 
                return TRUE; 
            } 
    } 
    return FALSE; 
} 
 
BOOL WINAPI RegisterDialogClasses(
    HANDLE  hInst
    )
{ 
    return TRUE; 
} 

LRESULT WINAPI ScreenSaverProcW (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{ 
static HDC          hdc;    /* device-context handle */ 
static RECT         rc;     /* RECT structure */ 
static UINT         uTimer; /* timer identifier */ 
static DWORD        dwNumDrawn = -1;
 
    switch(message) 
    { 
        case WM_CREATE: 
 
            // Retrieve the application name from the .rc file. 
            LoadString(hMainInstance, idsAppName, szAppName, 40); 
  
            // Retrieve any configuration data from the registry.
            GetFractalConfig (&nFractType, &nNumFracts); 

            // Set a timer for the screen saver window 
            uTimer = SetTimer(hwnd, 1, 1000, NULL); 

            srand( (unsigned)GetTickCount() );
            fMandelbrot = rand()%2;

//            stream = fopen( "fprintf.out", "w" );
//            fprintf(stream, "initialized\n");

            break; 
 
        case WM_ERASEBKGND: 
 
           /* 
            * The WM_ERASEBKGND message is issued before the 
            * WM_TIMER message, allowing the screen saver to 
            * paint the background as appropriate. 
            */ 
 
            hdc = GetDC(hwnd); 
            GetClientRect (hwnd, &rc); 
            FillRect (hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH)); 
            ReleaseDC(hwnd,hdc); 
            break; 
 
        case WM_TIMER: 
 
           /* 
            * The WM_TIMER message is issued at REDRAWTIME. This 
            * code repaints the entire desktop with black brush every time
            * nNumFracts fractals have been drawn, and calls the appropriate
            * fractal rendering function based on nFractType
            */ 
 
            if (uTimer)
                KillTimer(hwnd, uTimer);

            hdc = GetDC(hwnd); 
            GetClientRect(hwnd, &rc); 
            if (++dwNumDrawn >= nNumFracts) 
            {
                dwNumDrawn = 0;
                
                FillRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
            }

            switch (nFractType)
            {
                case 0:
                    DrawSierpinski(hdc, hwnd, rc, dwNumDrawn);
                    break;
                case 1:
                    DrawHieghway(hdc, hwnd, rc, dwNumDrawn);
                    break;
                case 2:
                    DrawTree(hdc, hwnd, rc, dwNumDrawn);
                    break;
                case 3:
                    DrawPlasma(hdc, hwnd, rc, dwNumDrawn);
                    break;
                case 4:
                    DrawJulia(hdc, hwnd, rc, dwNumDrawn, fMandelbrot);
                    break;
            }

            uTimer = SetTimer(hwnd, 1, REDRAWTIME, NULL); 

            ReleaseDC(hwnd,hdc); 
            break; 
 
        case WM_DESTROY: 
 
           /* 
            * When the WM_DESTROY message is issued, the screen saver 
            * must destroy any of the timers that were set at WM_CREATE 
            * time. 
            */ 
//            fclose(stream);
            if (uTimer) 
                KillTimer(hwnd, uTimer); 
            break; 
    } 
 
    // DefScreenSaverProc processes any messages ignored by ScreenSaverProc. 
    return DefScreenSaverProc(hwnd, message, wParam, lParam); 
} 
 
VOID FillSierpinski(HWND hwnd, Graphics *g, PointF one, PointF two, PointF three, int level, Brush *pBrush, Pen *pPen)
{
    MSG msg;

    if (level == 4 &&
         PeekMessage(&msg, hwnd, WM_KEYFIRST, WM_MOUSELAST, PM_NOREMOVE))
    {
        return;
    }
       
   g->DrawLine(pPen, one, two);
   g->DrawLine(pPen, two, three);
   g->DrawLine(pPen, three, one);

   PointF midpoints[3];
   midpoints[0].X = (two.X + one.X) / 2;
   midpoints[0].Y = (two.Y + one.Y) / 2;
   midpoints[1].X = (three.X + two.X) / 2;
   midpoints[1].Y = (three.Y + two.Y) / 2;
   midpoints[2].X = (one.X + three.X) / 2;
   midpoints[2].Y = (one.Y + three.Y) / 2;

   GraphicsPath triangle;
   triangle.AddPolygon(midpoints, 3); 
   g->FillPath(pBrush, &triangle);

   if (level-- > 0)
   {
       FillSierpinski(hwnd, g, two, midpoints[0], midpoints[1], level, pBrush, pPen);
       FillSierpinski(hwnd, g, three, midpoints[1], midpoints[2], level, pBrush, pPen);
       FillSierpinski(hwnd, g, one, midpoints[0], midpoints[2], level, pBrush, pPen);
   }

}

VOID DrawSierpinski(HDC hDC, HWND hwnd, RECT rc, int iColor)
{
    Graphics g(hDC);
    PointF points[3];
    Color  colors[3] = {
            Color(160,255,0,0),
            Color(130,0,255,0),
            Color(110,0,0,255)};
    int nColors = 3, iMinLen = 6;

    // Get some good random points.  Limit angles to be > 20 degrees, so
    // there are no skinny triangles
    for (int j = 0; j <= 20; j++)
    {
        for (int i = 0; i<= 2; i++)
        {
            points[i].X = (REAL)(rand() % (rc.right - rc.left));
            points[i].Y = (REAL)(rand() % (rc.bottom - rc.top));
        }

        double a,b,c,cosa,cosb,cosc;

        a = sqrt(pow(points[0].X - points[1].X, 2) + pow(points[0].Y - points[1].Y, 2));
        b = sqrt(pow(points[2].X - points[1].X, 2) + pow(points[2].Y - points[1].Y, 2));
        c = sqrt(pow(points[0].X - points[2].X, 2) + pow(points[0].Y - points[2].Y, 2));
        iMinLen = (int)min(a,min(b,c));
//fprintf(stream, " %6f  %6f  %6f   %d\n", a, b, c, j);

        cosa = (pow(a,2) - pow(b,2) - pow(c,2)) / (-2 * b * c);
        cosb = (pow(b,2) - pow(a,2) - pow(c,2)) / (-2 * a * c);
        cosc = (pow(c,2) - pow(a,2) - pow(b,2)) / (-2 * a * b);
//fprintf(stream, " %6f  %6f  %6f   %d\n", cosa, cosb, cosc, j);
        if ((fabs(cosa) < 0.939) && 
            (fabs(cosb) < 0.939) && 
            (fabs(cosc) < 0.939) &&
            (a > 35))
        {
//            fprintf(stream, "broke\n");
            break;
        }       
    }
            
    PathGradientBrush brush(points, nColors);
    brush.SetSurroundColors(colors, &nColors);

    Pen pen(Color(90, (255-iColor*90)%256, 0, (iColor*60)%256));

    g.SetSmoothingMode(SmoothingModeAntiAlias);

    FillSierpinski(hwnd, &g, points[0], points[1], points[2], min(6,max(3,iMinLen/70)), &brush, &pen);
}

VOID DrawHieghway(HDC hDC, HWND hwnd, RECT rc, int iColor)
{
   static PointF p1[16390], p2[16390];

   int iSize = 2, iLen, iPad = (rc.bottom - rc.top) / 4;
   PointF *parr1, *parr2, *ptemp;


   // Calculate a starting line that is not too close to the edges of the screen
   p1[0].X = (REAL)(iPad + (rand() % (rc.right - rc.left - iPad*2)));
   p1[0].Y = (REAL)(iPad + (rand() % (rc.bottom - rc.top - iPad*2)));
   iLen = (rand() % (min(rc.right - rc.left, rc.bottom - rc.top) / 2)) + iPad/2;
   
   if (rand() & 1)
   {
       p1[1].X = p1[0].X;
       if (p1[0].Y > (rc.bottom - rc.top)/2)
           p1[1].Y = p1[0].Y - iLen;
       else
           p1[1].Y = p1[0].Y + iLen;
   }
   else
   {
       if (p1[0].X > (rc.right - rc.left)/2)
           p1[1].X = p1[0].X - iLen;
       else
           p1[1].X = p1[0].X + iLen;
       p1[1].Y = p1[0].Y;
   }

  PointF pgrad1(p1[0].X, p1[0].Y), pgrad2(p1[1].X, p1[1].Y);
//fprintf(stream, " %6f  %6f  %6f  %6f\n", p1[0].X, p1[0].Y, p1[1].X, p1[1].Y);

   parr1 = p1;
   parr2 = p2;

   // Create dragon pattern to a level which is more or less dependent on the
   // overall size of the pattern.
   for (int i=0; i<max(MINHEIGHWAYLEVEL,min(sqrt(iLen/2), MAXHEIGHWAYLEVEL)); i++)
   {
      IterateHieghway(parr1, parr2, &iSize);
      ptemp = parr1;
      parr1 = parr2;
      parr2 = ptemp;
   }

   // Draw the resultant pattern with a gradient pen with random colors.
   Graphics g(hDC);
   LinearGradientBrush brush(pgrad1, pgrad2, 
        Color(230, (rand()%200)+55, (rand()%128)+55, rand()%256),
        Color(180, (rand()%100)+155, rand()%256, (rand()%200)+55));
   brush.SetWrapMode(WrapModeTileFlipXY);
   Pen pen(&brush);

// fprintf(stream, " %d  %d \n", iLen, iSize);

   MSG msg;

   for (int j=0; j<(iSize-1); j++)
   {
      g.DrawLine(&pen, parr1[j], parr1[j+1]);

      // Drawing takes a long time, so check for queued message periodically
      if (j%1000 == 0 &&
          PeekMessage(&msg, hwnd, WM_KEYFIRST, WM_MOUSELAST, PM_NOREMOVE))
      {
          break;
      }

   }

}

VOID IterateHieghway(PointF *points, PointF *newpoints, int *iSize)
{
   int j = 0;
   REAL x1,x2,y1,y2;
   BOOL right = TRUE;
   
   for (int i = 0; i < (*iSize - 1); i++)
   {
       x1 = points[i].X;
       x2 = points[i+1].X;
       y1 = points[i].Y;
       y2 = points[i+1].Y;

      newpoints[j].X = x1;
      newpoints[j++].Y = y1;

       if (y1 == y2)
       {
          newpoints[j].X = (x2 + x1) / 2;
          if (x2 > x1)
              if (right)
                 newpoints[j++].Y = y1 + (x2-x1)/2;
              else
                 newpoints[j++].Y = y1 - (x2-x1)/2;
          else // if (x1 > x2)
              if (right)
                 newpoints[j++].Y = y1 - (x1-x2)/2;
              else
                 newpoints[j++].Y = y1 + (x1-x2)/2;
      }
      else if (x1 == x2)
      {
          newpoints[j].Y = (y2 + y1) / 2;
          if (y2 > y1)
              if (right)
                  newpoints[j++].X = x1 - (y2-y1)/2;
              else
                  newpoints[j++].X = x1 + (y2-y1)/2;
          else  // if (y1 > y2)
              if (right)
                  newpoints[j++].X = x1 + (y1-y2)/2;
              else
                  newpoints[j++].X = x1 - (y1-y2)/2;
      }
      else
      {
          if ((x1 > x2 && y1 > y2) ||
              (x2 > x1 && y2 > y1))
          {
              if (right)
              {
                  newpoints[j].X = x1;
                  newpoints[j++].Y = y2;
              }
              else
              {
                  newpoints[j].X = x2;
                  newpoints[j++].Y = y1;
              }
          }
          else // if ((x2 > x1 && y1 > y2) ||
               //     (x1 > x2 && y2 > y1))
          {
              if (right)
              {
                  newpoints[j].X = x2;
                  newpoints[j++].Y = y1;
              }
              else
              {
                  newpoints[j].X = x1;
                  newpoints[j++].Y = y2;
              }
          }
      }

      right = !right;
  }

  newpoints[j].X = x2;
  newpoints[j++].Y = y2;
  
  *iSize = j;
}

VOID DrawTree(HDC hDC, HWND hwnd, RECT rc, int iColor)
{
/*    PointF points[] = {PointF(50,100),
                       PointF(70,100),
                       PointF(68,70),
                       PointF(58,56),
                       PointF(80,47),
                       PointF(65,24),
                       PointF(58,27),
                       PointF(68,43),
                       PointF(47,52),
                       PointF(55,70),
                       PointF(50,100)};
    PointF  scale[] = {PointF((REAL)0.7, (REAL)0.7), 
                       PointF((REAL)0.7, (REAL)0.7), 
                       PointF((REAL)0.7, (REAL)0.7)};
    REAL    rotate[] = {-65, -10, 65};
    PointF  translate[] = {PointF(-100,2), PointF(20,-45), PointF(35,-167)};
*/
    PointF points[3][11] = {PointF(50,100),
                            PointF(65,100),
                            PointF(62,30),
                            PointF(53,30),
                            PointF(50,100),
                            PointF(0,0),
                            PointF(0,0),
                            PointF(0,0),
                            PointF(0,0),
                            PointF(0,0),
                            PointF(0,0),
                       
                            PointF(50,100),
                            PointF(70,100),
                            PointF(68,70),
                            PointF(58,56),
                            PointF(80,47),
                            PointF(65,24),
                            PointF(58,27),
                            PointF(68,43),
                            PointF(47,52),
                            PointF(55,70),
                            PointF(50,100),
    
                            PointF(0,100),
                            PointF(15,100),
                            PointF(12,30),
                            PointF(3,30),
                            PointF(0,100),
                            PointF(0,0),
                            PointF(0,0),
                            PointF(0,0),
                            PointF(0,0),
                            PointF(0,0),
                            PointF(0,0)};
    int     numPoints[3] = {5,11,5};
    int     numBranches[3] = {3,4,3};
    int     numLevels[3] = {7,7,9};
    PointF  scale[3][4] = {PointF((REAL)0.7, (REAL)0.7), 
                           PointF((REAL)0.7, (REAL)0.7), 
                           PointF((REAL)0.7, (REAL)0.7),
                           PointF(0,0),
                       
                           PointF((REAL)0.75, (REAL)0.75), 
                           PointF((REAL)0.6, (REAL)0.6), 
                           PointF((REAL)0.8, (REAL)0.8),
                           PointF((REAL)0.35, (REAL)0.35),
    
                           PointF((REAL)0.65, (REAL)0.65), 
                           PointF((REAL)0.6, (REAL)0.6), 
                           PointF((REAL)0.6, (REAL)0.6),
                           PointF(0,0)};
    REAL    rotate[3][4] = {1, -35, 27, 0,
                            -65, -10, 65, 20,
                            0, -55, 55, 0};
    PointF  translate[3][4] = {PointF(24,-50), 
                               PointF(-33,10), 
                               PointF(50,-67),
                               PointF(0,0),
                                
                               PointF(-100,2), 
                               PointF(25,-40), 
                               PointF(25,-160),
                               PointF(180,25),
    
                               PointF(4,-55), 
                               PointF(-95,-30), 
                               PointF(95,-50),
                               PointF(0,0)};
//fprintf(stream, "%f  %f  %f  %f \n", points[0][0].X, points[0][1].X, points[0][2].X, points[0][3].X);
    Graphics g(hDC);
    GraphicsPath path;
    REAL  rScale = (REAL)(rand() % (rc.bottom - rc.top)) / 350;
    if (rScale < 0.2)
        rScale = 0.2f;
    REAL xTrans = (REAL)(rand() % ((rc.right - rc.left) * 2/3)) + ((rc.right - rc.left) * 1/6);
    REAL yTrans = (REAL)(rand() % ((rc.bottom - rc.top) * 2/3)) + ((rc.bottom - rc.top) * 1/6);
    REAL rRotate = (REAL)(rand() % 360);
    int iTree = rand() % 3;

//fprintf(stream, " %6f  %6f  %6f  %6f\n", rScale, xTrans, yTrans, rRotate);

    path.AddPolygon(points[iTree],numPoints[iTree]);
    g.TranslateTransform(xTrans, yTrans);
    g.ScaleTransform(rScale, rScale);
    g.RotateTransform(rRotate);
    DrawBranch(hwnd, &g, &path,  rScale < 1 ? numLevels[iTree]-1 : numLevels[iTree], 
                scale[iTree], rotate[iTree], translate[iTree], numBranches[iTree], 
                iColor);
}

VOID DrawBranch(HWND hwnd, Graphics *g, GraphicsPath *path, int iLevel, 
              PointF *scale, REAL *rotate, PointF *translate, 
              int iBranches, int iColor)
{
    MSG   msg;

    if (iLevel == 0 ||
        (iLevel == 6 &&
         PeekMessage(&msg, hwnd, WM_KEYFIRST, WM_MOUSELAST, PM_NOREMOVE)))
    {
        return;
    }
        
    SolidBrush brush(Color(200, rand()%256,(255-iLevel*20)%256,(iColor*125)%200 + rand()%50));
    g->FillPath(&brush, path);

    for (int i = 0; i<iBranches; i++)
    {
        GraphicsState state = g->Save();

        g->ScaleTransform(scale[i].X, scale[i].Y);
        g->RotateTransform(rotate[i]);
        g->TranslateTransform(translate[i].X, translate[i].Y);

        DrawBranch(hwnd, g, path,iLevel-1,scale,rotate,translate,iBranches,iColor);

        g->Restore(state);
    }
}

VOID DrawJulia(HDC hDC, HWND hwnd, RECT rc, int iColor, BOOL fMandelbrot)
{
    Graphics g(hDC);
    REAL cx=(REAL)0.3,cy=(REAL)0.588888;
    REAL x0,y0=-1.25,xI,yI=1.25;
    REAL xCenter, yCenter, delta=(REAL)(0.2/pow(2.5,iColor));
    REAL scrnx=(REAL)(rc.right-rc.left), scrny=(REAL)(rc.bottom-rc.top);
    REAL nx=scrnx,ny=scrny;
    int niter=45+8*(iColor+1),i,ncolors=28;
    Bitmap bitmap((int)nx, (int)ny, PixelFormat32bppARGB);

    if (fMandelbrot)
    {
        xCenter=(REAL)-0.561;
        yCenter=(REAL)-0.6432;
        x0 = (REAL)-1.75;
        xI = (REAL)1.0;
    }
    else
    {
        x0 = (REAL)-1.25;
        xI = (REAL)1.25;
        xCenter=(REAL)-0.11014;
        yCenter=(REAL)-0.509;
    }
    if (iColor > 0)
    {
       x0=(REAL)(xCenter-delta); 
       xI=(REAL)(xCenter+delta);
       y0=(REAL)(yCenter-delta); 
        yI=(REAL)(yCenter+delta);
    }

//    bitmap.LockBits

/*    SolidBrush brushs[] =
        {SolidBrush(Color(255,128,0,0)),
         SolidBrush(Color(255,255,0,0)),
         SolidBrush(Color(255,0,128,0)),
         SolidBrush(Color(255,0,255,0)),
         SolidBrush(Color(255,0,0,128)),
         SolidBrush(Color(255,0,0,255)),
         SolidBrush(Color(255,128,128,0)),
         SolidBrush(Color(255,255,255,0)),
         SolidBrush(Color(255,0,128,128)),
         SolidBrush(Color(255,0,255,255)),
         SolidBrush(Color(255,128,0,128)),
         SolidBrush(Color(255,255,0,255))};
    SolidBrush brushs[] =
        {SolidBrush(Color(255,248,40,18)),
         SolidBrush(Color(255,245,117,21)),
         SolidBrush(Color(255,255,171,18)),
         SolidBrush(Color(255,246,235,20)),
         SolidBrush(Color(255,213,255,13)),
         SolidBrush(Color(255,93,253,13)),
         SolidBrush(Color(255,13,253,218)),
         SolidBrush(Color(255,14,190,252)),
         SolidBrush(Color(255,15,116,255)),
         SolidBrush(Color(255,15,15,255)),
         SolidBrush(Color(255,207,15,250)),
         SolidBrush(Color(255,255,80,245))};
    Color colors[] =
        {Color(255,248,40,18),
         Color(255,245,117,21),
         Color(255,255,171,18),
         Color(255,246,235,20),
         Color(255,213,255,13),
         Color(255,93,253,13),
         Color(255,13,253,218),
         Color(255,14,190,252),
         Color(255,15,116,255),
         Color(255,15,15,255),
         Color(255,207,15,250),
         Color(255,255,80,245)};
     Color colors[] =
        {Color(255,0,0,0),
         Color(255,0,0,180),
         Color(255,0,30,150),
         Color(255,0,60,120),
         Color(255,0,90,90),
         Color(255,0,120,60),
         Color(255,0,150,30),
         Color(255,0,180,0),
         Color(255,30,150,0),
         Color(255,60,120,0),
         Color(255,90,90,0),
         Color(255,120,60,0),
         Color(255,150,30,0),
         Color(255,180,0,0),
         Color(255,150,0,30),
         Color(255,120,0,60),
         Color(255,90,0,90),
         Color(255,60,0,120),
         Color(255,30,0,150)};
*/     Color colors[] =
        {Color(255,0,0,0),
         Color(255,0,0,180),
         Color(255,0,20,160),
         Color(255,0,40,140),
         Color(255,0,60,120),
         Color(255,0,80,100),
         Color(255,0,100,80),
         Color(255,0,120,60),
         Color(255,0,140,40),
         Color(255,0,160,20),
         Color(255,0,180,0),
         Color(255,20,160,0),
         Color(255,40,140,0),
         Color(255,60,120,0),
         Color(255,80,100,0),
         Color(255,100,80,0),
         Color(255,120,60,0),
         Color(255,140,40,0),
         Color(255,160,20,0),
         Color(255,180,0,0),
         Color(255,160,0,20),
         Color(255,140,0,40),
         Color(255,120,0,60),
         Color(255,100,0,80),
         Color(255,80,0,100),
         Color(255,60,0,120),
         Color(255,40,0,140),
         Color(255,20,0,160),
        };
/*    Color colors[] =
        {Color(255,0,0,0),
         Color(255,0,89,186),
         Color(255,0,155,186),
         Color(255,0,186,155),
         Color(255,0,186,27),
         Color(255,186,186,0),
         Color(255,186,155,0),
         Color(255,186,118,0),
         Color(255,186,60,0),
         Color(255,186,0,0),
         Color(255,186,0,186),
         Color(255,97,0,186),
         Color(255,0,4,186)};

    Color colors[2];
    colors[0]=Color(255,rand()%256,rand()%256,rand()%256);
*/
    REAL dx,dy,px,py,x,y;
    REAL xx,yy,xsquared,ysquared;

        dx=(xI-x0)/nx;
        dy=(yI-y0)/ny;
        
        for (py=0; py<ny; py++)
        {
            MSG msg;

            if ((int)py % 50 == 0 &&
                PeekMessage(&msg, hwnd, WM_KEYFIRST, WM_MOUSELAST, PM_NOREMOVE))
                return;

            for (px=0; px<nx; px++)
            {
                x=x0+(px*dx);
                y=y0+(py*dy);

                if (fMandelbrot)
                {
                     cx=x;
                     cy=y;
                     x=0;
                     y=0;
                }
            
                xsquared=0;
                ysquared=0;

                for(i=0; (i<niter)&&(xsquared+ysquared < 4); i++)
                {
                    xsquared= x*x;
                    ysquared= y*y;
                    xx=xsquared - ysquared + cx;
                    yy = x * y * 2 + cy;
                    x = xx;
                    y = yy;
                }

                if (i==niter) 
                    i = 0;
                else
                    i = (i % (ncolors-1)) + 1;

                bitmap.SetPixel((int)px, (int)py, colors[i]);

//            g->FillRectangle((Brush*)&(brushs[i]), (int)px,(int)py,(int)1,(int)1);

            }
        }

        g.DrawImage(&bitmap, 0,0);
}

//index must be in range 0...5f9 (length 5fa)

//#define PLASMA_INDEX_MOD 0x5fa
//#define PLASMA_INDEX_MOD 0x2fd
INT PLASMA_TYPE;
INT PLASMA_INDEX_MOD;
ARGB IndexToSpectrum(INT index)
{
    //index = (index + PLASMA_INDEX_MOD) % PLASMA_INDEX_MOD;

    if ((index < 0) || (index >= PLASMA_INDEX_MOD))
        DebugBreak();
    
    INT r,g,b;

    switch (PLASMA_TYPE)
    {
    case 0:

        r = max(0, min(0xff, (0x1fe - abs(0x2fd - ((index+0x2fd) % PLASMA_INDEX_MOD)))));
        g = max(0, min(0xff, (0x1fe - abs(0x2fd - ((index+0xff) % PLASMA_INDEX_MOD)))));
        b = max(0, min(0xff, (0x1fe - abs(0x2fd - ((index+0x4fb) % PLASMA_INDEX_MOD)))));

        if (!((r == 0xff) || (g == 0xff) || (b == 0xff)))
            DebugBreak();

        if ((r == 0xff) && (g == 0) && (b == 0) && (index != 0))
            DebugBreak();
        break;
    case 1:
        r = 0;
        g = 0;
        b = 0;

        if (index < 0xff)
        {
            r = 0xff - index;
            g = index;
        }
        else if (index < 0x1fe)
        {
            g = 0xff - (index - 0xff);
            b = index - 0xff;
        }
        else
        {
            b = 0xff - (index - 0x1fe);
            r = index - 0x1fe;
        }
        break;
    case 2:
        r = 0xff;
        b = 0xff;
        g = 0xff;

        if (index < 0xff)
        {
            r = index;
            g = 0xff - index;
        }
        else if (index < 0x1fe)
        {
            g = index - 0xff;
            b = 0xff - (index - 0xff);
        }
        else
        {
            b = index - 0x1fe;
            r = 0xff - (index - 0x1fe);
        }
        break;
    }
/*
    return (0xff000000    |
            (BYTE)r << 16 |
            (BYTE)g << 8  |
            (BYTE)b);
*/
    
    return (((rand() % 255) + 1) << 24    |
            (BYTE)r << 16 |
            (BYTE)g << 8  |
            (BYTE)b);

}

INT SpectrumToIndex(ARGB argb)
{

    BYTE r = (BYTE)((argb & 0x00ff0000) >> 16);
    BYTE g = (BYTE)((argb & 0x0000ff00) >> 8);
    BYTE b = (BYTE)(argb & 0x000000ff);

    switch(PLASMA_TYPE)
    {
    case 0:
        //Is red high?
        if (0xff == r)
        {
            //...and is blue present?
            if (b > 0)
            {
                return PLASMA_INDEX_MOD - b;
            }
            else
            {
                return g;
            }
        }
        //how 'bout green?
        if (0xff == g)
        {
            if (r > 0)
            {
                return 0x1fe - r;
            }
            else
            {
                return 0x1fe + b;
            }
        }
        //else blue
        if (0xff == b)
        {
            if (g > 0)
            {
                return 0x3fc - g;
            }
            else
            {
                return 0x3fc + r;
            }
        }

        //WTH?
        DebugBreak();

        break;
    case 1:

        if (r == 0xff)
        {
            return 0;
        }
        if (g != 0)
        {
            if (r > 0)
            {
                return g;
            }
            else
            {
                return 0xff + b;
            }
        }
        if (b != 0)
        {
            return 0x1fe + r;
        }
        break;
    case 2:
        if (b == 0xff)
        {
            return r;
        }
        if (r == 0xff)
        {
            return 0xff + g;
        }
        if (g == 0xff)
        {
            return 0x1fe + b;
        }
        break;
    }
    
    DebugBreak();
    return -1;
}

INT MakeColor(INT c1, INT c2, INT deltamax)
{
    INT c = ((c1 + c2) >> 1) + ((deltamax > 0) ? ((rand() % (2*deltamax)) - deltamax) : 0);

    if (c < 0)
        c = 0;

    if (c > (PLASMA_INDEX_MOD - 1))
        c = PLASMA_INDEX_MOD - 1;

    if ((c < 0) || (c > PLASMA_INDEX_MOD - 1))
        DebugBreak();

    return c;
}

INT MakeColor(INT c1, INT c2, INT c3, INT c4, INT deltamax)
{
    INT c = ((c1 + c2 + c3 + c4) >> 2) + ((deltamax > 0) ? ((rand() % (2*deltamax)) - deltamax) : 0);

    if (c < 0)
        c = 0;

    if (c > (PLASMA_INDEX_MOD - 1))
        c = PLASMA_INDEX_MOD - 1;


    if ((c < 0) || (c > PLASMA_INDEX_MOD - 1))
        DebugBreak();

    return c;
}

BYTE MakeAlpha(BYTE a1, BYTE a2, INT deltamax)
{
    deltamax = (deltamax * 0xff) / PLASMA_INDEX_MOD;

    BYTE a = (((int)a1 + (int)a2) >> 1) + ((deltamax > 0) ? ((rand() % (2*deltamax)) - deltamax) : 0);

    if (a < 1)
        a = 1;

    if (a > 0xff)
        a = 0xff;

    return a;
}

BYTE MakeAlpha(BYTE a1, BYTE a2, BYTE a3, BYTE a4, INT deltamax)
{
    deltamax = (deltamax * 0xff) / PLASMA_INDEX_MOD;

    BYTE a = (((int)a1 + (int)a2 + (int)a3 + (int)a4) >> 2) + ((deltamax > 0) ? ((rand() % (2*deltamax)) - deltamax) : 0);

    if (a < 1)
        a = 1;

    if (a > 0xff)
        a = 0xff;

    return a;
}

//Note that this only works on squares! You want something else? Scale the graphics.
//BOOL HalfPlasma(HWND& hwnd, Graphics& g,BitmapData &bmpd, INT x0, INT y0, INT x1, INT y1, const Color& c00, const Color& c10, const Color& c01, const Color& c11,REAL scale)
BOOL HalfPlasma(HWND& hwnd, Graphics& g,BitmapData &bmpd, INT x0, INT y0, INT x1, INT y1,REAL scale)
{
    MSG msg;

    // Drawing takes a long time, so check for queued message periodically
    if (PeekMessage(&msg, hwnd, WM_KEYFIRST, WM_MOUSELAST, PM_NOREMOVE))
        return FALSE;

    if (((x0 + 1) >= x1) &&
        ((y0 + 1) >= y1))
    {
        return TRUE;
    }

    INT c00 = SpectrumToIndex(*((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + x0));
    INT c10 = SpectrumToIndex(*((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + x1));
    INT c01 = SpectrumToIndex(*((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + x0));
    INT c11 = SpectrumToIndex(*((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + x1));
    INT ch0, c0h, c1h, ch1, chh;

    BYTE a00 = (BYTE)((*((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + x0) & 0xff000000) >> 24);
    BYTE a10 = (BYTE)((*((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + x1) & 0xff000000) >> 24);
    BYTE a01 = (BYTE)((*((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + x0) & 0xff000000) >> 24);
    BYTE a11 = (BYTE)((*((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + x1) & 0xff000000) >> 24);
    BYTE ah0, a0h, a1h, ah1, ahh;

    INT deltamax = (INT)((x1 - x0)/scale);
    INT half = (x1 + 1 - x0) >> 1;
    INT xh = x0 + half;
    INT yh = y0 + half;

    if (0 == (*((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + xh) & 0xff000000))
    {
        ch0 = MakeColor(c00,c10,deltamax);
        ah0 = MakeAlpha(a00,a10,deltamax);
        *((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + xh) = (IndexToSpectrum(ch0) & 0x00ffffff) | (ah0 << 24);
    }
    else
    {
        ch0 = SpectrumToIndex(*((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + xh));
        ah0 = (BYTE)((*((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + xh) & 0xff000000) >> 24);
    }

    if (0 == (*((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + x0) & 0xff000000))
    {
        c0h = MakeColor(c00, c01, deltamax);
        a0h = MakeAlpha(a00, a01, deltamax);
        *((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + x0) = (IndexToSpectrum(c0h) & 0x00ffffff) | (a0h << 24);
    }
    else
    {
        c0h = SpectrumToIndex(*((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + x0));
        a0h = (BYTE)((*((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + x0) & 0xff000000) >> 24);
    }

    if (0 == (*((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + x1) & 0xff000000))
    {
        c1h = MakeColor(c10,c11,deltamax);
        a1h = MakeAlpha(a10,a11,deltamax);
        *((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + x1) = (IndexToSpectrum(c1h) & 0x00ffffff) | (a1h << 24);
    }
    else
    {
        c1h = SpectrumToIndex(*((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + x1));
        a1h = (BYTE)((*((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + x1) & 0xff000000) >> 24);
    }

    if (0 == (*((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + xh) & 0xff000000))
    {
        ch1 = MakeColor(c01,c11,deltamax);
        ah1 = MakeAlpha(a01,a11,deltamax);
        *((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + xh) = (IndexToSpectrum(ch1) & 0x00ffffff) | (ah1 << 24);
    }
    else
    {
        ch1 = SpectrumToIndex(*((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + xh));
        ah1 = (BYTE)((*((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + xh) & 0xff000000) >> 24);
    }

    if (0 == (*((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + xh) & 0xff000000))
    {
        chh = MakeColor(ch0,c0h,c1h,ch1,deltamax);
        ahh = MakeAlpha(ah0,a0h,a1h,ah1,deltamax);
        *((ARGB*)((BYTE*)bmpd.Scan0 + yh*bmpd.Stride) + xh) = (IndexToSpectrum(chh) & 0x00ffffff) | (ahh << 24);
    }

    if (!HalfPlasma(hwnd, g, bmpd,x0,y0,xh,yh,scale)) return FALSE;
    if (!HalfPlasma(hwnd, g, bmpd,xh,y0,x1,yh,scale)) return FALSE;
    if (!HalfPlasma(hwnd, g, bmpd,x0,yh,xh,y1,scale)) return FALSE;
    if (!HalfPlasma(hwnd, g, bmpd,xh,yh,x1,y1,scale)) return FALSE;

    return TRUE;
}

INT fx = 0;
INT fy = 0;

VOID DrawPlasma(HDC hDC, HWND hwnd, RECT rc, int iColor)
{
    Graphics g(hDC);
    INT x0,y0,x1,y1;
    REAL scale;
    BYTE alpha = 255;
    BOOL abort = FALSE;

    INT w = 1;
    INT size = min((rc.right - rc.left), (rc.bottom-rc.top));

    while(size > 0)
    {
        size >>= 1;
        w <<= 1;
    }

    if (rand() % 2)
    {
        w = min(w,1 << ((rand() % 5) + 4));
    }
    else
    {
        w = min(w,32);
    }


    Bitmap bmp(w,w,PixelFormat32bppARGB);
    Rect rect(0,0,w,w);
    BitmapData bmpd;
    bmp.LockBits(rect,0,PixelFormat32bppARGB,&bmpd);

    for (INT x = 0; x < w; x++)
    {
        for (INT y = 0; y < w; y++)
        {
            *((ARGB*)((BYTE*)bmpd.Scan0 + y*bmpd.Stride) + x) = Color::MakeARGB(0,0,0,0);
        }
    }

    x0 = 0;
    y0 = 0;
    x1 = x0 + w - 1;
    y1 = y0 + w - 1;

    switch(PLASMA_TYPE = (rand() % 3))
    {
    case 0:
        PLASMA_INDEX_MOD = 0x5fa;
        break;
    case 1:
    case 2:
        PLASMA_INDEX_MOD = 0x2fd;
        break;
    }

    scale = ((REAL)(w))/((REAL)PLASMA_INDEX_MOD);
    REAL sx = ((REAL)(rc.right-rc.left))/((REAL)(x1 - x0 + 1));
    REAL sy = ((REAL)(rc.bottom-rc.top))/((REAL)(y1 - y0 + 1));
            
    g.SetSmoothingMode(SmoothingModeAntiAlias);

    BYTE intMode = rand() % 3;

    switch(intMode)
    {
    case 2:
        g.SetInterpolationMode(InterpolationModeHighQualityBicubic);
        break;
    case 1:
        g.SetInterpolationMode(InterpolationModeHighQualityBilinear);
        break;
    case 0:
        g.SetInterpolationMode(InterpolationModeNearestNeighbor);
        break;
    }

    /*
    *((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + x0) = IndexToSpectrum(rand() % PLASMA_INDEX_MOD) ;
    *((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + x1) = IndexToSpectrum(rand() % PLASMA_INDEX_MOD);
    *((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + x0) = IndexToSpectrum(rand() % PLASMA_INDEX_MOD);
    *((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + x1) = IndexToSpectrum(rand() % PLASMA_INDEX_MOD);
*/

    *((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + x0) = (IndexToSpectrum(rand() % PLASMA_INDEX_MOD) & 0x00ffffff) | ((rand() % 255) << 24);
    *((ARGB*)((BYTE*)bmpd.Scan0 + y0*bmpd.Stride) + x1) = (IndexToSpectrum(rand() % PLASMA_INDEX_MOD) & 0x00ffffff) | ((rand() % 255) << 24);;
    *((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + x0) = (IndexToSpectrum(rand() % PLASMA_INDEX_MOD) & 0x00ffffff) | ((rand() % 255) << 24);;
    *((ARGB*)((BYTE*)bmpd.Scan0 + y1*bmpd.Stride) + x1) = (IndexToSpectrum(rand() % PLASMA_INDEX_MOD) & 0x00ffffff) | ((rand() % 255) << 24);;

    abort = !HalfPlasma(hwnd,g,bmpd,x0,y0,x1,y1,scale);

    bmp.UnlockBits(&bmpd);

    PointF points[3];

    if (!abort)
    {       

        // Get some good random points.  Limit angles to be > 20 degrees, so
        // there are no skinny rects
        for (int j = 0; j <= 20; j++)
        {
            for (int i = 0; i<= 2; i++)
            {
                points[i].X = (REAL)(rand() % (rc.right - rc.left));
                points[i].Y = (REAL)(rand() % (rc.bottom - rc.top));
            }

            double a,b,c,cosa,cosb,cosc;

            a = sqrt(pow(points[0].X - points[1].X, 2) + pow(points[0].Y - points[1].Y, 2));
            b = sqrt(pow(points[2].X - points[1].X, 2) + pow(points[2].Y - points[1].Y, 2));
            c = sqrt(pow(points[0].X - points[2].X, 2) + pow(points[0].Y - points[2].Y, 2));
            int iMinLen = (int)min(a,min(b,c));

            cosa = (pow(a,2) - pow(b,2) - pow(c,2)) / (-2 * b * c);
            cosb = (pow(b,2) - pow(a,2) - pow(c,2)) / (-2 * a * c);
            cosc = (pow(c,2) - pow(a,2) - pow(b,2)) / (-2 * a * b);
            if ((fabs(cosa) < 0.939) && 
                (fabs(cosb) < 0.939) && 
                (fabs(cosc) < 0.939) &&
                (a > 35))
            {
                break;
            }    
        }

        //g.DrawImage(&bmp,points,3); 
        INT halfKernel = intMode;
        g.DrawImage(&bmp, points, 3, (REAL)-halfKernel, (REAL)-halfKernel, 
                    (REAL)(w+halfKernel), (REAL)(w+halfKernel), UnitPixel, NULL, NULL, NULL); 
    }
}

VOID GetFractalConfig (DWORD *nType, DWORD *nSize)
{

        #define MYBUFFSIZE  32

        HKEY    hKey;
        HRESULT hr;
        DWORD   dwType;
        DWORD   dwBuffLen = sizeof(DWORD);
        
        hr = RegCreateKeyEx(HKEY_CURRENT_USER,
                            HKEY_PREFERENCES,
                            NULL,NULL, 
                            REG_OPTION_NON_VOLATILE, 
                            KEY_READ | KEY_WRITE,NULL,&hKey, NULL);
    
        if (ERROR_SUCCESS != hr)
        {
            *nSize = 4;
            *nType = 0;
            goto Done;
        }
    
        hr =  RegQueryValueEx(hKey, TEXT("FractalScnSvrType"),NULL,&dwType,
                              (LPBYTE)nType, &dwBuffLen);
    
        if (ERROR_SUCCESS != hr || dwType != REG_DWORD)
        {
            *nSize = 4;
            *nType = 0;
            goto Done;
        }

        dwBuffLen = MYBUFFSIZE;

        hr =  RegQueryValueEx(hKey, TEXT("FractalScnSvrNumber"),NULL,&dwType,
                              (LPBYTE)nSize, &dwBuffLen);
    
        if (ERROR_SUCCESS != hr || dwType != REG_DWORD)
        {
            *nSize = 4;
        }
Done:
        RegCloseKey(hKey);
    
}

VOID SetFractalConfig (DWORD nType, DWORD nSize)
{
        HKEY    hKey;
        HRESULT hr;
        DWORD   dwType = REG_DWORD;
        
        hr = RegCreateKeyEx(HKEY_CURRENT_USER,
                            HKEY_PREFERENCES,
                            NULL,NULL, 
                            REG_OPTION_NON_VOLATILE, 
                            KEY_READ | KEY_WRITE,NULL,&hKey, NULL);
    
        if (ERROR_SUCCESS != hr)
        {
            goto Done;
        }
    
        hr =  RegSetValueEx(hKey, TEXT("FractalScnSvrType"),NULL,dwType,
                              (LPBYTE)&nType, sizeof(DWORD));
    
        if (ERROR_SUCCESS != hr || dwType != REG_DWORD)
        {
            goto Done;
        }

        hr =  RegSetValueEx(hKey, TEXT("FractalScnSvrNumber"),NULL,dwType,
                              (LPBYTE)&nSize, sizeof(DWORD));
    
Done:
        RegCloseKey(hKey);
    

}