///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
//#define DEBUG_MULTIPART_PARSER
#include "multipart_parser.h"
#include <iostream>
#include "test.h"
#include <string.h>
#include <stdlib.h>

char const test_1_content[] = "multipart/form-data; boundary=xxyy";
char const test_1_file[] = 
"--xxyy\r\n"
"Content-Disposition: form-data; name=\"test1\"; filename=\"foo.txt\"\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"hello\r\n"
"\r\n"
"--xxyy\r\n"
"Content-Disposition: form-data; name=\"test2\"\r\n"
"\r\n"
"שלום\r\n"
"--xxyy\r\n"
"Content-Disposition: form-data; name=\"test3\"\r\n"
"\r\n"
"x\r"
"\r\n--xxyy\r\n"
"Content-Disposition: form-data; name=\"test4\"\r\n"
"\r\n"
"x\r\n-"
"\r\n--xxyy\r\n"
"Content-Disposition: form-data; name=\"test5\"\r\n"
"\r\n"
"x\r\n--"
"\r\n--xxyy\r\n"
"Content-Disposition: form-data; name=\"test6\"\r\n"
"\r\n"
"x\r\n--x"
"\r\n--xxyy\r\n"
"Content-Disposition: form-data; name=\"test7\"\r\n"
"\r\n"
"x\r\n--x-"
"\r\n--xxyy--\r\n";

char const test_2_content[] = "multipart/form-data; boundary=-AbC";
char const test_2_file[] = 
"---AbC\r\n"
"Content-Disposition: form-data; name=\"test1\"  \r\n"
"\r\n"
"hello"
"\r\n"
"---AbC--\r\n";


std::string getcontent(std::istream &in)
{
	std::string res;
	while(!in.eof()) {
		char c;
		in.get(c);
		if(in.gcount() == 1)
		res+=c;
	}
	return res;
}

struct random_consumer {
	random_consumer(int bs,cppcms::impl::multipart_parser &p,cppcms::impl::multipart_parser::files_type &f) :
		block_size(bs),
		parser(&p),
		files(&f)
	{
	}
	int block_size;
	cppcms::impl::multipart_parser *parser;
	cppcms::impl::multipart_parser::files_type *files;
	cppcms::impl::multipart_parser::parsing_result_type operator()(char const *buffer,int size)
	{
		while(size > 0) {
			int block = 1;
			if(block_size > 1)
				block = (rand() % (block_size-1) + 1);
			else if(block_size < 0) 
				block=size;

			if(block > size)
				block=size;
			cppcms::impl::multipart_parser::parsing_result_type res = parser->consume(buffer,block);
			buffer+=block;
			size-=block;
			if(res==cppcms::impl::multipart_parser::eof) {
				cppcms::impl::multipart_parser::files_type fblock = parser->get_files();
				for(unsigned i=0;i<fblock.size();i++) {
					files->push_back(fblock[i]);
				}
				return res;
			}
			if(res==cppcms::impl::multipart_parser::parsing_error)
				return res;
			if(res==cppcms::impl::multipart_parser::got_something) {
				cppcms::impl::multipart_parser::files_type fblock = parser->get_files();
				TEST(!fblock.empty());
				for(unsigned i=0;i<fblock.size();i++) {
					files->push_back(fblock[i]);
				}
			}
		}
		TEST(!"Never get there");
		return cppcms::impl::multipart_parser::eof;
	}

};

int main(int argc,char **argv)
{
	if(argc!=2) {
		std::cerr << "Required path to tests folder" << std::endl;
		return 1;
	}
	try {
		int block_size[]={ 1, -1, 5, 3, 10 };
		int max_mem_size[] = { 0, 3, 10000 };
		for(unsigned i=0;i<sizeof(block_size)/sizeof(block_size[0]);i++) {
			for(unsigned j=0;j<sizeof(max_mem_size)/sizeof(max_mem_size[0]);j++) {
				std::cout << "Testing for max-block-size = " << block_size[i] << " mem file size = " << max_mem_size[j] << std::endl;
				{
					cppcms::impl::multipart_parser parser("",max_mem_size[j]);
					TEST(parser.set_content_type(std::string(test_1_content)));
					cppcms::impl::multipart_parser::files_type files;
					random_consumer c(block_size[i],parser,files);
					TEST(c(test_1_file,strlen(test_1_file))==cppcms::impl::multipart_parser::eof);
					TEST(files.size()==7);
					TEST(files[0]->name()=="test1");
					TEST(files[0]->filename()=="foo.txt");
					TEST(files[0]->mime()=="text/plain");
					std::string content = getcontent(files[0]->data());
					TEST(content=="hello\r\n");
					TEST(files[1]->name()=="test2");
					TEST(files[1]->filename()=="");
					TEST(files[1]->mime()=="");
					TEST(getcontent(files[1]->data())=="שלום");
					TEST(getcontent(files[2]->data())=="x\r");
					TEST(getcontent(files[3]->data())=="x\r\n-");
					TEST(getcontent(files[4]->data())=="x\r\n--");
					TEST(getcontent(files[5]->data())=="x\r\n--x");
					TEST(getcontent(files[6]->data())=="x\r\n--x-");
				}
				{
					cppcms::impl::multipart_parser parser("",max_mem_size[j]);
					TEST(parser.set_content_type(std::string(test_2_content)));
					cppcms::impl::multipart_parser::files_type files;
					random_consumer c(block_size[i],parser,files);
					TEST(c(test_2_file,strlen(test_2_file))==cppcms::impl::multipart_parser::eof);
					TEST(files.size()==1);
					TEST(files[0]->name()=="test1");
					TEST(files[0]->filename()=="");
					TEST(files[0]->mime()=="");
					std::string content = getcontent(files[0]->data());
					TEST(content=="hello");
				}
				std::string boundaries[4]= { 
					"multipart/form-data; boundary=---------------------------11395741071221114234100471568",
					"multipart/form-data; boundary=----WebKitFormBoundaryuYwgZwieYHQ3+AR8",
					"multipart/form-data; boundary=----------jrFLC4hxayof1KyJEgmmCw",
					"multipart/form-data; boundary=---------------------------7da3a5810028"
				};
				std::string filenames[4] = {
					"firefox",
					"chrome",
					"opera",
					"ie" 
				};
				std::string location[2] = {
					".",
					""
				};
				for(int loc=0;loc<2;loc++) {
					std::cout << "- location [" << location[loc] <<']'<< std::endl;
					for(int i=0;i<4;i++) {
						std::cout << "-- Browser " << filenames[i] << std::endl;
						std::string file_name = std::string(argv[1])+"/multipart/" + filenames[i]+".txt";
						std::ifstream file(file_name.c_str(),std::ifstream::binary);
						if(!file) {
							std::cerr << "Failed to open file " << file_name << std::endl;
							return 1;
						}
						std::string content = getcontent(file);
						cppcms::impl::multipart_parser parser(location[loc],max_mem_size[j]);
						TEST(parser.set_content_type(boundaries[i]));
						cppcms::impl::multipart_parser::files_type files;
						random_consumer c(block_size[i],parser,files);
						TEST(c(content.c_str(),content.size())==cppcms::impl::multipart_parser::eof);
						TEST(files.size()==3);
						TEST(files[0]->name()=="submit-name");
						TEST(files[0]->mime()=="" && files[0]->filename().empty());
						TEST(getcontent(files[0]->data())=="שלום");
						TEST(files[1]->name()=="file");
						TEST(files[1]->mime()=="text/plain");
						if(i==3) // IE
							TEST(files[1]->filename()=="z:\\tmp\\שלום.txt" || files[1]->filename()=="z:tmpשלום.txt");
						else
							TEST(files[1]->filename()=="שלום.txt");
						
						TEST(getcontent(files[1]->data())=="שלום עולם!\n");
						files[1]->save_to("test.txt");
						{
							std::ifstream tmp("test.txt");
							TEST(tmp);
							TEST(getcontent(tmp)=="שלום עולם!\n");
							tmp.close();
							std::remove("test.txt");
						}

						TEST(files[2]->name()=="submit");
						TEST(files[2]->mime().empty());
						TEST(files[2]->filename().empty());
						TEST(getcontent(files[2]->data())=="Send");
					}
				}
			}
		}
		
	}
	catch(std::exception const &e) {
		std::cerr << "Fail: " <<e.what() << std::endl;
		return 1;
	}
	std::cout << "Ok" << std::endl;
	return 0;
}
