#include <Magick++.h>
#include <fstream.h>
#include <math.h>
#include <sys/time.h>
#include <unistd.h>


using namespace Magick;
FILE* stream;

class CImage
{
public:
  int maxX;
  int maxY;
  int cRes;
  int **intensity;

  void saveImage(char*);
  void decompressImage();
  void getInput(char*);
    
  CImage()
  {
    maxX = maxY = 0;
    intensity = NULL;
    cRes = 255;
  }

  ~CImage()
  {
    int i;
    for(i=0;i<maxY;i++)
      delete intensity[i];
    delete [] intensity;
  }
};

void CImage::saveImage(char* file)
{
  Image image(Geometry(maxX,maxY), "white");
  image.classType(DirectClass);
  Pixels view(image);
  PixelPacket *pixels=view.get(0,0,maxX,maxY);
  int i,j;
  for(int i=0;i<maxY;i++)
    for(int j=0;j<maxX;j++)
      (*(pixels+i*maxX+j))=ColorGray(float(intensity[i][j])/(float)cRes);
  image.write(file);
}

void CImage::getInput(char* input)
{
  char b;
  int j = 0,i,k;
  int c;
  b = getc(stream);
  maxX = b;
  b = getc(stream);
  maxY = b;
  while(!feof(stream))
    {
      b = getc(stream);
      c = b;
      if(c<0) c+=256;
      for(k=7;k>=0;k--)
	{
	  if(c%2==1)
	    input[j*8+k] = '1';
	  else
	    input[j*8+k] = '0';
	  c/=2;
	}
      j++;
    }
  input[j*8] = '\0';
}

void getACvalue(char* input, int& pos, int& n, int& p)
{
  int a,len,i,j,k;
  int codeLen[16][11] = {
    4 ,3 ,4 ,6 ,8 ,10,12,14,18,25,26,
    0 ,5 ,8 ,10,13,16,22,23,24,25,26,
    0 ,6 ,10,13,20,21,22,23,24,25,26,
    0 ,7 ,11,14,20,21,22,23,24,25,26,
    0 ,7 ,12,19,20,21,22,23,24,25,26,
    0 ,8 ,12,19,20,21,22,23,24,25,26,
    0 ,8 ,13,19,20,21,22,23,24,25,26,
    0 ,9 ,13,19,20,21,22,23,24,25,26,
    0 ,9 ,17,19,20,21,22,23,24,25,26,
    0 ,10,18,19,20,21,22,23,24,25,26,
    0 ,10,18,19,20,21,22,23,24,25,26,
    0 ,10,18,19,20,21,22,23,24,25,26,
    0 ,11,18,19,20,21,22,23,24,25,26,
    0 ,12,18,19,20,21,22,23,24,25,26,
    0 ,13,18,19,20,21,22,23,24,25,26,
    12,17,18,19,20,21,22,23,24,25,26
  };
  char* code[16][11] = {
    "1010\0",  "00\0",  "01\0",  "100\0",  "1011\0",  "11010\0",  "111000\0",  "1111000\0",  "1111110110\0",  "1111111110000010\0",  "1111111110000011\0",
    "\0","1100\0","111001\0","1111001\0","111110110\0","11111110110\0","1111111110000100\0","1111111110000101\0","1111111110000110\0","1111111110000111\0","1111111110001000\0",
    "\0","11011\0","11111000\0","1111110111\0","1111111110001001\0","1111111110001010\0","1111111110001011\0","1111111110001100\0","1111111110001101\0","1111111110001110\0","1111111110001111\0",
    "\0","111010\0","111110111\0","11111110111\0","1111111110010000\0","1111111110010001\0","1111111110010010\0","1111111110010011\0","1111111110010100\0","1111111110010101\0","1111111110010110\0",
    "\0","111011\0","1111111000\0","1111111110010111\0","1111111110011000\0","1111111110011001\0","1111111110011010\0","1111111110011011\0","1111111110011100\0","1111111110011101\0","1111111110011110\0",
    "\0","1111010\0","1111111001\0","1111111110011111\0","1111111110100000\0","1111111110100001\0","1111111110100010\0","1111111110100011\0","1111111110100100\0","1111111110100101\0","1111111110100110\0",
    "\0","1111011\0","11111111000\0","1111111110100111\0","1111111110101000\0","1111111110101001\0","1111111110101010\0","1111111110101011\0","1111111110101100\0","1111111110101101\0","1111111110101110\0",
    "\0","11111001\0","11111111001\0","1111111110101111\0","1111111110110000\0","1111111110110001\0","1111111110110010\0","1111111110110011\0","1111111110110100\0","1111111110110101\0","1111111110110110\0",
    "\0","11111010\0","111111111000000\0","1111111110110111\0","1111111110111000\0","1111111110111001\0","1111111110111010\0","1111111110111011\0","1111111110111100\0","1111111110111101\0","1111111110111110\0",
    "\0","111111000\0","1111111110111111\0","1111111111000000\0","1111111111000001\0","1111111111000010\0","1111111111000011\0","1111111111000100\0","1111111111000101\0","1111111111000110\0","1111111111000111\0",
    "\0","111111001\0","1111111111001000\0","1111111111001001\0","1111111111001010\0","1111111111001011\0","1111111111001100\0","1111111111001101\0","1111111111001110\0","1111111111001111\0","1111111111010000\0",
    "\0","111111010\0","1111111111010001\0","1111111111010010\0","1111111111010011\0","1111111111010100\0","1111111111010101\0","1111111111010110\0","1111111111010111\0","1111111111011000\0","1111111111011001\0",
    "\0","1111111010\0","1111111111011010\0","1111111111011011\0","1111111111011100\0","1111111111011101\0","1111111111011110\0","1111111111011111\0","1111111111100000\0","1111111111100001\0","1111111111100010\0",
    "\0","11111111010\0","1111111111100011\0","1111111111100100\0","1111111111100101\0","1111111111100110\0","1111111111100111\0","1111111111101000\0", "1111111111101001\0","1111111111101010\0","1111111111101011\0",
    "\0","111111110110\0","1111111111101100\0","1111111111101101\0","1111111111101110\0","1111111111101111\0","1111111111110000\0","1111111111110001\0","1111111111110010\0","1111111111110011\0","1111111111110100\0",
    "111111110111\0","1111111111110101\0","1111111111110110\0","1111111111110111\0","1111111111111000\0","1111111111111001\0","1111111111111010\0","1111111111111011\0","1111111111111100\0","1111111111111101\0","1111111111111110\0"
  };
  for(k=0;k<16;k++)
    for(i=0;i<11;i++)
      {
	a = 1;
	len = strlen(code[k][i]);
	for(j=0;j<len;j++)
	  if(code[k][i][j]!=input[pos+j])
	    a = 0;
	if ((a==1)&&(!(k!=0 && k!=15 && i==0)))
	  {
	    pos+=len;
	    len = codeLen[k][i]-len;
	    if(len==0)
	      {
		n=k;
		p=0;
		return;
	      }
	    n = k;
	    p=0;
	    for(j=0;j<len;j++)
	      {
		p*=2;
		if(input[pos+j]=='1')
		  p++;
	      }
	    if(input[pos]=='0')
	      p = p + 1 - (int)pow(2,len);
	    pos+=len;
	    return;
	  }
      }
  return;
}

int getDCvalue(char* input, int& pos)
{
  int a,len,i,j,p;
  int codeLen[12] = {3,4,5,5,7,8,10,12,14,16,18,20};
  char* code[12] = {"010\0","011\0","100\0","00\0","101\0","110\0","1110\0",
		    "11110\0","111110\0","1111110\0","11111110\0","111111110\0"};
  for(i=0;i<12;i++)
    {
      a = 1;
      len = strlen(code[i]);
      for(j=0;j<len;j++)
	if(code[i][j]!=input[pos+j])
	  a = 0;
      if (a==1)
	{
	  pos+=len;
	  len = codeLen[i]-len;
	  if(len==0)
	    return 0;
	  p=0;
	  for(j=0;j<len;j++)
	    {
	      p *= 2;
	      if(input[pos+j]=='1')
		p++;
	    }
	  if(input[pos]=='0')
	    p = p + 1 - (int)pow(2,len);
	  pos+=len;
	  return p;
	}
    }
  return 0;
}


void RLED(int ZZ[64],int RL[64])
{
  int rl=1;
  int i=1;
  int k = 0;
  ZZ[0] = RL[0];
  while(i<64)
    {
      if(RL[rl]==0 && RL[rl+1]==0)
	{
	  for(k=i;k<64;k++)
	    ZZ[k] = 0;
	  return;
	}
      for(k=0;k<RL[rl];k++)
	ZZ[i++] = 0;
      ZZ[i++] = RL[rl+1];
      rl+=2;
    }
}

void ZigZagD(int QF[8][8],int ZZ[64])
{
  int i=0,j=0,k=0,d=0;
  while(k<36)
    {
      QF[i][j] = ZZ[k++];
      if((i==0)&&(j%2==0))
	{
	  j++;
	  d=1;
	}
      else if((j==0)&&(i%2==1))
	{
	  i++;
	  d=0;
	}
      else if(d==0)
	{
	  i--;
	  j++;
	}
      else
	{
	  i++;
	  j--;
	}
    }
  i = 7;
  j = 1;
  while(k<64)
    {
      QF[i][j] = ZZ[k++];
      if((i==7)&&(j%2==0))
	{
	  j++;
	  d=0;
	}
      else if((j==7)&&(i%2==1))
	{
	  i++;
	  d=1;
	}
      else if(d==0)
	{
	  i--;
	  j++;
	}
      else
	{
	  i++;
	  j--;
	}
    }
}

void prn(int a[8][8])
{
  for(int i=0;i<8;i++,cout<<"\n")
    for(int j=0;j<8;j++)
      cout<<a[i][j]<<" ";
}


void QuantizeD(int F[8][8], int QF[8][8])
{
  int q[8][8] = {
    16,11,10,16,24,40,51,61,
    12,12,14,19,26,58,60,55,
    14,13,16,24,40,57,69,56,
    14,17,22,29,51,87,80,62,
    18,22,37,56,68,109,103,77,
    24,35,55,64,81,104,113,92,
    49,64,78,87,103,121,120,101,
    72,92,95,98,112,100,103,99 };
  int i,j;
  for(i=0;i<8;i++)
    for(j=0;j<8;j++)
      F[i][j] = QF[i][j]*q[i][j];
}


float C(int u)
{
  if(u==0)
    return (1.0/sqrt(8.0));
  else
    return (1.0/2.0);
}


void DCTD(int f[8][8],int F[8][8])
{
  float a;
  for(int x=0;x<8;x++)
    for(int y=0;y<8;y++)
      {
	a = 0.0;
	for(int u=0;u<8;u++)
	  for(int v=0;v<8;v++)
	    a += C(u)*C(v)*float(F[u][v])*cos((2.0*float(x)+1.0)*float(u)*3.14/16.0)*cos((2.0*float(y)+1.0)*float(v)*3.14/16.0);
	f[x][y] = int(a);
      }
}

void CImage::decompressImage()
{
  char input[80*80*32*26];
  getInput(input);
  maxX*=8;
  maxY*=8;
  intensity = new int*[maxY];
  int pos = 0;
  int DCval = 0;
  int end = 0;
  int n,p,i,j;
  for(i=0;i<maxY;i++)
    intensity[i] = new int[maxX];
  cout<<"\n\nImage Size : "<<maxY<<"*"<<maxX;
  int ypos = 0;
  int xpos = 0;
  int RL[64];
  int ZZ[64];
  int QF[8][8];
  int F[8][8];
  int f[8][8];
  int rl = 0;
  while(end==0)
    {
      cout<<"\nProcessing Block : "<<ypos<<"*"<<xpos;
      cout.flush();
      rl = 0;
      DCval += getDCvalue(input,pos);
      n=1;
      RL[rl++] = DCval;
      //      cout<<"DC : "<<DCval;
      while(!(n==0 && p==0))
	{
	  getACvalue(input,pos,n,p);
	  //  cout<<","<<n<<" "<<p;
	  RL[rl++] = n;
	  RL[rl++] = p;
	  //cin>>i;
	}
      end = 1;
      RL[rl++] = n;
      RL[rl++] = p;
      RLED(ZZ,RL);
      // cout<<"\n\n";
      ZigZagD(QF,ZZ);
      QuantizeD(F,QF);
      //prn(QF);
      DCTD(f,F);
      for(i=0;i<8;i++)
	for(j=0;j<8;j++)
	  intensity[ypos*8+i][xpos*8+j]=f[i][j]+128;
      xpos++;
      if(xpos==(maxX/8))
	{
	  xpos = 0;
	  ypos++;
	}
      for(i=0;i<9;i++)
	{
	  if(input[pos+i]=='0')
	    end=0;
	}
      //   cin>>i;
    }
  /*  for(i=0;i<maxY;i++,cout<<"\n")
    for(j=0;j<maxX;j++)
      cout<<intensity[i][j]<<" ";
  */
}


void main()
{
  CImage i;
  char fname[20];
  cout<<"Enter input file name : ";
  cin>>fname;
  strcat(fname,".Ajpg");
  stream = fopen(fname, "rt");
  cout<<"\n\nEnter name of output file (GIF file) : ";
  cin>>fname;
  i.decompressImage();
  i.saveImage(fname);
}










