#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gd.h"
#include "gmp.h"

// Given a permutation index and width,height and colour depth it will create an image.

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

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

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

mpz_t        *pmROC = NULL;
int          iNum      = 0;


int main
(
   int   iArgs    ,
   char *apsArgs[]
)
{
   FILE        *pF        = NULL;
   FILE        *pFilePng  = NULL;
   gdImagePtr   gdIpIn    = NULL;
   char        *pcArgImg  = NULL;
   char        *pcArgW    = NULL;
   char        *pcArgH    = NULL;
   char        *pcArgC    = NULL;
   char        *pcArgF    = NULL;
   char        *pcArgPerm = NULL;
   int          i         = 0;
   int          iIndex    = 0;
   int          iColour   = 0;
   int          iR        = 0;
   int          iG        = 0;
   int          iB        = 0;
   int          iW        = 0;
   int          iH        = 0;
   int          iC        = 16; // Default
   int          iX        = 0;
   int          iY        = 0;
   int          iDelta    = 0;
   int          aiColPal[256];
   mpz_t        mPerm;

   memset (aiColPal, 0, sizeof (int) * 256);

   if (iArgs > 1)
   {
     for (i = 0; i < iArgs; i++)
     {
        if (0 == strncmp ("-i=", apsArgs[i], 3))         // -i
        {
           pcArgImg = apsArgs[i] + 3;
        }
        else
        if (0 == strncmp ("-p=", apsArgs[i], 3))         // -p  permutation index
        {
           pcArgPerm = apsArgs[i] + 3;
           mpz_init (mPerm);
           gmp_sscanf (pcArgPerm, "%Zd", mPerm);

        }
        else
        if (0 == strncmp ("-w=", apsArgs[i], 3))         // -w  width
        {
           pcArgW = apsArgs[i] + 3;
           iW     = atoi (pcArgW);
        }
        else
        if (0 == strncmp ("-h=", apsArgs[i], 3))         // -h  height
        {
           pcArgH = apsArgs[i] + 3;
           iH     = atoi (pcArgH);
        }
        else
        if (0 == strncmp ("-c=", apsArgs[i], 3))         // -c  colours
        {
           pcArgC = apsArgs[i] + 3;
           iC     = atoi (pcArgC);
           if (iC > 256) iC = 256;
           iDelta = (int)((double)256 / (double)iC);
        }
        else
        if (0 == strncmp ("-f=", apsArgs[i], 3))         // -f permutation index in a text file.
        {
           pcArgF = apsArgs[i] + 3;

           pF = fopen (pcArgF,"r");
           if (NULL == pF)
              exit (printf ("fopen(%s) failed", pcArgF));

           mpz_init (mPerm);
           gmp_fscanf (pF, "%Zd", mPerm);
           fclose (pF);
        }
     }
   }
   else
   {
      exit (printf ("USAGE: png_from_perm\n\t-i=imagefileout.png\n\t-w=width\n\t-h=height\n\t-c=colors\n\t[-f=permkey.txt | -p=permkey]\n"));
   }

   if (NULL == pcArgImg)
      exit (printf ("-i 'imagefiletocreate' was not specified\n"));

   if (NULL == pcArgW)
      exit (printf ("-w width was not specified\n"));

   if (NULL == pcArgH)
      exit (printf ("-h height was not specified\n"));

   if (NULL == pcArgC)
      exit (printf ("-c colors was not specified\n"));

   if (NULL == pcArgF && NULL == pcArgPerm)
      exit (printf ("No permutation index specified.\n"));


   // Work out the rate of change for each column.
   // And store it in a lookup.

   iNum = iW * iH;
   pmROC = (mpz_t *)malloc (sizeof (mpz_t) * iNum);
   if (NULL == pmROC)
      exit (printf ("malloc() failed"));
   for (i = 0; i < iNum; i++)
   {
      mpz_init (pmROC[i]);
      m_rate_of_change (pmROC[i], i, iC);
     //  gmp_printf ("i=%d pmROC[i]=%Zd\n", i, pmROC[i]);
   }

   // Create the image.

	pFilePng = fopen (pcArgImg, "wb");
   if (NULL == pFilePng)
      exit (printf ("fopen(%s) failed", pcArgImg));

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

   // Allocate iC colours (max of 256)

   iR = 0;
   iG = 0;
   iB = 0;
   for (i = 0; i < iC-1; i++)
   {
     printf ("i=%02d (%d,%d,%d)\n", i, iR, iG, iB);
     aiColPal[i] = gdImageColorAllocate (gdIpIn, iR, iG, iB);

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

   // Make last one white

   aiColPal[iC-1] = gdImageColorAllocate (gdIpIn, 255, 255, 255);
   printf ("i=%d (%d,%d,%d)\n", iC-1, 255, 255, 255);


	printf ("File   :%s\n" , pcArgImg                   );
	printf ("Width  :%d\n" , gdImageSX (gdIpIn)         );
	printf ("Height :%d\n" , gdImageSY (gdIpIn)         );
	printf ("Colours:%d\n" , gdImageColorsTotal (gdIpIn));

   // Set the pixels.

   for (iY = 0; iY < iH; iY++)
   {
      for (iX = 0; iX < iW; iX++)
      {
          iIndex = (iY * iW) + iX;
          //printf ("iIndex=%d\n", iIndex);
          pixel_from_perm (&iColour, (long)iIndex, (long)iC, mPerm);
          gdImageSetPixel (gdIpIn, iX, iY, iColour);
      }
     printf ("iY=%d\n", iY);
   }

   // Save the image

   gdImagePng (gdIpIn, pFilePng);
   //   gdImageJpeg (gdIp, pFile, -1);

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

	gdImageDestroy (gdIpIn); // Returns void.

   for (i = 0; i < iNum; i++)
      mpz_clear (pmROC[i]);
   free (pmROC);
   pmROC = NULL;

   return (0);
}


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;

   *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[lIndex], mPerm);

   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);
}

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);
}

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);
}
