/*
 *  ADP (Another Data Processor) www.adp.la
 *  Copyright (C) 2010 Katsuhisa Ohfuji <katsuhisa@ohfuji.name>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 */

#ifndef ADP_BUILTIN_FILE_H
#define ADP_BUILTIN_FILE_H

struct ExecContext_Fread : public ExecContextBase {
	ExecContext_Fread(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextBase(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		try {
			if ( pred->size() != 2 ) return RERR_argment_number(excp, 2);
			const PString *fpath = get<PString>(0);
			if ( !fpath ) return false;

			basic_ifstream<char> inputFile( fpath->c_str(), ios_base::in | ios::binary);
			if ( !inputFile.is_open() ) return RERR_File_Open(excp);

			istreambuf_iterator<char> sin(inputFile);
			string	text(sin, istreambuf_iterator< char >());
			
			PString *po = pmm.newPString(pobjs,text);
			const PObject *out = get<PObject>(1);
			return out->unify(*po, this);	
		} catch (exception &) {
			//cout << ex.what();
			return RERR_File_Operation(excp);
		}
		return true;
	}
};

struct ExecContext_FreadN : public ExecContextBase {
	// read file
	basic_ifstream<char>      inputFile;
    istreambuf_iterator<char> sin;
    
	ExecContext_FreadN(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextBase(p_, f_, pred_, l) {}

	virtual void cleanup() { 
		ExecContextBase::cleanup(); 
		if ( inputFile.is_open() ) inputFile.close();
		sin = std::istreambuf_iterator<char>();
	}

	virtual bool first(PException &excp) { 
		if ( pred->size() < 2 ) return RERR_argment_number(excp, 2);
		const PString *fpath = get<PString>(0);
		if ( !fpath ) return false;
		inputFile.open(fpath->c_str(), ios_base::in);
		if ( !inputFile.is_open() ) return RERR_File_Open(excp);
		sin = istreambuf_iterator<char>(inputFile);
		return backtrack(excp);
	}
	virtual bool backtrack(PException &excp) {
		if ( sin == std::istreambuf_iterator<char>() ) return false;

		string	result;
		while (sin != std::istreambuf_iterator<char>() && *sin != '\n' && *sin != '\r' ) { 
			result += *sin;
			sin++;
		}
		while ( sin != std::istreambuf_iterator<char>() && (*sin == '\n' || *sin == '\r') ) {
			sin++;
		}

		PString *po = pmm.newPString(pobjs,result);
		const PVeriable *v = dynamic_cast<const PVeriable*>((*pred)[1]);
		if ( !v ) return false;
		(*gl)[v->idx] = po;
		return true;
	}

};

struct ExecContext_Fwrite_Base : public ExecContextBase {
	ios_base::openmode mode;
	bool endlflg;
	ExecContext_Fwrite_Base(ExecContext *p_,  void *f_, const PPredicate *pred_, VLocal *l, ios_base::openmode mode_, bool endlflg_) : ExecContextBase(p_, f_, pred_, l), mode(mode_), endlflg(endlflg_) {}

	bool filewrite(int data_idx, int file_idx, PException &excp) { 
		try {
			if ( pred->size() != 2 ) return RERR_argment_number(excp, 2);
			const PObject *data = get<PObject>(data_idx);
			const PString *fpath = get<PString>(file_idx);
			if ( !fpath || !data ) return false;

			basic_ofstream<char> outputFile( fpath->c_str(), mode);
			if ( !outputFile.is_open() ) return RERR_File_Open(excp);
			string	str;
			if ( data->cnv_string(str) ) {
				outputFile << str;
			}
			
			if ( endlflg ) 
				outputFile << endl;
			outputFile.close();
		} catch (exception &) {
			//cout << ex.what();
			return RERR_File_Operation(excp);
		}
		return true;
	}
};

struct ExecContext_WriteFile : public ExecContext_Fwrite_Base {
	ExecContext_WriteFile(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_Fwrite_Base(p_, f_, pred_, l, ios_base::out | ios_base::trunc | ios::binary, false) {}
	virtual bool first(PException &excp) { return filewrite( 0, 1, excp); }
};

struct ExecContext_Fwrite : public ExecContext_Fwrite_Base {
	ExecContext_Fwrite(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_Fwrite_Base(p_, f_, pred_, l, ios_base::out | ios_base::trunc | ios::binary, false) {}
	virtual bool first(PException &excp) { return filewrite( 1, 0, excp); }

};

struct ExecContext_FwriteN : public ExecContext_Fwrite_Base {
	ExecContext_FwriteN(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_Fwrite_Base(p_, f_, pred_, l, ios_base::out | ios_base::trunc , true) {}
	virtual bool first(PException &excp) { return filewrite( 1, 0, excp); }
};

struct ExecContext_AppendFile : public ExecContext_Fwrite_Base {
	ExecContext_AppendFile(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_Fwrite_Base(p_, f_, pred_, l, ios_base::app | ios::binary, false) {}
	virtual bool first(PException &excp) { return filewrite( 0, 1, excp); }
};


struct ExecContext_Fappend : public ExecContext_Fwrite_Base {
	ExecContext_Fappend(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_Fwrite_Base(p_, f_, pred_, l, ios_base::app | ios::binary, false) {}
	virtual bool first(PException &excp) { return filewrite( 1, 0, excp); }
};

struct ExecContext_AppendFileN : public ExecContext_Fwrite_Base {
	ExecContext_AppendFileN(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_Fwrite_Base(p_, f_, pred_, l, ios_base::app, true) {}
	virtual bool first(PException &excp) { return filewrite( 0, 1, excp); }
};

struct ExecContext_FappendN : public ExecContext_Fwrite_Base {
	ExecContext_FappendN(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_Fwrite_Base(p_, f_, pred_, l, ios_base::app, true) {}
	virtual bool first(PException &excp) { return filewrite( 1, 0, excp); }
};

struct ExecContext_Mkdir : public ExecContextRoot {
	ExecContext_Mkdir(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		try {
			if ( pred->size() != 1 ) return RERR_argment_number(excp, 1);
			const PString *fpath = get<PString>(0);
			if ( !fpath ) return RERR_argment_type(excp, 0);
			int	ret;
#ifdef _WIN32
			ret = _mkdir( fpath->c_str());
#else
			ret = mkdir( fpath->c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
#endif
			if ( ret == 0 ) {
				return true;
			} else {
				//cout << "errno = " << errno;
				return false;
			}
		} catch (exception &) {
			//cout << ex.what();
			return RERR_File_Operation(excp);
		}
		return true;
	}
};

struct ExecContext_CSV_Read : public ExecContext_FreadN {
	string              separator;
	vector< quote >     quotes;

	ExecContext_CSV_Read(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_FreadN(p_, f_, pred_, l)
	, quotes(), separator(",") {
		quotes.push_back( quote( '"', '"', '"') );
	}

	virtual bool backtrack(PException &excp) {
		vector< string >	rec;

		if ( !readCSV( rec, sin, separator, quotes) ) return false;

		size_t	vidx = 1;
		PArray *alloc_ary = 0;
		if ( !size_check_and_alloc_array(rec.size() + vidx, excp, &alloc_ary) ) return false;

		for (vector<string>::iterator i = rec.begin(); i != rec.end(); i++, vidx++ ) {
			const PString *p = pmm.newPString( pobjs, *i);
			if ( !unify<PString>( vidx, p, excp) ) return false;
		}
		return true;
	}
};

struct ExecContext_CSVH_Read : public ExecContext_FreadN {
	vector< string >	header;
	string              separator;
	vector< quote >     quotes;

	ExecContext_CSVH_Read(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_FreadN(p_, f_, pred_, l)
	, header(), quotes(), separator(",") {
		quotes.push_back( quote( '"', '"', '"') );
	}

	virtual bool first(PException &excp) { 
		if ( pred->size() < 2 ) return RERR_argment_number(excp, 2);
		const PString *fpath = get<PString>(0);
		if ( !fpath ) return false;
		inputFile.open(fpath->c_str(), ios_base::in);
		if ( !inputFile.is_open() ) return RERR_File_Open(excp);
		sin = istreambuf_iterator<char>(inputFile);

		if ( !readCSV( header, sin, separator, quotes) ) return false;
		
		return backtrack(excp);
	}

	virtual bool backtrack(PException &excp) {
		vector< string >	rec;

		if ( !readCSV( rec, sin, separator, quotes) ) return false;

		size_t	vidx = 1;
		PArray *alloc_ary = 0;
		if ( !size_check_and_alloc_array(rec.size() + vidx, excp, &alloc_ary) ) return false;

		size_t	idx = 0;
		for (vector<string>::iterator i = rec.begin(); i != rec.end(); i++, vidx++ ) {
			string *colname = 0;
			if ( idx < header.size() ) {
				colname = &header[idx++];
			}
			const PString *p = pmm.newPString( pobjs, *i);
			if ( !unify<PString>( vidx, p, excp, colname) ) return false;
		}
		return true;
	}
};


struct ExecContext_Apachelog_Read : public ExecContext_FreadN {
	ExecContext_Apachelog_Read(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContext_FreadN(p_, f_, pred_, l) {}
	virtual bool backtrack(PException &excp) {
		string               separator(" ");
		vector< quote >      quotes;
		quotes.push_back( quote( '"', '"', '\\') );
		quotes.push_back( quote( '[', ']', 0 ) );
		vector< string >	rec;

		if ( !readCSV( rec, sin, separator, quotes) ) return false;

		size_t	vidx = 1;
		PArray *alloc_ary = 0;
		if ( !size_check_and_alloc_array(rec.size() + vidx, excp, &alloc_ary) ) return false;

		for (vector<string>::iterator i = rec.begin(); i != rec.end(); i++, vidx++ ) {
			const PString *p = pmm.newPString( pobjs, *i);
			if ( !unify<PString>( vidx, p, excp) ) return false;
		}
		return true;
	}
};

struct ExecContext_Make_Temp_Path : public ExecContextRoot {
	ExecContext_Make_Temp_Path(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() != 2 ) return RERR_argment_number(excp, 2);
		const char *base = cnv_charp(0);
		if ( !base ) return RERR_argment_type(excp, 0);
		
		string temppath;
		if ( !make_temppath( string(base), temppath) ) return RERR_File_Operation(excp);

		PString	*po = pmm.newPString(pobjs,temppath);
		const PObject *out = get<PObject>(1);
		return out->unify(*po, this);	
	}
};

struct ExecContext_unlink : public ExecContextRoot {
	ExecContext_unlink(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() != 1 ) return RERR_argment_number(excp, 1);
		const char *path = cnv_charp(0);
		if ( !path ) return RERR_argment_type(excp, 0);
		
		if ( _unlink( path) ) return RERR_File_Operation(excp);

		return true;
	}
};

struct ExecContext_rmdir : public ExecContextRoot {
	ExecContext_rmdir(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() != 1 ) return RERR_argment_number(excp, 1);
		const char *path = cnv_charp(0);
		if ( !path ) return RERR_argment_type(excp, 0);
		
		if ( _rmdir( path) ) return RERR_File_Operation(excp);

		return true;
	}
};

struct ExecContext_remove : public ExecContextRoot {
	ExecContext_remove(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() != 1 ) return RERR_argment_number(excp, 1);
		const char *path = cnv_charp(0);
		if ( !path ) return RERR_argment_type(excp, 0);
		
		if ( remove( path) ) return RERR_File_Operation(excp);

		return true;
	}
};


struct ExecContext_Bsave : public ExecContextBase {
	ExecContext_Bsave(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextBase(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		try {
			if ( pred->size() != 2 ) return RERR_argment_number(excp, 2);
			const PObject *obj = get<PObject>(0);
			string cvobj;
			string header;

			if ( !toBinary( obj, cvobj, header) ) return RERR_argment_type(excp, 0);

			const PString *fpath = get<PString>(1);
			if ( !fpath ) return RERR_argment_type(excp, 1);

			basic_ofstream<char> outputFile( fpath->c_str(), ios_base::out | ios::binary);
			if ( !outputFile.is_open() ) return RERR_File_Open(excp);
			outputFile << header;
			outputFile << cvobj;
		} catch (exception &) {
			//cout << ex.what();
			return RERR_File_Operation(excp);
		}
		return true;
	}
};


struct ExecContext_Bload : public ExecContextBase {
	ExecContext_Bload(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextBase(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		try {
			if ( pred->size() != 2 ) return RERR_argment_number(excp, 2);
			const PString *fpath = get<PString>(0);
			if ( !fpath ) return false;

			basic_ifstream<char> inputFile( fpath->c_str(), ios_base::in | ios::binary);
			if ( !inputFile.is_open() ) return RERR_File_Open(excp);
			istreambuf_iterator<char> sin(inputFile);

			PObject *po = fromBinary< istreambuf_iterator<char> >( sin, istreambuf_iterator< char >(), pobjs);
			if ( po == 0 ) return RERR_File_Format(excp);

			const PObject *out = get<PObject>(1);
			return out->unify(*po, this);	
		} catch (exception &) {
			//cout << ex.what();
			return RERR_File_Operation(excp);
		}
		return true;
	}
};

struct ExecContext_Esave : public ExecContextBase {
	ExecContext_Esave(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextBase(p_, f_, pred_, l) {}
	void esave_kv( basic_ofstream<char> &outputFile, char mode, const string &key, const string &value ) {
		outputFile << mode;
		outputFile << key;
		outputFile << '\0';
		outputFile << value;
		outputFile << '\0';
	}

	virtual bool first(PException &excp) { 
		try {
			if ( pred->size() != 1 ) return RERR_argment_number(excp, 1);
			const PString *fpath = get<PString>(0);
			if ( !fpath ) return RERR_argment_type(excp, 0);
			basic_ofstream<char> outputFile( fpath->c_str(), ios_base::out | ios::binary);
			if ( !outputFile.is_open() ) return RERR_File_Open(excp);
			for ( ssmap::iterator i = envmap.begin(); i != envmap.end(); i++ ) {
				esave_kv( outputFile, 1, i->first, i->second);
			}
			for ( ssmap::iterator i = query.begin(); i != query.end(); i++ ) {
				esave_kv( outputFile, 2, i->first, i->second);
			}
			for ( ssmap::iterator i = cookies.begin(); i != cookies.end(); i++ ) {
				esave_kv( outputFile, 3, i->first, i->second);
			}
			return true;
		} catch (exception &) {
			return RERR_File_Operation(excp);
		}
		return true;
	}
};

struct ExecContext_Eload : public ExecContextBase {
	ExecContext_Eload(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextBase(p_, f_, pred_, l) {}
	void eload_kv( istreambuf_iterator<char> &i, istreambuf_iterator<char> e, char &m, string &key, string &value) {
		m = *i++;
		key.clear();
		value.clear();
		while ( i !=  e && *i != 0 ) {
			key.push_back(*i++);
		}
		if ( i != e ) i++;
		while ( i != e && *i != 0 ) {
			value.push_back(*i++);
		}
		if ( i != e ) i++;
	}

	virtual bool first(PException &excp) { 
		try {
			if ( pred->size() != 1 ) return RERR_argment_number(excp, 1);
			const PString *fpath = get<PString>(0);
			if ( !fpath ) return false;

			basic_ifstream<char> inputFile( fpath->c_str(), ios_base::in | ios::binary);
			if ( !inputFile.is_open() ) return RERR_File_Open(excp);
			istreambuf_iterator<char> sin(inputFile);

			char m;
			string key;
			string value;
			while ( sin != istreambuf_iterator< char >() ) {
				eload_kv( sin, istreambuf_iterator< char >(), m, key, value);
				switch ( m ) {
					case 1:
						envmap.insert( sspair(key, value) );
						break;
					case 2:
						query.insert( sspair(key, value) );
						break;
					case 3:
						cookies.insert( sspair(key, value) );
						break;
					default :
						return  RERR_File_Format(excp);
				}
			}
			sessionid.clear();
			sessionpath.clear();
			sessionwriteflg = false;
			ssmap::iterator i = cookies.find(string(ADP_SESSION_ID_STRING));
			if ( i != cookies.end() ) {
				sessionid = i->second;
				session = getSession(sessionid, sessionpath);
			} else {
				i = envmap.find(string(ADP_SESSION_ID_STRING));
				if ( i != envmap.end() ) {
					sessionid = i->second;
					session = getSession(sessionid, sessionpath);
				} else {
					session = 0;
				}
			}
			return true;
		} catch (exception &) {
			//cout << ex.what();
			return RERR_File_Operation(excp);
		}
		return true;
	}
};


#endif
