#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>                   // Windows.
#include <time.h>
#include "iup.h"
#include "cd.h"
#include "cdiup.h"
#include "gmp.h"
#include "gd.h"

/*
---struct_imgS()
*/
struct imgS
{
    long                lId;
    char                sName[255];
    mpz_t               tPermKey;
    mpz_t               Xdiv;     // e.g 100 for percentages
    mpz_t               Ydiv;     // e.g 100 for percentages
    long                lX;       // Actual position on graph in terms of its axis units
    long                lY;       // Actual position on graph in terms of its axis units
};

/*
---struct_permdbS()
*/
struct permdbS
{
    long                lId;
    long                lColours;
    long                lWidth;
    long                lHeight;
    long                lPixels;
    mpz_t               mPerms;
    gdImagePtr          pGdImage;


    struct imgS        *pSimg;
    long                lImgs;
};


/*
---prototypes()
*/

void m_rate_of_change
(
   mpz_t mRateOfChange,
   long  lColumn,
   long  lDepth
);

int yiq
(
   int iR,
   int iG,
   int iB
);

int gdImageGreyScale
(
   gdImagePtr gdImg
);

void m_times_cell_has_changed
(
   mpz_t mTimesChanged,
   mpz_t mRateOfChange,
   mpz_t mIteration
);

int pixel_from_perm
(
   int   *piColour,   // colour for this pixel
   long   lIndex,     // pixel index
   long   lDepth,     // Colour depth
   mpz_t  mPerm
);

int dialog_create
(
   void
);

int image_init
(
   gdImagePtr   *ppGdImage
);

int db_create
(
   char *pcName,
   long  lWidth,
   long  lHeight,
   long  lColours
);

int image_from_perm
(
   mpz_t mPermKey
);

int dialog_testimage
(
   int iImgWidth,
   int iImgHeight,
   int iDepth
);

int btnImportCallback
(
   Ihandle *self
);

int cdImagePreviewRedraw
(
   Ihandle *self, float x, float y
);

int btnToolsCallback
(
   Ihandle *self
);

int btnUpdateCallback
(
   Ihandle *self
);

int btnRandomCallback
(
   Ihandle *self
);

int btnCreateCallback
(
   Ihandle *self
);

int btnCreateDbCallback
(
   Ihandle *self
);

int btnOpenCallback
(
   Ihandle *self
);

/*
---GLOBALS()
*/

struct permdbS Spdb;
int            aiColPal [255];
int            iActive = 0;
FILE           *pFperm = NULL;
cdCanvas       *cd     = NULL;

/*
...main()
*/
int main
(
   int   iArgs    ,
   char *apsArgs[]
)
{
   Ihandle *dlg       = NULL;
   Ihandle *btnOpen   = NULL;
   Ihandle *btnCreate = NULL;
   Ihandle *btnImport = NULL;
   Ihandle *btnGraph  = NULL;
   Ihandle *btnStats  = NULL;
   Ihandle *btnTools  = NULL;
   Ihandle *lstImages = NULL;
   Ihandle *cdGraph   = NULL;
   Ihandle *cdPreview = NULL;
   Ihandle *txtInfo   = NULL;


   // Initialize the IUP libraries

   if (IUP_ERROR == IupOpen (&iArgs, &apsArgs))
  	   exit (printf ("IupOpen() failed"));


   // Create the BUTTONS

   btnOpen = IupButton ("Open", "btnOpen");
   if (NULL == btnOpen)
  	   return (printf ("IupButton() failed"));

   btnCreate = IupButton ("Create", "btnCreate");
   if (NULL == btnCreate)
  	   return (printf ("IupButton() failed"));

   btnImport = IupButton ("Import", "btnImport");
   if (NULL == btnImport)
  	   return (printf ("IupButton() failed"));

   btnGraph = IupButton ("Graph", "btnGraph");
   if (NULL == btnGraph)
  	   return (printf ("IupButton() failed"));

   btnStats = IupButton ("Stats", "btnStats");
   if (NULL == btnStats)
  	   return (printf ("IupButton() failed"));

   btnTools = IupButton ("Tools", "btnTools");
   if (NULL == btnTools)
  	   return (printf ("IupButton() failed"));


   IupSetAttributes (btnOpen,   "EXPAND=NO, SIZE=80x" );
   IupSetAttributes (btnCreate, "EXPAND=NO, SIZE=80x" );
   IupSetAttributes (btnImport, "EXPAND=NO, SIZE=80x" );
   IupSetAttributes (btnGraph,  "EXPAND=NO, SIZE=80x" );
   IupSetAttributes (btnStats,  "EXPAND=NO, SIZE=80x" );
   IupSetAttributes (btnTools,  "EXPAND=NO, SIZE=80x" );

   IupSetCallback   (btnCreate, "ACTION", (Icallback) btnCreateCallback );
   IupSetCallback   (btnOpen  , "ACTION", (Icallback) btnOpenCallback   );
   IupSetCallback   (btnImport, "ACTION", (Icallback) btnImportCallback );
   IupSetCallback   (btnTools , "ACTION", (Icallback) btnToolsCallback  );

//   IupSetCallback (btn, "ACTION", (Icallback) btnBrowseCallback );


   lstImages = IupList ("lstImages");
   if (NULL == lstImages)
  	   return (printf ("IupList() failed"));

   IupSetAttributes (lstImages,   "EXPAND=VERTICAL, SCROLLBAR=YES, AUTOHIDE=YES, SIZE=100x100" );


   cdGraph = IupCanvas ("cdGraph");
   if (NULL == cdGraph)
  	   return (printf ("IupCanvas() failed"));

   IupSetAttributes (cdGraph,   "EXPAND=YES" );


   cdPreview = IupCanvas ("cdPreview");
   if (NULL == cdPreview)
  	   return (printf ("IupCanvas() failed"));

   IupSetAttributes (cdPreview,   "EXPAND=NO, SIZE=100x100px" );


   txtInfo = IupText ("txtInfo");
   if (NULL == txtInfo)
  	   return (printf ("IupText() failed"));

   IupSetAttributes (txtInfo,   "EXPAND=HORIZONTAL, SIZE=400x100, MULTILINE=YES" );


   dlg = IupDialog
         (
            IupSetAttributes
            (
               IupVbox
               (
                  IupSetAttributes
                  (
                    IupHbox
                    (
                      btnOpen,
                      btnCreate,
                      btnImport,
                      btnGraph,
                      btnStats,
                      btnTools,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  IupSetAttributes
                  (
                    IupHbox
                    (
                      lstImages,
                      cdGraph,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  IupSetAttributes
                  (
                    IupHbox
                    (
                      cdPreview,
                      txtInfo,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  NULL
               ),
               "MARGIN=5x5, GAP=10, ALIGNMENT=ALEFT"
            )
         );

   IupSetAttribute ( dlg, "TITLE", "Image Permution Grapher");
   IupSetAttributes( dlg, "RESIZE=YES, MINSIZE=450x300" );
   IupShowXY       ( dlg, IUP_CENTER, IUP_CENTER );

   //IupMap (dlg);

//   IupSetAttribute (timer, "RUN", "YES");


   // Start the application event system.

   IupMainLoop();
   IupDestroy (dlg);
   IupClose();


   return (0);
}



/*
...dialog_testimage()
*/
int dialog_testimage
(
   int iImgWidth,
   int iImgHeight,
   int iDepth
)
{
   char    sAttrs [255];
   Ihandle  *dlg           = NULL;
   Ihandle  *btnRandom     = NULL;
   Ihandle  *btnUpdate     = NULL;
   Ihandle  *txtPermKey    = NULL;
   Ihandle  *cdImgPreview  = NULL;


   // Create the preview image.

   memset (sAttrs, 0, 255);

   cdImgPreview = IupCanvas ("cdImgPreview");
   if (NULL == cdImgPreview)
  	   return (printf ("IupCanvas() failed"));

   sprintf (sAttrs, "EXPAND=NO, SIZE=%dx%d", iImgWidth, iImgHeight);
   IupSetAttributes (cdImgPreview,  sAttrs);
   IupSetCallback   (cdImgPreview, "ACTION", (Icallback) cdImagePreviewRedraw);
   IupSetHandle     ("cdImgPreview", cdImgPreview);

   // Create the text item to hold the key.

   txtPermKey = IupText ("txtPermKey");
   if (NULL == txtPermKey)
  	   return (printf ("IupText() failed"));

   IupSetAttributes (txtPermKey,   "EXPAND=HORIZONTAL, SIZE=400x100, MULTILINE=YES" );
   IupSetHandle ("txtPermKey", txtPermKey);


   // Create the random key button.

   btnRandom = IupButton ("Random Key", "btnRandom");
   if (NULL == btnRandom)
  	   return (printf ("IupButton() failed"));

   IupSetCallback   (btnRandom, "ACTION", (Icallback) btnRandomCallback );

   // Create the update preview button.

   btnUpdate = IupButton ("Update Preview", "btnUpdate");
   if (NULL == btnUpdate)
  	   return (printf ("IupButton() failed"));

   IupSetCallback   (btnUpdate, "ACTION", (Icallback) btnUpdateCallback );



   dlg = IupDialog
         (
            IupSetAttributes
            (
               IupVbox
               (
                  IupSetAttributes
                  (
                    IupHbox
                    (
                      cdImgPreview,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  IupSetAttributes
                  (
                    IupHbox
                    (
                      txtPermKey,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  IupSetAttributes
                  (
                    IupHbox
                    (
                      btnRandom,
                      btnUpdate,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),
                  NULL
               ),
               "MARGIN=5x5, GAP=10, ALIGNMENT=ALEFT"
            )
         );


   IupMap (dlg);

   cd = cdCreateCanvas (CD_IUP, cdImgPreview);
//   cdCanvasYAxisMode   (cd, 1);
   cdCanvasForeground  (cd, CD_BLACK);


   IupSetAttribute ( dlg, "TITLE", "Key Tester");
   IupSetAttributes( dlg, "RESIZE=YES, MINSIZE=450x300" );
   IupShowXY       ( dlg, IUP_CENTER, IUP_CENTER );

   return (0);
}

/*
...btnCreateCallback()
*/
int btnCreateCallback
(
   Ihandle *self
)
{
   dialog_create();

   return IUP_DEFAULT;
}

/*
...btnCreateDbCallback()
*/
int btnCreateDbCallback
(
   Ihandle *self
)
{
   Ihandle *h      = NULL;
   char    *pcName = NULL;
   int      iW     = 0;
   int      iH     = 0;
   int      iC     = 0;

   h = IupGetHandle ("txtName");
   if (NULL != h)
      pcName = IupGetAttribute (h, "VALUE");

   h = IupGetHandle ("txtWidth");
   if (NULL != h)
      iW = IupGetInt (h, "VALUE");

   h = IupGetHandle ("txtHeight");
   if (NULL != h)
      iH = IupGetInt (h, "VALUE");

   h = IupGetHandle ("txtColours");
   if (NULL != h)
      iC = IupGetInt (h, "VALUE");


   db_create (pcName, iW, iH, iC);

   // Close the dialog.

   IupDestroy (IupGetDialog(self));

   return IUP_DEFAULT;
}

/*
...btnOpenCallback()
*/
int btnOpenCallback
(
   Ihandle *self
)
{
   Ihandle *dlg    = NULL;
   char    *pcName = NULL;
   int      iW     = 0;
   int      iH     = 0;
   int      iC     = 0;


   // Open file select dialog.

   dlg = IupFileDlg ();

   IupSetAttribute (dlg, "DIALOGTYPE"    , "OPEN");
   IupSetAttribute (dlg, "MULTIPLEFILES" , "NO");
   IupSetAttribute (dlg, "FILTER"        , "*.perm");
   IupSetAttribute (dlg, "TITLE"         , "Select Perm File...");

   IupPopup (dlg, IUP_CURRENT, IUP_CURRENT);

   switch (IupGetInt (dlg, "STATUS"))
   {
      case  0:
      {
         memset (&Spdb, 0, sizeof (struct permdbS));


         printf ("file=[%s]\n", IupGetAttribute (dlg, "VALUE"));
         pFperm = fopen (IupGetAttribute (dlg, "VALUE"), "r");
         if (NULL != pFperm)
         {
            mpz_init (Spdb.mPerms);

            // read id,w,h,c,p

            fscanf     (pFperm, "ID=%ld\n", &Spdb.lId);
            fscanf     (pFperm, "W=%ld\n" , &Spdb.lWidth);
            fscanf     (pFperm, "H=%ld\n" , &Spdb.lHeight);
            fscanf     (pFperm, "C=%ld\n" , &Spdb.lColours);
            gmp_fscanf (pFperm, "P=%Zd\n" ,  Spdb.mPerms);

            iActive = 1;
            image_init (&Spdb.pGdImage);
         }
      }
      break;
   }

   // Parse the file.

   //printf ("%ld, %ld, %ld, %ld\n", Spdb.lId, Spdb.lWidth, Spdb.lHeight, Spdb.lColours);
   //gmp_printf ("Spdb.mPerms=%Zd\n", Spdb.mPerms);

   // Close the dialog.

//   IupDestroy (IupGetDialog(self));

   return IUP_DEFAULT;
}

/*
...btnImportCallback()
*/
int btnImportCallback
(
   Ihandle *self
)
{
   char         sFn [255];
   FILE        *pF     = NULL;
   gdImagePtr   gdIn   = NULL;
   gdImagePtr   gdOut  = NULL;
   gdImagePtr   gdOut2 = NULL;
   Ihandle     *dlg    = NULL;
   char        *pcName = NULL;
   int          iW     = 0;
   int          iH     = 0;
   int          iC     = 0;
   int          iR     = 0;
   int          iG     = 0;
   int          iB     = 0;
   int          i      = 0;
   int          iDelta = 0;
   double       dScale = 0.0;


   memset (sFn, 0, 255);

   // Open file to import

   dlg = IupFileDlg ();

   IupSetAttribute (dlg, "DIALOGTYPE"    , "OPEN");
   IupSetAttribute (dlg, "MULTIPLEFILES" , "NO");
   IupSetAttribute (dlg, "FILTER"        , "*.png;*.jpg");
   IupSetAttribute (dlg, "TITLE"         , "Select PNG File...");

   IupPopup (dlg, IUP_CURRENT, IUP_CURRENT);

   switch (IupGetInt (dlg, "STATUS"))
   {
      case  0:
      {
         printf ("file=[%s]\n", IupGetAttribute (dlg, "VALUE"));
         pF = fopen (IupGetAttribute (dlg, "VALUE"), "rb");
         if (NULL != pF)
         {
            // Laod the image.

            if (NULL != strstr (IupGetAttribute (dlg, "VALUE"), ".jpg"))
            {
               gdIn = gdImageCreateFromJpeg (pF);
               if (NULL == gdIn)
	            	exit (printf( "gdImageCreateFromPng() failed"));

            }
            else
            {
               gdIn = gdImageCreateFromPng (pF);
               if (NULL == gdIn)
	            	exit (printf( "gdImageCreateFromPng() failed"));

            }

	         if (0 != fclose (pF))
               exit (printf ("fclose() failed ()"));


            // Resize so its at least wxh

            if (gdImageSX(gdIn) < gdImageSY(gdIn))
            {
               // X is smaller
               // Resize the X to out desired size and
               // then crop the Y

               dScale =  (double)Spdb.lWidth / (double)gdImageSX(gdIn);
               iW = Spdb.lWidth;
               iH = (int)((double)gdImageSY(gdIn) * dScale);
            }
            else
            {
               // Y is smaller
               // Resize the Y to desired size and
               // crop the X

               dScale =  (double)Spdb.lHeight / (double)gdImageSY(gdIn);
               iW = (int)((double)gdImageSX(gdIn) * dScale);
               iH = Spdb.lHeight;
            }

            printf ("SX=%d SY=%d dScale=%lf\n", gdImageSX(gdIn), gdImageSY(gdIn), dScale);
            printf ("iW=%d iH=%d\n", iW, iH);

            gdOut = gdImageCreateTrueColor (iW, iH);
            if (NULL == gdOut)
               exit (printf ("gdImageCreate () failed"));

            gdImageCopyResampled (gdOut, gdIn, 0, 0, 0, 0, iW, iH, gdImageSX(gdIn), gdImageSY(gdIn));

            // Cut out square w x h

            gdOut2 = gdImageCreateTrueColor (Spdb.lWidth, Spdb.lHeight);
            if (NULL == gdOut2)
               exit (printf ("gdImageCreate () failed"));


            gdImageCopyResampled (gdOut2, gdOut, 0, 0, 0, 0, Spdb.lWidth, Spdb.lHeight, Spdb.lWidth, Spdb.lHeight);

            // Make grey scale.

            gdImageGreyScale (gdOut2);

            gdImageTrueColorToPalette (gdOut2, 50, Spdb.lColours);


            // Save the image

            sprintf (sFn, "%s.png", IupGetAttribute (dlg, "VALUE"));
            pF = fopen (sFn, "wb");
            if (NULL != pF)
            {
               gdImagePng (gdOut2, pF);
//               gdImageJpeg (gdOut2, pF, 100);

               if (0 != fclose (pF))
                 return (printf("fclose() failed"));
            }

         	gdImageDestroy (gdIn); // Returns void.
         	gdImageDestroy (gdOut); // Returns void.
         	gdImageDestroy (gdOut2); // Returns void.
         }
      }
      break;
   }

   // Parse the file.

   //printf ("%ld, %ld, %ld, %ld\n", Spdb.lId, Spdb.lWidth, Spdb.lHeight, Spdb.lColours);
   //gmp_printf ("Spdb.mPerms=%Zd\n", Spdb.mPerms);

   // Close the dialog.

//   IupDestroy (IupGetDialog(self));

   return IUP_DEFAULT;
}


/*
...btnRandomCallback()
*/
int btnRandomCallback
(
   Ihandle *self
)
{
   mpz_t           mKey;
   mpz_t           mN;
   gmp_randstate_t mRand;


   mpz_init (mKey);
   mpz_init (mN);

   gmp_randinit_mt (mRand);

   // Convert to an image.

   gmp_sscanf (IupGetAttribute (IupGetHandle ("txtPermKey"), "VALUE"), "%Zd", mN);
   gmp_randseed (mRand, mN);

   mpz_urandomm (mKey, mRand, Spdb.mPerms);

   //gmp_printf ("mKey=%Zd\n", mKey);
   //gmp_printf ("mPerms=%Zd\n", Spdb.mPerms);
   IupSetAttribute (IupGetHandle ("txtPermKey"), "VALUE", mpz_get_str (NULL, 10, mKey));


   gmp_randclear (mRand);

   image_from_perm (mKey);

   cdImagePreviewRedraw (NULL, 0, 0);

   return IUP_DEFAULT;
}

/*
...btnUpdateCallback()
*/
int btnUpdateCallback
(
   Ihandle *self
)
{
   Ihandle *txtPermKey = NULL;
   Ihandle *h          = NULL;
   char    *pc         = NULL;
   mpz_t    mPermKey;

   // Use the key in the text item to create a permutation.

   // Get the text from the text item.

   txtPermKey = IupGetHandle ("txtPermKey");
   if (NULL != txtPermKey)
   {
     // Convert to an image.

     pc = IupGetAttribute (txtPermKey, "VALUE");
    // printf ("key=%s\n", pc);

     // Show image in the canvas.

     mpz_init (mPermKey);
     gmp_sscanf (pc, "%Zd", mPermKey);
     image_from_perm (mPermKey);
     mpz_clear (mPermKey);

     // Copy image to canvas.

     cdImagePreviewRedraw (NULL, 0, 0);

     //h = IupGetDialog (self);
     //if (NULL != h)
     //IupRefresh (h);
     //update_canvas_with_image (h);
   }

   return IUP_DEFAULT;
}


/*
...btnToolsCallback()
*/
int btnToolsCallback
(
   Ihandle *self
)
{

   dialog_testimage(Spdb.lWidth, Spdb.lHeight, Spdb.lColours);

   return IUP_DEFAULT;
}


/*
...dialog_create()
  Opens a dialog to accept a name, width, height and colours.
  It will create a file called name_w_h_c.perm, this database is then active.
*/
int dialog_create
(
   void
)
{
   // Open up the dialog to create a database.

   Ihandle *dlg          = NULL;
   Ihandle *btnCreate    = NULL;
   Ihandle *txtName      = NULL;
   Ihandle *txtWidth     = NULL;
   Ihandle *txtHeight    = NULL;
   Ihandle *txtColours   = NULL;



   // Create the text item to hold the key.

   txtName = IupText ("txtName");
   if (NULL == txtName)
  	   return (printf ("IupText() failed"));

   IupSetAttributes (txtName,   "EXPAND=NO, SIZE=100px, MULTILINE=NO" );
   IupSetHandle ("txtName", txtName);


   txtWidth = IupText ("txtWidth");
   if (NULL == txtWidth)
  	   return (printf ("IupText() failed"));

   IupSetAttributes (txtWidth,   "EXPAND=NO, SIZE=100px, MULTILINE=NO" );
   IupSetHandle ("txtWidth", txtWidth);


   txtHeight = IupText ("txtHeight");
   if (NULL == txtHeight)
  	   return (printf ("IupText() failed"));

   IupSetAttributes (txtHeight,   "EXPAND=NO, SIZE=100px, MULTILINE=NO" );
   IupSetHandle ("txtHeight", txtHeight);

   txtColours = IupText ("txtColours");
   if (NULL == txtColours)
  	   return (printf ("IupText() failed"));

   IupSetAttributes (txtColours,   "EXPAND=NO, SIZE=100px, MULTILINE=NO" );
   IupSetHandle ("txtColours", txtColours);


   // Create the random key button.

   btnCreate = IupButton ("Create", "btnCreate");
   if (NULL == btnCreate)
  	   return (printf ("IupButton() failed"));

   IupSetCallback   (btnCreate, "ACTION", (Icallback) btnCreateDbCallback );


   dlg = IupDialog
         (
            IupSetAttributes
            (
               IupVbox
               (
                  IupSetAttributes
                  (
                    IupHbox
                    (
                      IupLabel ("Name:"),
                      txtName,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  IupSetAttributes
                  (
                    IupHbox
                    (
                      IupLabel ("Width:"),
                      txtWidth,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  IupSetAttributes
                  (
                    IupHbox
                    (
                      IupLabel ("Height:"),
                      txtHeight,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  IupSetAttributes
                  (
                    IupHbox
                    (
                      IupLabel ("Colours:"),
                      txtColours,
                      NULL
                    ),
                    "ALIGNMENT=ATOP"
                  ),

                  btnCreate,
                  NULL
               ),
               "MARGIN=5x5, GAP=10, ALIGNMENT=ALEFT"
            )
         );

   //IupMap (dlg);

   IupSetAttribute ( dlg, "TITLE", "Create Datbase");
   IupSetAttributes( dlg, "RESIZE=YES" );
   IupShowXY       ( dlg, IUP_CENTER, IUP_CENTER );


   return IUP_DEFAULT;
}


/*
...db_create()
*/
int db_create
(
   char *pcName,
   long  lWidth,
   long  lHeight,
   long  lColours
)
{
   char  sFn [255];
   mpz_t mN;
   mpz_t mR;

   memset (sFn, 0, 255);
   memset (&Spdb, 0, sizeof (struct permdbS));

   Spdb.lId      = 1L;
   Spdb.lColours = lColours;
   Spdb.lWidth   = lWidth;
   Spdb.lHeight  = lHeight;
   Spdb.lPixels  = lWidth * lHeight;
   Spdb.pSimg    = NULL;
   Spdb.lImgs    = 0L;

   // Create the file.

   sprintf (sFn, "%s_%ld_%ld_%ld.perm", pcName, lWidth, lHeight, lColours);
   pFperm = fopen (sFn, "w");
   if (NULL == pFperm)
      return (printf ("fopen () failed"));

   // Calcualte the permutations.

   mpz_init   (Spdb.mPerms);
   mpz_init   (mN);
   mpz_init   (mR);

   mpz_set_ui (Spdb.mPerms, 0);
   mpz_set_ui (mN         , lColours);
   mpz_set_ui (mR         , Spdb.lPixels);

   mpz_pow_ui    (Spdb.mPerms, mN, mpz_get_ui(mR));

   // Output the data to the db.

   fprintf     (pFperm, "ID=%ld\n", Spdb.lId);
   fprintf     (pFperm, " W=%ld\n", lWidth);
   fprintf     (pFperm, " H=%ld\n", lHeight);
   fprintf     (pFperm, " C=%ld\n", lColours);
   gmp_fprintf (pFperm, " P=%Zd\n", Spdb.mPerms);

   iActive = 1;
   image_init (&Spdb.pGdImage);

   mpz_clear (mN);
   mpz_clear (mR);

   return IUP_DEFAULT;
}


/*
...image_init()

   Create an image buffer and sets the colour palete based on the
   values set in the db.
*/
int image_init
(
   gdImagePtr   *ppGdImage
)
{
   // Allocate iC colours (max of 256)

   int iDelta = (int)((double)256 / (double)Spdb.lColours);
   int iR     = 0;
   int iG     = 0;
   int iB     = 0;
   int i      = 0;

   // Create an image buffer

   *ppGdImage = gdImageCreate (Spdb.lWidth, Spdb.lHeight);
   if (NULL == *ppGdImage)
      exit (printf ("gdImageCreate () failed"));


   for (i = 0; i < Spdb.lColours-1; i++)
   {
     printf ("i=%02d (%d,%d,%d)\n", i, iR, iG, iB);
     gdImageColorAllocate (*ppGdImage, iR, iG, iB);
     aiColPal[i] = cdEncodeColor (iR, iG, iB);

     iR += iDelta;
     iG += iDelta;
     iB += iDelta;
   }

   // Make last one white
   gdImageColorAllocate (*ppGdImage, 255, 255, 255);
   aiColPal[Spdb.lColours-1] = cdEncodeColor (255,255,255);

   printf ("i=%ld (%d,%d,%d)\n", Spdb.lColours-1, 255, 255, 255);

   return (0);
}

/*
...image_from_perm()

  Update the gdimage pointer in the structure by using the permutation key.

*/
int image_from_perm
(
   mpz_t mPermKey
)
{
   int iY      = 0;
   int iX      = 0;
   int iIndex  = 0;
   int iColour = 0;

   for (iY = 0; iY < Spdb.lHeight; iY++)
   {
      //printf ("\niY=%d\n", iY);
      for (iX = 0; iX < Spdb.lWidth; iX++)
      {
          iIndex = (iY * Spdb.lWidth) + iX;
          //printf ("iIndex=%d\n", iIndex);
          pixel_from_perm (&iColour, (long)iIndex, (long)Spdb.lColours, mPermKey);
          gdImageSetPixel (Spdb.pGdImage, iX, iY, iColour);
          //printf ("%d,", iColour);
      }
   }

   return (0);
}

/*
...cdImagePreviewRedraw()

  Update the canvas with the gdimage pointer data.

*/
int cdImagePreviewRedraw
(
   Ihandle *self,
   float    x,
   float    y
)
{
   int      iY      = 0;
   int      iX      = 0;
   int      iIndex  = 0;
   int      iColour = 0;

   printf ("redraw\n");

   cdCanvasActivate  (cd);
   cdCanvasClear     (cd);

  // cdCanvasForeground(cd, CD_BLUE);
  // cdCanvasBox       (cd, 0, 50, 0, 50);

   for (iY = 0; iY < Spdb.lHeight; iY++)
   {
      for (iX = 0; iX < Spdb.lWidth; iX++)
      {
          iIndex = (iY * Spdb.lWidth) + iX;
          //printf ("iIndex=%d\n", iIndex);
          iColour = gdImageGetPixel (Spdb.pGdImage, iX, iY);
          cdCanvasPixel (cd, iX, cdCanvasInvertYAxis (cd, iY), aiColPal[iColour]);
      }
     // printf ("iY=%d\n", iY);
   }

   cdCanvasDeactivate (cd);
   cdCanvasFlush(cd);

   return (0);
}

/*
...pixel_from_perm()
*/
int pixel_from_perm
(
   int   *piColour,   // colour for this pixel
   long   lIndex,     // pixel index
   long   lDepth,     // Colour depth
   mpz_t  mPerm
)
{
   mpz_t mTC;
   mpz_t mTCD;
   mpz_t mN;
   mpz_t mValue;
   mpz_t mTimesChanged;
   mpz_t mRateOfChange;

   *piColour = 0;

   mpz_init   (mTimesChanged);
   mpz_set_ui (mTimesChanged, 0);

   m_rate_of_change (mRateOfChange, lIndex, lDepth);
   m_times_cell_has_changed (mTimesChanged, mRateOfChange, mPerm);
   //m_times_cell_has_changed (mTimesChanged, pmROC[lPx], mP);

   mpz_init   (mN);
   mpz_init   (mTC);
   mpz_init   (mTCD);
   mpz_init   (mValue);

   mpz_set_ui (mTC   , 0);
   mpz_set_ui (mTCD  , 0);
   mpz_set_ui (mValue, 0);
   mpz_set_ui (mN    , lDepth);

   mpz_set    (mTC, mTimesChanged);
   mpz_fdiv_q (mTCD, mTC, mN);   // timeschanged / depth

   //gmp_printf ("\nmTCD=%Zd mTimesChanged=%Zd mTC=%Zd mN=%Zd mRateOfChange=%Zd\n", mTCD, mTimesChanged, mTC, mN, mRateOfChange);

   mpz_mul    (mTCD, mTCD, mN);   // * depth
   mpz_sub    (mValue, mTimesChanged, mTCD);  // timeschanged - mTC

   *piColour = mpz_get_ui (mValue);
   //printf ("%d,", *piColour);

  // gmp_printf ("%Z02d,", mValue);
  // if (0 == fmod ((double)lPx+1, (double)lW))
  //    printf ("\n");

   mpz_clear (mTC);
   mpz_clear (mTCD);
   mpz_clear (mValue);

   mpz_clear (mTimesChanged);
   mpz_clear (mRateOfChange);

   return (0);
}

/*
...m_rate_of_change()
*/
void m_rate_of_change
(
   mpz_t mRateOfChange,
   long  lColumn,
   long  lDepth
)
{
   mpz_t mDepth;

   mpz_init (mRateOfChange);
   mpz_init (mDepth);

   mpz_set_ui (mDepth, lDepth);
   mpz_pow_ui (mRateOfChange, mDepth, lColumn);
   mpz_add_ui (mRateOfChange, mRateOfChange, 1L);
   //gmp_printf ("column=%ld, Rate of change=%Zd\n", lColumn, mRateOfChange);

   mpz_clear (mDepth);
}

/*
...m_times_cell_has_changed()
*/
void m_times_cell_has_changed
(
   mpz_t mTimesChanged,
   mpz_t mRateOfChange,
   mpz_t mIteration
)
{
   mpz_t mROC;

   mpz_init   (mROC);
   mpz_set    (mROC, mRateOfChange);
   mpz_sub_ui (mROC, mROC, 1);

   //gmp_printf ("mROC=%Zd mRateOfChange=%Zd mIteration=%Zd\n", mROC, mRateOfChange, mIteration);
   mpz_fdiv_q (mTimesChanged, mIteration, mROC);
   mpz_clear (mROC);
}

/*
...yiq()
*/
int yiq
(
   int iR,
   int iG,
   int iB
)
{
   return ((int) ((double)iR*0.299)+((double)iG*0.587)+((double)iB*0.114));
}

/*
...gdImageGreyScale()
*/
int gdImageGreyScale
(
   gdImagePtr gdImg
)
{
   int aiCol[256];
   int iX = 0;
   int iY = 0;
   int iR = 0;
   int iG = 0;
   int iB = 0;
   int iColour = 0;
   int i       = 0;
   int iC      = 0;
   int iDelta  = 0;


   iDelta = (256 / Spdb.lColours);

   for (i = 0; i < Spdb.lColours; i++)
   {
      aiCol[i] = gdImageColorAllocate (gdImg, iC, iC, iC);
      //printf ("i=%d aiCol[]=%d iDelta=%d\n", i, aiCol[i], iDelta);

      iC+=iDelta;
   }

   for (iY = 0; iY < gdImageSY (gdImg); iY++)
   {
      for (iX = 0; iX < gdImageSX (gdImg); iX++)
      {
         iColour = gdImageGetPixel (gdImg, iX, iY);
         //printf ("iColour=%d\n", iColour);
         iR = gdImageRed  (gdImg, iColour);
         iG = gdImageGreen(gdImg, iColour);
         iB = gdImageBlue (gdImg, iColour);

         iColour = yiq (iR, iG, iB);
         //printf ("iC=%d iR=%d iG=%d iB=%d\n", iColour, iR, iG, iB);
         gdImageSetPixel (gdImg, iX, iY, aiCol[iColour/iDelta]);
      }
   }

   return (0);
}
