#include <math.h>
#include <stdio.h>
#include "trippy.h"

#ifdef X
#include <X11/X.h>
#include <X11/Xlib.h>
#define MAXCOLOR 65535
#else
  #include <stdlib.h>
  #ifdef WINDOWS
    #include <windows.h>
    #define MAXCOLOR 255
    HPALETTE hPal;
    static HGLOBAL HChndl;
  #else
    #include <metawndo.h>
    #define MAXCOLOR 65535
  #endif
#endif

#include "color.h"

extern foo options;

static void createPalette();
static void fillPalette();

static XColor color_info[NCOLORS];
static unsigned long colors[NCOLORS][3];
int HCV;

#ifdef X
unsigned long
StrColor(char *string,unsigned long def)
{
  XColor screen_def_return, exact_def_return;
  if (string==(char*)0 || XAllocNamedColor(display,
                                           DefaultColormap(display,screen),
                                           string,&exact_def_return,
                                           &screen_def_return)==False)
    return def;
  else
    return screen_def_return.pixel;
}
#endif

void
randomize_color()
{
  XColor color;
  int i=rndm((long)numcolors-1);

  if(i==0) i=1;  /* overwriting the Background color is a Bad Thing */

  color.pixel = color_info[i].pixel;
  if(options.mono)
  {
    colors[i][0]= color.red =
    colors[i][1]= color.green =
    colors[i][2]= color.blue = rndm(MAXCOLOR);
  }
  else
  {
    colors[i][0]= color.red = rndm(MAXCOLOR);
    colors[i][1]= color.green = rndm(MAXCOLOR);
    colors[i][2]= color.blue = rndm(MAXCOLOR);
  }
  color.flags = DoRed|DoGreen|DoBlue;
  XStoreColor(display, colmap, &color);
  color_info[i].red = color.red;
  color_info[i].green = color.green;
  color_info[i].blue = color.blue;
}

void
randomize_colors()
{
  int i;
#ifdef WINDOWS
  XColor *color=(XColor*)LocalAlloc(LPTR,numcolors*sizeof(XColor));
#else
  XColor *color=(XColor*) malloc(numcolors*sizeof(XColor));
#endif
  for (i=1; i<numcolors; i++)
  {
    color[i].pixel = color_info[i].pixel;
    if(!options.mono)
    {
      colors[i][0]= color[i].red = rndm(MAXCOLOR);
      colors[i][1]= color[i].green = rndm(MAXCOLOR);
      colors[i][2]= color[i].blue = rndm(MAXCOLOR);
    }
    else
    {
      colors[i][0]= color[i].red =
      colors[i][1]= color[i].green =
      colors[i][2]= color[i].blue = rndm(MAXCOLOR);
    }
    color[i].flags = DoRed|DoGreen|DoBlue;
    XStoreColor(display, colmap, &color[i]);
    color_info[i].red = color[i].red;
    color_info[i].green = color[i].green;
    color_info[i].blue = color[i].blue;
  }
#ifdef WINDOWS
   LocalFree((HLOCAL)color);
 #else
  free (color);
#endif
  /*  XStoreColors(display,colmap, &color[1], numcolors-1); */
}

void
rotate_colors(int reset)
{
  XColor temp_color;
  int i;
  static int plusme1=0,plusme2=0,plusme3=0;
  static int add1=1,add2=1,add3=1;
  temp_color.pixel = color_info[1].pixel;

  if(reset)
  {
    plusme1=plusme2=plusme3=0;
    add1=add2=add3=1;
    return;
  }

  for (i=1; i< ((options.palette==0||options.mono)?numcolors-1:numcolors); i++)
  {
    if(options.mono || (options.palette==0))
      color_info[i].pixel = color_info[i+1].pixel;
    else
    {
      int a,b,c;
      
      a= (i+plusme1)%numcolors;
      if(a<1)
        a=numcolors-1+a;

      b= (i+plusme2)%numcolors;
      if(b<1)
        b=numcolors-1+b;

      c= (i+plusme3)%numcolors;
      if(c<1)
        c=numcolors-1+c;

      color_info[i].red   = colors[a][0];
      color_info[i].green = colors[b][1];
      color_info[i].blue  = colors[c][2];
    }
    color_info[i].flags = DoRed|DoGreen|DoBlue;
    XStoreColor(display,colmap,&color_info[i]);
  }
  if(options.palette>0 && !options.mono)
  {
    if(!rndm(5000l))
      add1++;
    else if(!rndm(5000l))
      add1--;
    else if(!rndm(5000l))
      add1 *= -1;
    plusme1+=add1;
    if(!rndm(5000l))
      add2++;
    else if(!rndm(5000l))
      add2--;
    else if (!rndm(5000l))
      add2*=-1;
    plusme2+=add2;
    if(!rndm(5000l))
      add3++;
    else if (!rndm(5000))
      add3--;
    else if (!rndm(5000))
      add3*= -1;
    plusme3+=add3;
  }
  else
  {
    color_info[numcolors-1].pixel = temp_color.pixel;

    color_info[numcolors-1].flags = DoRed|DoGreen|DoBlue;
    XStoreColor(display,colmap,&color_info[numcolors-1]);
  }

  /*  XStoreColors(display,colmap,&color_info[1],numcolors-1); */
}

/*
void
EndofTunnel()
{
  XColor temp_color;
  int i,j;

  for(j=1; j<numcolors-1; j++)
  {
    for (i=2; i<numcolors-1; i++)
    {
      color_info[i].pixel = color_info[i+1].pixel;
      color_info[i].flags = DoRed|DoGreen|DoBlue;
      XStoreColor(display,colmap,&color_info[i]);
    }
    color_info[1].pixel=color_info[i+1].pixel;
    color_info[1].flags=0;
    XStoreColor(display,colmap,&color_info[1]);
    color_info[numcolors-1].pixel = temp_color.pixel;
    color_info[numcolors-1].flags = DoRed|DoGreen|DoBlue;
    XStoreColor(display,colmap,&color_info[numcolors-1]);
  }
}
*/

void
resizePalette(int num,BOOL refill)
{
   HDC hdc;
   if(num==0)
     num=options.tryfor;
   ResizePalette(hPal,num);
   if(numcolors<num)
   {
     while(numcolors<num)
     {
       PALETTEENTRY color;
       color.peFlags=PC_RESERVED;
       SetPaletteEntries(hPal,numcolors,1 ,&color);
       numcolors++;
     }
   }
   numcolors=num;
   if(refill==TRUE)
     fillPalette();
   rotate_colors(1); /* reset the rotation scheme */
   hdc = GetDC(window[0]);
   SelectPalette(hdc,hPal,FALSE);
   RealizePalette(hdc);
   ReleaseDC(window[0],hdc);
}

void
get_them_colors()
{
  createPalette();
  fillPalette();
}

void
createPalette()
{
  unsigned long pixels[NCOLORS];
  int *plane_masks;
  int i;
#ifdef X
  XGCValues values;
#endif
#ifdef WINDOWS
  LOGPALETTE *lgpal;
  HDC hdc;
  HChndl = GlobalAlloc(GPTR,(NCOLORS*sizeof(int)));
  HC =(int *)GlobalLock(HChndl);
#else
  if ((HC =(int *)calloc(NCOLORS,sizeof(int))) == NULL) return ;
#endif
  HC[0]=0;
  if(options.tryfor==0)
    options.tryfor=NCOLORS;
  for (numcolors=options.tryfor; numcolors>=2; numcolors-=6) 
  {
    if (XAllocColorCells(display,colmap,True, plane_masks, 0,
                         pixels, (unsigned int)numcolors) != 0) 
    {
#ifdef WINDOWS
      lgpal = (LOGPALETTE*)LocalAlloc(LPTR,
                      sizeof(LOGPALETTE)+numcolors*sizeof(PALETTEENTRY));
      lgpal->palNumEntries= numcolors;
      lgpal->palVersion = 0x300;
      for(i=0;i<numcolors;i++)
      {
        lgpal->palPalEntry[i].peRed = color_info[i].red;
        lgpal->palPalEntry[i].peGreen = color_info[i].green;
        lgpal->palPalEntry[i].peBlue = color_info[i].blue;
        lgpal->palPalEntry[i].peFlags = PC_RESERVED;
      }
      hdc = GetDC(window[0]);
      if(numcolors>0)
        hPal= CreatePalette(lgpal);
      else
        hPal = GetStockObject(DEFAULT_PALETTE);
      SelectPalette(hdc,hPal,FALSE);
      RealizePalette(hdc);
      ReleaseDC(window[0],hdc);
      LocalFree((HLOCAL) lgpal);
      options.tryfor=numcolors;
      return;
    }
#endif
  }
  hdc = GetDC(window[0]);
  hPal = GetStockObject(DEFAULT_PALETTE);
  SelectPalette(hdc,hPal,FALSE);
  RealizePalette(hdc);
  ReleaseDC(window[0],hdc);
  numcolors=20; /* GetDeviceCaps(hdc,NUMCOLORS); */
  options.tryfor=0;
  for(i=0;i<numcolors;i++)
    color_gcs[i]=i;
  return;
}

void
fillPalette()
{
  register int i;
  if (options.dynamic_colors) 
  {
    for (i=0;i<numcolors;i++)
      color_info[i].pixel=i;
    randomize_colors();

    /*    fprintf(stderr,"Alloc'ing %d colors \n",numcolors);*/
    if (numcolors < 2)
      fatalerror("Cannot allocate R/W color cells","");
#ifdef X
    color_info[0].pixel=options.bgcolor;
#else
    color_info[0].pixel=0;
#endif
    for(i=0;i<numcolors;i++)
    {
#ifdef X
      values.foreground = color_info[i].pixel;
      values.background = options.bgcolor;
      color_gcs[i] = XCreateGC(display,window,
                               GCForeground|GCBackground,&values);
#else
  #ifdef WINDOWS
      color_gcs[i] = i; /* then hopefully, we use i to as an index */
  #else
      color_gcs[i] = color_info[i].pixel;
  #endif
#endif
    }
  }
  else if(!options.mono)  /* colors */
  {
    XColor screen_in_out;
    int poisson=numcolors;
    float fact = (float)MAXCOLOR/((float)poisson/6);
    screen_in_out.flags=DoRed|DoGreen|DoBlue;

    switch(options.palette)
      {
        case 3:
        {
          int stepX,stepY;
          int sqrColors,fin,x,y;
          sqrColors=sqrt(poisson);
          fin=0;
          x=sqrColors;
          y=(sqrColors-1)*sqrColors;
          
          do
          {
            colors[x][0]=rndm(MAXCOLOR);
            colors[x][1]=rndm(MAXCOLOR);
            colors[x][2]=rndm(MAXCOLOR);

            colors[y][0]=rndm(MAXCOLOR);
            colors[y][1]=rndm(MAXCOLOR);
            colors[y][2]=rndm(MAXCOLOR);

            if( (colors[x][0]+colors[y][0]> MAXCOLOR) ||
                (colors[x][1]+colors[y][1]> MAXCOLOR) ||
                (colors[x][2]+colors[y][2]> MAXCOLOR) ||

/* This code is to make the colors brighter, buuut  */
              ( (colors[x][0] < MAXCOLOR>>1) &&
                (colors[x][1] < MAXCOLOR>>1) &&
                (colors[x][2] < MAXCOLOR>>1) )  ||
                ( (colors[y][0] < MAXCOLOR>>1) &&
                  (colors[y][1] < MAXCOLOR>>1) &&
                  (colors[y][2] < MAXCOLOR>>1) )  )
 
            fin=0;
            else fin=1;
        } while (!fin);
        
        colors[0][0]=
          colors[0][1]=
          colors[0][2]=0;
        
        for(stepX=0;stepX<=x;stepX++)
        {
          colors[stepX][0]= colors[x][0]*((float)stepX/x);
          colors[stepX][1]= colors[x][1]*((float)stepX/x);
          colors[stepX][2]= colors[x][2]*((float)stepX/x);
        }

        for(stepY=0;stepY<=x;stepY++)
        {
          colors[stepY*x][0]= colors[y][0]*((float)stepY/x);
          colors[stepY*x][1]= colors[y][1]*((float)stepY/x);
          colors[stepY*x][2]= colors[y][2]*((float)stepY/x);
        }

        for(stepX=1;stepX<x;stepX++)
          for(stepY=1;stepY<x;stepY++)
          {
            colors[stepX+(stepY*x)][0]=
              colors[stepX][0] + colors[stepY*x][0];
            colors[stepX+(stepY*x)][1]=
              colors[stepX][1] + colors[stepY*x][1];
                colors[stepX+(stepY*x)][2]=
                  colors[stepX][2] + colors[stepY*x][2];
          }
        numcolors=sqrColors*sqrColors;
        break;
      }
      case 2:
      {
        for(numcolors=0;numcolors<((poisson>>1)+1);numcolors++)
        {
          colors[numcolors][0]=
            colors[poisson-numcolors][0]=
            colors[numcolors][1]=
            colors[poisson-numcolors][1]=
            colors[numcolors][2]=
            colors[poisson-numcolors][2]=
            (unsigned long)(2*numcolors*((float)MAXCOLOR/poisson));
        }
        poisson= poisson- (poisson%2);
        break;
      }
      case 1:
      {
        int tee=(poisson/6);
        for(numcolors=0;numcolors<=(tee);numcolors++)
        {
          colors[numcolors][0]=
            colors[numcolors+(tee*2)][1]=
            colors[numcolors+(tee*4)][2]=
            (unsigned long)(fact*numcolors);
          colors[numcolors+(tee)][0]=
            colors[numcolors+(3*tee)][1]=
            colors[numcolors+(tee*5)][2]=
            (unsigned long)(MAXCOLOR-(fact*numcolors));

          colors[numcolors][2]=
            colors[numcolors+tee][2]=
            colors[numcolors+(tee*2)][0]=
            colors[numcolors+(tee*3)][0]=
            colors[numcolors+(tee*4)][1]=
            colors[numcolors+(tee*5)][1]=
            (unsigned long)0;
        }
        poisson= poisson- (poisson%6);
        break;
      }
      default:
      case 0:
      {
        int tee= poisson/6;
        for(numcolors=0;numcolors<=(tee);numcolors++)
        {
          colors[numcolors][1]=
            colors[numcolors+(tee*2)][2]=
            colors[numcolors+(tee*4)][0]=
            (unsigned long)(fact*numcolors);
          colors[numcolors+(tee)][0]=
            colors[numcolors+(tee*3)][1]=
            colors[numcolors+(tee*5)][2]=
            (unsigned long)(MAXCOLOR-(fact*numcolors));
          colors[numcolors][0]=
            colors[numcolors+(tee)][1]=
            colors[numcolors+(tee*2)][1]=
            colors[numcolors+(tee*3)][2]=
            colors[numcolors+(tee*4)][2]=
            colors[numcolors+(tee*5)][0]=
            (unsigned long)MAXCOLOR;

          colors[numcolors][2]=
            colors[numcolors+(tee)][2]=
            colors[numcolors+(tee*2)][0]=
            colors[numcolors+(tee*3)][0]=
            colors[numcolors+(tee*4)][1]=
            colors[numcolors+(tee*5)][1]=
            (unsigned long)0;
        }
        poisson= poisson- (poisson%6);
        break;
      }
    }
/*      fprintf(stderr,"Alloc'ing %d colors \n",poisson); */

    colors[0][0]=colors[0][1]=colors[0][2]=0;

    for(numcolors=0;numcolors<poisson;numcolors++)
    {
      screen_in_out.flags = DoRed | DoGreen | DoBlue;
      screen_in_out.red   = colors[numcolors][0];
      screen_in_out.green = colors[numcolors][1];
      screen_in_out.blue  = colors[numcolors][2];
      screen_in_out.pixel = numcolors;


      if (XStoreColor(display, colmap, &screen_in_out)==False)
      {
        fatalerror("Cannot allocate colors","");
      }

/*
          fprintf(stderr, "Got to #%d in here:",numcolors);
          fprintf(stderr, "red:%ld\tgreen:%ld\tblue:%ld\n",
          colors[numcolors][0],colors[numcolors][1],
          colors[numcolors][2]);
          fflush(stderr);
*/
      color_info[numcolors].pixel = screen_in_out.pixel;
      color_info[numcolors].red = screen_in_out.red;
      color_info[numcolors].green = screen_in_out.green;
      color_info[numcolors].blue = screen_in_out.blue;
#ifdef X
      color_info[0].pixel=options.bgcolor;
      values.foreground = color_info[numcolors].pixel;
      values.background = options.bgcolor;
      color_gcs[numcolors] = XCreateGC(display,window,
                                       GCForeground|GCBackground,
                                       &values);
#else
  #ifdef WINDOWS
      color_gcs[numcolors] = numcolors;
  #else
      color_gcs[numcolors] = color_info[numcolors].pixel;
  #endif
#endif
    }
  }
  else if (DisplayCells(display,screen)<2)       /*mono*/
  {
    numcolors=2;
    color_info[0].pixel = WhitePixel(display,screen);
    color_info[1].pixel = BlackPixel(display,screen);
    for(i=0;i<numcolors;i++)
    {
#ifdef X
      values.foreground = color_info[i].pixel;
      values.background = options.bgcolor;
      color_gcs[i] = XCreateGC(display,window,
                               GCForeground|GCBackground,&values);
#else
      color_gcs[i] = color_info[numcolors].pixel;
#endif
    }
  }
  else                                           /*greyscale */
  {
    XColor screen_in_out;
    int poisson=numcolors;
    screen_in_out.flags=DoRed|DoGreen|DoBlue;

    for (numcolors=0; numcolors<poisson; numcolors++)
    {
      colors[numcolors][0]=
        colors[numcolors][1]=
        colors[numcolors][2]=
        (unsigned long)(numcolors*((float)MAXCOLOR/poisson));
      
      screen_in_out.flags = DoRed | DoGreen | DoBlue;
      screen_in_out.red = colors[numcolors][0];
      screen_in_out.green = colors[numcolors][1];
      screen_in_out.blue = colors[numcolors][2];
      screen_in_out.pixel = numcolors;
      
      if (XStoreColor(display, colmap,&screen_in_out)==False)
      {
        if (numcolors < 2)
          fatalerror("Cannot allocate colors","");
        break;
      }

      color_info[numcolors].pixel = screen_in_out.pixel;
      color_info[numcolors].red = screen_in_out.red;
      color_info[numcolors].green = screen_in_out.green;
      color_info[numcolors].blue = screen_in_out.blue;
#ifdef X
      values.foreground = color_info[numcolors].pixel;
      values.background = options.bgcolor;
      color_gcs[numcolors] = XCreateGC(display,window,
                                       GCForeground|GCBackground,
                                       &values);
#else
      color_gcs[numcolors] = color_info[numcolors].pixel;
#endif
    }
/*    fprintf(stderr,"Alloc'ing %d colors \n",poisson); */
  }
}

#ifdef WINDOWS
void
freeColors()
{
  GlobalFree(HChndl);
  XFreeColors();
}
#endif

#ifdef X
void
freeColors()
{
  XFreeColors(display, colmap, colors[0],numcolors,1);
  XFreeColors(display, colmap, colors[1],numcolors,1);
  XFreeColors(display, colmap, colors[2],numcolors,1);
}
#endif
