/*! 
*   \file  UtilitiesWeb.cpp
*   \brief Declares class to hold utility methods.
*    
*   \details  
*   Utilities provides support for downloading images.
*
*   \date      November 3, 2011
*   \copyright eBay Research Labs.
*/

#include <string>
#include <sstream>
#include <strings.h>
#include <unordered_map>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
#include "boost/date_time/gregorian/gregorian.hpp"
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/local_time/local_time.hpp"
#include "boost/lexical_cast.hpp"
#include "boost/regex.hpp"

#include "UtilitiesWeb.h"

using namespace boost;
using namespace std;

namespace bg = boost::gregorian;
namespace bp = boost::posix_time;
namespace bl = boost::local_time;
namespace bfs = boost::filesystem; 

// str will be split based on the separator sep. Extracted tokens will be in vecTokens.
void Tokenize(string &str, const char* separator, vector < string > &vecTokens)
{
	char_separator <char> sep(separator);

    tokenizer< char_separator<char> > tokens(str, sep);
    vecTokens.clear();
    BOOST_FOREACH(string t, tokens)
    {
        vecTokens.push_back(t);
    }
}


/*!
*   \brief  Convert input time string in MST to UNIX epoch time.
*   \param [in]   strTime     Input time string of format: %Y%m%d%H%M%S.
*   \param [out]  epochTime   UNIX epoch time after conversion.
*   \param [in]   defaultTime Default UNIX epoch time, when input format is wrong. Default = 0.
*   \return       Flag to indicate whether input string had correct format or not. 
*/
bool ConvertMSTString2EpochTime(const std::string &strTime, time_t &epochTime, time_t defaultTime)
{
    bool status = true;
    try
    {
        if (strTime.length() != 14)
        {
            // Length of %Y%m%d%H%M%S is 14. Example: 20110102083010 for 01/02/2011 08:30:10am
            epochTime = defaultTime;
            return false;
        }
        else
        {
            // Parse input date string.
            int rest = lexical_cast<int>(strTime.substr(8));

            int hours;
            int minutes;
            int seconds;
            hours    = rest / 10000;
            rest    -= hours* 10000;
            minutes  = rest / 100;
            seconds  = rest - minutes * 100;

            bg::date            inputDate(bg::from_undelimited_string(strTime.substr(0, 8))); // Extract date.
            bp::time_duration   inputTime(hours, minutes, seconds); 
            bl::time_zone_ptr   mstZone(new bl::posix_time_zone("MST-07"));
            bl::local_date_time givenTime(inputDate, inputTime, mstZone, bl::local_date_time::NOT_DATE_TIME_ON_ERROR);

            static const bp::ptime epoch(bg::date(1970,1,1));
            bp::time_duration diff = givenTime.utc_time() - epoch;    

            epochTime = diff.total_seconds();
        }

    }

    catch(...)
    {
        epochTime = defaultTime;
        status    = false;
    }
    return status;
}


/*!
*   \brief  Converts input time (UNIX epoch time) to string in MST.
*   \param [in]  UNIX epoch time.
*   \return MST string.
*/
string  ConvertEpochTime2MSTString(time_t dateTime)
{
    string strDateTime("");
    if (dateTime>0)
    {
        dateTime -= 7*60*60; // Convert UTC/GMT to MST. MST is 7 hours before UTC/GMT.
        struct tm *ptm = gmtime(&dateTime);
        char strTime [16];
        strftime(strTime, sizeof(strTime), "%Y%m%d%H%M%S", ptm);
        strDateTime = strTime;
    }

    return strDateTime;
}


/*!
*   \brief Given a directory, this function will return the list of immediate sub-folders.
*
*   \param [in]  rootDir     Root directory.
*   \param [out] subdirList  Children of rootDir that are directories.
*/
void GetSubdirectories(bfs::path rootDir, FilePathRecList &subdirList)
{
    if( !bfs::exists(rootDir) || !bfs::is_directory(rootDir) )
    {
        // rootDir does not exist or is not a directory. 
        return;
    }

    // Look at children of rootDir.
    bfs::directory_iterator dir_iter(rootDir), dir_end;
    for( ; dir_iter != dir_end; ++dir_iter )
    {
        // Check if child is a directory.
        if( bfs::is_directory(*dir_iter) )
        {
            //cout << "********" << *dir_iter << ": " << dir_iter->path().filename() << "********" << endl;
            string    name(dir_iter->path().filename().string());
            subdirList.push_back(make_pair(name, *dir_iter));
        }
    }
} // End of function: GetSubdirectories


/*!
*   \brief Given a directory, this function will return the list of files.
*
*   \param [in]  rootDir     Root directory.
*   \param [out] fileList    Children of rootDir that are files.
*   \param [out] extension   Optional filter for file extension (case will be ignored). 
*                           Default filter returns only .jpg files.
*/
void GetFilesInDirectory(bfs::path rootDir, FilePathList &fileList, string extension)
{
    // Note: Also see bfs::recursive_directory_iterator.

    if( !bfs::exists(rootDir) || !bfs::is_directory(rootDir) )
    {
        // rootDir does not exist or is not a directory. 
        return;
    }

    // Use regex to apply pattern. Case-insensitive.
    boost::regex pattern(extension, boost::regex::icase);   

    // Look at children of rootDir.
    bfs::directory_iterator dir_iter(rootDir), dir_end;
    for( ; dir_iter != dir_end; ++dir_iter )
    {
        // Check if child is a file.
        if( bfs::is_regular_file(dir_iter->status()) )
        {
            //cout << "********" << *dir_iter << ": " << dir_iter->path().filename() << "********" << endl;
            // Apply regex filter to file name.
            boost::smatch matches;
			const std::string fname(dir_iter->path().filename().string());
			// skip hidden files
			if (fname.find_first_of(".") == 0)
				continue;
			if( boost::regex_search(fname, matches, pattern) )
			{
				// Add this file to the list.
				fileList.push_back(*dir_iter);
			}
		}
	}
}  // End of function: GetFilesInDirectory

bfs::path hiddenName(const bfs::path& src)
{
	bfs::path ret;
	ret = ret.remove_filename();
	ret /= ".";
	ret /= src.filename();
	return ret;
}

bfs::path unhiddenName(const bfs::path& src)
{
	bfs::path ret(src);
	ret = ret.remove_filename();
	std::string fname(src.filename().string());
	fname.erase(0,1);
	ret /= fname;
	return ret;
}

bfs::path touchName(const bfs::path& src, const std::string& instance)
{
	std::string as_string = src.string();
	if (as_string.find( "." + instance + ".touch")!= std::string::npos)
		return as_string;
	else
		return as_string + "." + instance + ".touch";
}


const char* MANTIS_SITEID_STR_UNSUPPORTED = "Unsupported Site";

#define MANTIS_SITEID_US 0
#define MANTIS_SITEID_GB 3
#define MANTIS_SITEID_AU 15
#define MANTIS_SITEID_DE 77
#define MANTIS_SITEID_UNSUPPORTED -1

#define MANTIS_SITEID_STR_US "EBAY-US"
#define MANTIS_SITEID_STR_GB "EBAY-GB"
#define MANTIS_SITEID_STR_AU "EBAY-AU"
#define MANTIS_SITEID_STR_DE "EBAY-DE"

#define MANTIS_SITEID_STR_GB_LC "ebay-gb"
#define MANTIS_SITEID_STR_US_LC "ebay-us"


#define MANTIS_SITEID_NUMSTR_US "0"
#define MANTIS_SITEID_NUMSTR_GB "3"
#define MANTIS_SITEID_NUMSTR_AU "15"
#define MANTIS_SITEID_NUMSTR_DE "77"

int getNumericSiteID(const std::string& str)
{
	
	if (strcasecmp(str.c_str(), MANTIS_SITEID_NUMSTR_US)==0 || strcasecmp(str.c_str(), MANTIS_SITEID_STR_US)==0)
			return MANTIS_SITEID_US;	
	if (strcasecmp(str.c_str(), MANTIS_SITEID_NUMSTR_GB)==0 || strcasecmp(str.c_str(), MANTIS_SITEID_STR_GB)==0)
			return MANTIS_SITEID_GB;
	if (strcasecmp(str.c_str(), MANTIS_SITEID_NUMSTR_AU)==0 || strcasecmp(str.c_str(), MANTIS_SITEID_STR_AU)==0)
			return MANTIS_SITEID_AU;
	if (strcasecmp(str.c_str(), MANTIS_SITEID_NUMSTR_DE)==0 || strcasecmp(str.c_str(), MANTIS_SITEID_STR_DE)==0)
			return MANTIS_SITEID_DE;

	return MANTIS_SITEID_UNSUPPORTED;
}

std::string getStringSiteID(int id)
{
	const char* ret = 0;
	switch (id)
	{
	case MANTIS_SITEID_US:
		ret = MANTIS_SITEID_STR_US;
		break;
	case MANTIS_SITEID_GB:
		ret = MANTIS_SITEID_STR_GB;
		break;
	case MANTIS_SITEID_AU:
		ret = MANTIS_SITEID_STR_AU;
		break;
	case MANTIS_SITEID_DE:
		ret = MANTIS_SITEID_STR_DE;
		break;
	default:
		ret = MANTIS_SITEID_STR_UNSUPPORTED;
		break;
	}
	return ret;
}
