/********************************************************************/
/* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/

#include "MGCLStdAfx.h"

#include <ole2.h>
#include <comsvcs.h>

#include <Gdiplus.h>
#include <Gdiplusgraphics.h >
#include <GdiPlusEffects.h>
#include <Gdiplusheaders.h>

#include "mgGL/Color.h"
#include "mgGL/Image.h"

using namespace Gdiplus;

//Set only RGB of pixel2 without updating Alpha data.
void MGPixel::setRGB(const MGPixel& pixel2){
	unsigned char A=getAlpha();
	*this=pixel2;
	setAlpha(A);
}

///Conversion conrtructor from MGColor to MGPixel.
MGPixel::MGPixel(const MGColor& color){
	float rgba[4];
	color.get_color(rgba[0],rgba[1],rgba[2],rgba[3]);
	for(int i=0; i<4; i++){
		if(rgba[i]>1.f)
			rgba[i]=1.f;
		else if(rgba[i]<0.f)
			rgba[i]=0.f;
	}
	setRed(unsigned char(rgba[0]*255.));
	setGreen(unsigned char(rgba[1]*255.));
	setBlue(unsigned char(rgba[2]*255.));
	setAlpha(unsigned char(rgba[3]*255.));
}

//
//Implements MGTextureImages Class.
//MGTextureImages defines the attributes of TextureImages.


//Garbage image data constructor.
MGImage::MGImage(int wdth, int hght)
:m_width(wdth),m_height(hght){
	m_image=new MGPixel[wdth*hght];
}

///Conversion constructor from Gdiplus::Bitmap.
MGImage::MGImage(const MGImage& image2):m_width(image2.width()),m_height(image2.height()),
m_image(image2.m_image){
	MGImage* im2=const_cast<MGImage*>(&image2);
	im2->m_image=0;
}

///Extract a part of image2.
MGImage::MGImage(
	const MGImage& image2,
	int x,  int y,	///<left bottom address of image2.
	int wdth, int hght
):m_width(wdth),m_height(hght),m_image(new MGPixel[wdth*hght]){
	assert((x+wdth)<=image2.width() && (y+hght)<=image2.height());

	for(int j=0; j<hght; j++){
		int jrow_s=j*m_width;
		int jrow_s2=(j+y)*image2.m_width;
		for(int i=0; i<wdth; i++){
			MGPixel& pixelij=m_image[jrow_s+i];
			MGPixel& pixel2ij=image2.m_image[jrow_s2+x+i];
			pixelij=pixel2ij;
		}
	}
}

MGImage::MGImage(
	Gdiplus::Bitmap& bitmap
):m_width(bitmap.GetWidth()),m_height(bitmap.GetHeight()),
m_image(new MGPixel[m_width*m_height]){
	extract(bitmap,0,0,m_width,m_height);
}

//imageのcontrast変換 
MGImage::MGImage(
	Gdiplus::Bitmap& bitmap,
	const Gdiplus::BrightnessContrast& bc,
	double alpha
):m_width(bitmap.GetWidth()),m_height(bitmap.GetHeight()),
m_image(new MGPixel[m_width*m_height]){

	// 四角形の左上隅と右下隅の座標を定義
	RECT rect = {0, 0, m_width, m_height};

    // Increase the brightness in a portion of the image.
	Gdiplus::Effect& effect = (Gdiplus::Effect&)bc;
	bitmap.ApplyEffect(&effect, &rect);

	extract(bitmap,0,0,m_width,m_height,alpha);
}

//Extract a part of bitmap.
MGImage::MGImage(
	Gdiplus::Bitmap& bitmap,
	int x,  int y,	//left bottom address of bitmap.
	int width, int height
):m_width(width),m_height(height),
m_image(new MGPixel[width*height]){
	extract(bitmap,x,y,width,height);
}

//Extract a part of bitmap with transparent
MGImage::MGImage(
	Gdiplus::Bitmap& bitmap,
	int x,  int y,	//left bottom address of bitmap.
	int width, int height,
	double alpha
):m_width(width),m_height(height),
m_image(new MGPixel[width*height]){
	extract(bitmap,x,y,width,height,alpha);
}

MGImage::~MGImage(){
	delete[] m_image;
}

//Assignment.
MGImage& MGImage::operator=(const MGImage& image2){
	delete[] m_image;
	m_width=image2.width();
	m_height=image2.height();
	m_image=image2.m_image;
	MGImage* im2=const_cast<MGImage*>(&image2);
	im2->m_image=0;
	return *this;
}

MGPixel& MGImage::operator()(int i, int j){
	MGPixel* pixelP=image();
	return pixelP[j*m_width+i];
}
const MGPixel& MGImage::operator()(int i, int j)const{
	const MGPixel* pixelP=image();
	return pixelP[j*m_width+i];
}

///Generate a cloned MGImage.
///Returned is newed one, must be deleted.
///clone() does not affect this image data.
MGImage* MGImage::clone()const{
	MGImage* image2=new MGImage;
	image2->m_width=m_width;
	image2->m_height=m_height;
	size_t tlen=m_width*m_height;
	image2->m_image=new MGPixel[tlen];
	for(size_t i=0; i<tlen; i++){
		image2->m_image[i]=m_image[i];
	}
	return image2;
}

//Extract a part of bitmap into this, from(x,y) to (x+width, y+height).
void MGImage::extract(
	Gdiplus::Bitmap& bitmap,
	int x,  int y,	//left bottom address of bitmap.
	int width, int height,
	double alpha
){
	int bmW=bitmap.GetWidth(), bmH=bitmap.GetHeight();
	assert(x+width<=bmW);
	assert(y+height<=bmH);


	// Change the MGPixel data format
	MGPixel* pixelP=m_image;
	if(!pixelP)
		return;

	Rect rect(0,0,bmW,bmH);
	BitmapData bmData;
	bitmap.LockBits(&rect,ImageLockModeRead,PixelFormat32bppARGB,&bmData);
	int totalhm1=bmH-1;//total height -1.
	UINT* gdiPixels=(UINT*)bmData.Scan0;;
	for(int j=0; j<height; j++){
		int jpy=j+y;
		int jrow_s=j*m_width;
		int ybitmap=totalhm1-(j+y);
			//This is because MFC's row address is reverse order to MGImage's.
		for(int i=0; i<width; i++){
			MGPixel& pixelij=pixelP[jrow_s+i];
			Gdiplus::Color gdipixel=gdiPixels[ybitmap*bmData.Stride/4 + (i+x)];
			pixelij[0]=gdipixel.GetR();
			pixelij[1]=gdipixel.GetG();
			pixelij[2]=gdipixel.GetB();
			if (0 <= alpha && alpha <= 1.0){
				pixelij[3]=(unsigned char)(gdipixel.GetA() * alpha);
			} else {
				pixelij[3]=gdipixel.GetA();
			}
		}
	}
	bitmap.UnlockBits(&bmData);
}

//resize the image size to (width,height).
void MGImage::resize(
	int width,
	int height
){
	MGPixel* glimage2ptr(new MGPixel[width*height]);
	if(!glimage2ptr)
		return;

	int error=gluScaleImage(GL_RGBA,
		m_width,m_height,GL_UNSIGNED_BYTE,m_image,
		width,height,GL_UNSIGNED_BYTE,glimage2ptr);
	if(error){
		delete[] glimage2ptr;
	}else{
		delete[] m_image;
		m_image=glimage2ptr;
		m_width=width;
		m_height=height;
	}
}

///Fill all the pixel of this with the input color.
void MGImage::fill_color(
	const MGPixel& pdata
){
	for(int j=0; j<m_height; j++){
		int jw1=j*m_width;
		for(int i=0; i<m_width; i++)
			m_image[jw1+i]=pdata;
	}
}

///Fill all the pixels of the range (j, i1,i2) with the input color pdata.
//The color of Pixel(i,j) for i=i1,...i2 is set to pdata.
void MGImage::fill_color(
	const MGPixel& pdata,
	int j,
	int i1,
	int i2
){
	assert(0<=j && j<m_height);
	assert(0<=i1 && i1<m_width);
	assert(0<=i2 && i2<m_width);

	int jw1=j*m_width;
	for(int i=i1; i<=i2 ; i++)
		m_image[jw1+i]=pdata;
}

///Fill all the pixels of ranges with the input color pdata.
//In ranges, range(j,i1,i2) are stored.
//Let m=ranges.size(), then m=3n(always a mutiple of 3) where n is the number of ranges.
//Let (j,i1,i2)=(ranges[3*k],ranges[3*k+1], ranges[3*k+2]) for k=0,...,n-1,
//then PixelData(i,j) for i=i1,...i2 is one range for the height j of this mesh.
void MGImage::fill_color(
	const MGPixel& pdata,
	const std::vector<int>& ranges// Ranges(j,i1,i2) are input.
){
	int n=(int)ranges.size();
	for(int k3=0; k3<n;){
		int j=ranges[k3++];
		int i1=ranges[k3++];
		int i2=ranges[k3++];
		int jw1=j*m_width;
		for(int i=i1; i<=i2; i++)
			m_image[jw1+i]=pdata;
	}
}

void MGImage::fill_color_NoChangeAlpha(const MGPixel& pdata){
	for(int j=0; j<m_height; j++){
		int jw1=j*m_width;
		for(int i=0; i<m_width; i++){
			m_image[jw1+i].setRGB(pdata);
		}
	}
}
void MGImage::fill_color_NoChangeAlpha(const MGPixel& pdata, int j, int i1, int i2){
	assert(0<=j && j<m_height);
	assert(0<=i1 && i1<m_width);
	assert(0<=i2 && i2<m_width);
	int jw1=j*m_width;
	for(int i=i1; i<=i2 ; i++)
		m_image[jw1+i].setRGB(pdata);
}

//Copy all the pixels of image2 into this.
//This image's width and height are not changed. And part of this and image2
//are copied into this.
void MGImage::copy_color(
	const MGImage& image2//Source image data.
){
	int width=m_width<=image2.m_width ? m_width:image2.m_width;
	int height=m_height<=image2.m_height ? m_height:image2.m_height;
	for(int j=0; j<height; j++){
		int jw1=j*m_width;
		int jw2=j*image2.m_width;
		for(int i=0; i<width; i++)
			m_image[jw1+i]=image2.m_image[jw2+i];
	}
}

//Copy all the pixels of ranges in image2 into this.
//In ranges, range(j,i1,i2) are stored.
//Let m=ranges.size(), then m=3n(always a mutiple of 3) where n is the number of ranges.
//Let (j,i1,i2)=(ranges[3*k],ranges[3*k+1], ranges[3*k+2]) for k=0,...,n-1,
//then PixelData(i,j) for i=i1,...i2 is one range for the height j of this mesh.
void MGImage::copy_color(
	const MGImage& image2,//Source image data.
	const std::vector<int>& ranges// Ranges(j,i1,i2) are input.
		///ranges indicate the places of both this and image2.
){
	int n=(int)ranges.size();
	for(int k3=0; k3<n;){
		int j=ranges[k3++];
		int i1=ranges[k3++];
		int i2=ranges[k3++];
		int jw1=j*m_width;
		int jw2=j*image2.m_width;
		for(int i=i1; i<=i2; i++)
			m_image[jw1+i]=image2.m_image[jw2+i];
	}
}

///Test if pixel at (i,j) has zero_alpha value.
///Returns true if alpha value is zero.
bool MGImage::is_zero_alpha(int i, int j)const{
	const MGPixel& pelij=operator()(i,j);
	return pelij.getAlpha()==0;
}

//Test if any one of the four pixels of (i,j) to (i+1,j+1) is non zero or not.
//i must be < width()-1, and j must be < hieght()-1.
//If any one of them is nonzero, return true.
bool MGImage::includeNonZeroAlpha(int i, int j)const{
	const MGImage& image=*this;
	const MGPixel& pelij=image(i,j);
	if(pelij.getAlpha())
		return true;
	const MGPixel& pelip1j=image(i+1,j);
	if(pelip1j.getAlpha())
		return true;
	const MGPixel& pelip1jp1=image(i+1,j+1);
	if(pelip1jp1.getAlpha())
		return true;
	const MGPixel& pelijp1=image(i,j+1);
	if(pelijp1.getAlpha())
		return true;

	return false;
}

//Resize the image size to (width,height) filling the color to
//the extra part for the size(width,height).
//resize_with_fill_color() does not perform scaling to the image.
//width and height can be less than the original length. In this case,
//image trimming will be done.
void MGImage::resize_with_fill_color(
	int width,
	int height,
	const MGPixel& pdata
){
	MGPixel* glimage2ptr(new MGPixel[width*height]);
	if(!glimage2ptr)
		return;

	int w2=m_width;
	if(width<w2) w2=width;
	int h2=m_height;
	if(height<h2) w2=height;

	int i,j;
	MGPixel* pixelP1=m_image;
	for(j=0; j<h2; j++){
		int jw1=j*m_width, jw2=j*width;
		for(i=0; i<w2; i++)
			glimage2ptr[jw2+i]=pixelP1[jw1+i];
		for(;i<width; i++)
			glimage2ptr[jw2+i]=pdata;

	}
	for(; j<height; j++){
		int jw2=j*width;
		for(i=0; i<width; i++)
			glimage2ptr[jw2+i]=pdata;

	}
	delete[] m_image;
	m_image=glimage2ptr;
	m_width=width;
	m_height=height;
}

//Add border color(0,0,0,0)=(alfa=0) of pixel size2 for each perimeter.
void MGImage::resize_and_add_zero_border(int nwidth2,int nheight2){
	resize(nwidth2-4,nheight2-4);
	int i, j;
	MGPixel* image3ptr(new MGPixel[nwidth2*nheight2]);
	if(!image3ptr)
		return;

	int width2by2=nwidth2*2;
	int width2Mborder=nwidth2-4;

	for(i=0; i<width2by2; i++) image3ptr[i]=0;
	for(j=0; j<nheight2-4; j++){
		int jrow_s3=(j+2)*nwidth2;
		int jrow_s2=j*width2Mborder;
		image3ptr[jrow_s3]=0;image3ptr[jrow_s3+1]=0;
		int jrow_s3p2=jrow_s3+2;
		for(i=0; i<width2Mborder; i++){
			image3ptr[jrow_s3p2+i]=m_image[jrow_s2+i];
		}
		image3ptr[jrow_s3p2+width2Mborder]=0;
		image3ptr[jrow_s3p2+width2Mborder+1]=0;
	}
	int erow_s3=(nheight2-2)*nwidth2;
	for(i=0; i<width2by2; i++)
		image3ptr[erow_s3+i]=0;

	delete[] m_image;
	m_image=image3ptr;
	m_width=nwidth2;
	m_height=nheight2;
}

//Compute 2's power of width and height.
void MGImageCompute_2spower(
	int width, int height,
	int& width2, int& height2//The smallest 2's power of width and height will be output.
){
	int nshift=5;
	width2=64;
	while(width2<width && nshift<32){
		width2=width2<<1;nshift++;
	}

	nshift=5;
	height2=64;
	while(height2<height && nshift<32){
		height2=height2<<1 ;nshift++;
	}
}