///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#define CPPCMS_SOURCE
#include <cppcms/http_file.h>
#include <cppcms/urandom.h>
#include <cppcms/cppcms_error.h>
#include <booster/nowide/cstdio.h>
#include <booster/nowide/fstream.h>
#include <stdlib.h>

#include "tohex.h"

namespace cppcms {
namespace http {


struct file::impl_data {};

std::string file::name() const
{
	return name_;
}

std::string file::mime() const
{
	return mime_;
}

std::string file::filename() const
{
	return filename_;
}
long long file::size() 
{
	if(saved_in_file_) {
		std::streampos now=file_.tellp();
		file_.seekp(0,std::ios_base::end);
		long long size = file_.tellp();
		file_.seekp(now);
		return size;
	}
	else {
		return file_data_.tellp();
	}
}

std::istream &file::data()
{
	if(saved_in_file_)
		return file_;
	else
		return file_data_;
}


std::ostream &file::write_data()
{
	if(saved_in_file_) {
		return file_;
	}
	else {
		if(size() > static_cast<long long>(size_limit_)) {
			move_to_file();
			return file_;
		}
		else {
			return file_data_;
		}
	}
}

void file::copy_stream(std::istream &in,std::ostream &out)
{
	out << in.rdbuf();
}

void file::save_to(std::string const &filename)
{
	if(!saved_in_file_) {
		file_data_.clear();
		file_data_.seekg(0);
		save_by_copy(filename,file_data_);
		return;
	}
	file_.clear();
	file_.seekg(0);
	file_.sync();
	#ifdef CPPCMS_WIN32
		file_.close();
		/// we can't move opened file on windows as it would be locked 
		if(booster::nowide::rename(tmp_file_name_.c_str(),filename.c_str())!=0) {
			file_.open(tmp_file_name_.c_str(),std::ios_base::binary | std::ios_base::in | std::ios_base::out);
			if(!file_) {
				throw cppcms_error("Failed to reopen file");
			}
			save_by_copy(filename,file_);
			file_.close();
			booster::nowide::remove(tmp_file_name_.c_str());
		}
	#else
		if(booster::nowide::rename(tmp_file_name_.c_str(),filename.c_str())!=0) {
			save_by_copy(filename,file_);
			booster::nowide::remove(tmp_file_name_.c_str());
		}
		file_.close();
	#endif
	removed_ = 1;
}

void file::save_by_copy(std::string const &file_name,std::istream &in)
{
	booster::nowide::ofstream f(file_name.c_str(),std::ios_base::binary | std::ios_base::out);
	if(!f) {
		throw cppcms_error("Failed to save open file:"+file_name);
	}
	copy_stream(in,f);
	f << std::flush;
	f.close();

}

void file::set_memory_limit(size_t size)
{
	size_limit_ = size;
}

void file::set_temporary_directory(std::string const &d)
{
	temporary_dir_ = d;
}

void file::move_to_file()
{
	std::string tmp_dir;
	if(temporary_dir_.empty()) {
		char const *tmp=getenv("TEMP");
		if(!tmp)
			tmp=getenv("TMP");
		if(!tmp)
			tmp="/tmp";
		tmp_dir=tmp;
	}
	else {
		tmp_dir = temporary_dir_;
	}

	tmp_file_name_ = tmp_dir + "/cppcms_uploads_";
	urandom_device rnd;
	char buf[16];
	char rand[33]={0};
	rnd.generate(buf,16);
	impl::tohex(buf,sizeof(buf),rand);
	tmp_file_name_.append(rand);
	tmp_file_name_+=".tmp";
	file_.open(tmp_file_name_.c_str(),
		std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::trunc);
	if(!file_)
		throw cppcms_error("Failed to create temporary file");
	file_data_.seekg(0);
	copy_stream(file_data_,file_);
	file_data_.str("");
	saved_in_file_ = 1;
}




file::file() :
	size_limit_(1024*128),
	saved_in_file_(0),
	removed_(0)
{
}

file::~file()
{
	if(saved_in_file_ && !removed_) {
		file_.close();
		if(!tmp_file_name_.empty()) {
			booster::nowide::remove(tmp_file_name_.c_str());
		}
	}
}

void file::filename(std::string const &v)
{
	filename_=v;
}

void file::name(std::string const &v)
{
	name_=v;
}

void file::mime(std::string const &v)
{
	mime_=v;
}


} // http
} // cppcms 
