#include "common.h"
#include <string>
#include "stdarg.h"
#include <io.h>
#include <windows.h>
#include <ShlObj.h>
#include <Commdlg.h>
#include <tchar.h>

int string_split(std::string str, std::string pattern,std::vector<std::string> &out)
{
	std::string::size_type pos;
	int cnt = 0;
	str += pattern;
	int size = str.size();

	for (int i = 0; i<size; i++)
	{
		pos = str.find(pattern, i);
		if (pos<size)
		{
			cnt++;
			std::string s = str.substr(i, pos - i);
			out.push_back(s);
			i = pos + pattern.size() - 1;
		}
	}
	return cnt;
}

std::string format(const char *pszFmt, ...)
{
    std::string str;
    va_list args;
    va_start(args, pszFmt);
    {
        int nLength = _vscprintf(pszFmt, args);
        nLength += 1;
        std::vector<char> chars(nLength);
        _vsnprintf(chars.data(), nLength, pszFmt, args);
        str.assign(chars.data());
    }
    va_end(args);
    return str;
}

void DrawSelectRects(cv::Mat input,DisplayLabelConf &t_DisplayLabelConf,int Cnt)
{
	if (t_DisplayLabelConf.Flag[Cnt] & DisplayLabel_Type_Bit) {
		if (t_DisplayLabelConf.leftButtonDownFlag) {
			cv::rectangle(
				input,
				cv::Point(
					t_DisplayLabelConf.originalPoint.x * input.cols,
					t_DisplayLabelConf.originalPoint.y * input.rows),
				cv::Point(
					t_DisplayLabelConf.processPoint.x * input.cols,
					t_DisplayLabelConf.processPoint.y * input.rows),
				cv::Scalar(0, 0, 255),
				4);
		}
		for (int i = 0; i < t_DisplayLabelConf.RectVet[Cnt].size(); i++) {
			cv::rectangle(
				input,
				cv::Point(
					t_DisplayLabelConf.RectVet[Cnt][i].TL.x * input.cols,
					t_DisplayLabelConf.RectVet[Cnt][i].TL.y * input.rows),
				cv::Point(
					t_DisplayLabelConf.RectVet[Cnt][i].BR.x * input.cols,
					t_DisplayLabelConf.RectVet[Cnt][i].BR.y * input.rows),
				cv::Scalar(0, 0, 255),
				4);
		}
	}
}

bool CheckSelectRects(
	cv::Mat input,
	std::vector<std::vector<std::pair<int, cv::Rect> > > &VecRects,
	int VecCnt, 
	DisplayLabelConf &t_DisplayLabelConf, 
	int LabelCnt
)
{
	bool find;
	if (!(t_DisplayLabelConf.Flag[LabelCnt] & DisplayLabel_Type_Bit))
	{
		return false;
	}
	if (VecCnt < VecRects.size())
	{
		for (int i = 0; i < VecRects[VecCnt].size(); i++)
		{
			find = false;
			for(int j = 0; j < t_DisplayLabelConf.RectVet[LabelCnt].size(); j++)
			{
				cv::Rect LocalRect = VecRects[VecCnt][i].second;
				cv::Rect CheckRect = cv::Rect(
					cv::Point(
					t_DisplayLabelConf.RectVet[LabelCnt][j].TL.x* input.cols,
					t_DisplayLabelConf.RectVet[LabelCnt][j].TL.y* input.rows
					), 
					cv::Point(
					t_DisplayLabelConf.RectVet[LabelCnt][j].BR.x* input.cols,
					t_DisplayLabelConf.RectVet[LabelCnt][j].BR.y* input.rows
					)
				);

				cv::Rect tempRect = (LocalRect & CheckRect);
				if(LocalRect == tempRect)
				{
					find = true;
				}
			}
			if(!find)
			{
				return true;
			}
		}
	}
	return false;
}

void getFiles(std::string path, std::vector<std::string>& files)
{
	//文件句柄
	intptr_t   hFile = 0;
	//文件信息
	struct _finddata_t fileinfo;
	std::string p;
	if ((hFile = _findfirst(p.assign(path).c_str(), &fileinfo)) != -1)
	{
		do
		{
			if (!(fileinfo.attrib & _A_SUBDIR))
			{
				files.push_back(fileinfo.name);
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
}

std::string WstringToString(std::wstring wstr)
{
	int nLen = wcslen(wstr.c_str());
	std::string str;
	str.resize(nLen * 2, ' ');
	int nResult = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)wstr.c_str(), -1, (LPSTR)str.c_str(), nLen * 2, NULL, NULL);
	return str;
}

/*
TCHAR*转char*
*/
char* LPWSTR2LPSTR(LPWSTR lpwszStrIn)
{
    LPSTR pszOut = NULL;
    if (lpwszStrIn != NULL) {
        int nInputStrLen = wcslen(lpwszStrIn);
        int nOutputStrLen = WideCharToMultiByte(CP_ACP, 0, lpwszStrIn, nInputStrLen, NULL, 0, 0, 0) + 2;
        pszOut = new char[nOutputStrLen];
        if (pszOut != NULL) {
            memset(pszOut, 0x00, nOutputStrLen);
            WideCharToMultiByte(CP_ACP, 0, lpwszStrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0);
        }
    }
    return  pszOut;
}

//从string s的位置pos开始，向后找，找到第一个等于x的位置返回其位置
//找到，返回找到的位置，找不到返回-1
int __find(const std::string s, const int start, const char x) {
	if (start >= s.length())return -1;
	for (int i = start; i < s.length(); ++i) {
		if (s[i] == x) return i;
	}
	return -1;
}
//双斜杠转单斜杠
void pathConvert_Double2Single(std::string& s) {
	int start = 0;
	while (start < s.length()) {
		int pos = s.find('\\', start);
		if (pos == -1)break;
		s.replace(pos, 1,"/");
		start = pos + 1;
	}
}

int CALLBACK BrowseCallbackProc(
	HWND hwnd, UINT uMsg, LPARAM /*lParam*/, LPARAM lpData)
{
	LPWSTR  buf[1000];
	GetCurrentDirectory(1000, (LPWSTR)buf);
	if (uMsg == BFFM_INITIALIZED)
	{
		SendMessage(hwnd, BFFM_SETSELECTION, (WPARAM)TRUE, (LPARAM)buf);
	}

	return 0;
}

std::string SelectDirBRI()
{
	BROWSEINFO bi;
	bi.hwndOwner = NULL;
	bi.pidlRoot = CSIDL_DESKTOP;
	bi.pszDisplayName = NULL;
	bi.lpszTitle = NULL;//显?位于对话框左上部的提?信息
	bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;//有新建?件夹按钮
	bi.lpfn = BrowseCallbackProc;
	bi.iImage = 0;
	LPITEMIDLIST pidl = SHBrowseForFolder(&bi);//调?选择对话框
	if (pidl == NULL)
	{
		std::cout << "没有选择目录" << std::endl;
		return "";
	}
	TCHAR strFolder[MAX_PATH];
	SHGetPathFromIDList(pidl, strFolder);
	std::string sFolder = "";
	sFolder = WstringToString(strFolder);
	int pos = sFolder.find('\0');
	if (pos)
	{
		sFolder = sFolder.substr(0, pos);
	}
	return sFolder;
}

std::string SelectFileOFN()
{
	OPENFILENAME ofn;//#include <Commdlg.h>
	TCHAR szOpenFileNames[80 * MAX_PATH] = { 0 };
	TCHAR szPath[MAX_PATH];
	TCHAR szFileName[80 * MAX_PATH];

	int nLen = 0;
	TCHAR* p = NULL;
	ZeroMemory(&ofn, sizeof(ofn));

	// 结构体大小
	ofn.lStructSize = sizeof(ofn);
	// 拥有着窗口句柄
	ofn.hwndOwner = NULL;
	// 接收返回的文件名，注意第一个字符需要为NULL
	ofn.lpstrFile = szOpenFileNames;
	// 缓冲区长度
	ofn.nMaxFile = sizeof(szOpenFileNames);
	// _T可替换为TEXT，使用_T需要引tchar.h
	ofn.lpstrFile[0] = _T('\0');
	// 设置过滤
	ofn.lpstrFilter = _T("All\0*.*\0.mp4\0*.mp4\0.avi\0*.avi\0.mkv\0*.mkv\0.rmvb\0*.rmvb\0.f4v\0*.f4v\0.flv\0*.flv\0.m4v\0*.m4v\0.mpg\0*.mpg\0\0");
	// 过滤器索引
	ofn.nFilterIndex = 1;
	// 窗口标题
	ofn.lpstrTitle = _T("请选择图片");

	// 文件必须存在、允许多选、隐藏只读选项、对话框使用资源管理器风格的用户界面
	// 官方文档：https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamea
	ofn.Flags = OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_HIDEREADONLY | OFN_EXPLORER;

	// 如果打开文件失败，则不操作
	if (!::GetOpenFileName(&ofn)) {
		return "";
	}

	// 把第一个文件名前的复制到szPath,即:  
	// 如果只选了一个文件,就复制到最后一个'/'  
	// 如果选了多个文件,就复制到第一个NULL字符  
	lstrcpyn(szPath, szOpenFileNames, ofn.nFileOffset);

	// 当只选了一个文件时,下面这个NULL字符是必需的.  
	// 这里不区别对待选了一个和多个文件的情况
	szPath[ofn.nFileOffset] = '\0';
	nLen = lstrlen(szPath);

	// 如果选了多个文件,则必须加上'//'  
	if (szPath[nLen - 1] != '\\') {
		lstrcat(szPath, _T("\\"));
	}

	// 把指针移到第一个文件  
	p = szOpenFileNames + ofn.nFileOffset;

	// 对szFileName进行清零
	ZeroMemory(szFileName, sizeof(szFileName));

	// 定义字符串，用于拼接所选的所有文件的完整路径
	std::string str = "";

	while (*p) {
		// 读取文件名
		std::string fileName = LPWSTR2LPSTR(p);
		// 读取文件所在文件夹路径
		std::string filePath = LPWSTR2LPSTR(szPath);
		// 拼接文件完整路径
		std::string completePath = filePath + fileName;
		// 拼接字符串
		str += completePath;
		//移至下一个文件
		p += lstrlen(p) + 1;
	}

	return str;
	// 将string转为char*
	//char* strc = new char[strlen(str.c_str()) + 1];
	//const char* cc = str.c_str();
	//strcpy_s(strc, str.size() + 1, cc);
}

//https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog?redirectedfrom=MSDN
std::string SelectDirIFD()
{
	std::string folderpath = "";
	// CoCreate the File Open Dialog object.
	IFileDialog* pfd = NULL;
	HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_PPV_ARGS(&pfd));
	if (SUCCEEDED(hr))
	{
		// Set the options on the dialog.
		DWORD dwFlags;

		// Before setting, always get the options first in order 
		// not to override existing options.
		hr = pfd->GetOptions(&dwFlags);
		if (SUCCEEDED(hr))
		{
			// In this case, get shell items only for file system items.
			hr = pfd->SetOptions(dwFlags | FOS_PICKFOLDERS);
			if (SUCCEEDED(hr))
			{
				// Show the dialog
				hr = pfd->Show(NULL);
				if (SUCCEEDED(hr))
				{
					// Obtain the result once the user clicks 
					// the 'Open' button.
					// The result is an IShellItem object.
					IShellItem* psiResult;
					hr = pfd->GetResult(&psiResult);
					if (SUCCEEDED(hr))
					{
						// We are just going to print out the 
						// name of the file for sample sake.
						PWSTR pszFilePath = NULL;
						hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH,&pszFilePath);
						if (SUCCEEDED(hr))
						{
							folderpath = LPWSTR2LPSTR(pszFilePath);
							CoTaskMemFree(pszFilePath);
						}
						psiResult->Release();
					}
				}
			}
		}
		pfd->Release();
	}
	return folderpath;
}