#include "pch.h"
#include "CvxText.h"
#include <json/json.h>
#include "BaseUtility.h"
#include <iostream>
#include <fstream>

using namespace std;
using namespace cv;

#if defined(GNUC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored “-Wdeprecated-declarations”
#elif defined(_MSC_VER)
#pragma warning(disable : 4996)
#endif


void GetStringSize(HDC hDC, const char* str, int* w, int* h)
{
	SIZE size;
	GetTextExtentPoint32A(hDC, str, strlen(str), &size);
	if (w != 0) *w = size.cx;
	if (h != 0) *h = size.cy;
}

cv::Size GetTextSize(const char* str, int fontSize, const char* fn, bool italic, bool underline)
{
	HDC hDC = CreateCompatibleDC(0);
	LOGFONTA lf;
	lf.lfHeight = -fontSize;
	lf.lfWidth = 0;
	lf.lfEscapement = 0;
	lf.lfOrientation = 0;
	lf.lfWeight = 5;
	lf.lfItalic = italic;   //斜体
	lf.lfUnderline = underline; //下划线
	lf.lfStrikeOut = 0;
	lf.lfCharSet = DEFAULT_CHARSET;
	lf.lfOutPrecision = 0;
	lf.lfClipPrecision = 0;
	lf.lfQuality = PROOF_QUALITY;
	lf.lfPitchAndFamily = 0;
	strcpy_s(lf.lfFaceName, fn);

	HFONT hf = CreateFontIndirectA(&lf);
	HFONT hOldFont = (HFONT)SelectObject(hDC, hf);


	int strBaseW = 0, strBaseH = 0;
	int singleRow = 0;
	char buf[1 << 12];
	strcpy_s(buf, str);
	char *bufT[1 << 12];  // 这个用于分隔字符串后剩余的字符，可能会超出。
	//处理多行
	{
		int nnh = 0;
		int cw, ch;

		const char* ln = strtok_s(buf, "\n", bufT);
		while (ln != 0)
		{
			GetStringSize(hDC, ln, &cw, &ch);
			strBaseW = max(strBaseW, cw);
			strBaseH = max(strBaseH, ch);

			ln = strtok_s(0, "\n", bufT);
			nnh++;
		}
		singleRow = strBaseH;
		strBaseH *= nnh;

	}

	SelectObject(hDC, hOldFont);
	DeleteObject(hf);
	DeleteDC(hDC);

	cv::Size size;
	size.width = strBaseW;
	size.height = strBaseH;
	return size;
}


void putTextZH(cv::Mat &dst, cv::Size & rSize, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline)
{
	CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));

	int x, y, r, b;
	if (org.x > dst.cols || org.y > dst.rows) return;
	x = org.x < 0 ? -org.x : 0;
	y = org.y < 0 ? -org.y : 0;

	LOGFONTA lf;
	lf.lfHeight = -fontSize;
	lf.lfWidth = 0;
	lf.lfEscapement = 0;
	lf.lfOrientation = 0;
	lf.lfWeight = 5;
	lf.lfItalic = italic;   //斜体
	lf.lfUnderline = underline; //下划线
	lf.lfStrikeOut = 0;
	lf.lfCharSet = DEFAULT_CHARSET;
	lf.lfOutPrecision = 0;
	lf.lfClipPrecision = 0;
	lf.lfQuality = PROOF_QUALITY;
	lf.lfPitchAndFamily = 0;
	strcpy_s(lf.lfFaceName, fn);

	HFONT hf = CreateFontIndirectA(&lf);
	HDC hDC = CreateCompatibleDC(0);
	HFONT hOldFont = (HFONT)SelectObject(hDC, hf);

	int strBaseW = 0, strBaseH = 0;
	int singleRow = 0;
	char buf[1 << 12];
	strcpy_s(buf, str);
	char *bufT[1 << 12];  // 这个用于分隔字符串后剩余的字符，可能会超出。
	//处理多行
	{
		int nnh = 0;
		int cw, ch;

		const char* ln = strtok_s(buf, "\n", bufT);
		while (ln != 0)
		{
			GetStringSize(hDC, ln, &cw, &ch);
			strBaseW = max(strBaseW, cw);
			strBaseH = max(strBaseH, ch);

			ln = strtok_s(0, "\n", bufT);
			nnh++;
		}
		singleRow = strBaseH;
		strBaseH *= nnh;

	}

	rSize.width = strBaseW;
	rSize.height = strBaseH;

	if (org.x + strBaseW < 0 || org.y + strBaseH < 0)
	{
		SelectObject(hDC, hOldFont);
		DeleteObject(hf);
		DeleteObject(hDC);
		return;
	}

	r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
	b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
	org.x = org.x < 0 ? 0 : org.x;
	org.y = org.y < 0 ? 0 : org.y;

	BITMAPINFO bmp = { 0 };
	BITMAPINFOHEADER& bih = bmp.bmiHeader;
	int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));

	bih.biSize = sizeof(BITMAPINFOHEADER);
	bih.biWidth = strBaseW;
	bih.biHeight = strBaseH;
	bih.biPlanes = 1;
	bih.biBitCount = 24;
	bih.biCompression = BI_RGB;
	bih.biSizeImage = strBaseH * strDrawLineStep;
	bih.biClrUsed = 0;
	bih.biClrImportant = 0;

	void* pDibData = 0;
	HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);

	CV_Assert(pDibData != 0);
	HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);

	//color.val[2], color.val[1], color.val[0]
	SetTextColor(hDC, RGB(255, 255, 255));
	SetBkColor(hDC, 0);
	//SetStretchBltMode(hDC, COLORONCOLOR);

	strcpy_s(buf, str);
	const char* ln = strtok_s(buf, "\n", bufT);
	int outTextY = 0;
	while (ln != 0)
	{
		TextOutA(hDC, 0, outTextY, ln, strlen(ln));
		outTextY += singleRow;
		ln = strtok_s(0, "\n", bufT);
	}
	uchar* dstData = (uchar*)dst.data;
	int dstStep = dst.step / sizeof(dstData[0]);
	unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;
	unsigned char* pStr = (unsigned char*)pDibData + x * 3;
	for (int tty = y; tty <= b; ++tty)
	{
		unsigned char* subImg = pImg + (tty - y) * dstStep;
		unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
		for (int ttx = x; ttx <= r; ++ttx)
		{
			for (int n = 0; n < dst.channels(); ++n) {
				double vtxt = subStr[n] / 255.0;
				int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];
				subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
			}

			subStr += 3;
			subImg += dst.channels();
		}
	}

	SelectObject(hDC, hOldBmp);
	SelectObject(hDC, hOldFont);
	DeleteObject(hf);
	DeleteObject(hBmp);
	DeleteDC(hDC);
}

/////////////////////////////  数据收集卡  /////////////////////////////////////// 


// 获取字符串的画布上的宽度
int getLineStrWidth(string str, int fontSize, int ttBoxW, int tiSl, int backPix) {
	int iw = str.length()*fontSize*0.5;
	iw += ttBoxW;
	iw += tiSl;
	iw += backPix;
	iw += fontSize * 1.5; // 题号的宽度 最多不超过1.5个字体宽度 56.

	return iw;
}

// 转码 utf-8 2 ansi  
static void UTF82ANSI(LPCSTR lpBuff, int nLen, char* pData, int iMaxLen)
{
	ZeroMemory(pData, sizeof(char)*iMaxLen);
	int nCont = MultiByteToWideChar(CP_UTF8, 0, lpBuff, nLen, NULL, 0);
	WCHAR* szTemp = new WCHAR[nCont + 1];
	ZeroMemory(szTemp, sizeof(WCHAR)*(nCont + 1));
	MultiByteToWideChar(CP_UTF8, 0, lpBuff, nLen, szTemp, nCont);
	//获取缓冲区大小，并申请空间，缓冲区大小事按字节计算的   
	int len = WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, NULL, 0, NULL, NULL);
	if (len < iMaxLen) {
		WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, pData, len, NULL, NULL);
	}
	else {
		char* buffer = new char[len + 1];
		//宽字节编码转换成多字节编码   
		WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, buffer, len, NULL, NULL);
		buffer[len] = '\0';
		//删除缓冲区并返回值 
		strncpy(pData, buffer, iMaxLen - 1);
		delete[] buffer;
		buffer = NULL;
	}
	delete[]szTemp;
	szTemp = NULL;
}

// 二进制码生成获取
void createBinString(Mat & img, int pageNum, int ptw, int pth, cv::Point ptStart, vector<cv::Rect>& vecBoxPages) {
	vector<int> vecBinStr; vecBinStr.resize(12);
	for (size_t i = 11; i > 0; i--) {
		int ys = pageNum % 2;
		vecBinStr[i] = ys;
		pageNum = pageNum / 2;
	}

	vecBoxPages.clear();
	int ptPgW = ptw / 2; int ptPgH = pth / 2;
	for (int j = 0; j < 3; j++) {
		int _startY = ptStart.y + 15 + 50 * j;
		for (int i = 0; i < 4; i++) {
			int _startX = ptStart.x + 18 + i * 72;
			vecBoxPages.push_back(cv::Rect(_startX, _startY, ptPgW, ptPgH));
			if (vecBinStr[j * 4 + i])
				rectangle(img, vecBoxPages[vecBoxPages.size() - 1], cv::Scalar(0), -1);
			else
				rectangle(img, vecBoxPages[vecBoxPages.size() - 1], cv::Scalar(200, 200, 200), 2, 8, 0);
		}
	}
	return;
}

void generatePageJson(vector<cv::Rect>& vecPts, vector<cv::Rect>& vecBoxPages)
{
	Json::Value imageSize;
	imageSize["width"] = 1654;
	imageSize["height"] = 2344;

	Json::Value location(Json::arrayValue);
	for (auto& iter : vecPts)
	{
		Json::Value item;
		item["x"] = iter.x;
		item["y"] = iter.y;
		item["width"] = iter.width;
		item["height"] = iter.height;
		location.append(item);
	}

	Json::Value pagenumber(Json::arrayValue);
	int nSubVecSize = 4;// 每个小vector的容量
	for (size_t i = 0, j = 1; i < vecBoxPages.size(); i += nSubVecSize)
	{
		vector<cv::Rect> vecSmall;
		auto last = std::min(vecBoxPages.size(), i + nSubVecSize);
		vecSmall.insert(vecSmall.begin(), vecBoxPages.begin() + i, vecBoxPages.begin() + last);

		Json::Value opt(Json::arrayValue);
		std::string choice[] = { "A", "B", "C", "D" };
		for (int h = 0; h < vecSmall.size(); h++)
		{
			Json::Value bin;
			bin["x"] = vecSmall[h].x;
			bin["y"] = vecSmall[h].y;
			bin["width"] = vecSmall[h].width;
			bin["height"] = vecSmall[h].height;
			bin["optName"] = choice[h];
			opt.append(bin);
		}

		Json::Value item;
		item["id"] = j;
		item["opt"] = opt;
		pagenumber.append(item);
		j++;

	}

	Json::Value itemRoot;
	itemRoot["imageSize"] = imageSize;
	itemRoot["location"] = location;
	itemRoot["pagenumber"] = pagenumber;

	Json::FastWriter writer;
	std::string strJson = writer.write(itemRoot);

	CFile zip;
	zip.Open(_T("D:\\page.json"), CFile::modeCreate | CFile::modeWrite);

	DWORD wide_string_len = MultiByteToWideChar(CP_ACP, 0, strJson.c_str(), -1, NULL, 0);
	WCHAR* wide_string = new WCHAR[wide_string_len];
	wide_string_len = MultiByteToWideChar(CP_ACP, 0, strJson.c_str(), -1, wide_string, wide_string_len);
	DWORD utf8_string_len = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
	CHAR* utf8_string = new CHAR[utf8_string_len];
	utf8_string_len = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_string_len, NULL, NULL);
	zip.Write((void*)utf8_string, utf8_string_len - 1);
	//	LOG4CPLUS_INFO_FMT(pTestLogger, LOG4CPLUS_TEXT("%s"), CString(json_str.c_str()));
	delete wide_string;
	delete utf8_string;
	zip.Close();

}

std::string JsonToString(const Json::Value & root)
{
	static Json::Value def = []{
		Json::Value def;
		Json::StreamWriterBuilder::setDefaults(&def);
		def["emitUTF8"] = true;
		return def;
	}();
	std::ostringstream stream;
	Json::StreamWriterBuilder stream_builder;
	stream_builder.settings_ = def;//Config emitUTF8
	std::unique_ptr<Json::StreamWriter> writer(stream_builder.newStreamWriter());
	writer->write(root, &stream);
	return stream.str();
}

int dataCollectionPaper(int cols, int index, int fontSize, int lineGrayPix, bool engShow, std::vector<string>& vecLines, CString dir, std::string& strPngPath) {
	/* 这里要做一些栏数 和 vecLines的对应值确认 */
	if (2 == cols && 14 * 2 == vecLines.size())
		; // ok 
	if (3 == cols && 14 * 3 == vecLines.size())
		; // ok 
	if (4 == cols && 14 * 4 == vecLines.size())
		; // ok 

	// 生成画布图像 
	cv::Mat img(cv::Size(1654, 2344), CV_8UC3, cv::Scalar(255, 255, 255));
	int ptw = 80; int pth = 40;
	cv::Size fSize(0, 0);
	int leftPos = 100, rightPos = 1450;
	int topPos = 90;
	int fontW = rightPos - leftPos + ptw;
	vector<cv::Rect> vecPts = {
		cv::Rect(100,90,ptw,pth),
		cv::Rect(580,90,ptw,pth),
		cv::Rect(1450,90,ptw,pth),
		cv::Rect(100,2176,ptw,pth),
		cv::Rect(1450,2176,ptw,pth),
	};


	// 画定位点
	for (size_t i = 0; i < vecPts.size(); i++)
	{
		rectangle(img, vecPts[i], cv::Scalar(0, 0, 0), -1);
	}

	// 画示例框
	topPos += pth; topPos += 30;
	rectangle(img, cv::Rect(leftPos, topPos, fontW, 200), cv::Scalar(0, 0, 0), 2, 8, 0);

	// 画页码框
	cv::Rect boxPageNumber(leftPos + fontW - 300, topPos, 300, 200);
	rectangle(img, boxPageNumber, cv::Scalar(0, 0, 0), 2, 8, 0);

	// 生成二进制码流
	/* 二进制码流的坐标在下面函数里面实现，需要用书写获取 */
	vector<cv::Rect> vecBoxPages;
	createBinString(img, index, ptw, pth, cv::Point(boxPageNumber.x, boxPageNumber.y), vecBoxPages);
	string strPageNumInfo = "关联ID: " + to_string(index);
	putTextZH(img, fSize, strPageNumInfo.c_str(), cv::Point(boxPageNumber.x + 100, boxPageNumber.y + 50 * 2 + 20 + 15 + 15 + 15), Scalar(0), 20, "宋体");

	// 生成page.json
	//generatePageJson(vecPts, vecBoxPages);

	// 画警示信息
	string strMesInfo = "1、请在每题下方的横线上书写本题的内容!\
						\n\n2、题号不需要书写，标点符号需要原样书写。☆\
						\n\n3、如果本题书写有误，进行了涂抹修改等操作，请将题号前矩形框用任意笔进行填涂！☆☆\n";
	putTextZH(img, fSize, strMesInfo.c_str(), cv::Point(leftPos + 20, topPos + 20), Scalar(0), 25, "宋体");
	int lineTop1 = topPos + 20 + fSize.height + 20;
	cv::line(img, cv::Point(leftPos, lineTop1), cv::Point(rightPos + ptw - 300, topPos + 20 + fSize.height + 20), Scalar(0), 2, 8, 0);

	// 正确示例
	string strEgInfo = "正\n确\n示\n例";
	putTextZH(img, fSize, strEgInfo.c_str(), cv::Point(leftPos + 20, topPos + 20 + fSize.height + 20 + 3), Scalar(0), 20, "宋体");
	cv::line(img, cv::Point(leftPos + 20 + fSize.width + 10, lineTop1), cv::Point(leftPos + 20 + fSize.width + 10, topPos + 200), Scalar(0, 0, 0), 2, 8, 0);

	// 贴图区域
	/* 暂不实现 */

	topPos += 200;

	// 文字区域 
	static int maxLineNum = 14;	// 最大行数14
	static int ttBoxWidth = 30;  // 填涂框的边长
	static int tiSl = 10; // 题号和题干间的距离
	static int lineDis = 20;	// 两行之间的距离
	int colWidth = fontW / cols; // 单栏的宽度
	fSize.width = 0; fSize.height = 0;
	Json::Value root(Json::arrayValue);
	RNG rng;
	for (int col = 0; col < cols; col++)
	{ // 逐栏画题
		int colTopPos = topPos;
		int colLeftPos = leftPos + col * colWidth;
		for (size_t i = 0; i < maxLineNum; i++)
		{
			size_t vecIndex = i + col * maxLineNum;
			if (vecIndex >= vecLines.size())
			{
				break;
			}
			colTopPos += lineDis;
			int lineLen = ttBoxWidth + tiSl;
			// 画填涂框 30*30 大小
			cv::Rect rcbox(colLeftPos, colTopPos, ttBoxWidth, ttBoxWidth);
			rectangle(img, rcbox, cv::Scalar(0), 1, 8, 0);

			int iQNum = i + 1 + col * maxLineNum;
			string strStinfo = to_string(iQNum); strStinfo += ".";
			cv::Point ptNum(colLeftPos + ttBoxWidth + tiSl, colTopPos);
			putTextZH(img, fSize, strStinfo.c_str(), ptNum, Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离
			lineLen += fSize.width;
			cv::Rect rcNum(ptNum.x, ptNum.y, fSize.width, fSize.height);

			strStinfo.clear();
			string strTgInfo = vecLines[vecIndex];
			if (!engShow)
				; /*  移除词性   */
			//char szTemp[2048];
			//memset(szTemp, 0, sizeof(char)*(2048));
			//UTF82ANSI(strTgInfo.c_str(), strTgInfo.length(), szTemp, 2048);
			//strTgInfo = szTemp;
			cv::Point ptQues(colLeftPos + ttBoxWidth + tiSl + fSize.width, colTopPos);
			putTextZH(img, fSize, strTgInfo.c_str(), ptQues, Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离
			lineLen += fSize.width;
			cv::Rect rcQues(ptQues.x, ptQues.y, fSize.width, fSize.height);
			cv::Rect rcAns(colLeftPos + 30, colTopPos + fSize.height, colWidth - ttBoxWidth, 1);
			
			colTopPos = colTopPos + fSize.height * 3.5; //两行之间两倍的距离用于书写
			lineGrayPix = rng.operator ()(220);
			line(img, cv::Point(colLeftPos + 30, colTopPos), cv::Point(colLeftPos + colWidth - 1, colTopPos), Scalar(lineGrayPix, lineGrayPix, lineGrayPix), 1, 8, 0);
			rcAns.height = colTopPos - rcAns.y + lineDis;

			//生成识别信息
			Json::Value ttbox;
			ttbox["x"] = rcbox.x;
			ttbox["y"] = rcbox.y;
			ttbox["width"] = rcbox.width;
			ttbox["height"] = rcbox.height;

			Json::Value queNum;
			queNum["x"] = rcNum.x;
			queNum["y"] = rcNum.y;
			queNum["width"] = rcNum.width;
			queNum["height"] = rcNum.height;

			Json::Value queInfo;
			queInfo["x"] = rcQues.x;
			queInfo["y"] = rcQues.y;
			queInfo["width"] = rcQues.width;
			queInfo["height"] = rcQues.height;
			queInfo["info"] = strTgInfo.c_str();

			Json::Value ansInfo;
			ansInfo["x"] = rcAns.x;
			ansInfo["y"] = rcAns.y;
			ansInfo["width"] = rcAns.width;
			ansInfo["height"] = rcAns.height;

			Json::Value item;
			item["id"] = iQNum;
			item["ttbox"] = ttbox;
			item["queNum"] = queNum;
			item["queInfo"] = queInfo;
			item["ansInfo"] = ansInfo;
			
			root.append(item);

			// test
			/*rectangle(img, rcbox, cv::Scalar(0, 0, 200), 1, 8, 0);
			rectangle(img, rcNum, cv::Scalar(0, 200, 0), 1, 8, 0);
			rectangle(img, rcQues, cv::Scalar(100, 0, 0), 1, 8, 0);
			rectangle(img, rcAns, cv::Scalar(200, 0, 0), 1, 8, 0);*/
		}
	}

	std::string strJson = JsonToString(root); 
	
	CString strTemplatePath;
	strTemplatePath.Format(L"%s/json/%d.json", dir, index);
	CFile zip;
	zip.Open(strTemplatePath, CFile::modeCreate | CFile::modeWrite);
	zip.Write((void*)strJson.c_str(), strJson.length());
	zip.Close();

	wchar_t tempPath[MAX_PATH];
	DWORD dwSize = MAX_PATH;
	GetTempPath(dwSize, tempPath);//获取临时文件夹路径
	static int tmp_index = 1;
	wchar_t szPath[MAX_PATH];
	_stprintf(szPath, L"%stmp_%d.png", tempPath, tmp_index);
	tmp_index++;
	if (tmp_index > 10)
	{
		tmp_index = 1;
	}
	
	strPngPath = TstringToGB2312(szPath);
	cv::imwrite(strPngPath, img);

	
	/*cv::namedWindow("fuck", 1);
	cv::imshow("fuck", img);
	cv::waitKey(0);*/





	

	return 0;
}

int get_gray_num(cv::Mat & img, cv::Rect & box, int &meanGray, int threshold) {
	uchar * ptr;
	int count = 0;
	long long totalGrayNum = 0;
	for (int h = box.y; h < box.y + box.height; h++) {
		ptr = img.ptr<uchar>(h);
		for (int w = box.x; w < box.x + box.width; w++) {
			int pix = static_cast<int>(ptr[w]);
			totalGrayNum += pix;
			if (pix < threshold)
				count++;
		}
	}
	meanGray = totalGrayNum / box.area();
	return count;
}

int get_around_box_gray(cv::Mat & img, cv::Rect & box) {
	int left = (box.x - box.width / 2) > 0 ? (box.x - box.width / 2) : 0;
	int top = (box.y - box.height / 2) > 0 ? (box.y - box.height / 2) : 0;
	int right = (box.x + box.width * 3 / 2) < (img.cols - 1) ? (box.x + box.width * 3 / 2) : (img.cols - 1);
	int bottom = (box.y + box.height * 3 / 2) < (img.rows - 1) ? (box.y + box.height * 3 / 2) : (img.rows - 1);
	uchar * ptr;
	std::int64_t total = 0;
	int number = 0;
	for (int h = top; h < bottom; h++) {
		ptr = img.ptr<uchar>(h);
		for (int w = left; w < right; w++) {
			if (w > box.x && w<(box.x + box.width) && h>box.y&&h < (box.y + box.height))
				continue;
			total += static_cast<std::int64_t>(ptr[w]);
			number++;
		}
	}
	return static_cast<int>(total / number);
}

#define USE_THRESHOLD_VAL 200
bool analysis_ttbox_mark(cv::Mat & imgSrc, cv::Rect & rc) {
	int thre = min(200, max(get_around_box_gray(imgSrc, rc), 160));
	int gray = get_gray_num(imgSrc, rc, thre, USE_THRESHOLD_VAL);
	double ds = gray * 1.0 / rc.area()*1.0;
	if (ds > 0.50)
		return true;
	return false;
}

int cutPaper(int pageNum, std::string strJsonPath, std::string strPaperPath, std::string strSavePath)
{
	ifstream in(strJsonPath, ios::binary);
	if (!in.is_open())
	{
		return 1;
	}

	Json::Features features;
	Json::Reader reader(features);
	Json::Value root;
	if (!reader.parse(in, root))
	{
		return 1;
	}

	if (!root.isArray())
	{
		return 1;
	}

	cv::Mat src = cv::imread(strPaperPath);
	if (src.empty())
	{
		return 1;
	}

	auto pfGetPos = [](const Json::Value & value, cv::Rect& rc) {
		int x = 0, y = 0, w = 0, h = 0;
		if (value.isMember("x"))
		{
			x = value["x"].asInt();
		}
		if (value.isMember("y"))
		{
			y = value["y"].asInt();
		}
		if (value.isMember("width"))
		{
			w = value["width"].asInt();
		}
		if (value.isMember("height"))
		{
			h = value["height"].asInt();
		}
		rc = cv::Rect(x, y, w, h);
	};

	for (int i = 0; i < root.size(); i++)
	{
		Json::Value row = root[i];
		
		std::string id = row["id"].asString();
		std::string info = row["queInfo"]["info"].asString();

		Json::Value ttbox = row["ttbox"];
		Json::Value ansInfo = row["ansInfo"];
		
		cv::Rect  rc_ttbox;
		cv::Rect  rc_ansInfo;
		pfGetPos(ttbox, rc_ttbox);
		pfGetPos(ansInfo, rc_ansInfo);
		
		// 选择框灰度判断
		cv::Mat gray_src;
		cv::cvtColor(src, gray_src, COLOR_BGR2GRAY);
		bool ret = analysis_ttbox_mark(gray_src, rc_ttbox);
		
		char szTxtPath[MAX_PATH] = { 0 };
		char szJpgPath[MAX_PATH] = { 0 };
		DWORD dwCount = GetTickCount();
		sprintf(szTxtPath, "%s\\%s\\%d_%s_%ld.txt", strSavePath.c_str(), ret ? "abnormal" : "normal", pageNum, id.c_str(), dwCount);
		sprintf(szJpgPath, "%s\\%s\\%d_%s_%ld.jpg", strSavePath.c_str(), ret ? "abnormal" : "normal", pageNum, id.c_str(), dwCount);

		CFile zip;
		zip.Open(CA2T(szTxtPath), CFile::modeCreate | CFile::modeWrite);
		zip.Write((void*)info.c_str(), info.length());
		zip.Close();

		cv::Mat cut = src(rc_ansInfo);
		cv::imwrite(szJpgPath, cut);
	}

	return 0;
}


int PareseModeJson(preinfo::templatesInfo&  temeplatInfo)
{
	ifstream in("./page.json", ios::binary);
	if (!in.is_open())
	{
		return 1;
	}
	int  uuid = 100000;  /// 每个框子的一个独立ID 不会有重复的
	Json::Features features;
	Json::Reader reader(features);
	Json::Value root;
	if (!reader.parse(in, root))
	{
		return 1;
	}

	if (!root.isObject())
	{
		return 1;
	}
	//1.获取模板大小信息
	preinfo::PaperTemplateInfo page;
	Json::Value imageSize = root["imageSize"];
	page.height = imageSize["height"].asInt();
	page.width = imageSize["width"].asInt();
	
	//2.获取定位点信息
	Json::Value loction = root["location"];
	auto funGetPos = [&](Json::Value item) {
		preinfo::PaperRect<double> pb;
		int x = item["x"].asInt();
		int y = item["y"].asInt();
		int h = item["height"].asInt();
		int w = item["width"].asInt();
		pb.centerx = x + w / 2.0;
		pb.centery = y + h / 2.0;
		pb.width = w;
		pb.height = h;
		return pb;
	};
	double offsetx = page.width; double offsety = page.height;
	for (int i = 0; i < loction.size(); i++)
	{
		Json::Value row = loction[i];
		preinfo::LocationPoint lc;
		lc.id = uuid++;
		auto tm = funGetPos(row);
		lc.centerx = tm.centerx;
		lc.centery = tm.centery;
		lc.width = tm.width;
		lc.height = tm.height;
		if (offsetx > lc.centerx) offsetx = lc.getX();
		if (offsety > lc.centery) offsety = lc.getY();
		page.vecLocaltionPoints.push_back(lc);
		
	}
	//3.获取客观题信息
	Json::Value pagenumber = root["pagenumber"];
	for (int i = 0; i < pagenumber.size(); i++)
	{
		Json::Value item = pagenumber[i];
		string strId = item["id"].asString();
		preinfo::_QuestionChoice _qstc;
		_qstc.id = uuid++;
		_qstc.vecTiHao.push_back(atoi(strId.c_str()));
		double dBoundBox[4] = { 100000,100000,0,0 };
		_qstc.type = preinfo::BOX_QUESTION_CHOICE_M;
		auto itOpts = item["opt"];
		_qstc.number = itOpts.size();
		std::vector<preinfo::TtItem> _vctOpt;
		for (int i = 0; i < itOpts.size(); i++)
		{
			preinfo::TtItem opt;
			auto tm = funGetPos(itOpts[i]);
			opt.centerx = tm.centerx;
			opt.centery = tm.centery;
			opt.width = tm.width;
			opt.height = tm.height;
			_qstc.itemSize.width = opt.width;
			_qstc.itemSize.height = opt.height;
			dBoundBox[0] = dBoundBox[0] > opt.getX() ? opt.getX() : dBoundBox[0];
			dBoundBox[1] = dBoundBox[1] > opt.getY() ? opt.getY() : dBoundBox[1];
			dBoundBox[2] = dBoundBox[2] < (opt.centerx + opt.width / 2.0) ? (opt.centerx + opt.width / 2.0) : dBoundBox[2];
			dBoundBox[3] = dBoundBox[3] < (opt.centery + opt.height / 2.0) ? (opt.centery + opt.height / 2.0) : dBoundBox[3];
			
			auto subItem = itOpts[i];
			opt.optName = subItem["optName"].asString();
			_vctOpt.push_back(opt);
		}
		_qstc.width = (dBoundBox[2] - dBoundBox[0]);
		_qstc.height = (dBoundBox[3] - dBoundBox[1]);
		_qstc.centerx = dBoundBox[0] + _qstc.width / 2.0;
		_qstc.centery = dBoundBox[1] + _qstc.height / 2.0;

		_qstc.groups = _vctOpt;
		page._vecQtChoices.push_back(_qstc);
	}
	temeplatInfo.push_back(page);
	return 0;
}